Linux内核之SPI协议

SPI(Serial Peripheral Interface,串行外设接口)是一种同步串行的行业标准,但是并没有像I2C那样有标准文档,它还有主从、可片选的特性。

图源自Serial Peripheral Interface-wikipedia

时序图

放个经典老图,来源未知。相位和极性决定了采样点,主从采样点一致时数据正确,不一致时会导致数据错误但硬件自己其实无法察觉。

作为一种事实标准,SPI还衍生出众多的总线协议:eSPI DSPI QSPI QPI,以及仅有MOSI\SCLK两根线的单工SPI。

应用场景:

  • 读写寄存器
  • 中型数据传输(速率一般为1MHz~20MHz)
  • EEPROM、FLASH等储存器件
  • 一些固件升级

Linux

文档:https://www.kernel.org/doc/html/v5.14/spi/index.html

源码:drivers/spi

分层没有I2C那样好,主打的就是一堆,主打能用就行。一个目录放满...

头文件 include/linux/spi

公共的结构体位于 include/linux/spi/spi.h

spi_controller SPI控制器结构体

关键的收发函数指针都在这里。这个结构体会由 spi-xxxx.c初始化,如赛灵思的SOC会使用drivers/spi/spi-xilinx.c#platform_driver 的probe来初始化这个结构体以及SoC片上SPI控制器。如下图440行就是SPI控制器收发函数的指针赋值。

c 复制代码
/**
 * struct spi_controller - interface to SPI master or slave controller
 * @dev: device interface to this driver
 * @list: link with the global spi_controller list
 * @bus_num: board-specific (and often SOC-specific) identifier for a
 *	given SPI controller.
 * @num_chipselect: chipselects are used to distinguish individual
 *	SPI slaves, and are numbered from zero to num_chipselects.
 *	each slave has a chipselect signal, but it's common that not
 *	every chipselect is connected to a slave.
 * @dma_alignment: SPI controller constraint on DMA buffers alignment.
 * @mode_bits: flags understood by this controller driver
 * @buswidth_override_bits: flags to override for this controller driver
 * @bits_per_word_mask: A mask indicating which values of bits_per_word are
 *	supported by the driver. Bit n indicates that a bits_per_word n+1 is
 *	supported. If set, the SPI core will reject any transfer with an
 *	unsupported bits_per_word. If not set, this value is simply ignored,
 *	and it's up to the individual driver to perform any validation.
 * @min_speed_hz: Lowest supported transfer speed
 * @max_speed_hz: Highest supported transfer speed
 * @flags: other constraints relevant to this driver
 * @slave: indicates that this is an SPI slave controller
 * @target: indicates that this is an SPI target controller
 * @devm_allocated: whether the allocation of this struct is devres-managed
 * @max_transfer_size: function that returns the max transfer size for
 *	a &spi_device; may be %NULL, so the default %SIZE_MAX will be used.
 * @max_message_size: function that returns the max message size for
 *	a &spi_device; may be %NULL, so the default %SIZE_MAX will be used.
 * @io_mutex: mutex for physical bus access
 * @add_lock: mutex to avoid adding devices to the same chipselect
 * @bus_lock_spinlock: spinlock for SPI bus locking
 * @bus_lock_mutex: mutex for exclusion of multiple callers
 * @bus_lock_flag: indicates that the SPI bus is locked for exclusive use
 * @setup: updates the device mode and clocking records used by a
 *	device's SPI controller; protocol code may call this.  This
 *	must fail if an unrecognized or unsupported mode is requested.
 *	It's always safe to call this unless transfers are pending on
 *	the device whose settings are being modified.
 * @set_cs_timing: optional hook for SPI devices to request SPI master
 * controller for configuring specific CS setup time, hold time and inactive
 * delay interms of clock counts
 * @transfer: adds a message to the controller's transfer queue.
 * @cleanup: frees controller-specific state
 * @can_dma: determine whether this controller supports DMA
 * @dma_map_dev: device which can be used for DMA mapping
 * @cur_rx_dma_dev: device which is currently used for RX DMA mapping
 * @cur_tx_dma_dev: device which is currently used for TX DMA mapping
 * @queued: whether this controller is providing an internal message queue
 * @kworker: pointer to thread struct for message pump
 * @pump_messages: work struct for scheduling work to the message pump
 * @queue_lock: spinlock to synchronise access to message queue
 * @queue: message queue
 * @cur_msg: the currently in-flight message
 * @cur_msg_completion: a completion for the current in-flight message
 * @cur_msg_incomplete: Flag used internally to opportunistically skip
 *	the @cur_msg_completion. This flag is used to check if the driver has
 *	already called spi_finalize_current_message().
 * @cur_msg_need_completion: Flag used internally to opportunistically skip
 *	the @cur_msg_completion. This flag is used to signal the context that
 *	is running spi_finalize_current_message() that it needs to complete()
 * @cur_msg_mapped: message has been mapped for DMA
 * @fallback: fallback to PIO if DMA transfer return failure with
 *	SPI_TRANS_FAIL_NO_START.
 * @last_cs_mode_high: was (mode & SPI_CS_HIGH) true on the last call to set_cs.
 * @last_cs: the last chip_select that is recorded by set_cs, -1 on non chip
 *           selected
 * @xfer_completion: used by core transfer_one_message()
 * @busy: message pump is busy
 * @running: message pump is running
 * @rt: whether this queue is set to run as a realtime task
 * @auto_runtime_pm: the core should ensure a runtime PM reference is held
 *                   while the hardware is prepared, using the parent
 *                   device for the spidev
 * @max_dma_len: Maximum length of a DMA transfer for the device.
 * @prepare_transfer_hardware: a message will soon arrive from the queue
 *	so the subsystem requests the driver to prepare the transfer hardware
 *	by issuing this call
 * @transfer_one_message: the subsystem calls the driver to transfer a single
 *	message while queuing transfers that arrive in the meantime. When the
 *	driver is finished with this message, it must call
 *	spi_finalize_current_message() so the subsystem can issue the next
 *	message
 * @unprepare_transfer_hardware: there are currently no more messages on the
 *	queue so the subsystem notifies the driver that it may relax the
 *	hardware by issuing this call
 *
 * @set_cs: set the logic level of the chip select line.  May be called
 *          from interrupt context.
 * @optimize_message: optimize the message for reuse
 * @unoptimize_message: release resources allocated by optimize_message
 * @prepare_message: set up the controller to transfer a single message,
 *                   for example doing DMA mapping.  Called from threaded
 *                   context.
 * @transfer_one: transfer a single spi_transfer.
 *
 *                  - return 0 if the transfer is finished,
 *                  - return 1 if the transfer is still in progress. When
 *                    the driver is finished with this transfer it must
 *                    call spi_finalize_current_transfer() so the subsystem
 *                    can issue the next transfer. If the transfer fails, the
 *                    driver must set the flag SPI_TRANS_FAIL_IO to
 *                    spi_transfer->error first, before calling
 *                    spi_finalize_current_transfer().
 *                    Note: transfer_one and transfer_one_message are mutually
 *                    exclusive; when both are set, the generic subsystem does
 *                    not call your transfer_one callback.
 * @handle_err: the subsystem calls the driver to handle an error that occurs
 *		in the generic implementation of transfer_one_message().
 * @mem_ops: optimized/dedicated operations for interactions with SPI memory.
 *	     This field is optional and should only be implemented if the
 *	     controller has native support for memory like operations.
 * @mem_caps: controller capabilities for the handling of memory operations.
 * @unprepare_message: undo any work done by prepare_message().
 * @slave_abort: abort the ongoing transfer request on an SPI slave controller
 * @target_abort: abort the ongoing transfer request on an SPI target controller
 * @cs_gpiods: Array of GPIO descriptors to use as chip select lines; one per CS
 *	number. Any individual value may be NULL for CS lines that
 *	are not GPIOs (driven by the SPI controller itself).
 * @use_gpio_descriptors: Turns on the code in the SPI core to parse and grab
 *	GPIO descriptors. This will fill in @cs_gpiods and SPI devices will have
 *	the cs_gpiod assigned if a GPIO line is found for the chipselect.
 * @unused_native_cs: When cs_gpiods is used, spi_register_controller() will
 *	fill in this field with the first unused native CS, to be used by SPI
 *	controller drivers that need to drive a native CS when using GPIO CS.
 * @max_native_cs: When cs_gpiods is used, and this field is filled in,
 *	spi_register_controller() will validate all native CS (including the
 *	unused native CS) against this value.
 * @pcpu_statistics: statistics for the spi_controller
 * @dma_tx: DMA transmit channel
 * @dma_rx: DMA receive channel
 * @dummy_rx: dummy receive buffer for full-duplex devices
 * @dummy_tx: dummy transmit buffer for full-duplex devices
 * @fw_translate_cs: If the boot firmware uses different numbering scheme
 *	what Linux expects, this optional hook can be used to translate
 *	between the two.
 * @ptp_sts_supported: If the driver sets this to true, it must provide a
 *	time snapshot in @spi_transfer->ptp_sts as close as possible to the
 *	moment in time when @spi_transfer->ptp_sts_word_pre and
 *	@spi_transfer->ptp_sts_word_post were transmitted.
 *	If the driver does not set this, the SPI core takes the snapshot as
 *	close to the driver hand-over as possible.
 * @irq_flags: Interrupt enable state during PTP system timestamping
 * @queue_empty: signal green light for opportunistically skipping the queue
 *	for spi_sync transfers.
 * @must_async: disable all fast paths in the core
 *
 * Each SPI controller can communicate with one or more @spi_device
 * children.  These make a small bus, sharing MOSI, MISO and SCK signals
 * but not chip select signals.  Each device may be configured to use a
 * different clock rate, since those shared signals are ignored unless
 * the chip is selected.
 *
 * The driver for an SPI controller manages access to those devices through
 * a queue of spi_message transactions, copying data between CPU memory and
 * an SPI slave device.  For each such message it queues, it calls the
 * message's completion function when the transaction completes.
 */
struct spi_controller {
	struct device	dev;

	struct list_head list;

	/*
	 * Other than negative (== assign one dynamically), bus_num is fully
	 * board-specific. Usually that simplifies to being SoC-specific.
	 * example: one SoC has three SPI controllers, numbered 0..2,
	 * and one board's schematics might show it using SPI-2. Software
	 * would normally use bus_num=2 for that controller.
	 */
	s16			bus_num;

	/*
	 * Chipselects will be integral to many controllers; some others
	 * might use board-specific GPIOs.
	 */
	u16			num_chipselect;

	/* Some SPI controllers pose alignment requirements on DMAable
	 * buffers; let protocol drivers know about these requirements.
	 */
	u16			dma_alignment;

	/* spi_device.mode flags understood by this controller driver */
	u32			mode_bits;

	/* spi_device.mode flags override flags for this controller */
	u32			buswidth_override_bits;

	/* Bitmask of supported bits_per_word for transfers */
	u32			bits_per_word_mask;
#define SPI_BPW_MASK(bits) BIT((bits) - 1)
#define SPI_BPW_RANGE_MASK(min, max) GENMASK((max) - 1, (min) - 1)

	/* Limits on transfer speed */
	u32			min_speed_hz;
	u32			max_speed_hz;

	/* Other constraints relevant to this driver */
	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_CONTROLLER_GPIO_SS		BIT(5)	/* GPIO CS must select slave */
#define SPI_CONTROLLER_SUSPENDED	BIT(6)	/* Currently suspended */
	/*
	 * The spi-controller has multi chip select capability and can
	 * assert/de-assert more than one chip select at once.
	 */
#define SPI_CONTROLLER_MULTI_CS		BIT(7)

	/* Flag indicating if the allocation of this struct is devres-managed */
	bool			devm_allocated;

	union {
		/* Flag indicating this is an SPI slave controller */
		bool			slave;
		/* Flag indicating this is an SPI target controller */
		bool			target;
	};

	/*
	 * On some hardware transfer / message size may be constrained
	 * the limit may depend on device transfer settings.
	 */
	size_t (*max_transfer_size)(struct spi_device *spi);
	size_t (*max_message_size)(struct spi_device *spi);

	/* I/O mutex */
	struct mutex		io_mutex;

	/* Used to avoid adding the same CS twice */
	struct mutex		add_lock;

	/* Lock and mutex for SPI bus locking */
	spinlock_t		bus_lock_spinlock;
	struct mutex		bus_lock_mutex;

	/* Flag indicating that the SPI bus is locked for exclusive use */
	bool			bus_lock_flag;

	/*
	 * Setup mode and clock, etc (SPI driver may call many times).
	 *
	 * IMPORTANT:  this may be called when transfers to another
	 * device are active.  DO NOT UPDATE SHARED REGISTERS in ways
	 * which could break those transfers.
	 */
	int			(*setup)(struct spi_device *spi);

	/*
	 * set_cs_timing() method is for SPI controllers that supports
	 * configuring CS timing.
	 *
	 * This hook allows SPI client drivers to request SPI controllers
	 * to configure specific CS timing through spi_set_cs_timing() after
	 * spi_setup().
	 */
	int (*set_cs_timing)(struct spi_device *spi);

	/*
	 * Bidirectional bulk transfers
	 *
	 * + The transfer() method may not sleep; its main role is
	 *   just to add the message to the queue.
	 * + For now there's no remove-from-queue operation, or
	 *   any other request management
	 * + To a given spi_device, message queueing is pure FIFO
	 *
	 * + The controller's main job is to process its message queue,
	 *   selecting a chip (for masters), then transferring data
	 * + If there are multiple spi_device children, the i/o queue
	 *   arbitration algorithm is unspecified (round robin, FIFO,
	 *   priority, reservations, preemption, etc)
	 *
	 * + Chipselect stays active during the entire message
	 *   (unless modified by spi_transfer.cs_change != 0).
	 * + The message transfers use clock and SPI mode parameters
	 *   previously established by setup() for this device
	 */
	int			(*transfer)(struct spi_device *spi,
						struct spi_message *mesg);

	/* Called on release() to free memory provided by spi_controller */
	void			(*cleanup)(struct spi_device *spi);

	/*
	 * Used to enable core support for DMA handling, if can_dma()
	 * exists and returns true then the transfer will be mapped
	 * prior to transfer_one() being called.  The driver should
	 * not modify or store xfer and dma_tx and dma_rx must be set
	 * while the device is prepared.
	 */
	bool			(*can_dma)(struct spi_controller *ctlr,
					   struct spi_device *spi,
					   struct spi_transfer *xfer);
	struct device *dma_map_dev;
	struct device *cur_rx_dma_dev;
	struct device *cur_tx_dma_dev;

	/*
	 * These hooks are for drivers that want to use the generic
	 * controller transfer queueing mechanism. If these are used, the
	 * transfer() function above must NOT be specified by the driver.
	 * Over time we expect SPI drivers to be phased over to this API.
	 */
	bool				queued;
	struct kthread_worker		*kworker;
	struct kthread_work		pump_messages;
	spinlock_t			queue_lock;
	struct list_head		queue;
	struct spi_message		*cur_msg;
	struct completion               cur_msg_completion;
	bool				cur_msg_incomplete;
	bool				cur_msg_need_completion;
	bool				busy;
	bool				running;
	bool				rt;
	bool				auto_runtime_pm;
	bool				cur_msg_mapped;
	bool                            fallback;
	bool				last_cs_mode_high;
	s8				last_cs[SPI_CS_CNT_MAX];
	u32				last_cs_index_mask : SPI_CS_CNT_MAX;
	struct completion               xfer_completion;
	size_t				max_dma_len;

	int (*optimize_message)(struct spi_message *msg);
	int (*unoptimize_message)(struct spi_message *msg);
	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);
	union {
		int (*slave_abort)(struct spi_controller *ctlr);
		int (*target_abort)(struct spi_controller *ctlr);
	};

	/*
	 * These hooks are for drivers that use a generic implementation
	 * of transfer_one_message() provided by the core.
	 */
	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);

	/* Optimized handlers for SPI memory-like operations. */
	const struct spi_controller_mem_ops *mem_ops;
	const struct spi_controller_mem_caps *mem_caps;

	/* GPIO chip select */
	struct gpio_desc	**cs_gpiods;
	bool			use_gpio_descriptors;
	s8			unused_native_cs;
	s8			max_native_cs;

	/* Statistics */
	struct spi_statistics __percpu	*pcpu_statistics;

	/* DMA channels for use with core dmaengine helpers */
	struct dma_chan		*dma_tx;
	struct dma_chan		*dma_rx;

	/* Dummy data for full duplex devices */
	void			*dummy_rx;
	void			*dummy_tx;

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

	/*
	 * Driver sets this field to indicate it is able to snapshot SPI
	 * transfers (needed e.g. for reading the time of POSIX clocks)
	 */
	bool			ptp_sts_supported;

	/* Interrupt enable state during PTP system timestamping */
	unsigned long		irq_flags;

	/* Flag for enabling opportunistic skipping of the queue in spi_sync */
	bool			queue_empty;
	bool			must_async;
};

spi.c里实现了transfer_one_messagetransfer接口

一般SoC厂商会实现:

spi_driver结构体

注释里写着:主机端的协议驱动

struct spi_driver - Host side "protocol" driver`

  • id_table,就是一个识别而已。里面是name和一个私有64位数据。可以分别通过 spi.c的spi_get_device_idspi_get_device_match_data函数获取
  • probe/remove作用是将driver绑定/解绑定到 spi device
  • device_driver的作用有点大,这里的SPI作为其他驱动的底层支持,例如我们有基于SPI的RTC、PHY等等器件,那么就会用到这个。
c 复制代码
/**
 * struct spi_driver - Host side "protocol" driver
 * @id_table: List of SPI devices supported by this driver
 * @probe: Binds this driver to the spi device.  Drivers can verify
 *	that the device is actually present, and may need to configure
 *	characteristics (such as bits_per_word) which weren't needed for
 *	the initial configuration done during system setup.
 * @remove: Unbinds this driver from the spi device
 * @shutdown: Standard shutdown callback used during system state
 *	transitions such as powerdown/halt and kexec
 * @driver: SPI device drivers should initialize the name and owner
 *	field of this structure.
 *
 * This represents the kind of device driver that uses SPI messages to
 * interact with the hardware at the other end of a SPI link.  It's called
 * a "protocol" driver because it works through messages rather than talking
 * directly to SPI hardware (which is what the underlying SPI controller
 * driver does to pass those messages).  These protocols are defined in the
 * specification for the device(s) supported by the driver.
 *
 * As a rule, those device protocols represent the lowest level interface
 * supported by a driver, and it will support upper level interfaces too.
 * Examples of such upper levels include frameworks like MTD, networking,
 * MMC, RTC, filesystem character device nodes, and hardware monitoring.
 */
struct spi_driver {
	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;
};
c 复制代码
/* spi */
//in /include/linux/mod_devicetable.h
#define SPI_NAME_SIZE	32
#define SPI_MODULE_PREFIX "spi:"

struct spi_device_id {
	char name[SPI_NAME_SIZE];
	kernel_ulong_t driver_data;	/* Data private to the driver */
};

spi_device结构体

位于 include/linux/spi

注释很清楚了,就不解释了

c 复制代码
/**
 * 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.
 * @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: Array of physical chipselect, spi->chipselect[i] gives
 *	the corresponding physical CS for logical CS i.
 * @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.
 * @rt: Make the pump thread real time priority.
 * @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
 * @driver_override: If the name of a driver is written to this attribute, then
 *	the device will bind to the named driver and only the named driver.
 *	Do not set directly, because core frees it; use driver_set_override() to
 *	set or clear it.
 * @cs_gpiod: Array of GPIO descriptors of the corresponding chipselect lines
 *	(optional, NULL when not using a GPIO line)
 * @word_delay: delay to be inserted between consecutive
 *	words of a transfer
 * @cs_setup: delay to be introduced by the controller after CS is asserted
 * @cs_hold: delay to be introduced by the controller before CS is deasserted
 * @cs_inactive: delay to be introduced by the controller after CS is
 *	deasserted. If @cs_change_delay is used from @spi_transfer, then the
 *	two delays will be added up.
 * @pcpu_statistics: statistics for the spi_device
 * @cs_index_mask: Bit mask of the active chipselect(s) in the chipselect array
 *
 * 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;
	u32			max_speed_hz;
	u8			chip_select[SPI_CS_CNT_MAX];
	u8			bits_per_word;
	bool			rt;
#define SPI_NO_TX		BIT(31)		/* No transmit wire */
#define SPI_NO_RX		BIT(30)		/* No receive wire */
	/*
	 * TPM specification defines flow control over SPI. Client device
	 * can insert a wait state on MISO when address is transmitted by
	 * controller on MOSI. Detecting the wait state in software is only
	 * possible for full duplex controllers. For controllers that support
	 * only half-duplex, the wait state detection needs to be implemented
	 * in hardware. TPM devices would set this flag when hardware flow
	 * control is expected from SPI controller.
	 */
#define SPI_TPM_HW_FLOW		BIT(29)		/* TPM HW flow control */
	/*
	 * All bits defined above should be covered by SPI_MODE_KERNEL_MASK.
	 * The SPI_MODE_KERNEL_MASK has the SPI_MODE_USER_MASK counterpart,
	 * which is defined in 'include/uapi/linux/spi/spi.h'.
	 * The bits defined here are from bit 31 downwards, while in
	 * SPI_MODE_USER_MASK are from 0 upwards.
	 * These bits must not overlap. A static assert check should make sure of that.
	 * If adding extra bits, make sure to decrease the bit index below as well.
	 */
#define SPI_MODE_KERNEL_MASK	(~(BIT(29) - 1))
	u32			mode;
	int			irq;
	void			*controller_state;
	void			*controller_data;
	char			modalias[SPI_NAME_SIZE];
	const char		*driver_override;
	struct gpio_desc	*cs_gpiod[SPI_CS_CNT_MAX];	/* Chip select gpio desc */
	struct spi_delay	word_delay; /* Inter-word delay */
	/* CS delays */
	struct spi_delay	cs_setup;
	struct spi_delay	cs_hold;
	struct spi_delay	cs_inactive;

	/* The statistics */
	struct spi_statistics __percpu	*pcpu_statistics;

	/* Bit mask of the chipselect(s) that the driver need to use from
	 * the chipselect array.When the controller is capable to handle
	 * multiple chip selects & memories are connected in parallel
	 * then more than one bit need to be set in cs_index_mask.
	 */
	u32			cs_index_mask : SPI_CS_CNT_MAX;

	/*
	 * 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
	 *  - chipselect delays
	 *  - ...
	 */
};

spi.c(SPI总线驱动)

路径 drivers/spi/spi.c

虽然代码还不错,但是一个源码文件5000行不是梦...呕~,如果没有搜索功能,找个probe都要找半天

首先是初始化,postcore_initcall(spi_init); 该函数在初始化时就被调用。

初始化,可以查看 include/linux/init.h和段定义include/asm-generic/vmlinux.lds.h,数字越小优先级越高。postcore_initcall排第四位,优先级非常高。

spi_init的主要操作:分配内存<=32b,总线注册, class类注册,可选功能:CONFIG_SPI_SLAVE从机注册,CONFIG_OF_DYNAMIC设备树,CONFIG_ACPI注册即ACPI表

注册用到的结构体:

c 复制代码
const struct bus_type spi_bus_type = {
	.name		= "spi",
	.dev_groups	= spi_dev_groups,
	.match		= spi_match_device,
	.uevent		= spi_uevent,
	.probe		= spi_probe,
	.remove		= spi_remove,
	.shutdown	= spi_shutdown,
};
EXPORT_SYMBOL_GPL(spi_bus_type);
  • spi_dev_groups 是sysfs组,里面一车面包人(很多sysfs读写接口,从56行到311行都是: drivers/spi/spi.c#L56-L311

  • spi_match_device 很简单,就是提供匹配函数,spidev.c后面会用到。其实就是override-设备树-ACPI-id_table这样优先级匹配而已。

  • spi_uevent 是uevent事件回调,uevent提供了"用户空间通知"的功能实现,当内核中有Kobject的增加/删除/修改等动作时,会通知用户空间

  • spi_shutdown 是关闭回调,这里只是调用dev->driver->shutdown函数而已。

  • spi_probe/spi_remove

    其中spi_probe是这样的:

  1. 从设备树节点获取并配置:时钟、irq中断、
  2. dev_pm_domain_attach 附加到电源管理域(power manager domain)上
  3. 调用struct spi_device->struct device dev->struct device_driver driver的probe函数

可选功能注册的结构体是

c 复制代码
static struct class spi_master_class = {
	.name		= "spi_master",
	.dev_release	= spi_controller_release,
	.dev_groups	= spi_master_groups,
};
//...
static struct class spi_slave_class = {
	.name		= "spi_slave",
	.dev_release	= spi_controller_release,
	.dev_groups	= spi_slave_groups,
};
  • spi_controller_release 就一个kfree释放内存
  • spi_master_groupsspi_slave_groups就是一车面包人(sysfs组

其他的就都通过EXPORT_SYMBOL_GPL宏向外暴露函数API了,多达40个...

SPI数据传输-内核态API

暴露API:EXPORT_SYMBOL_GPL(spi_sync);

流程:spi_sync==互斥锁=>__spi_sync

作用:将spi_message

c 复制代码
static int __spi_sync(struct spi_device *spi, struct spi_message *message)
{
	DECLARE_COMPLETION_ONSTACK(done);
	unsigned long flags;
	int status;
	struct spi_controller *ctlr = spi->controller;

	if (__spi_check_suspended(ctlr)) {
		dev_warn_once(&spi->dev, "Attempted to sync while suspend\n");
		return -ESHUTDOWN;
	}

	status = spi_maybe_optimize_message(spi, message);
	if (status)
		return status;

	SPI_STATISTICS_INCREMENT_FIELD(ctlr->pcpu_statistics, spi_sync);
	SPI_STATISTICS_INCREMENT_FIELD(spi->pcpu_statistics, spi_sync);

	/*
	 * Checking queue_empty here only guarantees async/sync message
	 * ordering when coming from the same context. It does not need to
	 * guard against reentrancy from a different context. The io_mutex
	 * will catch those cases.
	 */
	if (READ_ONCE(ctlr->queue_empty) && !ctlr->must_async) {
		message->actual_length = 0;
		message->status = -EINPROGRESS;

		trace_spi_message_submit(message);

		SPI_STATISTICS_INCREMENT_FIELD(ctlr->pcpu_statistics, spi_sync_immediate);
		SPI_STATISTICS_INCREMENT_FIELD(spi->pcpu_statistics, spi_sync_immediate);

		__spi_transfer_message_noqueue(ctlr, message);

		return message->status;
	}

	/*
	 * There are messages in the async queue that could have originated
	 * from the same context, so we need to preserve ordering.
	 * Therefor we send the message to the async queue and wait until they
	 * are completed.
	 */
	message->complete = spi_complete;
	message->context = &done;

	spin_lock_irqsave(&ctlr->bus_lock_spinlock, flags);
	status = __spi_async(spi, message);
	spin_unlock_irqrestore(&ctlr->bus_lock_spinlock, flags);

	if (status == 0) {
		wait_for_completion(&done);
		status = message->status;
	}
	message->context = NULL;

	return status;
}

另外还有一个异步API spi_async,里面用了队列,队列空则使用同步,队列不空则调用__spi_async将内容通过spi_controller->transfer(spi, message)发送。

spidev.c (提供用户态支持的SPIDEV内核驱动)

路径 drivers/spi/spidev.c

它是SPI userspace API的内核驱动支持。

入口函数,注册了 字符设备、Class、spi_driver

注册字符设备 chrdev

c 复制代码
873		status = register_chrdev(SPIDEV_MAJOR, "spi", &spidev_fops);

其字符设备的fops如下

c 复制代码
static const struct file_operations spidev_fops = {
	.owner =	THIS_MODULE,
	/* REVISIT switch to aio primitives, so that userspace
	 * gets more complete API coverage.  It'll simplify things
	 * too, except for the locking.
	 */
	.write =	spidev_write,
	.read =		spidev_read,
	.unlocked_ioctl = spidev_ioctl,
	.compat_ioctl = spidev_compat_ioctl,
	.open =		spidev_open,
	.release =	spidev_release,
	.llseek =	no_llseek,
};

定义了 open/close/read/write/64位ioctl/32位ioctl/llseek 这几个常见接口。

  • open/close写法类似,这里只截图 spidev_open

  • read/write写法类似,这里只截图了spidev_write函数内容

    这里将用户态数据复制到dev的私有数据spidev_data里。然后调用spidev_sync_write将私有数据写入硬件。
    spidev_sync_write队列>spidev_sync互斥锁>spidev_sync_unlocked===>位于spi.c的内核态驱动spi_sync

c 复制代码
struct spidev_data {
	dev_t			devt;
	struct mutex		spi_lock;
	struct spi_device	*spi;
	struct list_head	device_entry;

	/* TX/RX buffers are NULL unless this device is open (users > 0) */
	struct mutex		buf_lock;
	unsigned		users;
	u8			*tx_buffer;
	u8			*rx_buffer;
	u32			speed_hz;
};
  • ioctl里面主要是分支调用 spi_setup 配置参数

    其中调用32位ioctl时,SPI_IOC_MAGIC _IOC_NR _IOC_WRITE 这三种会走32位兼容性操作,其他都会转给64位ioctl处理。

  • llseek 指向 no_llseek,即没有!

注册Class name="spidev"

c 复制代码
877		status = class_register(&spidev_class);

其Class的name成员值为"spidev"

c 复制代码
/* The main reason to have this class is to make mdev/udev create the
 * /dev/spidevB.C character device nodes exposing our userspace API.
 * It also simplifies memory management.
 */

static const struct class spidev_class = {
	.name = "spidev",
};

注册driver

c 复制代码
883		status = spi_register_driver(&spidev_spi_driver);
c 复制代码
static struct spi_driver spidev_spi_driver = {
	.driver = {
		.name =		"spidev",
		.of_match_table = spidev_dt_ids,
		.acpi_match_table = spidev_acpi_ids,
	},
	.probe =	spidev_probe,
	.remove =	spidev_remove,
	.id_table =	spidev_spi_ids,

	/* NOTE:  suspend/resume methods are not necessary here.
	 * We don't do anything except pass the requests to/from
	 * the underlying controller.  The refrigerator handles
	 * most issues; the controller driver handles the rest.
	 */
};

其中

  • name="spidev"
  • dts/acpi匹配
  • id_table
  • probe/remove
    这里的probe内容稍微多点
c 复制代码
static int spidev_probe(struct spi_device *spi)
{
	int (*match)(struct device *dev);
	struct spidev_data	*spidev;
	int			status;
	unsigned long		minor;

	match = device_get_match_data(&spi->dev);
	if (match) {
		status = match(&spi->dev);
		/*...*/
	}

	/* Allocate driver data */
	spidev = kzalloc(sizeof(*spidev), GFP_KERNEL);
	if (!spidev)
		return -ENOMEM;

	/* Initialize the driver data */
	spidev->spi = spi;
	mutex_init(&spidev->spi_lock);
	mutex_init(&spidev->buf_lock);

	INIT_LIST_HEAD(&spidev->device_entry);

	/* If we can allocate a minor number, hook up this device.
	 * Reusing minors is fine so long as udev or mdev is working.
	 */
	mutex_lock(&device_list_lock);
	minor = find_first_zero_bit(minors, N_SPI_MINORS);
	if (minor < N_SPI_MINORS) {
		struct device *dev;

		spidev->devt = MKDEV(SPIDEV_MAJOR, minor);
		dev = device_create(&spidev_class, &spi->dev, spidev->devt,
				    spidev, "spidev%d.%d",
				    spi->controller->bus_num, spi_get_chipselect(spi, 0));
		status = PTR_ERR_OR_ZERO(dev);
	} else {/*...*/}
	if (status == 0) {
		set_bit(minor, minors);
		list_add(&spidev->device_entry, &device_list);
	}
	mutex_unlock(&device_list_lock);

	spidev->speed_hz = spi->max_speed_hz;

	if (status == 0)
		spi_set_drvdata(spi, spidev);
	/*...*/

	return status;
}

为节省篇幅,容错处理我用/*...*/代替了。

这里probe主要就是

  1. 获取spi总线bus_type结构体的.match成员函数指针,然后调用match函数。(override-设备树-ACPI-id_tables这样的优先级顺序)
  2. 给结构体分配内存
  3. 填充结构体
  4. 创建字符设备
  5. 配置driver的私有数据成员

应用层

内核给应用层准备了一个 SPI userspace API,底层就是spidev.c,只需要在内核menuconfig配置开关,然后open /dev/spidevB.C 即可。如果需要全双工,可以看看 tools/spi/spidev_fdx.c 这个例子。

另外有个很重要的事情要记得,ioctl使用的结构体,它的tx_buf/rx_buf只有64位长,而不像有些RTOS那样是个指针,所以Linux内核把形式参数做成了接收spi_ioc_transfer数组。

c 复制代码
struct spi_ioc_transfer {
	__u64		tx_buf;
	__u64		rx_buf;

	__u32		len;
	__u32		speed_hz;

	__u16		delay_usecs;
	__u8		bits_per_word;
	__u8		cs_change;
	__u8		tx_nbits;
	__u8		rx_nbits;
	__u8		word_delay_usecs;
	__u8		pad;

	/* If the contents of 'struct spi_ioc_transfer' ever change
	 * incompatibly, then the ioctl number (currently 0) must change;
	 * ioctls with constant size fields get a bit more in the way of
	 * error checking than ones (like this) where that field varies.
	 *
	 * NOTE: struct layout is the same in 64bit and 32bit userspace.
	 */
};

总结:

  • 总线驱动 spi.c: postcore_init注册bus_type
  • 控制器驱动 spi_controller 由各SoC厂商提供 spi-xxx.c 的 platform_driver初始化片上的SPI控制器。
  • 主机驱动 spi_driver
  • 从机驱动
  • 内核态 spi_device
  • 用户态 spidev

看得很折磨...