Declaração do problema
O Linux possui uma interface padrão para trabalhar com o GPIO através do sysfs. A documentação para isso pode ser encontrada
aqui .
Em resumo, existem arquivos "exportar" e "não exportar" na pasta "/ sys / class / gpio". Ao escrever o número X no arquivo de exportação, você pode abrir a interface no espaço do usuário para controlar o GPIOX
Após abrir a interface, a pasta / sys / class / gpio / gpioX / aparecerá na qual existirão arquivos como "value" ou "direction", escrevendo "in" ou "out" no arquivo "direction" e escrevendo 1 ou 0 no arquivo "Valor" pode controlar a saída GPIO diretamente da linha de comando.
Para que o comando “echo X> / sys / class / gpio / export” crie a pasta “gpioX”, o driver do controlador GPIO deve ser registrado no kernel, que abre a interface para as linhas GPIO.
Aconteceu que estou trabalhando na portabilidade do coreboot para uma placa personalizada baseada no processador Intel Haswell i7 [Para quem não sabe, o coreboot é um código aberto do projeto BIOS de código aberto (
https://www.coreboot.org/ ) ] A ponte sul do LynxpointLP, na qual existem 94 linhas GPIO, está embutida no meu processador. E eu queria abri-los no sysfs ...
Solução de problemas (comunicação de driver e dispositivo no Linux)
Após uma breve pesquisa no código do kernel, descobri que esse driver já foi gravado, está localizado no arquivo "drivers \ gpio \ gpio-lynxpoint.c" e é ativado usando o Kconfig
config GPIO_LYNXPOINT tristate "Intel Lynxpoint GPIO support" depends on ACPI && X86 select GPIOLIB_IRQCHIP help driver for GPIO functionality on Intel Lynxpoint PCH chipset Requires ACPI device enumeration code to set up a platform device.
A opção GPIO_LYNXPOINT foi ativada no kernel com o qual eu estava trabalhando, no entanto, não havia uma única pasta "gpiochipN" para o controlador GPIO na pasta "/ sys / class / gpio /" (que deveria ser), e mesmo esse script não exportou nenhum linhas
$ for i in {0..255}; do echo $i > /sys/class/gpio/export; done
Observando o código de inicialização principal ou a documentação dessa ponte sul, é possível ver que o controlador GPIO não é um dispositivo PCI separado. Faz parte de outro dispositivo PCI: LPC Interface Bridge. Usando os registros do espaço de configuração PCI deste dispositivo, você deve ativar o controlador GPIO e atribuí-lo BASE_ADDRESS no espaço de E / S. Isso abrirá uma janela no espaço de E / S de 1KV. Ao escrever / ler bytes nesta janela, você pode controlar as linhas GPIO.
O que podemos ver no código coreboot:
southbridge \ intel \ lynxpoint \ pch.h:
#define DEFAULT_GPIOBASE 0x1400 #define DEFAULT_GPIOSIZE 0x400 ... #define GPIO_BASE 0x48 #define GPIO_CNTL 0x4C ... #define PCH_LPC_DEV PCI_DEV(0, 0x1f, 0)
southbridge \ intel \ lynxpoint \ early_pch.c:
pci_write_config32(PCH_LPC_DEV, GPIO_BASE, DEFAULT_GPIOBASE|1); pci_write_config8(PCH_LPC_DEV, GPIO_CNTL, 0x10);
Se olharmos para os registros de dispositivos LPC no Linux através de "lspci -xxx", veremos que os dados gravados por nós estão nesses registros. Então, tudo parece estar configurado como deveria.
Continuando a examinar o código do driver, notei que o driver do Linux se comunica com o dispositivo através do campo .acpi_match_table. Como nosso dispositivo não pode ser enumerado (ele não está localizado no barramento PCI ou USB), ele precisa de um driver de plataforma, e a conexão desse driver com o dispositivo é via tabelas ACPI. O caso usual do x86, no caso do ARM, registraríamos nosso dispositivo no DeviceTree ou usaríamos os códigos antigos no kernel.
drivers \ gpio \ gpio-lynxpoint.c:
static const struct acpi_device_id lynxpoint_gpio_acpi_match[] = { { "INT33C7", 0 }, { "INT3437", 0 }, { } }; MODULE_DEVICE_TABLE(acpi, lynxpoint_gpio_acpi_match); static struct platform_driver lp_gpio_driver = { .probe = lp_gpio_probe, .remove = lp_gpio_remove, .driver = { .name = "lp_gpio", .pm = &lp_gpio_pm_ops, .acpi_match_table = ACPI_PTR(lynxpoint_gpio_acpi_match), }, };
Funciona assim: se o kernel, ao analisar a tabela ACPI, vir um dispositivo com o identificador _HID "INT33C7", ele tentará encontrar o driver da plataforma para ele com identificadores correspondentes nos campos da estrutura ".driver-> acpi_match_table".
Quando uma correspondência é encontrada, o Linux executa a função do driver .probe.
Como se viu, o código ACPI para este dispositivo foi apresentado no coreboot, acabei de comentar. Comentou devido ao fato de que para este dispositivo o Windows não conseguiu encontrar o driver e exibiu "Dispositivo desconhecido" no gerenciador de dispositivos. Mais sobre isso abaixo.
Então, estamos interessados nas informações do arquivo
src \ southbridge \ intel \ lynxpoint \ acpi \ serialio.asl (o código é um pouco simplificado):
Scope (\_SB) { Device (PCI0) { ... Device (GPIO) {
Para entender esse código em detalhes, você deve se familiarizar com a sintaxe ASL na
especificação ACPI .
Mas, em resumo, esse código cria um dispositivo com o identificador "INT33C7", que possui 2 recursos:
I/O memory: 1400-17ff; IRQ: 14;
Dentro de sua função .probe Linux, o driver recebe os recursos do dispositivo acima da seguinte maneira:
io_rc = platform_get_resource(pdev, IORESOURCE_IO, 0); irq_rc = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
Com base nesses dados, o código do driver preencherá a estrutura gpio_chip e registrará o controlador gpio no sistema, o que o tornará acessível através da interface sysfs.
Após devolver o código ASL do dispositivo e recompilar a imagem do BIOS, o sistema conseguiu acessar o GPIO por meio de sysfs.
Para começar, a pasta “gpiochip162” apareceu em / sys / class / gpio. Esta pasta contém o arquivo "base" e "ngpio". O arquivo base é responsável pelo número do primeiro GPIO deste controlador, ngpio pelo número.
$ cat /sys/class/gpio/gpiochip162/base 162 $ cat /sys/class/gpio/gpiochip162/ngpio 94
Assim, tudo foi exportado como deveria. Executamos o script:
$ for i in {162..255}; do echo $i > /sys/class/gpio/export; done
Depois disso, as subpastas gpioN aparecerão na pasta / sys / class / gpio /, dentro da qual haverá arquivos para controlar o status da linha.
Alguns comentários:
- A pasta / sys / class / gpio162 / é responsável pelo gerenciamento do GPIO0, a pasta / sys / class / gpio163 / é responsável pelo GPIO1 Essa mudança ocorreu devido ao fato de o driver durante a inicialização da estrutura de controle “struct gpio_chip" atribuir "gc-> base = -1;". Ou seja, eu deixei o kernel para escolher os números pessoalmente. Isso geralmente não é crítico, mas vale a pena lembrar.
- O acesso é concedido apenas às linhas GPIO configuradas como GPIO, e não como nenhum recurso nativo da Southbridge. Para essas linhas, o driver exibe informações em dmesg: "gpio% d reservado para ACPI". No caso do coreboot, o GPIO é configurado no arquivo “gpio.h” na pasta da placa-mãe.
- O dispositivo e o driver também podem ser mapeados usando o método _CID (Compatible ID), e a documentação para nosso tópico no kernel é apresentada no documento “Enumeração de dispositivos baseados em ACPI”
Vale ressaltar que o dispositivo INT33C7 não possui 2 placas-mãe proprietárias no mesmo chipset (do IBASE e DFI) nas tabelas ACPI. É verdade que as linhas GPIO provavelmente não são exibidas (eu não olhei a documentação naquele momento em detalhes).
Identificador "INT33C7"
Depois de aumentar a funcionalidade do sysfs, tive uma pergunta: de onde veio o número de identificação “INT33C7”?
Depois de examinar a documentação do método _HID, ficou claro que vale a pena examinar
http://www.uefi.org/PNP_ACPI_Registry_HID (ID do hardware)_HID (ID do hardware)Este objeto é usado para fornecer ao OSPM a ID PNP ou ACPI ID do dispositivo *
Ao descrever uma plataforma, o uso de qualquer objeto _HID é opcional. No entanto, um objeto _HID deve ser
usado para descrever qualquer dispositivo que será enumerado pelo OSPM. OSPM enumera apenas um dispositivo
quando nenhum enumerador de barramento puder detectar o ID do dispositivo. Por exemplo, os dispositivos em um barramento ISA são
enumerados pelo OSPM. Use o objeto _ADR para descrever dispositivos enumerados por enumeradores de barramento
diferente do OSPM.
Argumentos:Nenhuma
Valor de retorno:Um número inteiro ou string contendo o HID
Um objeto _HID é avaliado como um ID do tipo EISA numérico compactado de 32 bits ou uma sequência de caracteres. Se um
string, o formato deve ser um ID alfanumérico de PNP ou ACPI sem asterisco ou outro
caracteres.
Um ID PNP válido deve estar no formato "AAA ####", em que A é uma letra maiúscula e # é um hexadecimal
dígito. Um ID de ACPI válido deve estar no formato "NNNN ####", em que N é uma letra maiúscula ou um
dígito ('0' - '9') e # é um dígito hexadecimal. Esta especificação reserva a cadeia "ACPI" para uso apenas
com lista de dispositivos definidos. Além disso, reserva todas as seqüências que representam 4 dígitos HEX para
uso exclusivo com IDs de fornecedor atribuídos ao PCI.
* -PNP ID e ACPI ID Registry estão em
http://www.uefi.org/PNP_ACPI_Registry Existem 3 pontos neste link:
- todos os tipos de identificadores de três letras (PNP ID) são indicados aqui
- Os IDs de PNP iniciados por "PNP" reservados pela Microsoft são indicados aqui.
- todos os tipos de identificadores de quatro letras (ACPI ID) são indicados aqui
Não está muito claro por que, mas na lista de IDs do PNP, você pode descobrir que os identificadores "INT" estão reservados na INTERPHASE CORPORATION:
INTERPHASE CORPORATION INT 11/29/1996
Aparentemente, uma lista única de identificadores completos de dispositivo (parte da letra + digital) não é publicada. Mas, com a ajuda do Google, foi possível encontrar listas de dispositivos e seus _HID, por exemplo,
aqui ou
aqui .
Eles indicam:
INT33C7=Intel Serial I/O GPIO Host Controller
E, julgando pelo restante das linhas desta lista, todos os dispositivos INTxxxx são dispositivos Intel (agora parece bastante óbvio, mas a conexão com a INTERPHASE CORPORATION ainda não está clara; também não está muito claro por que a numeração começa com números tão grandes, mas é visível em Discrição da Intel).
Driver e dispositivo de comunicação no Windows
Tendo satisfeito minha curiosidade, decidi baixar o Windows na minha placa. Como esperado, o sistema não conseguiu encontrar um driver para o dispositivo. Não houve ajuda dos drivers para as placas IBASE e DFI, o que é compreensível, porque no BIOS dessas placas este dispositivo não é indicado.
Consegui encontrar um driver
no site da MicrosoftNo entanto, esse driver é apresentado apenas para o Windows 8.1 e superior. Ainda estou trabalhando com o Windows 7.
No entanto, tentei baixar um dos drivers e especificar sua pasta ao procurar um driver para o meu dispositivo desconhecido.
No entanto, o expedidor não pôde mapear o driver para o dispositivo. Embora o arquivo inf contenha claramente informações sobre o dispositivo INT33C7.
[Manufacturer] %INTEL%=Intel,NTamd64.6.3 [Intel.NTamd64.6.3] %iaLPSS_GPIO.DeviceDesc_LPT%=iaLPSS_GPIO_Device, ACPI\INT33C7 %iaLPSS_GPIO.DeviceDesc_WPT%=iaLPSS_GPIO_Device, ACPI\INT3437
No processo de análise do arquivo INF, verificou-se que a seção [Fabricante] indicava claramente que não era para o meu sistema:
O que significa Intel.NTamd64.6.3 pode ser entendido a
partir da descrição :
nt[Architecture][.[OSMajorVersion][.[OSMinorVersion] OSMajorVersion=6 => Windows 7/Windows 8.1/Windows Server 2012 R2/... OSMinorVersion=3 => Windows 8.1/Windows Server 2012 R2
Tentando empurrar o driver do Windows 7, substituindo Intel.NTamd64.6.3 por Intel.NTamd64.6.1, para dizer o mínimo, falhou, pois me deu uma tela azul da morte e um sistema operacional não inicializável, e por isso tive que fazer uma recuperação.
O driver do Win7 foi encontrado apenas em um site incompreensível na Internet e, em seguida, o dispositivo no gerenciador de dispositivos é exibido com um ponto de exclamação.
Percebendo sua impotência, decidi testar a funcionalidade no Windows 10. Houve uma surpresa agradável.
O software de dispositivo de chipset Intel (Utilitário de atualização INF) instalou o driver do meu controlador sem problemas.

Como você pode ver, este dispositivo possui os recursos indicados por nós.

Em teoria, depois de instalar o driver com o controlador GPIO, provavelmente será possível trabalhar com as funções IOCTL (
como neste documento) .
No entanto, como não havia tarefa de programação GPIO no Windows, a busca por um documento semelhante para o meu chipset foi adiada.
Conclusão:
Este artigo examinou a conexão entre o driver e o dispositivo usando o método _HID ACPI. Essa comunicação pode ser necessária em um sistema x86 para dispositivos que não podem ser enumerados.
- No caso do Linux, a comunicação com o driver é via .acpi_match_table
- No caso do Windows, a comunicação com o driver é feita através de um arquivo INF