Linux SPI子系统(2):SPI核心层

这篇具有很好参考价值的文章主要介绍了Linux SPI子系统(2):SPI核心层。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

SPI核心层 

1. SPI子系统初始化

2. 重要的数据结构

2.1 struct spi_controller

2.2 struct spi_driver

2.3 struct spi_device

2.4 struct spi_transfer和struct spi_message

3. 重要的API

3.1 spi_register_controller

3.2 数据准备函数:spi_message_init和spi_message_add_tail

3.3 数据传输函数:spi_sync和spi_async

4. 参考文章


SPI核心层 

上次简单介绍了下Linux SPI子系统的系统结构,主要有3部分组成,分别是SPI核心、SPI总线驱动(控制器驱动)以及SPI设备驱动。

SPI核心层代码位于drivers/spi/spi.c,头文件位于include/linux/spi/spi.h,SPI核心提供了SPI总线驱动和设备驱动的注册、注销方法,并提供一些需要控制器驱动实现的回调函数。

本文主要介绍SPI核心层,包括重要的数据结构和API。

1. SPI子系统初始化

/* drivers/spi/spi.c */

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

/* 2 */
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);

	/* Attempt an OF style match */
	if (of_driver_match_device(dev, drv))
		return 1;

	/* Then try ACPI */
	if (acpi_driver_match_device(dev, drv))
		return 1;

	if (sdrv->id_table)
		return !!spi_match_id(sdrv->id_table, spi);

	return strcmp(spi->modalias, drv->name) == 0;
}

/* 3 */
static struct class spi_master_class = {
	.name		= "spi_master",
	.owner		= THIS_MODULE,
	.dev_release	= spi_controller_release,
	.dev_groups	= spi_master_groups,
};

 1. struct bus_type是总线的类型,spi_bus_type即类型为SPI的总线,其中的match成员函数spi_match_device的作用是将SPI设备和设备驱动进行匹配。

2. 从四个方面进行匹配,分别从驱动中的of_match_table的compatible和acpi_match_table的compatible与dts中的compatible进行比较,看是否一致、如支持id数组,则查找与id数组中匹配的SPI设备、比较驱动和设备的名字是否一致。

3. struct class是设备的类,spi_master_class即SPI控制器设备的类。

/* 4 */
static int __init spi_init(void)
{
	int	status;

	buf = kmalloc(SPI_BUFSIZ, GFP_KERNEL);
	if (!buf) {
		status = -ENOMEM;
		goto err0;
	}

	status = bus_register(&spi_bus_type);
	if (status < 0)
		goto err1;

	status = class_register(&spi_master_class);
	if (status < 0)
		goto err2;

	if (IS_ENABLED(CONFIG_SPI_SLAVE)) {
		status = class_register(&spi_slave_class);
		if (status < 0)
			goto err3;
	}

	if (IS_ENABLED(CONFIG_OF_DYNAMIC))
		WARN_ON(of_reconfig_notifier_register(&spi_of_notifier));
	if (IS_ENABLED(CONFIG_ACPI))
		WARN_ON(acpi_reconfig_notifier_register(&spi_acpi_notifier));

	return 0;

err3:
	class_unregister(&spi_master_class);
err2:
	bus_unregister(&spi_bus_type);
err1:
	kfree(buf);
	buf = NULL;
err0:
	return status;
}

4. spi_init是SPI子系统的初始化函数,起作用的注册了spi bus和spi master class。

2. 重要的数据结构

2.1 struct spi_controller

#define spi_master			spi_controller

struct spi_controller {
    /* SPI设备的设备结构体 */
	struct device	dev;
    /* 链表头 */
	struct list_head list;
	/* SPI总线序号 */
	s16			bus_num;
	/* 片选信号的数量 */
	u16			num_chipselect;
	/* SPI控制器DMA缓冲区对齐定义 */
	u16			dma_alignment;
	/* 工作模式位 */
	u16			mode_bits;
	/* 传输支持的 bits_per_word 的位掩码 */
	u32			bits_per_word_mask;
#define SPI_BPW_MASK(bits) BIT((bits) - 1)
#define SPI_BIT_MASK(bits) (((bits) == 32) ? ~0U : (BIT(bits) - 1))
#define SPI_BPW_RANGE_MASK(min, max) (SPI_BIT_MASK(max) - SPI_BIT_MASK(min - 1))
	/* 传输速度限制 */
	u32			min_speed_hz;
	u32			max_speed_hz;

	/* 与此驱动程序相关的其他限制 */
	u16			flags;
#define SPI_CONTROLLER_HALF_DUPLEX	BIT(0)	/* can't do full duplex */
#define SPI_CONTROLLER_NO_RX		BIT(1)	/* can't do buffer read */
#define SPI_CONTROLLER_NO_TX		BIT(2)	/* can't do buffer write */
#define SPI_CONTROLLER_MUST_RX		BIT(3)	/* requires rx */
#define SPI_CONTROLLER_MUST_TX		BIT(4)	/* requires tx */

#define SPI_MASTER_GPIO_SS		BIT(5)	/* GPIO CS must select slave */

	/* 指示这是非devres托管控制器的标志 */
	bool			devm_allocated;

	/* 指示这是一个 SPI 从控制器的标志 */
	bool			slave;

	/* 在某些硬件传输/消息大小可能受到限制,该限制取决于设备传输设置 */
	size_t (*max_transfer_size)(struct spi_device *spi);
	size_t (*max_message_size)(struct spi_device *spi);

	/* I/O互斥锁 */
	struct mutex		io_mutex;

	/* 用于SPI总线锁定的lock和mutex */
	spinlock_t		bus_lock_spinlock;
	struct mutex		bus_lock_mutex;

	/* 指示 SPI 总线被锁定为独占使用的标志 */
	bool			bus_lock_flag;

	/* 设置模式和时钟等(spi驱动可能调用多次) */
	int			(*setup)(struct spi_device *spi);

	/* 双向批量传输 */
	int			(*transfer)(struct spi_device *spi,
						struct spi_message *mesg);

	/* 调用 release()以释放spi_controller提供的内存 */
	void			(*cleanup)(struct spi_device *spi);

	/* 用于启用对DMA处理的核心支持,如果 can_dma()存在并返回true,则传输将在调用 
       transfer_one()之前进行映射 */
	bool			(*can_dma)(struct spi_controller *ctlr,
					   struct spi_device *spi,
					   struct spi_transfer *xfer);

	/* 下面的成员适用于想要使用通用控制器传输队列机制的驱动程序 */
	bool				queued;
	struct kthread_worker		kworker;
	struct task_struct		*kworker_task;
	struct kthread_work		pump_messages;
	spinlock_t			queue_lock;
	struct list_head		queue;
	struct spi_message		*cur_msg;
	bool				idling;
	bool				busy;
	bool				running;
	bool				rt;
	bool				auto_runtime_pm;
	bool                            cur_msg_prepared;
	bool				cur_msg_mapped;
	struct completion               xfer_completion;
	size_t				max_dma_len;

	int (*prepare_transfer_hardware)(struct spi_controller *ctlr);
	int (*transfer_one_message)(struct spi_controller *ctlr,
				    struct spi_message *mesg);
	int (*unprepare_transfer_hardware)(struct spi_controller *ctlr);
	int (*prepare_message)(struct spi_controller *ctlr,
			       struct spi_message *message);
	int (*unprepare_message)(struct spi_controller *ctlr,
				 struct spi_message *message);
	int (*slave_abort)(struct spi_controller *ctlr);
	int (*spi_flash_read)(struct  spi_device *spi,
			      struct spi_flash_read_message *msg);
	bool (*spi_flash_can_dma)(struct spi_device *spi,
				  struct spi_flash_read_message *msg);
	bool (*flash_read_supported)(struct spi_device *spi);

	/* 下面的成员适用于使用核心提供的transfer_one_message()的通用实现的驱动程序 */
	void (*set_cs)(struct spi_device *spi, bool enable);
	int (*transfer_one)(struct spi_controller *ctlr, struct spi_device *spi,
			    struct spi_transfer *transfer);
	void (*handle_err)(struct spi_controller *ctlr,
			   struct spi_message *message);

	/* 为类似SPI内存的操作优化了处理程序 */
	const struct spi_controller_mem_ops *mem_ops;
	const struct spi_controller_mem_caps *mem_caps;

	/* gpio片选 */
	int			*cs_gpios;

	/* 统计数据 */
	struct spi_statistics	statistics;

	/* DMA通道 */
	struct dma_chan		*dma_tx;
	struct dma_chan		*dma_rx;

	/* 全双工设备的虚拟数据 */
	void			*dummy_rx;
	void			*dummy_tx;

	int (*fw_translate_cs)(struct spi_controller *ctlr, unsigned int cs);
};

 struct spi_controller是SPI控制器驱动的核心数据结构,spi_controller代表一个SPI控制器,在编写控制器驱动时需要对其中的一些参数和回调函数进行填充或实现。

2.2 struct spi_driver

struct spi_driver {
    /* 支持的SPI device的设备表 */
	const struct spi_device_id *id_table;
	int			(*probe)(struct spi_device *spi);
	int			(*remove)(struct spi_device *spi);
	void			(*shutdown)(struct spi_device *spi);
	struct device_driver	driver;
};

  spi_driver代表一个SPI设备驱动,实现id_table或者of_match_table等,使得匹配成功即可;匹配成功后调用probe函数,驱动卸载时调用remove函数。

2.3 struct spi_device

struct spi_device {
	struct device		dev;
	struct spi_controller	*controller;
	struct spi_controller	*master;	/* compatibility layer */
	u32			max_speed_hz;
	u8			chip_select;
	u8			bits_per_word;
	u16			mode;
#define	SPI_CPHA	0x01			/* clock phase */
#define	SPI_CPOL	0x02			/* clock polarity */
#define	SPI_MODE_0	(0|0)			/* (original MicroWire) */
#define	SPI_MODE_1	(0|SPI_CPHA)
#define	SPI_MODE_2	(SPI_CPOL|0)
#define	SPI_MODE_3	(SPI_CPOL|SPI_CPHA)
#define	SPI_CS_HIGH	0x04			/* chipselect active high? */
#define	SPI_LSB_FIRST	0x08			/* per-word bits-on-wire */
#define	SPI_3WIRE	0x10			/* SI/SO signals shared */
#define	SPI_LOOP	0x20			/* loopback mode */
#define	SPI_NO_CS	0x40			/* 1 dev/bus, no chipselect */
#define	SPI_READY	0x80			/* slave pulls low to pause */
#define	SPI_TX_DUAL	0x100			/* transmit with 2 wires */
#define	SPI_TX_QUAD	0x200			/* transmit with 4 wires */
#define	SPI_RX_DUAL	0x400			/* receive with 2 wires */
#define	SPI_RX_QUAD	0x800			/* receive with 4 wires */
#define	SPI_CS_WORD	0x1000			/* toggle cs after each word */
#define	SPI_TX_OCTAL	0x2000			/* transmit with 8 wires */
#define	SPI_RX_OCTAL	0x4000			/* receive with 8 wires */
#define	SPI_3WIRE_HIZ	0x8000			/* high impedance turnaround */
	int			irq;
	void			*controller_state;
	void			*controller_data;
	char			modalias[SPI_NAME_SIZE];
	int			cs_gpio;	/* chip select gpio */

	/* the statistics */
	struct spi_statistics	statistics;

	/*
	 * likely need more hooks for more protocol options affecting how
	 * the controller talks to each chip, like:
	 *  - memory packing (12 bit samples into low bits, others zeroed)
	 *  - priority
	 *  - drop chipselect after each word
	 *  - chipselect delays
	 *  - ...
	 */
};

spi_device就不多介绍了,现在一般都是用dts来描述,由系统自动生成spi_device。

2.4 struct spi_transfer和struct spi_message

struct spi_transfer {
	const void	*tx_buf;
	void		*rx_buf;
	unsigned int len;

	dma_addr_t	tx_dma;
	dma_addr_t	rx_dma;
	struct sg_table tx_sg;
	struct sg_table rx_sg;

	unsigned	dummy_data:1;
	unsigned	cs_change:1;
	unsigned	tx_nbits:3;
	unsigned	rx_nbits:3;
#define	SPI_NBITS_SINGLE	0x01 /* 1bit transfer */
#define	SPI_NBITS_DUAL		0x02 /* 2bits transfer */
#define	SPI_NBITS_QUAD		0x04 /* 4bits transfer */
	u8		bits_per_word;
	u16		delay_usecs;
	u32		speed_hz;

    /*用于链接到spi_message,用来连接的双向链接节点*/
	struct list_head transfer_list;
};
struct spi_message {
    /*spi_transfer链表队列 */
	struct list_head	transfers;
    /*传输的目的设备*/
	struct spi_device	*spi;
    /*如果为真,此次调用提供dma和cpu虚拟地址 */
	unsigned		is_dma_mapped:1;

	/* 通过回调告知完成 */
	void			(*complete)(void *context);
	void			*context;
	unsigned int	frame_length;
	unsigned int	actual_length;
	int			status;

	struct list_head	queue;
	void			*state;

	/* list of spi_res reources when the spi message is processed */
	struct list_head        resources;
};

spi_message用于执行数据传输的原子序列,每个数据传输由spi_transfer表示。该序列是“原子的”,因为在该序列完成之前没有其他spi_message可以使用该SPI总线。在某些系统上,许多这样的序列可以作为单个编程的DMA传输执行。在所有系统上,这些消息都是排队的,并且可能在与其他设备进行事务处理后完成。发送到给定spi_device的消息始终按FIFO顺序执行。 

3. 重要的API

3.1 spi_register_controller

#define spi_register_master(_ctlr)	spi_register_controller(_ctlr)

int spi_register_controller(struct spi_controller *ctlr)

作用是将spi控制器驱动注册到内核。

3.2 数据准备函数:spi_message_init和spi_message_add_tail

/* include/linux/spi/spi.h */
static inline void spi_message_init_no_memset(struct spi_message *m)
{
	INIT_LIST_HEAD(&m->transfers);
	INIT_LIST_HEAD(&m->resources);
}

static inline void spi_message_init(struct spi_message *m)
{
	memset(m, 0, sizeof(*m));
	spi_message_init_no_memset(m);
}

static inline void
spi_message_add_tail(struct spi_transfer *t, struct spi_message *m)
{
	list_add_tail(&t->transfer_list, &m->transfers);
}

spi_message_init函数作用是初始化spi_message,将message清零,初始化transfers链表头。

spi_message_add_tail函数作用是将spi_transfer尾部加入到spi_message链表中。

3.3 数据传输函数:spi_sync和spi_async

SPI数据传输分为同步方式和异步方式,同步方式即发起数据传输后,必须等待传输完成后才能返回,期间不能做其他事情;异步方式即发起数据传输后,不用管传输是否完成,直接返回,期间可以做其他事情。

/* 调用关系 */
spi_sync(struct spi_device *spi, struct spi_message *message)
    |-> __spi_sync(struct spi_device *spi, struct spi_message *message)

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

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

		wait_for_completion(&done);
		status = message->status;
	}
	message->context = NULL;
	return status;
}

 spi_sync:首先对spi_message中的complete回调函数进行初始化;之后调用__spi_queued_transfer函数,这里第三个参数传的是false,所以只将message尾部加入到ctlr链表中后返回;随后调用__spi_pump_messages,将spi_message发出去;最后调用wait_for_completion函数,等待完成的信号量,当接收到信号量时说明传输完成并返回。

/* 调用关系 */
spi_async(struct spi_device *spi, struct spi_message *message)
    |-> __spi_async(struct spi_device *spi, struct spi_message *message)
        |-> ctlr->transfer(spi, message)

static int __spi_queued_transfer(struct spi_device *spi,
				 struct spi_message *msg,
				 bool need_pump)
{
	struct spi_controller *ctlr = spi->controller;
	unsigned long flags;

	spin_lock_irqsave(&ctlr->queue_lock, flags);

	if (!ctlr->running) {
		spin_unlock_irqrestore(&ctlr->queue_lock, flags);
		return -ESHUTDOWN;
	}
	msg->actual_length = 0;
	msg->status = -EINPROGRESS;

	list_add_tail(&msg->queue, &ctlr->queue);
	if (!ctlr->busy && need_pump)
		kthread_queue_work(&ctlr->kworker, &ctlr->pump_messages);

	spin_unlock_irqrestore(&ctlr->queue_lock, flags);
	return 0;
}

spi_async:如果控制器驱动没有实现transfer函数,内核会自动将transfer函数初始化为spi_queued_transfer函数,此函数中,将message尾部加入到ctlr链表中,随后唤醒工作线程,把具体的工作交付给worker后返回,不等待传输完成。

4. 参考文章

http://t.csdn.cn/Ppqna文章来源地址https://www.toymoban.com/news/detail-427235.html

到了这里,关于Linux SPI子系统(2):SPI核心层的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包赞助服务器费用

相关文章

  • 【嵌入式Linux内核驱动】SPI子系统 | 硬件原理 | 应用编程 | 内核驱动 | 总体框架

    【嵌入式Linux内核驱动】SPI子系统 | 硬件原理 | 应用编程 | 内核驱动 | 总体框架

    1.1 SPI通信协议 SPI(Serial Peripheral Interface)是由Motorola公司开发的一种通用数据总线 四根通信线:SCK(Serial Clock)、MOSI(Master Output Slave Input)、MISO(Master Input Slave Output)、SS(Slave Select) 同步,全双工 支持总线挂载多设备(一主多从) 1.2 硬件连接 多NSS独立片选方式 菊花

    2024年02月16日
    浏览(12)
  • spi 子系统

    spi 子系统

    spi 分为主机模式和从机模式,一般soc 自带的spi 控制器,我们都将它用作主机模式与外挂的从设备通信。从设备例如 oled芯片、flash芯片、陀螺仪芯片等等。 那么spi 驱动和设备,自然也就分为主机驱动、设备和从机驱动、设备。那么如何在Linux 下查看这些信息呢? 首先查看s

    2024年02月10日
    浏览(7)
  • 网络综合布线核心——七大子系统

    网络综合布线核心——七大子系统

    试问怎么去设计网络综合布线,那么必然离不开七大子系统,它们分别是: 配线(水平)子系统、干线(垂直)子系统、建筑群子系统、工作区子系统、管理间子系统、设备间子系统、电气防护系统。 下面就详细的介绍一下这七大子系统的功能: 一、工作区子系统:工作区

    2024年02月04日
    浏览(9)
  • HCIA-HarmonyOS设备开发认证V2.0-IOT硬件子系统-SPI

    HCIA-HarmonyOS设备开发认证V2.0-IOT硬件子系统-SPI

    SPI 是串行外设接口(Serial Peripheral Interface)是一种高速的全双工同步的通信总线。 SPI 是由 Motorola 公司开发,用于在主设备和从设备之间进行通信,常用于与闪存、实时时钟、传感器以及模数转换器等进行通信。 SPI 通信通常由主设备发起,通过以下步骤完成一次通信: 通过

    2024年02月22日
    浏览(13)
  • 第2讲 KMD ISP子系统缩略词及目录结构

    第2讲 KMD ISP子系统缩略词及目录结构

    CPAS(Camera Peripherals and Support) CDM(Camera Data Mover) TFE(Thin Front End) IFE(Image Front End) OPE(Offline Processing Engine) BPS(Bayer Processing Segment) SFE(Sensor Front End) LRME(Low Resolution Motion Estimation) CSID(Camera Serial Interface Decoder) UMD(User Mode Driver) KMD(Kernel Mode Driver) AB(Arbitrated Bandwidth) IB(Instantaneous Bandwidth)

    2024年02月16日
    浏览(6)
  • 第五章 I/O管理 六、I/O核心子系统

    第五章 I/O管理 六、I/O核心子系统

    目录 一、核心子系统 1、I/O调度 2、设备保护 二、假脱机技术 1、脱机: 2、假脱机(SPOOLing技术): 3、应用: 1.独占式设备: 2.共享设备: 4、共享打印机原理分析 三、总结 设备独立性软件 设备驱动程序 中断处理程序 1、I/O调度 用某种算法确定一个好的顺序来处理各个I

    2024年02月07日
    浏览(11)
  • 【Windows 11】安装 Android子系统 和 Linux子系统

    【Windows 11】安装 Android子系统 和 Linux子系统

    本文使用电脑系统: 主要就是安装一个名为: 适用于Android的Windows子系统 (WSA)的软件。 首先在电脑的设置里面:时间和语言——语言和地区里面把地区改为美国。 然后到微软商店搜索: Amazon AppStore 。 安装亚马逊应用商店的时候,会首先提示你安装前面说的WSA。如此,我

    2024年02月09日
    浏览(14)
  • Window10安装linux子系统及子系统安装1Panel面板

    Window10安装linux子系统及子系统安装1Panel面板

    原文地址:Window10安装linux子系统及子系统安装1Panel面板 - Stars-One的杂货小窝 最近看到halo博客发布了2.10.0,终于是新增了个备份功能,于是有了念头想要升级下 但是目前我还是使用halo1.5版本,所以跨版本迁移可能会有问题,官方提议还是先用个测试环境进行测试验证是否有问题 但

    2024年02月08日
    浏览(11)
  • Linux reset子系统

    文章代码分析基于linux-5.19.13,架构基于aarch64(ARM64)。 复杂IC内部有很多具有独立功能的硬件模块,例如CPU cores、GPU cores、USB控制器、MMC控制器、等等,出于功耗、稳定性等方面的考虑,有些IC在内部为这些硬件模块设计了复位信号(reset signals),软件可通过寄存器(一般

    2024年02月16日
    浏览(7)
  • Linux Input子系统

    Linux Input子系统

    按键、鼠标、键盘、触摸屏等都属于输入(input)设备,Linux 内核为此专门做了一个叫做  input 子系统 的 框架 来处理输入事件。本质属于字符设备。 1. input子系统结构如下:  input 子系统分为 input 驱动层、input 核心层、input 事件处理层,最终给用户空间提供可访问的设备节点

    2024年02月10日
    浏览(7)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包