当前位置: 首页 > 工具软件 > spi > 使用案例 >

SPI设备树处理过程

魏君博
2023-12-01

一、spi_device结构体

/**
 * struct spi_device - Controller side proxy for an SPI slave device
 * @dev: Driver model representation of the device.
 * @controller: SPI controller used with the device.
 * @master: Copy of controller, for backwards compatibility.
 * @max_speed_hz: Maximum clock rate to be used with this chip
 *	(on this board); may be changed by the device's driver.
 *	The spi_transfer.speed_hz can override this for each transfer.
 * @chip_select: Chipselect, distinguishing chips handled by @controller.
 * @mode: The spi mode defines how data is clocked out and in.
 *	This may be changed by the device's driver.
 *	The "active low" default for chipselect mode can be overridden
 *	(by specifying SPI_CS_HIGH) as can the "MSB first" default for
 *	each word in a transfer (by specifying SPI_LSB_FIRST).
 * @bits_per_word: Data transfers involve one or more words; word sizes
 *	like eight or 12 bits are common.  In-memory wordsizes are
 *	powers of two bytes (e.g. 20 bit samples use 32 bits).
 *	This may be changed by the device's driver, or left at the
 *	default (0) indicating protocol words are eight bit bytes.
 *	The spi_transfer.bits_per_word can override this for each transfer.
 * @irq: Negative, or the number passed to request_irq() to receive
 *	interrupts from this device.
 * @controller_state: Controller's runtime state
 * @controller_data: Board-specific definitions for controller, such as
 *	FIFO initialization parameters; from board_info.controller_data
 * @modalias: Name of the driver to use with this device, or an alias
 *	for that name.  This appears in the sysfs "modalias" attribute
 *	for driver coldplugging, and in uevents used for hotplugging
 * @cs_gpio: gpio number of the chipselect line (optional, -ENOENT when
 *	not using a GPIO line)
 *
 * @statistics: statistics for the spi_device
 *
 * A @spi_device is used to interchange data between an SPI slave
 * (usually a discrete chip) and CPU memory.
 *
 * In @dev, the platform_data is used to hold information about this
 * device that's meaningful to the device's protocol driver, but not
 * to its controller.  One example might be an identifier for a chip
 * variant with slightly different functionality; another might be
 * information about how this particular board wires the chip's pins.
 */
struct spi_device {
	struct device		dev;
	struct spi_controller	*controller;
	struct spi_controller	*master;	/* compatibility layer */
	u32			max_speed_hz;		//该设备能支持的SPI设备的最大时钟
	u8			chip_select;		//在spi_master下的第几个设备,在spi_master中有一个cs_gpio数组,里面存放了各个spi设备的片选引脚,
	//spi_device的片选引脚就是:cs_gpios[spi_device.chip_select]
	u8			bits_per_word;		//每个基本的SPI传输涉及多少位,在使用spi控制器时,一般往寄存器里写入数据,SPI控制器会一位一位地发送出去。
	//一个寄存器是32位,通常叫word。寄存器里有多少位会被发送出去,这取决于bits_per_word。
	//扩展:bits_per_word可以大于32位,这适用于DMA突发模式
	u16			mode;
#define	SPI_CPHA	0x01			/* clock phase */		//采样周期,第一个周期还是第二个周期
#define	SPI_CPOL	0x02			/* clock polarity */	//平时的时钟极性
#define	SPI_MODE_0	(0|0)			/* (original MicroWire) */	//组合起来有四种,SCK平时为低电平,第1个周期采样
#define	SPI_MODE_1	(0|SPI_CPHA)		//SCK为低,第2个周期采样
#define	SPI_MODE_2	(SPI_CPOL|0)		//SCK为高电平,第1个周期采样
#define	SPI_MODE_3	(SPI_CPOL|SPI_CPHA)		//SCK平时为高电平,第2个周期采样
#define	SPI_CS_HIGH	0x04			/* chipselect active high? */		//引脚信号高电平有效
#define	SPI_LSB_FIRST	0x08			/* per-word bits-on-wire */		//一般来说都是MSB,高位先传输。这个表示低位先传输。有些SPI设备并不支持低位传输
#define	SPI_3WIRE	0x10			/* SI/SO signals shared */		//SDO、SDI共用一条线
#define	SPI_LOOP	0x20			/* loopback mode */			//回环模式,SDO、SDI连接在一起
#define	SPI_NO_CS	0x40			/* 1 dev/bus, no chipselect */	//只有一个spi设备,没有片选,也不需要
#define	SPI_READY	0x80			/* slave pulls low to pause */	//spi信号可以拉低信号,表示暂停、表示未就绪
#define	SPI_TX_DUAL	0x100			/* transmit with 2 wires */		//发送数据时有2条信号线
#define	SPI_TX_QUAD	0x200			/* transmit with 4 wires */		//发送数据时有4条信号线
#define	SPI_RX_DUAL	0x400			/* receive with 2 wires */		//接收数据时有2条信号线
#define	SPI_RX_QUAD	0x800			/* receive with 4 wires */		//接收数据时有4条信号线
	int			irq;
	void			*controller_state;
	void			*controller_data;
	char			modalias[SPI_NAME_SIZE];
	int			cs_gpio;	/* chip select gpio */	//可选项,也可以把spi_device的片选引脚记录在这里

	/* 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设备树格式

对于SPI master,就是SPI控制器,它下面可以连接多个SPI设备。
在设备树里,使用一个节点来表示SPI Master,使用子节点来表示挂在下面的SPI设备

2.1 SPI Master

必须的属性:

  • #address-cells:这个SPI Master下的SPI设备,需要多少个cell来表述它的片选引脚
  • #size-cells:必须设置为0
  • compatible:根据它找到SPI Master驱动
    可选属性:
  • cs-gpios: SPI Master可以使用多个GPIO当作片选,可以在这个属性列出那些GPIO
  • num-cs: 片选引脚总数
    其他属性和驱动程序相关,不同SPI Master驱动程序要求的属性不一样

2.2 SPI device

在对应的SPI Master设备树节点下,每一个节点都对应一个SPI设备,
这些SPI Device必选的属性如下:

  • compatible:根据它找到SPI Device驱动
  • reg:用来表示它使用哪个片选引脚
  • spi-max-frequency:必选,该SPI设备支持的最大SPI时钟
    可选属性如下:
  • spi-cpol:这是一个空属性(没有值),表示CPOL为1,即平时SPI时钟为低电平
  • spi-cpha:这是一个空属性(没有值),表示CPHA为1,即在时钟的第2个边沿采样数据
  • spi-cs-high:这是一个空属性(没有值),表示片选引脚高电平有效
  • spi-3wire:这是一个空属性(没有值),表示使用SPI 三线模式
  • spi-lsb-first:这是一个空属性(没有值),表示使用SPI传输数据时先传输最低为(LSB)
  • spi-tx-bus-width:表示有几条MOSI引脚,没有这个属性时默认只有1条MOSI引脚
  • spi-rx-bus-width:表示有几条MISO引脚,没有这个属性时默认只有1条MISO引脚
  • spi-rx-delay-us:单位时毫秒,表示每次读传输后要延时多久
  • spi-tx-delay-us:单位时毫秒,表示每次写传输后要延时多久

2.3 设备树示例

	spi@f00 {				//表示一个spi_master
		#address-cells = <1>;			//reg属性需要多上长度描述
		#size-cells = <0>;			//必须是0
		compatible = "fsl,mpc5200b-spi","fsl,mpc5200-spi";		//用于匹配spi_master驱动程序
		reg = <0xf00 0x20>;
		interrupts = <2 13 0 2 14 0>;
		interrupt-parent = <&mpc5200_pic>;
		//cs-gpios = <&gpio1 0 0>, <0>, <&gpio1 1 0>, <&gpio1 2 0>; 可以定义使用多少个片选引脚
		
		ethernet-switch@0 {			//表示挂载在spi下的设备0
			compatible = "micrel,ks8995m";
			spi-max-frequency = <1000000>;		//spi的最大频率
			reg = <0>;
		};

		codec@1 {				//表示挂载在spi下的设备1
			compatible = "ti,tlv320aic26";
			spi-max-frequency = <100000>;
			reg = <1>;
		};
	};

三、设备树实例

在设备树里,会有一个节点用来表示SPI控制器
在这个SPI控制器下面,连接有哪些SPI设备?会在设备树里使用子节点来描述SPI设备

3.1 使用GPIO模拟的SPI控制器

spi3{
	compatible = "spi-gpio";
	status = "okay";
	gpio-sck = <&gpio0 5 GPIO_ACTIVE_LOW>;
	gpio-mosi = <&gpio1 15 GPIO_ACTIVE_LOW>;
	cs-gpio = <&gpio0 27 GPIO_ACTIVE_LOW>;
	num-chipselects = <1>;
	#address-cells = <1>;
	#size-cells = <0>;

	gpio_spi: gpio_spi@0 {
		compatible = "fairchild,74hc595";
		gpio-controller;
		#gpio-cells = <2>;
		reg = <0>;
		registers-number = <1>;
		spi-max-frequency = <100000>;
	};
};

驱动中的调用:

//driver/spi/spi-gpio.c
//在compatible="spi-gpio"匹配后,调用probe函数
static int spi_gpio_probe(struct platform_device *pdev)
{
	int				status;
	struct spi_master		*master;
	struct spi_gpio			*spi_gpio;
	struct spi_gpio_platform_data	*pdata;
	u16 master_flags = 0;
	bool use_of = 0;

	status = spi_gpio_probe_dt(pdev);
	//...
	pdata = dev_get_platdata(&pdev->dev);
	//分配一个spi_master
	master = spi_alloc_master(&pdev->dev, sizeof(*spi_gpio));
	//...
	spi_gpio = spi_master_get_devdata(master);

	spi_gpio->cs_gpios = devm_kcalloc(&pdev->dev,
				pdata->num_chipselect,
				sizeof(*spi_gpio->cs_gpios),
				GFP_KERNEL);
	//....
	status = spi_gpio_request(&pdev->dev, spi_gpio,
				  pdata->num_chipselect, &master_flags);
	//...设置spi_master
	master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 32);
	master->mode_bits = SPI_3WIRE | SPI_CPHA | SPI_CPOL;
	master->flags = master_flags;
	master->bus_num = pdev->id;
	/* The master needs to think there is a chipselect even if not connected */
	master->num_chipselect = spi_gpio->has_cs ? pdata->num_chipselect : 1;
	master->setup = spi_gpio_setup;
	master->cleanup = spi_gpio_cleanup;
#ifdef CONFIG_OF
	master->dev.of_node = pdev->dev.of_node;
#endif

	spi_gpio->bitbang.master = master;
	spi_gpio->bitbang.chipselect = spi_gpio_chipselect;
	spi_gpio->bitbang.set_line_direction = spi_gpio_set_direction;

	if ((master_flags & SPI_MASTER_NO_TX) == 0) {
		spi_gpio->bitbang.txrx_word[SPI_MODE_0] = spi_gpio_txrx_word_mode0;
		spi_gpio->bitbang.txrx_word[SPI_MODE_1] = spi_gpio_txrx_word_mode1;
		spi_gpio->bitbang.txrx_word[SPI_MODE_2] = spi_gpio_txrx_word_mode2;
		spi_gpio->bitbang.txrx_word[SPI_MODE_3] = spi_gpio_txrx_word_mode3;
	} else {
		spi_gpio->bitbang.txrx_word[SPI_MODE_0] = spi_gpio_spec_txrx_word_mode0;
		spi_gpio->bitbang.txrx_word[SPI_MODE_1] = spi_gpio_spec_txrx_word_mode1;
		spi_gpio->bitbang.txrx_word[SPI_MODE_2] = spi_gpio_spec_txrx_word_mode2;
		spi_gpio->bitbang.txrx_word[SPI_MODE_3] = spi_gpio_spec_txrx_word_mode3;
	}
	spi_gpio->bitbang.setup_transfer = spi_bitbang_setup_transfer;
	spi_gpio->bitbang.flags = SPI_CS_HIGH;
	//这里注册spi_master
	status = spi_bitbang_start(&spi_gpio->bitbang);
	if (status)
		spi_master_put(master);

	return status;
}

int spi_bitbang_start(struct spi_bitbang *bitbang)
{
	struct spi_master *master = bitbang->master;
	int ret;
	//....注册spi_master
	ret = spi_register_master(spi_master_get(master));
	//...
	return 0;
}

3.2 IMX6ULL SPI控制器

arch/arm/boot/dts/imx6ull.dtsi

ecspi3: ecspi@02010000 {
	#address-cells = <1>;
	#size-cells = <0>;
	compatible = "fsl,imx6ul-ecspi","fsl,imx5l-ecspi";
	reg = <0x02010000> <0x4000>;
	interrupts = <GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>;
	clocks = <&clks IMX6UL_CLK_ECSPI3>,
			<&clks IMX6UL_CLK_ECSPI3>;
	clock-names = "jpg", "per";
	dmas = <&sdma 7 7 1>, <&sdma 8 7 2>;
	dma-names = "rx", "tx";
	status = "disabled";
};

arch/arm/boot/dts/100ask_imx6ull-14x14.dts

&ecspi3 {
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_ecspi3>;
	cs-gpios = <&gpio1 20 GPIO_ACTIVE_LOW>;
	status = "okay";
	
	spidev: icm20608@0 {
		compatible = "invensense,icm20608";
		interrupt-parent = <&gpio1>;
		interrupts = <1 1>;
		spi-max-frequency = <8000000>;
		reg = <0>;
	};
};

3.3 STM32MP157 SPI控制器

arch/arm/boot/dts/stm32mp151.dtsi

spi5: spi@44009000 {
	#address-cells = <1>;
	#size-cells = <0>;
	compatible = "st,stm32h7-spi";
	reg = <0x44009000 0x400>;
	interrupts = <GIC_SPI 85 IRQ_TYPE_LEVEL_HIGH>;
	clocks = <&rcc SPI5_K>;
	resets = <&rcc SPI5_R>;
	dmas = <&dmamux1 85 0x400 0x01>,
		<&dmamux1 86 0x400 0x01>;
	dma-names = "rx", "tx";
	power-domains = <&pd_core>;
	status = "disabled";
};

arch/arm/boot/dts/stm32mp157c-100ask-512d-lcd-v1.dts

&spi5 {
	pinctrl-names = "default", "sleep";
	pinctrl-0 = <&spi5_pins_a>;
	pinctrl-1 = <&spi5_sleep_pins_a>;
	status = "okay";
	cs-gpios = <&gpioh 5 GPIO_ACTIVE_LOW>;
	spidev: icm20608@0 {
		compatible = "invensense,icm20608";
		interrupts = <0 IRQ_TYPE_EDGE_FALLING>;
		interrupt-parent = <&gpioz>;
		spi-max-frequency = <8000000>;
		reg = <0>;
	};
};

四、设备树处理过程

在注册spi_master时,调用spi_register_controller

int spi_register_controller(struct spi_controller *ctlr)
{
	//...注册spi_master
	if (!spi_controller_is_slave(ctlr)) {
		status = of_spi_register_master(ctlr);
		if (status)
			return status;
	}

	//...注册完master注册device
	/* Register devices from the device tree and ACPI */
	of_register_spi_devices(ctlr);
	acpi_register_spi_devices(ctlr);
done:
	return status;
}

内核源码:drivers/spi/spi.c

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

	if (!np)
		return 0;

	nb = of_gpio_named_count(np, "cs-gpios");		//解析片选引脚节点
	ctlr->num_chipselect = max_t(int, nb, ctlr->num_chipselect);

	/* Return error only for an incorrectly formed cs-gpios property */
	if (nb == 0 || nb == -ENOENT)
		return 0;
	else if (nb < 0)
		return nb;

	cs = devm_kcalloc(&ctlr->dev, ctlr->num_chipselect, sizeof(int),
			  GFP_KERNEL);
	ctlr->cs_gpios = cs;		//把片选引脚信息记录在这里

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

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

	for (i = 0; i < nb; i++)
		cs[i] = of_get_named_gpio(np, "cs-gpios", i);

	return 0;
}

//解析每个spi_device
static void of_register_spi_devices(struct spi_controller *ctlr)
{
	//...
	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);		//对每个节点进行解析
		//...
	}
}

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

	/* Alloc an spi_device */
	spi = spi_alloc_device(ctlr);
	//...

	/* Select device driver */
	rc = of_modalias_node(nc, spi->modalias,
				sizeof(spi->modalias));
	//...

	rc = of_spi_parse_dt(ctlr, spi, nc);		//这里解析spi_device节点的所有属性
	//...

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

	/* Register the new device */
	rc = spi_add_device(spi);
	//...

	return spi;
	//...
}

 类似资料: