Linux内核之SPI协议

linux,spi · 浏览次数 : 10

小编点评

**driver.h** ```c #include #include #include #include struct spi_device *driver; struct spi_controller_data *tspidev_data; struct spi_device *tspidev_probe(struct spi_device *spi); void tspidev_remove(struct spi_device *spi); void tspidev_id_table_probe(struct spi_device *spi); void tspidev_remove(struct spi_device *spi) { mutex_lock(&device_list_lock); list_del(&tspidev->device_entry); mutex_unlock(&device_list_lock); } struct spi_device *tspidev_id_table_probe(struct spi_device *spi) { // TODO: Implement the id_table_probe function. return NULL; } ``` **driver.c** ```c #include #include #include #include struct spi_device *driver; struct spi_controller_data *tspidev_data; struct spi_device *tspidev_probe(struct spi_device *spi) { struct spi_controller_data *data = container_of(spi, struct spi_controller_data, driver); data->tspidev = spi; mutex_init(&data->device_list_lock); mutex_init(&data->buf_lock); INIT_LIST_HEAD(&data->device_entry); return &data->driver->driver; } void tspidev_remove(struct spi_device *spi) { mutex_lock(&device_list_lock); list_del(&spi->device_entry); mutex_unlock(&device_list_lock); } void tspidev_id_table_probe(struct spi_device *spi) { // TODO: Implement the id_table_probe function. } ```

正文

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

SPI
图源自Serial Peripheral Interface-wikipedia

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

作为一种事实标准,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
image

分层没有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控制器收发函数的指针赋值。
spi-xilinx.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接口
spi_transfer_one_message
spi_transfer_one_message

一般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等等器件,那么就会用到这个。
/**
 * 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;
};

/* 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
注释很清楚了,就不解释了

/**
 * 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表
spi_init图

注册用到的结构体:

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
    image

  • 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函数
    spi_probe图

可选功能注册的结构体是

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

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

image

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

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

注册字符设备 chrdev

873		status = register_chrdev(SPIDEV_MAJOR, "spi", &spidev_fops);

其字符设备的fops如下

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
    image

  • read/write写法类似,这里只截图了spidev_write函数内容
    image
    这里将用户态数据复制到dev的私有数据spidev_data里。然后调用spidev_sync_write将私有数据写入硬件。
    spidev_sync_write队列>spidev_sync互斥锁>spidev_sync_unlocked===>位于spi.c的内核态驱动spi_sync

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

image

  • ioctl里面主要是分支调用 spi_setup 配置参数
    image
    其中调用32位ioctl时,SPI_IOC_MAGIC _IOC_NR _IOC_WRITE 这三种会走32位兼容性操作,其他都会转给64位ioctl处理。
    image

  • llseek 指向 no_llseek,即没有!

注册Class name="spidev"

877		status = class_register(&spidev_class);

其Class的name成员值为"spidev"

/* 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

883		status = spi_register_driver(&spidev_spi_driver);
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匹配
    image
  • id_table
    image
  • probe/remove
    这里的probe内容稍微多点
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 这个例子。

image

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

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

看得很折磨...

与Linux内核之SPI协议相似的内容:

Linux内核之SPI协议

SPI(Serial Peripheral Interface,串行外设接口)是一种同步串行的行业标准,但是并没有像I2C那样有标准文档,它还有主从、可片选的特性。 图源自Serial Peripheral Interface-wikipedia 时序图 放个经典老图,来源未知。相位和极性决定了采样

orangepi zero2在linux5.4以上内核使用ili9341

背景 根据orangepi zero2用户手册说明,linux5.13内核不能使用 modprobe fbtft_device 驱动spi lcd 查看linux内核源码提交记录,发现在v5.4-rc3中删除了fbtft_device.c文件 commit如下 staging/fbtft: Remo

Linux内核之I2C协议

I2C协议标准文档 THE I2C-BUS SPECIFICATION VERSION 2.1 JANUARY 2000: https://www.csd.uoc.gr/~hy428/reading/i2c_spec.pdf I2C全称Inter-IC,又写作IIC,有些又归类为TWI(Two-Wi

[转帖]Linux内核参数之nf_conntrack

https://www.jianshu.com/p/d2b28703e376 问题描述 近日线下测试环境的一个cloudstack计算节点上的虚拟机出现频繁的丢包情况,经过排查日志发现这个计算节点的包被丢弃,日志信息如下: Oct 29 16:18:14 a2 kernel: __ratelimit

[转帖]Linux内核源码之Netfilter框架

https://zhuanlan.zhihu.com/p/413179209 一、概述:Netfilter是什么 对于不经常接触Linux内核的应用层开发者来说,可能对Netfilter了解的比较少。但大多数Linux用户多少都用过或知道iptables,然而,iptables的功能实现就是在Net

[转帖]Linux内核参数之nf_conntrack

https://www.jianshu.com/p/d2b28703e376 问题描述 近日线下测试环境的一个cloudstack计算节点上的虚拟机出现频繁的丢包情况,经过排查日志发现这个计算节点的包被丢弃,日志信息如下: Oct 29 16:18:14 a2 kernel: __ratelimit

[转帖]TCP的blacklog之全连接队列与半连接队列的深入研究

文章目录 Linux内核探测工具systemtap的安装与使用backlog、半连接队列、全连接队列是什么半连接队列、全连接队列基本概念 linux 内核是如何计算半连接队列、全连接队列的半连接队列的大小的计算模拟半连接队列占满全连接队列(Accept Queue) SYN+ACK重传次数全连接队列

[转帖]Linux之Shell 脚本执行三种方式

什么是Shell? Shell是用户与内核进行交互操作的一种接口,目前最流行的Shell称为bash ShellShell也是一门编程语言<解释型的编程语言>,即shell脚本一个系统可以存在多个shell,可以通过cat /etc/shells命令查看系统中安装的shell,不同的shell可能支

[转帖]Linux性能调优之内存负载调优的一些笔记

https://zhuanlan.zhihu.com/p/548770928 写在前面 整理一些Linux内存调优的笔记,分享给小伙伴 博文没有涉及的Demo,理论方法偏多,可以用作内存调优入门 博文内容涉及: Linux内存管理的基本理论 寻找内存泄露的进程 内存交换空间调优 不同方式的内存回收

[转帖]Linux kernel内存管理之overcommit相关参数

前言 了解 linux kernel内存管理,首先可以从用户空间的角度来看kernel的内存管理,执行ls /proc/sys/vm的命令,就可以看到vm运行的所有参数,其中就包含了跟overcommit相关的参数。 Memory overcommit概念介绍 要了解这类参数首先要理解什么是comm