Protocolo SPI no Kernel Linux

O SPI (Serial Peripheral Interface) é um padrão industrial para comunicação serial síncrona, sem uma documentação padronizada oficial como o I2C. Possui características de mestre/escravo com seleção de chip.

Digarama de Temporização

A fase e a polaridade determinam o ponto de amostragem. Quando os pontos de amostragem do mestre e do escravo estão alinhados, os dados são corretos; caso contrário, ocorrem erros que o hardware não detecta.

Como padrão de fato, o SPI originou diversos protocolos de barramento: eSPI, DSPI, QSPI, QPI, além de SPI simplex com apenas MOSI/SCLK, e até mesmo características de amostragem dupla borda (DTR).

Casos de uso incluem acesso a registradores, trensferência de dados média (taxas de 1MHz a 20MHz), dispositivos de armazenamento como EEPROM e FLASH, e atualizações de firmware.

Implementação no Kernel Linux

Documentação: kernel.org/spi
Código-fonte: drivers/spi

A organização é menos estruturada que o I2C, focando em funcionalidade prática. Os cabeçalhos estão em include/linux/spi/.

Estrutura spi_controlador

Estrutura principal para instâncias de controlador SPI, contendo ponteiros de função essenciais para transmissão. Inicializada pelos drivers de controlador específicos como spi-xxxx.c.

/**
 * struct spi_controlador - interface para controlador mestre ou escravo SPI
 * @dispositivo: interface do dispositivo para este driver
 * @lista: link com a lista global de spi_controlador
 * @numero_barramento: identificador específico da placa (geralmente SoC) para um controlador SPI
 * @num_selecao_chip: seleções de chip para distinguir escravos SPI, numeradas de zero a num_selecao_chips
 * @alinhamento_dma: alinhamento de buffers DMA imposto pelo controlador SPI
 * @bits_modo: flags compreendidas pelo driver do controlador
 * @bits_sobreescrita_largura: flags para sobreescrita deste driver de controlador
 * @mascara_bits_por_palavra: indica quais valores de bits_por_palavra são suportados
 * @freq_min_hz: menor velocidade de transferência suportada
 * @freq_max_hz: maior velocidade de transferência suportada
 * @sinalizadores: outras restrições relevantes para este driver
 * @eh_escravo: indica se é um controlador SPI escravo
 * @eh_alvo: indica se é um controlador SPI alvo
 * @alocado_devm: se a alocação desta struct é gerenciada por devres
 * @tamanho_maximo_transferencia: função que retorna o tamanho máximo de transferência
 * @tamanho_maximo_mensagem: função que retorna o tamanho máximo de mensagem
 * @mutex_io: mutex para acesso ao barramento físico
 * @trava_adicao: mutex para evitar adição de dispositivos ao mesmo chip select
 * @trava_barramento_spinlock: spinlock para travamento do barramento SPI
 * @trava_barramento_mutex: mutex para exclusão de múltiplos chamadores
 * @sinalizador_trava_barramento: indica se o barramento SPI está travado
 * @configurar: atualiza modo e configurações de clock do dispositivo
 * @configurar_temporizacao_cs: hook opcional para configurar tempos de CS
 * @transferir: adiciona uma mensagem à fila de transferência do controlador
 * @limpar: libera estado específico do controlador
 * @pode_dma: determina se o controlador suporta DMA
 * @dispositivo_mapeamento_dma: dispositivo usado para mapeamento DMA
 * @dispositivo_rx_dma_atual: dispositivo atualmente usado para mapeamento DMA de RX
 * @dispositivo_tx_dma_atual: dispositivo atualmente usado para mapeamento DMA de TX
 * @enfileirado: se o controlador fornece uma fila interna de mensagens
 * @trabalhador_kernel: ponteiro para estrutura de thread do bombeador de mensagens
 * @bombeamento_mensagens: estrutura de trabalho para agendar trabalho no bombeador
 * @trava_fila: spinlock para sincronizar acesso à fila de mensagens
 * @fila: fila de mensagens
 * @mensagem_atual: a mensagem atualmente em voo
 * @completacao_mensagem_atual: uma completacao para a mensagem atual
 * @mensagem_atual_incompleta: flag para pular oportunistamente a completacao
 * @mensagem_atual_necessita_completacao: flag para sinalizar necessidade de completacao
 * @mensagem_atual_mapeada: mensagem foi mapeada para DMA
 * @fallback: fallback para PIO se transferência DMA falhar
 * @ultimo_modo_cs_alto: se (modo & SPI_CS_ALTO) era verdadeiro na última chamada
 * @ultimo_cs: último chip_select registrado
 * @completacao_transferencia: usada pelo core para transfer_one_message()
 * @ocupado: bombeador de mensagens está ocupado
 * @executando: bombeador de mensagens está executando
 * @tempo_real: se esta fila deve ser executada como tarefa em tempo real
 * @auto_pm_runtime: o core deve garantir referência PM durante preparação
 * @comprimento_maximo_dma: comprimento máximo de uma transferência DMA
 * @preparar_hardware_transferencia: mensagem logo chegará da fila
 * @transferir_uma_mensagem: transfere uma única mensagem
 * @despreparar_hardware_transferencia: não há mais mensagens na fila
 * @configurar_cs: define o nível lógico da linha de seleção de chip
 * @otimizar_mensagem: otimiza mensagem para reutilização
 * @desotimizar_mensagem: libera recursos alocados por otimizar_mensagem
 * @preparar_mensagem: configura o controlador para transferir uma mensagem
 * @transferir_um: transfere uma única spi_transfer
 * @lidar_com_erro: lida com erros na implementação genérica
 * @operacoes_mem: operações otimizadas para interações com memória SPI
 * @capacidades_mem: capacidades do controlador para operações de memória
 * @despreparar_mensagem: desfaz trabalho feito por preparar_mensagem()
 * @abortar_escravo: aborta solicitação de transferência em controlador escravo
 * @abortar_alvo: aborta solicitação de transferência em controlador alvo
 * @gpiods_cs: array de descritores GPIO para linhas de seleção de chip
 * @usar_descritores_gpio: ativa código para analisar e obter descritores GPIO
 * @cs_nativo_nao_usado: primeiro CS nativo não usado quando usar GPIO
 * @max_cs_nativo: valor máximo para validação de CS nativos
 * @estatisticas_cpu: estatísticas por CPU
 * @dma_tx: canal DMA de transmissão
 * @dma_rx: canal DMA de recepção
 * @rx_dummy: buffer de recepção dummy para dispositivos full-duplex
 * @tx_dummy: buffer de transmissão dummy para dispositivos full-duplex
 * @traduzir_cs_firmware: traduz numeração entre firmware e Linux
 * @suporta_ptp_sts: se driver fornece snapshot de tempo para SPI
 * @sinalizadores_irq: estado de interrupção durante marcação de tempo PTP
 * @fila_vazia: sinal verde para pular fila em transfers spi_sync
 * @deve_ser_async: desativa todos os caminhos rápidos no core
 */
struct spi_controlador {
	struct device	dispositivo;
	struct list_head lista;
	s16		numero_barramento;
	u16		num_selecao_chip;
	u16		alinhamento_dma;
	u32		bits_modo;
	u32		bits_sobreescrita_largura;
	u32		mascara_bits_por_palavra;
	u32		freq_min_hz;
	u32		freq_max_hz;
	u16		sinalizadores;
	bool		alocado_devm;
	union {
		bool	eh_escravo;
		bool	eh_alvo;
	};
	size_t (*tamanho_maximo_transferencia)(struct spi_dispositivo *spi);
	size_t (*tamanho_maximo_mensagem)(struct spi_dispositivo *spi);
	struct mutex	mutex_io;
	struct mutex	trava_adicao;
	spinlock_t	trava_barramento_spinlock;
	struct mutex	trava_barramento_mutex;
	bool		sinalizador_trava_barramento;
	int		(*configurar)(struct spi_dispositivo *spi);
	int		(*configurar_temporizacao_cs)(struct spi_dispositivo *spi);
	int		(*transferir)(struct spi_dispositivo *spi, struct spi_mensagem *msg);
	void		(*limpar)(struct spi_dispositivo *spi);
	bool		(*pode_dma)(struct spi_controlador *ctlr, struct spi_dispositivo *spi, struct spi_transferencia *xfer);
	struct device	*dispositivo_mapeamento_dma;
	struct device	*dispositivo_rx_dma_atual;
	struct device	*dispositivo_tx_dma_atual;
	bool		enfileirado;
	struct kthread_worker	*trabalhador_kernel;
	struct kthread_work	bombeamento_mensagens;
	spinlock_t	trava_fila;
	struct list_head fila;
	struct spi_mensagem	*mensagem_atual;
	struct completion	completacao_mensagem_atual;
	bool		mensagem_atual_incompleta;
	bool		mensagem_atual_necessita_completacao;
	bool		ocupado;
	bool		executando;
	bool		tempo_real;
	bool		auto_pm_runtime;
	bool		mensagem_atual_mapeada;
	bool		fallback;
	bool		ultimo_modo_cs_alto;
	s8		ultimo_cs[SPI_CS_CNT_MAX];
	u32		ultimo_cs_index_mask : SPI_CS_CNT_MAX;
	struct completion	completacao_transferencia;
	size_t		comprimento_maximo_dma;
	int (*otimizar_mensagem)(struct spi_mensagem *msg);
	int (*desotimizar_mensagem)(struct spi_mensagem *msg);
	int (*preparar_hardware_transferencia)(struct spi_controlador *ctlr);
	int (*transferir_uma_mensagem)(struct spi_controlador *ctlr, struct spi_mensagem *msg);
	int (*despreparar_hardware_transferencia)(struct spi_controlador *ctlr);
	int (*preparar_mensagem)(struct spi_controlador *ctlr, struct spi_mensagem *mensagem);
	int (*despreparar_mensagem)(struct spi_controlador *ctlr, struct spi_mensagem *mensagem);
	union {
		int (*abortar_escravo)(struct spi_controlador *ctlr);
		int (*abortar_alvo)(struct spi_controlador *ctlr);
	};
	void (*configurar_cs)(struct spi_dispositivo *spi, bool habilitar);
	int (*transferir_um)(struct spi_controlador *ctlr, struct spi_dispositivo *spi, struct spi_transferencia *transferencia);
	void (*lidar_com_erro)(struct spi_controlador *ctlr, struct spi_mensagem *mensagem);
	const struct spi_controlador_operacoes_mem *operacoes_mem;
	const struct spi_controlador_capacidades_mem *capacidades_mem;
	struct gpio_desc	**gpiods_cs;
	bool		usar_descritores_gpio;
	s8		cs_nativo_nao_usado;
	s8		max_cs_nativo;
	struct spi_estatisticas __percpu	*estatisticas_cpu;
	struct dma_chan		*dma_tx;
	struct dma_chan		*dma_rx;
	void		*rx_dummy;
	void		*tx_dummy;
	int (*traduzir_cs_firmware)(struct spi_controlador *ctlr, unsigned cs);
	bool		suporta_ptp_sts;
	unsigned long	sinalizadores_irq;
	bool		fila_vazia;
	bool		deve_ser_async;
};

Estrutura spi_driver

Driver de protocolo do lado do host, usado para interagir com dispositivos SPI através de mensagens.

/**
 * struct spi_driver - driver de protocolo do lado do host
 * @tabela_id: Lista de dispositivos SPI suportados por este driver
 * @probe: Vincula este driver ao dispositivo SPI
 * @remove: Desvincula este driver do dispositivo SPI
 * @desligamento: Callback padrão de desligamento
 * @driver: Drivers de dispositivo SPI devem inicializar nome e proprietário
 */
struct spi_driver {
	const struct spi_id_dispositivo *tabela_id;
	int	(*probe)(struct spi_dispositivo *spi);
	int	(*remove)(struct spi_dispositivo *spi);
	void	(*desligamento)(struct spi_dispositivo *spi);
	struct device_driver	driver;
};

/* Definição de ID */
#define SPI_TAM_NOME	32
#define SPI_PREFIXO_MODULO "spi:"

struct spi_id_dispositivo {
	char nome[SPI_TAM_NOME];
	kernel_ulong_t dados_driver;
};

Estrutura spi_dispositivo

Representa um dispositivo escravo SPI do lado do controlador.

/**
 * struct spi_dispositivo - proxy do lado do controlador para um escravo SPI
 * @dispositivo: Representação do dispositivo no modelo de drivers
 * @controlador: Controlador SPI usado com o dispositivo
 * @freq_maxima_hz: Frequência máxima de clock para este chip
 * @selecao_chip: Array de seleção física de chip
 * @modo: Modo SPI define como os dados são transmitidos/recebidos
 * @bits_por_palavra: Tamanho da palavra em bits
 * @tempo_real: Tornar thread de bombeamento com prioridade real
 * @irq: Número de interrupção
 * @estado_controlador: Estado runtime do controlador
 * @dados_controlador: Definições específicas da placa
 * @modalias: Nome do driver para uso com este dispositivo
 * @substituicao_driver: Se nome de driver for escrito, vincula ao driver nomeado
 * @gpiod_cs: Array de descritores GPIO para seleção de chip
 * @atraso_palavra: atraso entre palavras consecutivas
 * @configuracao_cs: atraso após CS ser asserido
 * @manutencao_cs: atraso antes de CS ser desasserido
 * @cs_inativo: atraso após CS ser desasserido
 * @estatisticas_cpu: estatísticas por CPU
 * @mascara_index_cs: máscara de bits dos chip selects ativos
 */
struct spi_dispositivo {
	struct device		dispositivo;
	struct spi_controlador	*controlador;
	u32		freq_maxima_hz;
	u8		selecao_chip[SPI_CS_CNT_MAX];
	u8		bits_por_palavra;
	bool		tempo_real;
#define SPI_SEM_TX		BIT(31)
#define SPI_SEM_RX		BIT(30)
#define SPI_TPM_CONTROLE_HW	BIT(29)
#define SPI_MASCARA_MODO_KERNEL	(~(BIT(29) - 1))
	u32		modo;
	int		irq;
	void		*estado_controlador;
	void		*dados_controlador;
	char		modalias[SPI_TAM_NOME];
	const char	*substituicao_driver;
	struct gpio_desc	*gpiod_cs[SPI_CS_CNT_MAX];
	struct spi_delay	atraso_palavra;
	struct spi_delay	configuracao_cs;
	struct spi_delay	manutencao_cs;
	struct spi_delay	cs_inativo;
	struct spi_estatisticas __percpu	*estatisticas_cpu;
	u32		mascara_index_cs : SPI_CS_CNT_MAX;
};

Driver de Barramento spi.c

Localização: drivers/spi/spi.c. O arquivo contém mais de 5000 linhas, mas é funcional. A inicialização ocorre em postcore_initcall(spi_init), que registra o tipo de barramento, classe e recursos opcionais.

Estrutura do barramento registrada:

const struct bus_type tipo_barramento_spi = {
	.nome		= "spi",
	.grupos_dispositivo	= grupos_dispositivo_spi,
	.combinar		= combinar_dispositivo_spi,
	.uevent		= uevent_spi,
	.probe		= probe_spi,
	.remove		= remover_spi,
	.desligamento	= desligamento_spi,
};
EXPORT_SYMBOL_GPL(tipo_barramento_spi);

As funções de transferência de dados incluem spi_sincronizar para operações síncronas e spi_assincronizar para assíncronas, gerenciando filas e sincronização.

Drivers de Controlador spi-xxx.c

Fornecidos por fabricantse de SoC para inicializar o controlador SPI específico, como o driver spi-xilinx.c da Xilinx.

Driver de Suporte ao Usuário spidev.c

Localização: drivers/spi/spidev.c. Fornece a interface de espaço de usuário para SPI, registrando um dispositivo de caractere, classe e driver SPI.

O driver define operações de arquivo para open/close/read/write/ioctl, permitindo que aplicações acessem dispositivos SPI via /dev/spidevB.C.

static const struct file_operations operacoes_arquivo_spidev = {
	.proprietario =	THIS_MODULE,
	.escrever =	spidev_escrever,
	.ler =		spidev_ler,
	.ioctl_desbloqueado = spidev_ioctl,
	.compat_ioctl = spidev_compat_ioctl,
	.abrir =	spidev_abrir,
	.liberar =	spidev_liberar,
	.llseek =	sem_llseek,
};

A estrutura de dados privada mantém buffers e configurações:

struct dados_spidev {
	dev_t			devt;
	struct mutex		trava_spi;
	struct spi_dispositivo	*spi;
	struct list_head	entrada_dispositivo;
	struct mutex		trava_buffer;
	unsigned		usuarios;
	u8			*buffer_tx;
	u8			*buffer_rx;
	u32			freq_hz;
};

Interface de Espaço de Usuário

Para aplicações em espaço de usuário, a API SPI é acessada através do dispositivo de caractere /dev/spidevB.C, com comandos ioctl para configuração e transferência de dados. A estrutura spi_ioc_transferencia define os parâmetros de transferência:

struct spi_ioc_transferencia {
	__u64		buf_tx;
	__u64		buf_rx;
	__u32		comprimento;
	__u32		freq_hz;
	__u16		atraso_usecs;
	__u8		bits_por_palavra;
	__u8		alteracao_cs;
	__u8		nbits_tx;
	__u8		nbits_rx;
	__u8		atraso_palavra_usecs;
	__u8		pad;
};

Tags: SPI protocol Linux Kernel Device Drivers Embedded Systems SPI controller

Publicado em 8-9 22:33