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