Import Raspberry Pi foundation pinctl utility

This commit is contained in:
Tony Kuker 2024-05-31 23:42:45 +00:00
parent 13cfc6f3a0
commit e1bf397515
12 changed files with 3807 additions and 0 deletions

View File

@ -0,0 +1,12 @@
cmake_minimum_required(VERSION 3.10...3.27)
include(GNUInstallDirs)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Werror -pedantic")
#set project name
project(pinctrl)
#add executables
add_executable(pinctrl pinctrl.c gpiolib.c util.c gpiochip_bcm2835.c gpiochip_bcm2712.c gpiochip_rp1.c)
install(TARGETS pinctrl RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
install(FILES pinctrl-completion.bash RENAME pinctrl DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/bash-completion/completions")

36
cpp/hal/pinctrl/README.md Normal file
View File

@ -0,0 +1,36 @@
# pinctrl
pinctrl is a more powerful replacement for raspi-gpio, a tool for displaying
and modifying the GPIO and pin muxing state of a system. It accesses the
hardware directly, bypassing the kernel drivers, and as such requires root
privilege (run with "sudo").
The improvements over raspi-gpio include:
* GPIO controllers are detected at runtime from Device Tree.
* GPIOs can be referred to by name or number.
* Pin mode (-p) switches the UI to be in terms of 40-way header pin numbers.
* The "poll" command causes it to constantly monitor the specified pins,
displaying any level changes it sees. For slow signals (up to a few hundred
kHz) it can act as a basic logic analyser.
* The "get" and "set" keywords are optional in most cases.
* Splitting into a general gpiolib library and a separate client application
allows new applications to be added easily.
**Build Instructions**
Install the prerequisites with "sudo apt install cmake" - you need at least version 3.10 of cmake. Run the following commands, either here or in the top-level directory to build and install everything:
- *cmake .*
- *make*
- *sudo make install*
**Usage**
* `sudo pinctrl` (Display the state of all recognised GPIOs)
* `sudo pinctrl -p` (Show the state of the 40-way header pins)
* `sudo pinctrl 4,6 op dl` (Make GPIOs 4 and 6 outputs, driving low)
* `sudo pinctrl poll BT_CTS,BT_RTS` (Monitor the levels of the Bluetooth flow control signals)
* `pinctrl funcs 9-11` (List the available alternate functions on GPIOs 9, 10 and 11)
* `pinctrl help` (Show the full usage guide)

View File

@ -0,0 +1,42 @@
#ifndef GPIOCHIP_H
#define GPIOCHIP_H
#include "gpiolib.h"
#define DECLARE_GPIO_CHIP(name, compatible, iface, size, data) \
GPIO_CHIP_T name ## _chip __attribute__ ((section ("gpiochips"))) __attribute__ ((used)) = \
{ #name, compatible, iface, size, data }
typedef struct GPIO_CHIP_INTERFACE_ GPIO_CHIP_INTERFACE_T;
typedef struct GPIO_CHIP_
{
const char *name;
const char *compatible;
const GPIO_CHIP_INTERFACE_T *interface;
int size;
uintptr_t data;
} GPIO_CHIP_T;
struct GPIO_CHIP_INTERFACE_
{
void * (*gpio_create_instance)(const GPIO_CHIP_T *chip, const char *dtnode);
int (*gpio_count)(void *priv);
void * (*gpio_probe_instance)(void *priv, volatile uint32_t *base);
GPIO_FSEL_T (*gpio_get_fsel)(void *priv, uint32_t gpio);
void (*gpio_set_fsel)(void *priv, uint32_t gpio, const GPIO_FSEL_T func);
void (*gpio_set_drive)(void *priv, uint32_t gpio, GPIO_DRIVE_T drv);
void (*gpio_set_dir)(void *priv, uint32_t gpio, GPIO_DIR_T dir);
GPIO_DIR_T (*gpio_get_dir)(void *priv, uint32_t gpio);
int (*gpio_get_level)(void *priv, uint32_t gpio); /* The actual level observed */
GPIO_DRIVE_T (*gpio_get_drive)(void *priv, uint32_t gpio); /* What it is being driven as */
GPIO_PULL_T (*gpio_get_pull)(void *priv, uint32_t gpio);
void (*gpio_set_pull)(void *priv, uint32_t gpio, GPIO_PULL_T pull);
const char * (*gpio_get_name)(void *priv, uint32_t gpio);
const char * (*gpio_get_fsel_name)(void *priv, uint32_t gpio, GPIO_FSEL_T fsel);
};
extern const GPIO_CHIP_T __start_gpiochips;
extern const GPIO_CHIP_T __stop_gpiochips;
#endif

View File

@ -0,0 +1,875 @@
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "gpiochip.h"
#include "util.h"
#define ARRAY_SIZE(_a) (sizeof(_a)/sizeof(_a[0]))
/* 2712 definitions */
#define BCM2712_GIO_DATA 0x04
#define BCM2712_GIO_IODIR 0x08
#define BCM2712_PAD_PULL_OFF 0
#define BCM2712_PAD_PULL_DOWN 1
#define BCM2712_PAD_PULL_UP 2
#define BCM2712_MAX_INSTANCES 2
#define BCM2712_FSEL_COUNT 9
#define FLAGS_AON 1
#define FLAGS_C0 2
#define FLAGS_D0 4
#define FLAGS_GPIO 8
#define FLAGS_PINCTRL 16
struct bcm2712_inst
{
volatile uint32_t *gpio_base;
volatile uint32_t *pinmux_base;
unsigned pad_offset;
uint32_t *bank_widths;
unsigned flags;
unsigned num_gpios;
unsigned num_banks;
};
static unsigned num_instances;
static struct bcm2712_inst bcm2712_instances[BCM2712_MAX_INSTANCES] = { 0 };
static unsigned shared_flags;
static const char *bcm2712_c0_gpio_alt_names[][BCM2712_FSEL_COUNT - 1] =
{
{ "BSC_M3_SDA" , "VC_SDA0" , "GPCLK0" , "ENET0_LINK" , "VC_PWM1_0" , "VC_SPI0_CE1_N" , "IR_IN" , }, // 0
{ "BSC_M3_SCL" , "VC_SCL0" , "GPCLK1" , "ENET0_ACTIVITY" , "VC_PWM1_1" , "SR_EDM_SENSE" , "VC_SPI0_CE0_N" , "VC_TXD3" , }, // 1
{ "PDM_CLK" , "I2S_CLK0_IN" , "GPCLK2" , "VC_SPI4_CE1_N" , "PKT_CLK0" , "VC_SPI0_MISO" , "VC_RXD3" , }, // 2
{ "PDM_DATA0" , "I2S_LR0_IN" , "VC_SPI4_CE0_N" , "PKT_SYNC0" , "VC_SPI0_MOSI" , "VC_CTS3" , }, // 3
{ "PDM_DATA1" , "I2S_DATA0_IN" , "ARM_RTCK" , "VC_SPI4_MISO" , "PKT_DATA0" , "VC_SPI0_SCLK" , "VC_RTS3" , }, // 4
{ "PDM_DATA2" , "VC_SCL3" , "ARM_TRST" , "SD_CARD_LED_E" , "VC_SPI4_MOSI" , "PKT_CLK1" , "VC_PCM_CLK" , "VC_SDA5" , }, // 5
{ "PDM_DATA3" , "VC_SDA3" , "ARM_TCK" , "SD_CARD_WPROT_E" , "VC_SPI4_SCLK" , "PKT_SYNC1" , "VC_PCM_FS" , "VC_SCL5" , }, // 6
{ "I2S_CLK0_OUT" , "SPDIF_OUT" , "ARM_TDI" , "SD_CARD_PRES_E" , "VC_SDA3" , "ENET0_RGMII_START_STOP", "VC_PCM_DIN" , "VC_SPI4_CE1_N" , }, // 7
{ "I2S_LR0_OUT" , "AUD_FS_CLK0" , "ARM_TMS" , "SD_CARD_VOLT_E" , "VC_SCL3" , "ENET0_MII_TX_ERR" , "VC_PCM_DOUT" , "VC_SPI4_CE0_N" , }, // 8
{ "I2S_DATA0_OUT" , "AUD_FS_CLK0" , "ARM_TDO" , "SD_CARD_PWR0_E" , "ENET0_MII_RX_ERR" , "SD_CARD_VOLT_C" , "VC_SPI4_SCLK" , }, // 9
{ "BSC_M3_SCL" , "MTSIF_DATA4_ALT1" , "I2S_CLK0_IN" , "I2S_CLK0_OUT" , "VC_SPI5_CE1_N" , "ENET0_MII_CRS" , "SD_CARD_PWR0_C" , "VC_SPI4_MOSI" , }, // 10
{ "BSC_M3_SDA" , "MTSIF_DATA5_ALT1" , "I2S_LR0_IN" , "I2S_LR0_OUT" , "VC_SPI5_CE0_N" , "ENET0_MII_COL" , "SD_CARD_PRES_C" , "VC_SPI4_MISO" , }, // 11
{ "SPI_S_SS0B" , "MTSIF_DATA6_ALT1" , "I2S_DATA0_IN" , "I2S_DATA0_OUT" , "VC_SPI5_MISO" , "VC_I2CSL_MOSI" , "SD0_CLK" , "SD_CARD_VOLT_D", }, // 12
{ "SPI_S_MISO" , "MTSIF_DATA7_ALT1" , "I2S_DATA1_OUT" , "USB_VBUS_PRESENT" , "VC_SPI5_MOSI" , "VC_I2CSL_CE_N" , "SD0_CMD" , "SD_CARD_PWR0_D", }, // 13
{ "SPI_S_MOSI_OR_BSC_S_SDA", "VC_I2CSL_SCL_SCLK", "ENET0_RGMII_RX_OK", "ARM_TCK" , "VC_SPI5_SCLK" , "VC_PWM0_0" , "VC_SDA4" , "SD_CARD_PRES_D", }, // 14
{ "SPI_S_SCK_OR_BSC_S_SCL" , "VC_I2CSL_SDA_MISO", "VC_SPI3_CE1_N" , "ARM_TMS" , "VC_PWM0_1" , "VC_SCL4" , "GPCLK0" , }, // 15
{ "SD_CARD_PRES_B" , "I2S_CLK0_OUT" , "VC_SPI3_CE0_N" , "I2S_CLK0_IN" , "SD0_DAT0" , "ENET0_RGMII_MDIO" , "GPCLK1" , }, // 16
{ "SD_CARD_WPROT_B" , "I2S_LR0_OUT" , "VC_SPI3_MISO" , "I2S_LR0_IN" , "EXT_SC_CLK" , "SD0_DAT1" , "ENET0_RGMII_MDC", "GPCLK2" , }, // 17
{ "SD_CARD_LED_B" , "I2S_DATA0_OUT" , "VC_SPI3_MOSI" , "I2S_DATA0_IN" , "SD0_DAT2" , "ENET0_RGMII_IRQ" , "VC_PWM1_0" , }, // 18
{ "SD_CARD_VOLT_B" , "USB_PWRFLT" , "VC_SPI3_SCLK" , "PKT_DATA1" , "SPDIF_OUT" , "SD0_DAT3" , "IR_IN" , "VC_PWM1_1" , }, // 19
{ "SD_CARD_PWR0_B" , "UUI_TXD" , "VC_TXD0" , "ARM_TMS" , "UART_TXD_2" , "USB_PWRON" , "VC_PCM_CLK" , "VC_TXD4" , }, // 20
{ "USB_PWRFLT" , "UUI_RXD" , "VC_RXD0" , "ARM_TCK" , "UART_RXD_2" , "SD_CARD_VOLT_B" , "VC_PCM_FS" , "VC_RXD4" , }, // 21
{ "USB_PWRON" , "ENET0_LINK" , "VC_CTS0" , "MTSIF_ATS_RST" , "UART_RTS_2" , "USB_VBUS_PRESENT" , "VC_PCM_DIN" , "VC_SDA5" , }, // 22
{ "USB_VBUS_PRESENT" , "ENET0_ACTIVITY" , "VC_RTS0" , "MTSIF_ATS_INC" , "UART_CTS_2" , "I2S_DATA2_OUT" , "VC_PCM_DOUT" , "VC_SCL5" , }, // 23
{ "MTSIF_ATS_RST" , "PKT_CLK0" , "UART_RTS_0" , "ENET0_RGMII_RX_CLK", "ENET0_RGMII_START_STOP", "VC_SDA4" , "VC_TXD3" , }, // 24
{ "MTSIF_ATS_INC" , "PKT_SYNC0" , "SC0_CLK" , "UART_CTS_0" , "ENET0_RGMII_RX_EN_CTL" , "ENET0_RGMII_RX_OK" , "VC_SCL4" , "VC_RXD3" , }, // 25
{ "MTSIF_DATA1" , "PKT_DATA0" , "SC0_IO" , "UART_TXD_0" , "ENET0_RGMII_RXD_00" , "VC_TXD4" , "VC_SPI5_CE0_N" , }, // 26
{ "MTSIF_DATA2" , "PKT_CLK1" , "SC0_AUX1" , "UART_RXD_0" , "ENET0_RGMII_RXD_01" , "VC_RXD4" , "VC_SPI5_SCLK" , }, // 27
{ "MTSIF_CLK" , "PKT_SYNC1" , "SC0_AUX2" , "ENET0_RGMII_RXD_02", "VC_CTS4" , "VC_SPI5_MOSI" , }, // 28
{ "MTSIF_DATA0" , "PKT_DATA1" , "SC0_PRES" , "ENET0_RGMII_RXD_03", "VC_RTS4" , "VC_SPI5_MISO" , }, // 29
{ "MTSIF_SYNC" , "PKT_CLK2" , "SC0_RST" , "SD2_CLK" , "ENET0_RGMII_TX_CLK" , "GPCLK0" , "VC_PWM0_0" , }, // 30
{ "MTSIF_DATA3" , "PKT_SYNC2" , "SC0_VCC" , "SD2_CMD" , "ENET0_RGMII_TX_EN_CTL" , "VC_SPI3_CE1_N" , "VC_PWM0_1" , }, // 31
{ "MTSIF_DATA4" , "PKT_DATA2" , "SC0_VPP" , "SD2_DAT0" , "ENET0_RGMII_TXD_00" , "VC_SPI3_CE0_N" , "VC_TXD3" , }, // 32
{ "MTSIF_DATA5" , "PKT_CLK3" , "SD2_DAT1" , "ENET0_RGMII_TXD_01", "VC_SPI3_SCLK" , "VC_RXD3" , }, // 33
{ "MTSIF_DATA6" , "PKT_SYNC3" , "EXT_SC_CLK" , "SD2_DAT2" , "ENET0_RGMII_TXD_02" , "VC_SPI3_MOSI" , "VC_SDA5" , }, // 34
{ "MTSIF_DATA7" , "PKT_DATA3" , "SD2_DAT3" , "ENET0_RGMII_TXD_03", "VC_SPI3_MISO" , "VC_SCL5" , }, // 35
{ "SD0_CLK" , "MTSIF_ATS_RST" , "SC0_RST" , "I2S_DATA1_IN" , "VC_TXD3" , "VC_TXD2" , }, // 36
{ "SD0_CMD" , "MTSIF_ATS_INC" , "SC0_VCC" , "VC_SPI0_CE1_N" , "I2S_DATA2_IN" , "VC_RXD3" , "VC_RXD2" , }, // 37
{ "SD0_DAT0" , "MTSIF_DATA4_ALT" , "SC0_VPP" , "VC_SPI0_CE0_N" , "I2S_DATA3_IN" , "VC_CTS3" , "VC_RTS2" , }, // 38
{ "SD0_DAT1" , "MTSIF_DATA5_ALT" , "SC0_CLK" , "VC_SPI0_MISO" , "VC_RTS3" , "VC_CTS2" , }, // 39
{ "SD0_DAT2" , "MTSIF_DATA6_ALT" , "SC0_IO" , "VC_SPI0_MOSI" , "BSC_M3_SDA" , }, // 40
{ "SD0_DAT3" , "MTSIF_DATA7_ALT" , "SC0_PRES" , "VC_SPI0_SCLK" , "BSC_M3_SCL" , }, // 41
{ "VC_SPI0_CE1_N" , "MTSIF_CLK_ALT" , "VC_SDA0" , "SD_CARD_PRES_A" , "MTSIF_CLK_ALT1" , "ARM_TRST" , "PDM_CLK" , "SPI_M_SS1B" , }, // 42
{ "VC_SPI0_CE0_N" , "MTSIF_SYNC_ALT" , "VC_SCL0" , "SD_CARD_PWR0_A" , "MTSIF_SYNC_ALT1" , "ARM_RTCK" , "PDM_DATA0" , "SPI_M_SS0B" , }, // 43
{ "VC_SPI0_MISO" , "MTSIF_DATA0_ALT" , "ENET0_LINK" , "SD_CARD_LED_A" , "MTSIF_DATA0_ALT1" , "ARM_TDO" , "PDM_DATA1" , "SPI_M_MISO" , }, // 44
{ "VC_SPI0_MOSI" , "MTSIF_DATA1_ALT" , "ENET0_ACTIVITY" , "SD_CARD_VOLT_A" , "MTSIF_DATA1_ALT1" , "ARM_TCK" , "PDM_DATA2" , "SPI_M_MOSI" , }, // 45
{ "VC_SPI0_SCLK" , "MTSIF_DATA2_ALT" , "SD_CARD_WPROT_A" , "MTSIF_DATA2_ALT1" , "ARM_TDI" , "PDM_DATA3" , "SPI_M_SCK" , }, // 46
{ "ENET0_ACTIVITY" , "MTSIF_DATA3_ALT" , "I2S_DATA3_OUT" , "MTSIF_DATA3_ALT1" , "ARM_TMS" , }, // 47
{ "SC0_RST" , "USB_PWRFLT" , "SPDIF_OUT" , "MTSIF_ATS_RST" , }, // 48
{ "SC0_VCC" , "USB_PWRON" , "AUD_FS_CLK0" , "MTSIF_ATS_INC" , }, // 49
{ "SC0_VPP" , "USB_VBUS_PRESENT" , "SC0_AUX1" , }, // 50
{ "SC0_CLK" , "ENET0_LINK" , "SC0_AUX2" , "SR_EDM_SENSE" , }, // 51
{ "SC0_IO" , "ENET0_ACTIVITY" , "VC_PWM1_1" , }, // 52
{ "SC0_PRES" , "ENET0_RGMII_RX_OK", "EXT_SC_CLK" , }, // 53
};
static const char *bcm2712_d0_gpio_alt_names[][BCM2712_FSEL_COUNT - 1] =
{
{ "" }, // 0
{ "VC_SCL0" , "USB_PWRFLT" , "GPCLK0" , "SD_CARD_LED_E" , "VC_SPI3_CE1_N" , "SR_EDM_SENSE" , "VC_SPI0_CE0_N" , "VC_TXD0" , }, // 1
{ "VC_SDA0" , "USB_PWRON" , "GPCLK1" , "SD_CARD_WPROT_E" , "VC_SPI3_CE0_N" , "CLK_OBSERVE" , "VC_SPI0_MISO" , "VC_RXD0" , }, // 2
{ "VC_SCL3" , "USB_VBUS_PRESENT" , "GPCLK2" , "SD_CARD_PRES_E" , "VC_SPI3_MISO" , "VC_SPI0_MOSI" , "VC_CTS0" , }, // 3
{ "VC_SDA3" , "VC_PWM1_1" , "VC_SPI3_CE0_N" , "SD_CARD_VOLT_E" , "VC_SPI3_MOSI" , "VC_SPI0_SCLK" , "VC_RTS0" , }, // 4
{ "" }, // 5
{ "" }, // 6
{ "" }, // 7
{ "" }, // 8
{ "" }, // 9
{ "BSC_M3_SCL" , "VC_PWM1_0" , "VC_SPI3_CE1_N" , "SD_CARD_PWR0_E" , "VC_SPI3_SCLK" , "GPCLK0" , }, // 10
{ "BSC_M3_SDA" , "VC_SPI3_MISO" , "CLK_OBSERVE" , "SD_CARD_PRES_C" , "GPCLK1" , }, // 11
{ "SPI_S_SS0B" , "VC_SPI3_MOSI" , "SD_CARD_PWR0_C" , "SD_CARD_VOLT_D" , }, // 12
{ "SPI_S_MISO" , "VC_SPI3_SCLK" , "SD_CARD_PRES_C" , "SD_CARD_PWR0_D" , }, // 13
{ "SPI_S_MOSI_OR_BSC_S_SDA", "UUI_TXD" , "ARM_TCK" , "VC_PWM0_0" , "VC_SDA0" , "SD_CARD_PRES_D" , }, // 14
{ "SPI_S_SCK_OR_BSC_S_SCL" , "UUI_RXD" , "ARM_TMS" , "VC_PWM0_1" , "VC_SCL0" , "GPCLK0" , }, // 15
{ "" }, // 16
{ "" }, // 17
{ "SD_CARD_PRES_F" , "VC_PWM1_0" , }, // 18
{ "SD_CARD_PWR0_F" , "USB_PWRFLT" , "VC_PWM1_1" , }, // 19
{ "VC_SDA3" , "UUI_TXD" , "VC_TXD0" , "ARM_TMS" , "VC_TXD2" , }, // 20
{ "VC_SCL3" , "UUI_RXD" , "VC_RXD0" , "ARM_TCK" , "VC_RXD2" , }, // 21
{ "SD_CARD_PRES_F" , "VC_CTS0" , "VC_SDA3" , }, // 22
{ "VC_RTS0" , "VC_SCL3" , }, // 23
{ "SD_CARD_PRES_B" , "VC_SPI0_CE1_N" , "ARM_TRST" , "UART_RTS_0" , "USB_PWRFLT" , "VC_RTS2" , "VC_TXD0" , }, // 24
{ "SD_CARD_WPROT_B" , "VC_SPI0_CE0_N" , "ARM_TCK" , "UART_CTS_0" , "USB_PWRON" , "VC_CTS2" , "VC_RXD0" , }, // 25
{ "SD_CARD_LED_B" , "VC_SPI0_MISO" , "ARM_TDI" , "UART_TXD_0" , "USB_VBUS_PRESENT" , "VC_TXD2" , "VC_SPI0_CE0_N" , }, // 26
{ "SD_CARD_VOLT_B" , "VC_SPI0_MOSI" , "ARM_TMS" , "UART_RXD_0" , "VC_RXD2" , "VC_SPI0_SCLK" , }, // 27
{ "SD_CARD_PWR0_B" , "VC_SPI0_SCLK" , "ARM_TDO" , "VC_SDA0" , "VC_SPI0_MOSI" , }, // 28
{ "ARM_RTCK" , "VC_SCL0" , "VC_SPI0_MISO" , }, // 29
{ "SD2_CLK" , "GPCLK0" , "VC_PWM0_0" , }, // 30
{ "SD2_CMD" , "VC_SPI3_CE1_N" , "VC_PWM0_1" , }, // 31
{ "SD2_DAT0" , "VC_SPI3_CE0_N" , "VC_TXD3" , }, // 32
{ "SD2_DAT1" , "VC_SPI3_SCLK" , "VC_RXD3" , }, // 33
{ "SD2_DAT2" , "VC_SPI3_MOSI" , "VC_SDA5" , }, // 34
{ "SD2_DAT3" , "VC_SPI3_MISO" , "VC_SCL5" , }, // 35
};
static const char *bcm2712_c0_aon_gpio_alt_names[][BCM2712_FSEL_COUNT - 1] =
{
{ "IR_IN" , "VC_SPI0_CE1_N" , "VC_TXD3" , "VC_SDA3" , "TE0" , "VC_SDA0" , }, // 0
{ "VC_PWM0_0" , "VC_SPI0_CE0_N" , "VC_RXD3" , "VC_SCL3" , "TE1" , "AON_PWM0" , "VC_SCL0" , "VC_PWM1_0" , }, // 1
{ "VC_PWM0_1" , "VC_SPI0_MISO" , "VC_CTS3" , "CTL_HDMI_5V" , "FL0" , "AON_PWM1" , "IR_IN" , "VC_PWM1_1" , }, // 2
{ "IR_IN" , "VC_SPI0_MOSI" , "VC_RTS3" , "AON_FP_4SEC_RESETB", "FL1" , "SD_CARD_VOLT_G" , "AON_GPCLK" , }, // 3
{ "GPCLK0" , "VC_SPI0_SCLK" , "VC_I2CSL_SCL_SCLK", "AON_GPCLK" , "PM_LED_OUT" , "AON_PWM0" , "SD_CARD_PWR0_G", "VC_PWM0_0" , }, // 4
{ "GPCLK1" , "IR_IN" , "VC_I2CSL_SDA_MISO", "CLK_OBSERVE" , "AON_PWM1" , "SD_CARD_PRES_G" , "VC_PWM0_1" , }, // 5
{ "UART_TXD_1" , "VC_TXD4" , "GPCLK2" , "CTL_HDMI_5V" , "VC_TXD0" , "VC_SPI3_CE0_N" , }, // 6
{ "UART_RXD_1" , "VC_RXD4" , "GPCLK0" , "AON_PWM0" , "VC_RXD0" , "VC_SPI3_SCLK" , }, // 7
{ "UART_RTS_1" , "VC_RTS4" , "VC_I2CSL_MOSI" , "CTL_HDMI_5V" , "VC_RTS0" , "VC_SPI3_MOSI" , }, // 8
{ "UART_CTS_1" , "VC_CTS4" , "VC_I2CSL_CE_N" , "AON_PWM1" , "VC_CTS0" , "VC_SPI3_MISO" , }, // 9
{ "TSIO_CLK_OUT" , "CTL_HDMI_5V" , "SC0_AUX1" , "SPDIF_OUT" , "VC_SPI5_CE1_N", "USB_PWRFLT" , "AON_GPCLK" , "SD_CARD_VOLT_F", }, // 10
{ "TSIO_DATA_IN" , "UART_RTS_0" , "SC0_AUX2" , "AUD_FS_CLK0" , "VC_SPI5_CE0_N", "USB_VBUS_PRESENT", "VC_RTS2" , "SD_CARD_PWR0_F", }, // 11
{ "TSIO_DATA_OUT" , "UART_CTS_0" , "VC_RTS0" , "TSIO_VCTRL" , "VC_SPI5_MISO" , "USB_PWRON" , "VC_CTS2" , "SD_CARD_PRES_F", }, // 12
{ "BSC_M1_SDA" , "UART_TXD_0" , "VC_TXD0" , "UUI_TXD" , "VC_SPI5_MOSI" , "ARM_TMS" , "VC_TXD2" , "VC_SDA3" , }, // 13
{ "BSC_M1_SCL" , "UART_RXD_0" , "VC_RXD0" , "UUI_RXD" , "VC_SPI5_SCLK" , "ARM_TCK" , "VC_RXD2" , "VC_SCL3" , }, // 14
{ "IR_IN" , "AON_FP_4SEC_RESETB", "VC_CTS0" , "PM_LED_OUT" , "CTL_HDMI_5V" , "AON_PWM0" , "AON_GPCLK" , }, // 15
{ "AON_CPU_STANDBYB", "GPCLK0" , "PM_LED_OUT" , "CTL_HDMI_5V" , "VC_PWM0_0" , "USB_PWRON" , "AUD_FS_CLK0" , }, // 16
// Pad out the bank to 32 entries
{ "" }, { "" }, { "" }, { "" }, { "" }, { "" }, { "" }, // 17-23
{ "" }, { "" }, { "" }, { "" }, { "" }, { "" }, { "" }, { "" }, // 24-31
{ "HDMI_TX0_BSC_SCL", "HDMI_TX0_AUTO_I2C_SCL", "BSC_M0_SCL", "VC_SCL0", }, // sgpio 0
{ "HDMI_TX0_BSC_SDA", "HDMI_TX0_AUTO_I2C_SDA", "BSC_M0_SDA", "VC_SDA0", }, // sgpio 1
{ "HDMI_TX1_BSC_SCL", "HDMI_TX1_AUTO_I2C_SCL", "BSC_M1_SCL", "VC_SCL4", "CTL_HDMI_5V", }, // sgpio 2
{ "HDMI_TX1_BSC_SDA", "HDMI_TX1_AUTO_I2C_SDA", "BSC_M1_SDA", "VC_SDA4", }, // sgpio 3
{ "AVS_PMU_BSC_SCL", "BSC_M2_SCL", "VC_SCL5", "CTL_HDMI_5V", }, // sgpio 4
{ "AVS_PMU_BSC_SDA", "BSC_M2_SDA", "VC_SDA5", }, // sgpio 5
};
static const char *bcm2712_d0_aon_gpio_alt_names[][BCM2712_FSEL_COUNT - 1] =
{
{ "IR_IN" , "VC_SPI0_CE1_N" , "VC_TXD0" , "VC_SDA3" , "UART_TXD_0" , "VC_SDA0" , }, // 0
{ "VC_PWM0_0" , "VC_SPI0_CE0_N" , "VC_RXD0" , "VC_SCL3" , "UART_RXD_0" , "AON_PWM0" , "VC_SCL0" , "VC_PWM1_0" , }, // 1
{ "VC_PWM0_1" , "VC_SPI0_MISO" , "VC_CTS0" , "CTL_HDMI_5V" , "UART_CTS_0" , "AON_PWM1" , "IR_IN" , "VC_PWM1_1" , }, // 2
{ "IR_IN" , "VC_SPI0_MOSI" , "VC_RTS0" , "UART_RTS_0" , "SD_CARD_VOLT_G" , "AON_GPCLK" , }, // 3
{ "GPCLK0" , "VC_SPI0_SCLK" , "PM_LED_OUT" , "AON_PWM0" , "SD_CARD_PWR0_G" , "VC_PWM0_0" , }, // 4
{ "GPCLK1" , "IR_IN" , "AON_PWM1" , "SD_CARD_PRES_G" , "VC_PWM0_1" , }, // 5
{ "UART_TXD_1" , "VC_TXD2" , "CTL_HDMI_5V" , "GPCLK2" , "VC_SPI3_CE0_N" , }, // 6
{ "" }, // 7
{ "UART_RTS_1" , "VC_RTS2" , "CTL_HDMI_5V" , "VC_SPI0_CE1_N" , "VC_SPI3_SCLK" , }, // 8
{ "UART_CTS_1" , "VC_CTS2" , "VC_CTS0" , "AON_PWM1" , "VC_SPI0_CE0_N" , "VC_RTS2" , "VC_SPI3_MOSI" , }, // 9
{ "" }, // 10
{ "" }, // 11
{ "UART_RXD_1" , "VC_RXD2" , "VC_RTS0" , "VC_SPI0_MISO" , "USB_PWRON" , "VC_CTS2" , "VC_SPI3_MISO" , }, // 12
{ "BSC_M1_SDA" , "VC_TXD0" , "UUI_TXD" , "VC_SPI0_MOSI" , "ARM_TMS" , "VC_TXD2" , "VC_SDA3" , }, // 13
{ "BSC_M1_SCL" , "AON_GPCLK" , "VC_RXD0" , "UUI_RXD" , "VC_SPI0_SCLK" , "ARM_TCK" , "VC_RXD2" , "VC_SCL3" , }, // 14
// Pad out the bank to 32 entries
{ "" }, // 15
{ "" }, { "" }, { "" }, { "" }, { "" }, { "" }, { "" }, { "" }, // 16-23
{ "" }, { "" }, { "" }, { "" }, { "" }, { "" }, { "" }, { "" }, // 24-31
{ "HDMI_TX0_BSC_SCL", "HDMI_TX0_AUTO_I2C_SCL", "BSC_M0_SCL", "VC_SCL0", }, // sgpio 0
{ "HDMI_TX0_BSC_SDA", "HDMI_TX0_AUTO_I2C_SDA", "BSC_M0_SDA", "VC_SDA0", }, // sgpio 1
{ "HDMI_TX1_BSC_SCL", "HDMI_TX1_AUTO_I2C_SCL", "BSC_M1_SCL", "VC_SCL0", "CTL_HDMI_5V", }, // sgpio 2
{ "HDMI_TX1_BSC_SDA", "HDMI_TX1_AUTO_I2C_SDA", "BSC_M1_SDA", "VC_SDA0", }, // sgpio 3
{ "AVS_PMU_BSC_SCL", "BSC_M2_SCL", "VC_SCL3", "CTL_HDMI_5V", }, // sgpio 4
{ "AVS_PMU_BSC_SDA", "BSC_M2_SDA", "VC_SDA3", }, // sgpio 5
};
static const int bcm2712_gpio_d0_to_c0[] =
{
-1, 0, 1, 2, 3, -1, -1, -1, -1, -1,
4, 5, 6, 7, 8, 9, -1, -1, 10, 11,
12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
22, 23, 24, 25, 26, 27
};
static const int bcm2712_gpio_aon_d0_to_c0[] =
{
0, 1, 2, 3, 4, 5, 6, -1, 7, 8,
-1, -1, 9, 10, 11, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1,
32, 33, 34, 35, 36, 37
};
static volatile uint32_t *bcm2712_gpio_base(struct bcm2712_inst *inst,
unsigned gpio,
unsigned int *bit)
{
unsigned bank = gpio / 32;
gpio %= 32;
if ((bank >= inst->num_banks) || (gpio >= inst->bank_widths[bank]) ||
!inst->gpio_base)
return NULL;
*bit = gpio;
return inst->gpio_base + bank * (0x20 / 4);
}
static volatile uint32_t *bcm2712_pinmux_base(struct bcm2712_inst *inst,
unsigned gpio,
unsigned int *bit)
{
unsigned bank, gpio_offset;
if (gpio >= inst->num_gpios || !inst->pinmux_base)
return NULL;
if (inst->flags & FLAGS_D0)
{
if (inst->flags & FLAGS_AON)
gpio = bcm2712_gpio_aon_d0_to_c0[gpio];
else
gpio = bcm2712_gpio_d0_to_c0[gpio];
if ((int)gpio < 0)
return NULL;
}
bank = gpio / 32;
gpio_offset = gpio % 32;
if ((bank >= inst->num_banks) || (gpio_offset >= inst->bank_widths[bank]))
return NULL;
if (inst->flags & FLAGS_AON)
{
if (bank == 1)
{
if (gpio_offset == 4)
{
*bit = 0;
return inst->pinmux_base + 1;
}
else if (gpio_offset == 5)
{
*bit = 0;
return inst->pinmux_base + 2;
}
else
{
*bit = gpio_offset * 4;
return inst->pinmux_base;
}
}
*bit = (gpio_offset % 8) * 4;
return inst->pinmux_base + 3 + (gpio_offset / 8);
}
*bit = (gpio_offset % 8) * 4;
return inst->pinmux_base + (bank * 4) + (gpio_offset / 8);
}
static volatile uint32_t *bcm2712_pad_base(struct bcm2712_inst *inst,
unsigned gpio,
unsigned int *bit)
{
unsigned bank, gpio_offset;
if (gpio >= inst->num_gpios || !inst->pinmux_base)
return NULL;
if (inst->flags & FLAGS_D0)
{
if (inst->flags & FLAGS_AON)
gpio = bcm2712_gpio_aon_d0_to_c0[gpio];
else
gpio = bcm2712_gpio_d0_to_c0[gpio];
if ((int)gpio < 0)
return NULL;
}
bank = gpio / 32;
gpio_offset = gpio % 32;
if ((bank >= inst->num_banks) || (gpio_offset >= inst->bank_widths[bank]))
return NULL;
if ((inst->flags & FLAGS_AON) && (bank > 0))
{
/* There is no SGPIO pad control (that I know of) */
return NULL;
}
gpio = gpio_offset + inst->pad_offset;
*bit = (gpio % 15) * 2;
return inst->pinmux_base + (gpio / 15);
}
static int bcm2712_gpio_get_level(void *priv, unsigned gpio)
{
struct bcm2712_inst *inst = priv;
unsigned int bit;
volatile uint32_t *gpio_base = bcm2712_gpio_base(inst, gpio, &bit);
if (!gpio_base)
return -1;
return !!(gpio_base[BCM2712_GIO_DATA / 4] & (1 << bit));
}
static void bcm2712_gpio_set_drive(void *priv, unsigned gpio, GPIO_DRIVE_T drv)
{
struct bcm2712_inst *inst = priv;
unsigned int bit;
volatile uint32_t *gpio_base = bcm2712_gpio_base(inst, gpio, &bit);
uint32_t gpio_val;
if (!gpio_base)
return;
gpio_val = gpio_base[BCM2712_GIO_DATA / 4];
gpio_val = (gpio_val & ~(1U << bit)) | (drv << bit);
gpio_base[BCM2712_GIO_DATA / 4] = gpio_val;
}
static GPIO_DRIVE_T bcm2712_gpio_get_drive(void *priv, unsigned gpio)
{
struct bcm2712_inst *inst = priv;
unsigned int bit;
volatile uint32_t *gpio_base = bcm2712_gpio_base(inst, gpio, &bit);
uint32_t gpio_val;
if (!gpio_base)
return DRIVE_MAX;
gpio_val = gpio_base[BCM2712_GIO_DATA / 4];
return (gpio_val & (1U << bit)) ? DRIVE_HIGH : DRIVE_LOW;
}
static void bcm2712_gpio_set_dir(void *priv, unsigned gpio, GPIO_DIR_T dir)
{
struct bcm2712_inst *inst = priv;
unsigned int bit;
volatile uint32_t *gpio_base = bcm2712_gpio_base(inst, gpio, &bit);
uint32_t gpio_val;
if (!gpio_base)
return;
gpio_val = gpio_base[BCM2712_GIO_IODIR / 4];
gpio_val &= ~(1U << bit);
gpio_val |= ((dir == DIR_INPUT) << bit);
gpio_base[BCM2712_GIO_IODIR / 4] = gpio_val;
}
static GPIO_DIR_T bcm2712_gpio_get_dir(void *priv, unsigned gpio)
{
struct bcm2712_inst *inst = priv;
unsigned int bit;
volatile uint32_t *gpio_base = bcm2712_gpio_base(inst, gpio, &bit);
uint32_t gpio_val;
if (!gpio_base)
return DIR_MAX;
gpio_val = gpio_base[BCM2712_GIO_IODIR / 4];
return (gpio_val & (1U << bit)) ? DIR_INPUT : DIR_OUTPUT;
}
static GPIO_FSEL_T bcm2712_pinctrl_get_fsel(void *priv, unsigned gpio)
{
struct bcm2712_inst *inst = priv;
unsigned int pinmux_bit;
volatile uint32_t *pinmux_base = bcm2712_pinmux_base(inst, gpio, &pinmux_bit);
int fsel;
if (!pinmux_base)
return -1;
fsel = ((*pinmux_base >> pinmux_bit) & 0xf);
if (fsel == 0)
return GPIO_FSEL_GPIO;
else if (fsel < BCM2712_FSEL_COUNT)
return GPIO_FSEL_FUNC1 + (fsel - 1);
else if (fsel == 0xf) // Choose one value as a considered NONE
return GPIO_FSEL_NONE;
/* Unknown FSEL */
return -1;
}
static void bcm2712_pinctrl_set_fsel(void *priv, unsigned gpio, const GPIO_FSEL_T func)
{
struct bcm2712_inst *inst = priv;
unsigned int pinmux_bit;
volatile uint32_t *pinmux_base = bcm2712_pinmux_base(inst, gpio, &pinmux_bit);
uint32_t pinmux_val;
int fsel;
if (!pinmux_base)
return;
if (func == GPIO_FSEL_INPUT || func == GPIO_FSEL_OUTPUT || func == GPIO_FSEL_GPIO)
{
// Set direction before switching
// N.B. We explicitly interpret a request for FUNC_A0/GPIO as "last/current GPIO dir"
fsel = 0;
if (func == GPIO_FSEL_INPUT)
bcm2712_gpio_set_dir(priv, gpio, DIR_INPUT);
else if (func == GPIO_FSEL_OUTPUT)
bcm2712_gpio_set_dir(priv, gpio, DIR_OUTPUT);
}
else if (func >= GPIO_FSEL_FUNC0 && func <= GPIO_FSEL_FUNC8)
{
fsel = func - GPIO_FSEL_FUNC0;
}
else
{
return;
}
pinmux_val = *pinmux_base;
pinmux_val &= ~(0xf << pinmux_bit);
pinmux_val |= (fsel << pinmux_bit);
*pinmux_base = pinmux_val;
}
static GPIO_PULL_T bcm2712_pinctrl_get_pull(void *priv, unsigned gpio)
{
struct bcm2712_inst *inst = priv;
unsigned int bit;
volatile uint32_t *pad_base = bcm2712_pad_base(inst, gpio, &bit);
uint32_t pad_val;
if (!pad_base)
return PULL_MAX;
pad_val = (*pad_base >> bit) & 0x3;
switch (pad_val)
{
case BCM2712_PAD_PULL_OFF:
return PULL_NONE;
case BCM2712_PAD_PULL_DOWN:
return PULL_DOWN;
case BCM2712_PAD_PULL_UP:
return PULL_UP;
default:
return PULL_MAX; /* This is an error */
}
}
static void bcm2712_pinctrl_set_pull(void *priv, unsigned gpio, GPIO_PULL_T pull)
{
struct bcm2712_inst *inst = priv;
unsigned int bit = 0;
volatile uint32_t *pad_base = bcm2712_pad_base(inst, gpio, &bit);
uint32_t padval;
int val;
if (!pad_base)
return;
switch (pull)
{
case PULL_NONE:
val = BCM2712_PAD_PULL_OFF;
break;
case PULL_DOWN:
val = BCM2712_PAD_PULL_DOWN;
break;
case PULL_UP:
val = BCM2712_PAD_PULL_UP;
break;
default:
assert(0);
return;
}
padval = *pad_base;
padval &= ~(3 << bit);
padval |= (val << bit);
*pad_base = padval;
}
static void *bcm2712_gpio_create_instance(const GPIO_CHIP_T *chip,
const char *dtnode)
{
struct bcm2712_inst *inst = NULL;
uint32_t *widths;
unsigned num_banks, inst_gpios;
unsigned flags = FLAGS_GPIO | chip->data;
unsigned bank;
unsigned i;
widths = dt_read_cells(dtnode, "brcm,gpio-bank-widths", &num_banks);
if (!widths)
return NULL;
inst_gpios = 0;
for (bank = 0; bank < num_banks; bank++)
{
inst_gpios = ROUND_UP(inst_gpios, 32) + widths[bank];
}
flags |= shared_flags;
if (widths[0] < 32)
{
flags |= FLAGS_AON;
if (widths[0] == 15)
flags |= FLAGS_D0;
else
flags |= FLAGS_C0;
}
else if (!(flags & (FLAGS_C0 | FLAGS_D0)))
{
size_t names_len;
char *names = dt_read_prop(dtnode, "gpio-line-names", &names_len);
if (!names[0])
flags |= FLAGS_D0;
dt_free(names);
}
shared_flags |= (flags & (FLAGS_C0 | FLAGS_D0));
/* look for a corresponding pinctrl instance */
for (i = 0; i < num_instances; i++)
{
struct bcm2712_inst *pinctrl_inst = &bcm2712_instances[i];
pinctrl_inst->flags |= shared_flags;
if (!((pinctrl_inst->flags ^ flags) & FLAGS_AON))
{
if (pinctrl_inst->flags & FLAGS_GPIO)
{
assert(!"duplicate gpio nodes?");
return NULL;
}
inst = pinctrl_inst;
break;
}
}
if (!inst)
{
if (num_instances == BCM2712_MAX_INSTANCES)
return NULL;
inst = &bcm2712_instances[num_instances++];
}
inst->num_gpios = inst_gpios;
inst->num_banks = num_banks;
inst->bank_widths = widths;
inst->flags |= flags;
return (void *)inst;
}
static int bcm2712_gpio_count(void *priv)
{
struct bcm2712_inst *inst = priv;
return inst->num_gpios;
}
static void *bcm2712_gpio_probe_instance(void *priv, volatile uint32_t *base)
{
struct bcm2712_inst *inst = priv;
inst->gpio_base = base;
return inst;
}
static void *bcm2712_pinctrl_create_instance(const GPIO_CHIP_T *chip,
const char *dtnode)
{
struct bcm2712_inst *inst = NULL;
unsigned flags = FLAGS_PINCTRL | chip->data;
unsigned reg_cells, reg_size;
uint32_t *reg;
unsigned i;
if (dtnode)
{
reg = dt_read_cells(dtnode, "reg", &reg_cells);
if (!reg || reg_cells < 2)
return NULL;
reg_size = (reg_cells > 1) ? reg[reg_cells - 1] : 0;
dt_free(reg);
switch (reg_size)
{
case 0x1c:
assert((flags & FLAGS_AON) && (flags & FLAGS_D0));
break;
case 0x20:
assert(((flags & FLAGS_AON) && !(flags & FLAGS_D0)) ||
(!(flags & FLAGS_AON) && (flags & FLAGS_D0)));
break;
case 0x30:
assert(!(flags & FLAGS_AON) && !(flags & FLAGS_D0));
break;
default:
assert(0);
}
}
shared_flags |= (flags & (FLAGS_C0 | FLAGS_D0));
/* look for a corresponding gpio instance */
for (i = 0; i < num_instances; i++)
{
struct bcm2712_inst *gpio_inst = &bcm2712_instances[i];
gpio_inst->flags |= shared_flags;
if (!((gpio_inst->flags ^ flags) & FLAGS_AON))
{
if (gpio_inst->flags & FLAGS_PINCTRL)
{
assert(!"duplicate pinctrl nodes?");
return NULL;
}
inst = gpio_inst;
break;
}
}
if (!inst)
{
if (num_instances == BCM2712_MAX_INSTANCES)
return NULL;
inst = &bcm2712_instances[num_instances++];
}
inst->flags |= flags;
return (void *)inst;
}
static int bcm2712_pinctrl_count(void *priv)
{
struct bcm2712_inst *inst = priv;
if (inst->flags & FLAGS_GPIO)
return 0; /* Don't occupy any GPIO space */
if (!inst->num_gpios)
{
switch (inst->flags & (FLAGS_AON | FLAGS_C0 | FLAGS_D0))
{
case 0:
case FLAGS_C0:
inst->num_gpios = 54;
break;
case FLAGS_D0:
inst->num_gpios = 36;
break;
case FLAGS_AON:
case FLAGS_AON | FLAGS_D0:
case FLAGS_AON | FLAGS_C0:
inst->num_gpios = 38;
break;
default:
break;
}
}
return inst->num_gpios;
}
static void *bcm2712_pinctrl_probe_instance(void *priv, volatile uint32_t *base)
{
struct bcm2712_inst *inst = priv;
unsigned pad_offset;
inst->pinmux_base = base;
switch (inst->flags & (FLAGS_D0 | FLAGS_C0 | FLAGS_AON))
{
case FLAGS_C0:
default:
pad_offset = 112;
break;
case FLAGS_D0:
pad_offset = 65;
break;
case FLAGS_AON:
case FLAGS_C0 | FLAGS_AON:
pad_offset = 100;
break;
case FLAGS_D0 | FLAGS_AON:
pad_offset = 84;
break;
}
inst->pad_offset = pad_offset;
return inst;
}
static const char *bcm2712_pinctrl_get_fsel_name(void *priv, unsigned gpio, GPIO_FSEL_T fsel)
{
struct bcm2712_inst *inst = priv;
const char *name = NULL;
switch (fsel)
{
case GPIO_FSEL_GPIO:
case GPIO_FSEL_FUNC0:
name = "gpio";
break;
case GPIO_FSEL_INPUT:
name = "input";
break;
case GPIO_FSEL_OUTPUT:
name = "output";
break;
case GPIO_FSEL_NONE:
name = "none";
break;
case GPIO_FSEL_FUNC1:
case GPIO_FSEL_FUNC2:
case GPIO_FSEL_FUNC3:
case GPIO_FSEL_FUNC4:
case GPIO_FSEL_FUNC5:
case GPIO_FSEL_FUNC6:
case GPIO_FSEL_FUNC7:
case GPIO_FSEL_FUNC8:
if (gpio < inst->num_gpios)
{
switch (inst->flags & (FLAGS_AON | FLAGS_C0 | FLAGS_D0))
{
case FLAGS_C0 | FLAGS_AON:
case FLAGS_AON:
name = bcm2712_c0_aon_gpio_alt_names[gpio][fsel - 1];
break;
case FLAGS_C0:
case 0:
name = bcm2712_c0_gpio_alt_names[gpio][fsel - 1];
break;
case FLAGS_D0 | FLAGS_AON:
name = bcm2712_d0_aon_gpio_alt_names[gpio][fsel - 1];
break;
case FLAGS_D0:
name = bcm2712_d0_gpio_alt_names[gpio][fsel - 1];
break;
}
if (!name)
name = "-";
}
break;
default:
break;
}
return name;
}
static const char *bcm2712_gpio_get_name(void *priv, unsigned gpio)
{
struct bcm2712_inst *inst = priv;
const char *fsel_name;
static char name_buf[16];
unsigned gpio_offset;
unsigned bank;
fsel_name = bcm2712_pinctrl_get_fsel_name(priv, gpio, GPIO_FSEL_FUNC1);
if (!fsel_name || !fsel_name[0])
return NULL;
bank = gpio / 32;
gpio_offset = gpio % 32;
if ((inst->flags & FLAGS_GPIO) &&
((bank >= inst->num_banks) ||
(gpio_offset >= inst->bank_widths[bank])))
return NULL;
if (inst->flags & FLAGS_AON)
{
if (bank == 1)
sprintf(name_buf, "AON_SGPIO%d", gpio_offset);
else
sprintf(name_buf, "AON_GPIO%d", gpio_offset);
}
else
{
sprintf(name_buf, "GPIO%d", gpio);
}
return name_buf;
}
static const GPIO_CHIP_INTERFACE_T bcm2712_gpio_interface =
{
.gpio_create_instance = bcm2712_gpio_create_instance,
.gpio_count = bcm2712_gpio_count,
.gpio_probe_instance = bcm2712_gpio_probe_instance,
.gpio_get_fsel = bcm2712_pinctrl_get_fsel,
.gpio_set_fsel = bcm2712_pinctrl_set_fsel,
.gpio_set_drive = bcm2712_gpio_set_drive,
.gpio_set_dir = bcm2712_gpio_set_dir,
.gpio_get_dir = bcm2712_gpio_get_dir,
.gpio_get_level = bcm2712_gpio_get_level,
.gpio_get_drive = bcm2712_gpio_get_drive,
.gpio_get_pull = bcm2712_pinctrl_get_pull,
.gpio_set_pull = bcm2712_pinctrl_set_pull,
.gpio_get_name = bcm2712_gpio_get_name,
.gpio_get_fsel_name = bcm2712_pinctrl_get_fsel_name,
};
DECLARE_GPIO_CHIP(brcmstb, "brcm,brcmstb-gpio",
&bcm2712_gpio_interface, 0x40, 0);
static const GPIO_CHIP_INTERFACE_T bcm2712_pinctrl_interface =
{
.gpio_create_instance = bcm2712_pinctrl_create_instance,
.gpio_count = bcm2712_pinctrl_count,
.gpio_probe_instance = bcm2712_pinctrl_probe_instance,
.gpio_get_fsel = bcm2712_pinctrl_get_fsel,
.gpio_set_fsel = bcm2712_pinctrl_set_fsel,
.gpio_set_drive = bcm2712_gpio_set_drive,
.gpio_set_dir = bcm2712_gpio_set_dir,
.gpio_get_dir = bcm2712_gpio_get_dir,
.gpio_get_level = bcm2712_gpio_get_level,
.gpio_get_drive = bcm2712_gpio_get_drive,
.gpio_get_pull = bcm2712_pinctrl_get_pull,
.gpio_set_pull = bcm2712_pinctrl_set_pull,
.gpio_get_name = bcm2712_gpio_get_name,
.gpio_get_fsel_name = bcm2712_pinctrl_get_fsel_name,
};
DECLARE_GPIO_CHIP(bcm2712, "brcm,bcm2712-pinctrl",
&bcm2712_pinctrl_interface, 0x30, 0);
DECLARE_GPIO_CHIP(bcm2712_aon, "brcm,bcm2712-aon-pinctrl",
&bcm2712_pinctrl_interface, 0x20, FLAGS_AON);
DECLARE_GPIO_CHIP(bcm2712c0, "brcm,bcm2712c0-pinctrl",
&bcm2712_pinctrl_interface, 0x30, FLAGS_C0);
DECLARE_GPIO_CHIP(bcm2712c0_aon, "brcm,bcm2712c0-aon-pinctrl",
&bcm2712_pinctrl_interface, 0x20, FLAGS_C0 | FLAGS_AON);
DECLARE_GPIO_CHIP(bcm2712d0, "brcm,bcm2712d0-pinctrl",
&bcm2712_pinctrl_interface, 0x20, FLAGS_D0);
DECLARE_GPIO_CHIP(bcm2712d0_aon, "brcm,bcm2712d0-aon-pinctrl",
&bcm2712_pinctrl_interface, 0x1c, FLAGS_D0 | FLAGS_AON);

View File

@ -0,0 +1,462 @@
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "gpiochip.h"
#include "util.h"
#define ARRAY_SIZE(_a) (sizeof(_a)/sizeof(_a[0]))
#define BCM2835_NUM_GPIOS 54
#define BCM2835_ALT_COUNT 6
#define BCM2711_NUM_GPIOS 54
#define BCM2711_ALT_COUNT 6
/* 2835 register offsets */
#define GPFSEL0 0
#define GPFSEL1 1
#define GPFSEL2 2
#define GPFSEL3 3
#define GPFSEL4 4
#define GPFSEL5 5
#define GPSET0 7
#define GPSET1 8
#define GPCLR0 10
#define GPCLR1 11
#define GPLEV0 13
#define GPLEV1 14
#define GPPUD 37
#define GPPUDCLK0 38
#define GPPUDCLK1 39
/* 2711 has a different mechanism for pin pull-up/down/enable */
#define GPPUPPDN0 57 /* Pin pull-up/down for pins 15:0 */
#define GPPUPPDN1 58 /* Pin pull-up/down for pins 31:16 */
#define GPPUPPDN2 59 /* Pin pull-up/down for pins 47:32 */
#define GPPUPPDN3 60 /* Pin pull-up/down for pins 57:48 */
static const char *bcm2835_gpio_alt_names[BCM2835_NUM_GPIOS][BCM2835_ALT_COUNT] =
{
{ "SDA0" , "SA5" , "PCLK" , "AVEOUT_VCLK" , "AVEIN_VCLK" , 0 , },
{ "SCL0" , "SA4" , "DE" , "AVEOUT_DSYNC" , "AVEIN_DSYNC", 0 , },
{ "SDA1" , "SA3" , "LCD_VSYNC" , "AVEOUT_VSYNC" , "AVEIN_VSYNC", 0 , },
{ "SCL1" , "SA2" , "LCD_HSYNC" , "AVEOUT_HSYNC" , "AVEIN_HSYNC", 0 , },
{ "GPCLK0" , "SA1" , "DPI_D0" , "AVEOUT_VID0" , "AVEIN_VID0" , "ARM_TDI" , },
{ "GPCLK1" , "SA0" , "DPI_D1" , "AVEOUT_VID1" , "AVEIN_VID1" , "ARM_TDO" , },
{ "GPCLK2" , "SOE_N_SE" , "DPI_D2" , "AVEOUT_VID2" , "AVEIN_VID2" , "ARM_RTCK" , },
{ "SPI0_CE1_N", "SWE_N_SRW_N", "DPI_D3" , "AVEOUT_VID3" , "AVEIN_VID3" , 0 , },
{ "SPI0_CE0_N", "SD0" , "DPI_D4" , "AVEOUT_VID4" , "AVEIN_VID4" , 0 , },
{ "SPI0_MISO" , "SD1" , "DPI_D5" , "AVEOUT_VID5" , "AVEIN_VID5" , 0 , },
{ "SPI0_MOSI" , "SD2" , "DPI_D6" , "AVEOUT_VID6" , "AVEIN_VID6" , 0 , },
{ "SPI0_SCLK" , "SD3" , "DPI_D7" , "AVEOUT_VID7" , "AVEIN_VID7" , 0 , },
{ "PWM0" , "SD4" , "DPI_D8" , "AVEOUT_VID8" , "AVEIN_VID8" , "ARM_TMS" , },
{ "PWM1" , "SD5" , "DPI_D9" , "AVEOUT_VID9" , "AVEIN_VID9" , "ARM_TCK" , },
{ "TXD0" , "SD6" , "DPI_D10" , "AVEOUT_VID10" , "AVEIN_VID10", "TXD1" , },
{ "RXD0" , "SD7" , "DPI_D11" , "AVEOUT_VID11" , "AVEIN_VID11", "RXD1" , },
{ "FL0" , "SD8" , "DPI_D12" , "CTS0" , "SPI1_CE2_N" , "CTS1" , },
{ "FL1" , "SD9" , "DPI_D13" , "RTS0" , "SPI1_CE1_N" , "RTS1" , },
{ "PCM_CLK" , "SD10" , "DPI_D14" , "I2CSL_SDA_MOSI", "SPI1_CE0_N" , "PWM0" , },
{ "PCM_FS" , "SD11" , "DPI_D15" , "I2CSL_SCL_SCLK", "SPI1_MISO" , "PWM1" , },
{ "PCM_DIN" , "SD12" , "DPI_D16" , "I2CSL_MISO" , "SPI1_MOSI" , "GPCLK0" , },
{ "PCM_DOUT" , "SD13" , "DPI_D17" , "I2CSL_CE_N" , "SPI1_SCLK" , "GPCLK1" , },
{ "SD0_CLK" , "SD14" , "DPI_D18" , "SD1_CLK" , "ARM_TRST" , 0 , },
{ "SD0_CMD" , "SD15" , "DPI_D19" , "SD1_CMD" , "ARM_RTCK" , 0 , },
{ "SD0_DAT0" , "SD16" , "DPI_D20" , "SD1_DAT0" , "ARM_TDO" , 0 , },
{ "SD0_DAT1" , "SD17" , "DPI_D21" , "SD1_DAT1" , "ARM_TCK" , 0 , },
{ "SD0_DAT2" , "TE0" , "DPI_D22" , "SD1_DAT2" , "ARM_TDI" , 0 , },
{ "SD0_DAT3" , "TE1" , "DPI_D23" , "SD1_DAT3" , "ARM_TMS" , 0 , },
{ "SDA0" , "SA5" , "PCM_CLK" , "FL0" , 0 , 0 , },
{ "SCL0" , "SA4" , "PCM_FS" , "FL1" , 0 , 0 , },
{ "TE0" , "SA3" , "PCM_DIN" , "CTS0" , 0 , "CTS1" , },
{ "FL0" , "SA2" , "PCM_DOUT" , "RTS0" , 0 , "RTS1" , },
{ "GPCLK0" , "SA1" , "RING_OCLK" , "TXD0" , 0 , "TXD1" , },
{ "FL1" , "SA0" , "TE1" , "RXD0" , 0 , "RXD1" , },
{ "GPCLK0" , "SOE_N_SE" , "TE2" , "SD1_CLK" , 0 , 0 , },
{ "SPI0_CE1_N", "SWE_N_SRW_N", 0 , "SD1_CMD" , 0 , 0 , },
{ "SPI0_CE0_N", "SD0" , "TXD0" , "SD1_DAT0" , 0 , 0 , },
{ "SPI0_MISO" , "SD1" , "RXD0" , "SD1_DAT1" , 0 , 0 , },
{ "SPI0_MOSI" , "SD2" , "RTS0" , "SD1_DAT2" , 0 , 0 , },
{ "SPI0_SCLK" , "SD3" , "CTS0" , "SD1_DAT3" , 0 , 0 , },
{ "PWM0" , "SD4" , 0 , "SD1_DAT4" , "SPI2_MISO" , "TXD1" , },
{ "PWM1" , "SD5" , "TE0" , "SD1_DAT5" , "SPI2_MOSI" , "RXD1" , },
{ "GPCLK1" , "SD6" , "TE1" , "SD1_DAT6" , "SPI2_SCLK" , "RTS1" , },
{ "GPCLK2" , "SD7" , "TE2" , "SD1_DAT7" , "SPI2_CE0_N" , "CTS1" , },
{ "GPCLK1" , "SDA0" , "SDA1" , "TE0" , "SPI2_CE1_N" , 0 , },
{ "PWM1" , "SCL0" , "SCL1" , "TE1" , "SPI2_CE2_N" , 0 , },
{ "SDA0" , "SDA1" , "SPI0_CE0_N", 0 , 0 , "SPI2_CE1_N", },
{ "SCL0" , "SCL1" , "SPI0_MISO" , 0 , 0 , "SPI2_CE0_N", },
{ "SD0_CLK" , "FL0" , "SPI0_MOSI" , "SD1_CLK" , "ARM_TRST" , "SPI2_SCLK" , },
{ "SD0_CMD" , "GPCLK0" , "SPI0_SCLK" , "SD1_CMD" , "ARM_RTCK" , "SPI2_MOSI" , },
{ "SD0_DAT0" , "GPCLK1" , "PCM_CLK" , "SD1_DAT0" , "ARM_TDO" , 0 , },
{ "SD0_DAT1" , "GPCLK2" , "PCM_FS" , "SD1_DAT1" , "ARM_TCK" , 0 , },
{ "SD0_DAT2" , "PWM0" , "PCM_DIN" , "SD1_DAT2" , "ARM_TDI" , 0 , },
{ "SD0_DAT3" , "PWM1" , "PCM_DOUT" , "SD1_DAT3" , "ARM_TMS" , 0 , },
};
static const char *bcm2711_gpio_alt_names[BCM2711_NUM_GPIOS][BCM2711_ALT_COUNT] =
{
{ "SDA0" , "SA5" , "PCLK" , "SPI3_CE0_N" , "TXD2" , "SDA6" , },
{ "SCL0" , "SA4" , "DE" , "SPI3_MISO" , "RXD2" , "SCL6" , },
{ "SDA1" , "SA3" , "LCD_VSYNC" , "SPI3_MOSI" , "CTS2" , "SDA3" , },
{ "SCL1" , "SA2" , "LCD_HSYNC" , "SPI3_SCLK" , "RTS2" , "SCL3" , },
{ "GPCLK0" , "SA1" , "DPI_D0" , "SPI4_CE0_N" , "TXD3" , "SDA3" , },
{ "GPCLK1" , "SA0" , "DPI_D1" , "SPI4_MISO" , "RXD3" , "SCL3" , },
{ "GPCLK2" , "SOE_N_SE" , "DPI_D2" , "SPI4_MOSI" , "CTS3" , "SDA4" , },
{ "SPI0_CE1_N", "SWE_N_SRW_N", "DPI_D3" , "SPI4_SCLK" , "RTS3" , "SCL4" , },
{ "SPI0_CE0_N", "SD0" , "DPI_D4" , "I2CSL_CE_N" , "TXD4" , "SDA4" , },
{ "SPI0_MISO" , "SD1" , "DPI_D5" , "I2CSL_SDI_MISO", "RXD4" , "SCL4" , },
{ "SPI0_MOSI" , "SD2" , "DPI_D6" , "I2CSL_SDA_MOSI", "CTS4" , "SDA5" , },
{ "SPI0_SCLK" , "SD3" , "DPI_D7" , "I2CSL_SCL_SCLK", "RTS4" , "SCL5" , },
{ "PWM0_0" , "SD4" , "DPI_D8" , "SPI5_CE0_N" , "TXD5" , "SDA5" , },
{ "PWM0_1" , "SD5" , "DPI_D9" , "SPI5_MISO" , "RXD5" , "SCL5" , },
{ "TXD0" , "SD6" , "DPI_D10" , "SPI5_MOSI" , "CTS5" , "TXD1" , },
{ "RXD0" , "SD7" , "DPI_D11" , "SPI5_SCLK" , "RTS5" , "RXD1" , },
{ 0 , "SD8" , "DPI_D12" , "CTS0" , "SPI1_CE2_N" , "CTS1" , },
{ 0 , "SD9" , "DPI_D13" , "RTS0" , "SPI1_CE1_N" , "RTS1" , },
{ "PCM_CLK" , "SD10" , "DPI_D14" , "SPI6_CE0_N" , "SPI1_CE0_N" , "PWM0_0" , },
{ "PCM_FS" , "SD11" , "DPI_D15" , "SPI6_MISO" , "SPI1_MISO" , "PWM0_1" , },
{ "PCM_DIN" , "SD12" , "DPI_D16" , "SPI6_MOSI" , "SPI1_MOSI" , "GPCLK0" , },
{ "PCM_DOUT" , "SD13" , "DPI_D17" , "SPI6_SCLK" , "SPI1_SCLK" , "GPCLK1" , },
{ "SD0_CLK" , "SD14" , "DPI_D18" , "SD1_CLK" , "ARM_TRST" , "SDA6" , },
{ "SD0_CMD" , "SD15" , "DPI_D19" , "SD1_CMD" , "ARM_RTCK" , "SCL6" , },
{ "SD0_DAT0" , "SD16" , "DPI_D20" , "SD1_DAT0" , "ARM_TDO" , "SPI3_CE1_N" , },
{ "SD0_DAT1" , "SD17" , "DPI_D21" , "SD1_DAT1" , "ARM_TCK" , "SPI4_CE1_N" , },
{ "SD0_DAT2" , 0 , "DPI_D22" , "SD1_DAT2" , "ARM_TDI" , "SPI5_CE1_N" , },
{ "SD0_DAT3" , 0 , "DPI_D23" , "SD1_DAT3" , "ARM_TMS" , "SPI6_CE1_N" , },
{ "SDA0" , "SA5" , "PCM_CLK" , 0 , "MII_A_RX_ERR" , "RGMII_MDIO" , },
{ "SCL0" , "SA4" , "PCM_FS" , 0 , "MII_A_TX_ERR" , "RGMII_MDC" , },
{ 0 , "SA3" , "PCM_DIN" , "CTS0" , "MII_A_CRS" , "CTS1" , },
{ 0 , "SA2" , "PCM_DOUT" , "RTS0" , "MII_A_COL" , "RTS1" , },
{ "GPCLK0" , "SA1" , 0 , "TXD0" , "SD_CARD_PRES" , "TXD1" , },
{ 0 , "SA0" , 0 , "RXD0" , "SD_CARD_WRPROT" , "RXD1" , },
{ "GPCLK0" , "SOE_N_SE" , 0 , "SD1_CLK" , "SD_CARD_LED" , "RGMII_IRQ" , },
{ "SPI0_CE1_N", "SWE_N_SRW_N", 0 , "SD1_CMD" , "RGMII_START_STOP", 0 , },
{ "SPI0_CE0_N", "SD0" , "TXD0" , "SD1_DAT0" , "RGMII_RX_OK" , "MII_A_RX_ERR", },
{ "SPI0_MISO" , "SD1" , "RXD0" , "SD1_DAT1" , "RGMII_MDIO" , "MII_A_TX_ERR", },
{ "SPI0_MOSI" , "SD2" , "RTS0" , "SD1_DAT2" , "RGMII_MDC" , "MII_A_CRS" , },
{ "SPI0_SCLK" , "SD3" , "CTS0" , "SD1_DAT3" , "RGMII_IRQ" , "MII_A_COL" , },
{ "PWM1_0" , "SD4" , 0 , "SD1_DAT4" , "SPI0_MISO" , "TXD1" , },
{ "PWM1_1" , "SD5" , 0 , "SD1_DAT5" , "SPI0_MOSI" , "RXD1" , },
{ "GPCLK1" , "SD6" , 0 , "SD1_DAT6" , "SPI0_SCLK" , "RTS1" , },
{ "GPCLK2" , "SD7" , 0 , "SD1_DAT7" , "SPI0_CE0_N" , "CTS1" , },
{ "GPCLK1" , "SDA0" , "SDA1" , 0 , "SPI0_CE1_N" , "SD_CARD_VOLT", },
{ "PWM0_1" , "SCL0" , "SCL1" , 0 , "SPI0_CE2_N" , "SD_CARD_PWR0", },
{ "SDA0" , "SDA1" , "SPI0_CE0_N", 0 , 0 , "SPI2_CE1_N" , },
{ "SCL0" , "SCL1" , "SPI0_MISO" , 0 , 0 , "SPI2_CE0_N" , },
{ "SD0_CLK" , 0 , "SPI0_MOSI" , "SD1_CLK" , "ARM_TRST" , "SPI2_SCLK" , },
{ "SD0_CMD" , "GPCLK0" , "SPI0_SCLK" , "SD1_CMD" , "ARM_RTCK" , "SPI2_MOSI" , },
{ "SD0_DAT0" , "GPCLK1" , "PCM_CLK" , "SD1_DAT0" , "ARM_TDO" , "SPI2_MISO" , },
{ "SD0_DAT1" , "GPCLK2" , "PCM_FS" , "SD1_DAT1" , "ARM_TCK" , "SD_CARD_LED" , },
{ "SD0_DAT2" , "PWM0_0" , "PCM_DIN" , "SD1_DAT2" , "ARM_TDI" , 0 , },
{ "SD0_DAT3" , "PWM0_1" , "PCM_DOUT" , "SD1_DAT3" , "ARM_TMS" , 0 , },
};
static GPIO_FSEL_T bcm2835_gpio_get_fsel(void *priv, unsigned gpio)
{
volatile uint32_t *base = priv;
/* GPFSEL0-5 with 10 sels per reg, 3 bits per sel (so bits 0:29 used) */
uint32_t reg = GPFSEL0 + (gpio / 10);
uint32_t lsb = (gpio % 10) * 3;
if (gpio < BCM2835_NUM_GPIOS)
{
switch ((base[reg] >> lsb) & 7)
{
case 0: return GPIO_FSEL_INPUT;
case 1: return GPIO_FSEL_OUTPUT;
case 2: return GPIO_FSEL_FUNC5;
case 3: return GPIO_FSEL_FUNC4;
case 4: return GPIO_FSEL_FUNC0;
case 5: return GPIO_FSEL_FUNC1;
case 6: return GPIO_FSEL_FUNC2;
case 7: return GPIO_FSEL_FUNC3;
}
}
return GPIO_FSEL_MAX;
}
static void bcm2835_gpio_set_fsel(void *priv, unsigned gpio, const GPIO_FSEL_T func)
{
volatile uint32_t *base = priv;
/* GPFSEL0-5 with 10 sels per reg, 3 bits per sel (so bits 0:29 used) */
uint32_t reg = GPFSEL0 + (gpio / 10);
uint32_t lsb = (gpio % 10) * 3;
int fsel;
switch (func)
{
case GPIO_FSEL_INPUT: fsel = 0; break;
case GPIO_FSEL_OUTPUT: fsel = 1; break;
case GPIO_FSEL_FUNC0: fsel = 4; break;
case GPIO_FSEL_FUNC1: fsel = 5; break;
case GPIO_FSEL_FUNC2: fsel = 6; break;
case GPIO_FSEL_FUNC3: fsel = 7; break;
case GPIO_FSEL_FUNC4: fsel = 3; break;
case GPIO_FSEL_FUNC5: fsel = 2; break;
default:
return;
}
if (gpio < BCM2835_NUM_GPIOS)
base[reg] = (base[reg] & ~(0x7 << lsb)) | (fsel << lsb);
}
static GPIO_DIR_T bcm2835_gpio_get_dir(void *priv, unsigned gpio)
{
GPIO_FSEL_T fsel = bcm2835_gpio_get_fsel(priv, gpio);
if (fsel == GPIO_FSEL_INPUT)
return DIR_INPUT;
else if (fsel == GPIO_FSEL_OUTPUT)
return DIR_OUTPUT;
else
return DIR_MAX;
}
static void bcm2835_gpio_set_dir(void *priv, unsigned gpio, GPIO_DIR_T dir)
{
GPIO_FSEL_T fsel;
if (dir == DIR_INPUT)
fsel = GPIO_FSEL_INPUT;
else if (dir == DIR_OUTPUT)
fsel = GPIO_FSEL_OUTPUT;
else
return;
bcm2835_gpio_set_fsel(priv, gpio, fsel);
}
static int bcm2835_gpio_get_level(void *priv, unsigned gpio)
{
volatile uint32_t *base = priv;
if (gpio >= BCM2835_NUM_GPIOS)
return -1;
return (base[GPLEV0 + (gpio / 32)] >> (gpio % 32)) & 1;
}
GPIO_DRIVE_T bcm2835_gpio_get_drive(void *priv, unsigned gpio)
{
/* This is a write-only mechanism */
UNUSED(priv);
UNUSED(gpio);
return DRIVE_MAX;
}
static void bcm2835_gpio_set_drive(void *priv, unsigned gpio, GPIO_DRIVE_T drv)
{
volatile uint32_t *base = priv;
if (gpio < BCM2835_NUM_GPIOS && drv <= DRIVE_HIGH)
base[(drv ? GPSET0 : GPCLR0) + (gpio / 32)] = (1 << (gpio % 32));
}
static GPIO_PULL_T bcm2835_gpio_get_pull(void *priv, unsigned gpio)
{
/* This is a write-only mechanism */
UNUSED(priv);
UNUSED(gpio);
return PULL_MAX;
}
static void bcm2835_gpio_set_pull(void *priv, unsigned gpio, GPIO_PULL_T pull)
{
volatile uint32_t *base = priv;
int clkreg = GPPUDCLK0 + (gpio / 32);
int clkbit = 1 << (gpio % 32);
if (gpio >= BCM2835_NUM_GPIOS || pull < PULL_NONE || pull > PULL_UP)
return;
base[GPPUD] = pull;
usleep(10);
base[clkreg] = clkbit;
usleep(10);
base[GPPUD] = 0;
usleep(10);
base[clkreg] = 0;
usleep(10);
}
static const char *bcm2835_gpio_get_name(void *priv, unsigned gpio)
{
static char name_buf[16];
UNUSED(priv);
sprintf(name_buf, "GPIO%d", gpio);
return name_buf;
}
static const char *bcm2835_gpio_get_fsel_name(void *priv, unsigned gpio, GPIO_FSEL_T fsel)
{
const char *name = NULL;
UNUSED(priv);
switch (fsel)
{
case GPIO_FSEL_INPUT:
name = "input";
break;
case GPIO_FSEL_OUTPUT:
name = "output";
break;
case GPIO_FSEL_FUNC0:
case GPIO_FSEL_FUNC1:
case GPIO_FSEL_FUNC2:
case GPIO_FSEL_FUNC3:
case GPIO_FSEL_FUNC4:
case GPIO_FSEL_FUNC5:
if (gpio < BCM2835_NUM_GPIOS)
{
name = bcm2835_gpio_alt_names[gpio][fsel];
if (!name)
name = "-";
}
break;
default:
break;
}
return name;
}
static GPIO_PULL_T bcm2711_gpio_get_pull(void *priv, unsigned gpio)
{
volatile uint32_t *base = priv;
int reg = GPPUPPDN0 + (gpio / 16);
int lsb = (gpio % 16) * 2;
if (gpio < BCM2711_NUM_GPIOS)
{
switch ((base[reg] >> lsb) & 3)
{
case 0: return PULL_NONE;
case 1: return PULL_UP;
case 2: return PULL_DOWN;
}
}
return PULL_MAX;
}
static void bcm2711_gpio_set_pull(void *priv, unsigned gpio, GPIO_PULL_T pull)
{
volatile uint32_t *base = priv;
int reg = GPPUPPDN0 + (gpio / 16);
int lsb = (gpio % 16) * 2;
int pull_val;
if (gpio >= BCM2711_NUM_GPIOS)
return;
switch (pull)
{
case PULL_NONE:
pull_val = 0;
break;
case PULL_UP:
pull_val = 1;
break;
case PULL_DOWN:
pull_val = 2;
break;
default:
return;
}
base[reg] = (base[reg] & ~(3 << lsb)) | (pull_val << lsb);
}
static const char *bcm2711_gpio_get_fsel_name(void *priv, unsigned gpio, GPIO_FSEL_T fsel)
{
const char *name = NULL;
UNUSED(priv);
switch (fsel)
{
case GPIO_FSEL_INPUT:
name = "input";
break;
case GPIO_FSEL_OUTPUT:
name = "output";
break;
case GPIO_FSEL_FUNC0:
case GPIO_FSEL_FUNC1:
case GPIO_FSEL_FUNC2:
case GPIO_FSEL_FUNC3:
case GPIO_FSEL_FUNC4:
case GPIO_FSEL_FUNC5:
if (gpio < BCM2711_NUM_GPIOS)
{
name = bcm2711_gpio_alt_names[gpio][fsel];
if (!name)
name = "-";
}
break;
default:
break;
}
return name;
}
static void *bcm2835_gpio_create_instance(const GPIO_CHIP_T *chip,
const char *dtnode)
{
UNUSED(dtnode);
return (void *)chip;
}
static int bcm2835_gpio_count(void *priv)
{
UNUSED(priv);
return BCM2835_NUM_GPIOS;
}
static void *bcm2835_gpio_probe_instance(void *priv, volatile uint32_t *base)
{
UNUSED(priv);
return (void *)base;
}
static const GPIO_CHIP_INTERFACE_T bcm2835_gpio_interface =
{
.gpio_create_instance = bcm2835_gpio_create_instance,
.gpio_count = bcm2835_gpio_count,
.gpio_probe_instance = bcm2835_gpio_probe_instance,
.gpio_get_fsel = bcm2835_gpio_get_fsel,
.gpio_set_fsel = bcm2835_gpio_set_fsel,
.gpio_set_drive = bcm2835_gpio_set_drive,
.gpio_set_dir = bcm2835_gpio_set_dir,
.gpio_get_dir = bcm2835_gpio_get_dir,
.gpio_get_level = bcm2835_gpio_get_level,
.gpio_get_drive = bcm2835_gpio_get_drive,
.gpio_get_pull = bcm2835_gpio_get_pull,
.gpio_set_pull = bcm2835_gpio_set_pull,
.gpio_get_name = bcm2835_gpio_get_name,
.gpio_get_fsel_name = bcm2835_gpio_get_fsel_name,
};
DECLARE_GPIO_CHIP(bcm2835, "brcm,bcm2835-gpio", &bcm2835_gpio_interface,
0x30000, 0);
static const GPIO_CHIP_INTERFACE_T bcm2711_gpio_interface =
{
.gpio_create_instance = bcm2835_gpio_create_instance,
.gpio_count = bcm2835_gpio_count,
.gpio_probe_instance = bcm2835_gpio_probe_instance,
.gpio_get_fsel = bcm2835_gpio_get_fsel,
.gpio_set_fsel = bcm2835_gpio_set_fsel,
.gpio_set_drive = bcm2835_gpio_set_drive,
.gpio_set_dir = bcm2835_gpio_set_dir,
.gpio_get_dir = bcm2835_gpio_get_dir,
.gpio_get_level = bcm2835_gpio_get_level,
.gpio_get_drive = bcm2835_gpio_get_drive,
.gpio_get_pull = bcm2711_gpio_get_pull,
.gpio_set_pull = bcm2711_gpio_set_pull,
.gpio_get_name = bcm2835_gpio_get_name,
.gpio_get_fsel_name = bcm2711_gpio_get_fsel_name,
};
DECLARE_GPIO_CHIP(bcm2711, "brcm,bcm2711-gpio",
&bcm2711_gpio_interface, 0x30000, 0);

View File

@ -0,0 +1,504 @@
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "gpiochip.h"
#include "util.h"
#define RP1_NUM_GPIOS 54
#define RP1_IO_BANK0_OFFSET 0x00000000
#define RP1_IO_BANK1_OFFSET 0x00004000
#define RP1_IO_BANK2_OFFSET 0x00008000
#define RP1_SYS_RIO_BANK0_OFFSET 0x00010000
#define RP1_SYS_RIO_BANK1_OFFSET 0x00014000
#define RP1_SYS_RIO_BANK2_OFFSET 0x00018000
#define RP1_PADS_BANK0_OFFSET 0x00020000
#define RP1_PADS_BANK1_OFFSET 0x00024000
#define RP1_PADS_BANK2_OFFSET 0x00028000
#define RP1_RW_OFFSET 0x0000
#define RP1_XOR_OFFSET 0x1000
#define RP1_SET_OFFSET 0x2000
#define RP1_CLR_OFFSET 0x3000
#define RP1_GPIO_CTRL_FSEL_LSB 0
#define RP1_GPIO_CTRL_FSEL_MASK (0x1f << RP1_GPIO_CTRL_FSEL_LSB)
#define RP1_GPIO_CTRL_OUTOVER_LSB 12
#define RP1_GPIO_CTRL_OUTOVER_MASK (0x03 << RP1_GPIO_CTRL_OUTOVER_LSB)
#define RP1_GPIO_CTRL_OEOVER_LSB 14
#define RP1_GPIO_CTRL_OEOVER_MASK (0x03 << RP1_GPIO_CTRL_OEOVER_LSB)
#define RP1_PADS_OD_SET (1 << 7)
#define RP1_PADS_IE_SET (1 << 6)
#define RP1_PADS_PUE_SET (1 << 3)
#define RP1_PADS_PDE_SET (1 << 2)
#define RP1_GPIO_IO_REG_STATUS_OFFSET(offset) (((offset * 2) + 0) * sizeof(uint32_t))
#define RP1_GPIO_IO_REG_CTRL_OFFSET(offset) (((offset * 2) + 1) * sizeof(uint32_t))
#define RP1_GPIO_PADS_REG_OFFSET(offset) (sizeof(uint32_t) + (offset * sizeof(uint32_t)))
#define RP1_GPIO_SYS_RIO_REG_OUT_OFFSET 0x0
#define RP1_GPIO_SYS_RIO_REG_OE_OFFSET 0x4
#define RP1_GPIO_SYS_RIO_REG_SYNC_IN_OFFSET 0x8
#define rp1_gpio_write32(base, peri_offset, reg_offset, value) \
base[(peri_offset + reg_offset)/4] = value
#define rp1_gpio_read32(base, peri_offset, reg_offset) \
base[(peri_offset + reg_offset)/4]
typedef struct
{
uint32_t io[3];
uint32_t pads[3];
uint32_t sys_rio[3];
} GPIO_STATE_T;
typedef enum
{
RP1_FSEL_ALT0 = 0x0,
RP1_FSEL_ALT1 = 0x1,
RP1_FSEL_ALT2 = 0x2,
RP1_FSEL_ALT3 = 0x3,
RP1_FSEL_ALT4 = 0x4,
RP1_FSEL_ALT5 = 0x5,
RP1_FSEL_ALT6 = 0x6,
RP1_FSEL_ALT7 = 0x7,
RP1_FSEL_ALT8 = 0x8,
RP1_FSEL_COUNT,
RP1_FSEL_SYS_RIO = RP1_FSEL_ALT5,
RP1_FSEL_NULL = 0x1f
} RP1_FSEL_T;
static const GPIO_STATE_T gpio_state = {
.io = {RP1_IO_BANK0_OFFSET, RP1_IO_BANK1_OFFSET, RP1_IO_BANK2_OFFSET},
.pads = {RP1_PADS_BANK0_OFFSET, RP1_PADS_BANK1_OFFSET, RP1_PADS_BANK2_OFFSET},
.sys_rio = {RP1_SYS_RIO_BANK0_OFFSET, RP1_SYS_RIO_BANK1_OFFSET, RP1_SYS_RIO_BANK2_OFFSET},
};
static const int rp1_bank_base[] = {0, 28, 34};
static const char *rp1_gpio_fsel_names[RP1_NUM_GPIOS][RP1_FSEL_COUNT] =
{
{ "SPI0_SIO3" , "DPI_PCLK" , "TXD1" , "SDA0" , 0 , "SYS_RIO00" , "PROC_RIO00" , "PIO0" , "SPI2_CE0" , },
{ "SPI0_SIO2" , "DPI_DE" , "RXD1" , "SCL0" , 0 , "SYS_RIO01" , "PROC_RIO01" , "PIO1" , "SPI2_SIO1", },
{ "SPI0_CE3" , "DPI_VSYNC" , "CTS1" , "SDA1" , "IR_RX0" , "SYS_RIO02" , "PROC_RIO02" , "PIO2" , "SPI2_SIO0", },
{ "SPI0_CE2" , "DPI_HSYNC" , "RTS1" , "SCL1" , "IR_TX0" , "SYS_RIO03" , "PROC_RIO03" , "PIO3" , "SPI2_SCLK", },
{ "GPCLK0" , "DPI_D0" , "TXD2" , "SDA2" , "RI0" , "SYS_RIO04" , "PROC_RIO04" , "PIO4" , "SPI3_CE0" , },
{ "GPCLK1" , "DPI_D1" , "RXD2" , "SCL2" , "DTR0" , "SYS_RIO05" , "PROC_RIO05" , "PIO5" , "SPI3_SIO1", },
{ "GPCLK2" , "DPI_D2" , "CTS2" , "SDA3" , "DCD0" , "SYS_RIO06" , "PROC_RIO06" , "PIO6" , "SPI3_SIO0", },
{ "SPI0_CE1" , "DPI_D3" , "RTS2" , "SCL3" , "DSR0" , "SYS_RIO07" , "PROC_RIO07" , "PIO7" , "SPI3_SCLK", },
{ "SPI0_CE0" , "DPI_D4" , "TXD3" , "SDA0" , 0 , "SYS_RIO08" , "PROC_RIO08" , "PIO8" , "SPI4_CE0" , },
{ "SPI0_MISO" , "DPI_D5" , "RXD3" , "SCL0" , 0 , "SYS_RIO09" , "PROC_RIO09" , "PIO9" , "SPI4_SIO0", },
{ "SPI0_MOSI" , "DPI_D6" , "CTS3" , "SDA1" , 0 , "SYS_RIO010", "PROC_RIO010", "PIO10" , "SPI4_SIO1", },
{ "SPI0_SCLK" , "DPI_D7" , "RTS3" , "SCL1" , 0 , "SYS_RIO011", "PROC_RIO011", "PIO11" , "SPI4_SCLK", },
{ "PWM0_CHAN0", "DPI_D8" , "TXD4" , "SDA2" , "AAUD_LEFT" , "SYS_RIO012", "PROC_RIO012", "PIO12" , "SPI5_CE0" , },
{ "PWM0_CHAN1", "DPI_D9" , "RXD4" , "SCL2" , "AAUD_RIGHT" , "SYS_RIO013", "PROC_RIO013", "PIO13" , "SPI5_SIO1", },
{ "PWM0_CHAN2", "DPI_D10" , "CTS4" , "SDA3" , "TXD0" , "SYS_RIO014", "PROC_RIO014", "PIO14" , "SPI5_SIO0", },
{ "PWM0_CHAN3", "DPI_D11" , "RTS4" , "SCL3" , "RXD0" , "SYS_RIO015", "PROC_RIO015", "PIO15" , "SPI5_SCLK", },
{ "SPI1_CE2" , "DPI_D12" , "DSI0_TE_EXT" , 0 , "CTS0" , "SYS_RIO016", "PROC_RIO016", "PIO16" , },
{ "SPI1_CE1" , "DPI_D13" , "DSI1_TE_EXT" , 0 , "RTS0" , "SYS_RIO017", "PROC_RIO017", "PIO17" , },
{ "SPI1_CE0" , "DPI_D14" , "I2S0_SCLK" , "PWM0_CHAN2" , "I2S1_SCLK" , "SYS_RIO018", "PROC_RIO018", "PIO18" , "GPCLK1", },
{ "SPI1_MISO" , "DPI_D15" , "I2S0_WS" , "PWM0_CHAN3" , "I2S1_WS" , "SYS_RIO019", "PROC_RIO019", "PIO19" , },
{ "SPI1_MOSI" , "DPI_D16" , "I2S0_SDI0" , "GPCLK0" , "I2S1_SDI0" , "SYS_RIO020", "PROC_RIO020", "PIO20" , },
{ "SPI1_SCLK" , "DPI_D17" , "I2S0_SDO0" , "GPCLK1" , "I2S1_SDO0" , "SYS_RIO021", "PROC_RIO021", "PIO21" , },
{ "SD0_CLK" , "DPI_D18" , "I2S0_SDI1" , "SDA3" , "I2S1_SDI1" , "SYS_RIO022", "PROC_RIO022", "PIO22" , },
{ "SD0_CMD" , "DPI_D19" , "I2S0_SDO1" , "SCL3" , "I2S1_SDO1" , "SYS_RIO023", "PROC_RIO023", "PIO23" , },
{ "SD0_DAT0" , "DPI_D20" , "I2S0_SDI2" , 0 , "I2S1_SDI2" , "SYS_RIO024", "PROC_RIO024", "PIO24" , "SPI2_CE1" , },
{ "SD0_DAT1" , "DPI_D21" , "I2S0_SDO2" , "MIC_CLK" , "I2S1_SDO2" , "SYS_RIO025", "PROC_RIO025", "PIO25" , "SPI3_CE1" , },
{ "SD0_DAT2" , "DPI_D22" , "I2S0_SDI3" , "MIC_DAT0" , "I2S1_SDI3" , "SYS_RIO026", "PROC_RIO026", "PIO26" , "SPI5_CE1" , },
{ "SD0_DAT3" , "DPI_D23" , "I2S0_SDO3" , "MIC_DAT1" , "I2S1_SDO3" , "SYS_RIO027", "PROC_RIO027", "PIO27" , "SPI1_CE1" , },
{ "SD1_CLK" , "SDA4" , "I2S2_SCLK" , "SPI6_MISO" , "VBUS_EN0" , "SYS_RIO10" , "PROC_RIO10" , },
{ "SD1_CMD" , "SCL4" , "I2S2_WS" , "SPI6_MOSI" , "VBUS_OC0" , "SYS_RIO11" , "PROC_RIO11" , },
{ "SD1_DAT0" , "SDA5" , "I2S2_SDI0" , "SPI6_SCLK" , "TXD5" , "SYS_RIO12" , "PROC_RIO12" , },
{ "SD1_DAT1" , "SCL5" , "I2S2_SDO0" , "SPI6_CE0" , "RXD5" , "SYS_RIO13" , "PROC_RIO13" , },
{ "SD1_DAT2" , "GPCLK3" , "I2S2_SDI1" , "SPI6_CE1" , "CTS5" , "SYS_RIO14" , "PROC_RIO14" , },
{ "SD1_DAT3" , "GPCLK4" , "I2S2_SDO1" , "SPI6_CE2" , "RTS5" , "SYS_RIO15" , "PROC_RIO15" , },
{ "PWM1_CHAN2", "GPCLK3" , "VBUS_EN0" , "SDA4" , "MIC_CLK" , "SYS_RIO20" , "PROC_RIO20" , },
{ "SPI8_CE1" , "PWM1_CHAN0" , "VBUS_OC0" , "SCL4" , "MIC_DAT0" , "SYS_RIO21" , "PROC_RIO21" , },
{ "SPI8_CE0" , "TXD5" , "PCIE_CLKREQ_N", "SDA5" , "MIC_DAT1" , "SYS_RIO22" , "PROC_RIO22" , },
{ "SPI8_MISO" , "RXD5" , "MIC_CLK" , "SCL5" , "PCIE_CLKREQ_N", "SYS_RIO23" , "PROC_RIO23" , },
{ "SPI8_MOSI" , "RTS5" , "MIC_DAT0" , "SDA6" , "AAUD_LEFT" , "SYS_RIO24" , "PROC_RIO24" , "DSI0_TE_EXT", },
{ "SPI8_SCLK" , "CTS5" , "MIC_DAT1" , "SCL6" , "AAUD_RIGHT" , "SYS_RIO25" , "PROC_RIO25" , "DSI1_TE_EXT", },
{ "PWM1_CHAN1", "TXD5" , "SDA4" , "SPI6_MISO" , "AAUD_LEFT" , "SYS_RIO26" , "PROC_RIO26" , },
{ "PWM1_CHAN2", "RXD5" , "SCL4" , "SPI6_MOSI" , "AAUD_RIGHT" , "SYS_RIO27" , "PROC_RIO27" , },
{ "GPCLK5" , "RTS5" , "VBUS_EN1" , "SPI6_SCLK" , "I2S2_SCLK" , "SYS_RIO28" , "PROC_RIO28" , },
{ "GPCLK4" , "CTS5" , "VBUS_OC1" , "SPI6_CE0" , "I2S2_WS" , "SYS_RIO29" , "PROC_RIO29" , },
{ "GPCLK5" , "SDA5" , "PWM1_CHAN0" , "SPI6_CE1" , "I2S2_SDI0" , "SYS_RIO210", "PROC_RIO210", },
{ "PWM1_CHAN3", "SCL5" , "SPI7_CE0" , "SPI6_CE2" , "I2S2_SDO0" , "SYS_RIO211", "PROC_RIO211", },
{ "GPCLK3" , "SDA4" , "SPI7_MOSI" , "MIC_CLK" , "I2S2_SDI1" , "SYS_RIO212", "PROC_RIO212", "DSI0_TE_EXT", },
{ "GPCLK5" , "SCL4" , "SPI7_MISO" , "MIC_DAT0" , "I2S2_SDO1" , "SYS_RIO213", "PROC_RIO213", "DSI1_TE_EXT", },
{ "PWM1_CHAN0", "PCIE_CLKREQ_N", "SPI7_SCLK" , "MIC_DAT1" , "TXD5" , "SYS_RIO214", "PROC_RIO214", },
{ "SPI8_SCLK" , "SPI7_SCLK" , "SDA5" , "AAUD_LEFT" , "RXD5" , "SYS_RIO215", "PROC_RIO215", },
{ "SPI8_MISO" , "SPI7_MOSI" , "SCL5" , "AAUD_RIGHT" , "VBUS_EN2" , "SYS_RIO216", "PROC_RIO216", },
{ "SPI8_MOSI" , "SPI7_MISO" , "SDA6" , "AAUD_LEFT" , "VBUS_OC2" , "SYS_RIO217", "PROC_RIO217", },
{ "SPI8_CE0" , 0 , "SCL6" , "AAUD_RIGHT" , "VBUS_EN3" , "SYS_RIO218", "PROC_RIO218", },
{ "SPI8_CE1" , "SPI7_CE0" , 0 , "PCIE_CLKREQ_N", "VBUS_OC3" , "SYS_RIO219", "PROC_RIO219", },
};
static void rp1_gpio_get_bank(int num, int *bank, int *offset)
{
*bank = *offset = 0;
if (num >= RP1_NUM_GPIOS)
{
assert(0);
return;
}
if (num < rp1_bank_base[1])
*bank = 0;
else if (num < rp1_bank_base[2])
*bank = 1;
else
*bank = 2;
*offset = num - rp1_bank_base[*bank];
}
static uint32_t rp1_gpio_ctrl_read(volatile uint32_t *base, int bank, int offset)
{
return rp1_gpio_read32(base, gpio_state.io[bank], RP1_GPIO_IO_REG_CTRL_OFFSET(offset));
}
static void rp1_gpio_ctrl_write(volatile uint32_t *base, int bank, int offset,
uint32_t value)
{
rp1_gpio_write32(base, gpio_state.io[bank], RP1_GPIO_IO_REG_CTRL_OFFSET(offset), value);
}
static uint32_t rp1_gpio_pads_read(volatile uint32_t *base, int bank, int offset)
{
return rp1_gpio_read32(base, gpio_state.pads[bank], RP1_GPIO_PADS_REG_OFFSET(offset));
}
static void rp1_gpio_pads_write(volatile uint32_t *base, int bank, int offset,
uint32_t value)
{
rp1_gpio_write32(base, gpio_state.pads[bank], RP1_GPIO_PADS_REG_OFFSET(offset), value);
}
static uint32_t rp1_gpio_sys_rio_out_read(volatile uint32_t *base, int bank,
int offset)
{
UNUSED(offset);
return rp1_gpio_read32(base, gpio_state.sys_rio[bank], RP1_GPIO_SYS_RIO_REG_OUT_OFFSET);
}
static uint32_t rp1_gpio_sys_rio_sync_in_read(volatile uint32_t *base, int bank,
int offset)
{
UNUSED(offset);
return rp1_gpio_read32(base, gpio_state.sys_rio[bank],
RP1_GPIO_SYS_RIO_REG_SYNC_IN_OFFSET);
}
static void rp1_gpio_sys_rio_out_set(volatile uint32_t *base, int bank, int offset)
{
rp1_gpio_write32(base, gpio_state.sys_rio[bank],
RP1_GPIO_SYS_RIO_REG_OUT_OFFSET + RP1_SET_OFFSET, 1U << offset);
}
static void rp1_gpio_sys_rio_out_clr(volatile uint32_t *base, int bank, int offset)
{
rp1_gpio_write32(base, gpio_state.sys_rio[bank],
RP1_GPIO_SYS_RIO_REG_OUT_OFFSET + RP1_CLR_OFFSET, 1U << offset);
}
static uint32_t rp1_gpio_sys_rio_oe_read(volatile uint32_t *base, int bank)
{
return rp1_gpio_read32(base, gpio_state.sys_rio[bank],
RP1_GPIO_SYS_RIO_REG_OE_OFFSET);
}
static void rp1_gpio_sys_rio_oe_clr(volatile uint32_t *base, int bank, int offset)
{
rp1_gpio_write32(base, gpio_state.sys_rio[bank],
RP1_GPIO_SYS_RIO_REG_OE_OFFSET + RP1_CLR_OFFSET,
1U << offset);
}
static void rp1_gpio_sys_rio_oe_set(volatile uint32_t *base, int bank, int offset)
{
rp1_gpio_write32(base, gpio_state.sys_rio[bank],
RP1_GPIO_SYS_RIO_REG_OE_OFFSET + RP1_SET_OFFSET,
1U << offset);
}
static void rp1_gpio_set_dir(void *priv, uint32_t gpio, GPIO_DIR_T dir)
{
volatile uint32_t *base = priv;
int bank, offset;
rp1_gpio_get_bank(gpio, &bank, &offset);
if (dir == DIR_INPUT)
rp1_gpio_sys_rio_oe_clr(base, bank, offset);
else if (dir == DIR_OUTPUT)
rp1_gpio_sys_rio_oe_set(base, bank, offset);
else
assert(0);
}
static GPIO_DIR_T rp1_gpio_get_dir(void *priv, unsigned gpio)
{
volatile uint32_t *base = priv;
int bank, offset;
GPIO_DIR_T dir;
uint32_t reg;
rp1_gpio_get_bank(gpio, &bank, &offset);
reg = rp1_gpio_sys_rio_oe_read(base, bank);
dir = (reg & (1U << offset)) ? DIR_OUTPUT : DIR_INPUT;
return dir;
}
static GPIO_FSEL_T rp1_gpio_get_fsel(void *priv, unsigned gpio)
{
volatile uint32_t *base = priv;
int bank, offset;
uint32_t reg;
GPIO_FSEL_T fsel;
RP1_FSEL_T rsel;
rp1_gpio_get_bank(gpio, &bank, &offset);
reg = rp1_gpio_ctrl_read(base, bank, offset);
rsel = ((reg & RP1_GPIO_CTRL_FSEL_MASK) >> RP1_GPIO_CTRL_FSEL_LSB);
if (rsel == RP1_FSEL_SYS_RIO)
fsel = GPIO_FSEL_GPIO;
else if (rsel == RP1_FSEL_NULL)
fsel = GPIO_FSEL_NONE;
else if (rsel < RP1_FSEL_COUNT)
fsel = (GPIO_FSEL_T)rsel;
else
fsel = GPIO_FSEL_MAX;
return fsel;
}
static void rp1_gpio_set_fsel(void *priv, unsigned gpio, const GPIO_FSEL_T func)
{
volatile uint32_t *base = priv;
int bank, offset;
uint32_t ctrl_reg;
uint32_t pad_reg;
uint32_t old_pad_reg;
RP1_FSEL_T rsel;
if (func < (GPIO_FSEL_T)RP1_FSEL_COUNT)
rsel = (RP1_FSEL_T)func;
else if (func == GPIO_FSEL_INPUT ||
func == GPIO_FSEL_OUTPUT ||
func == GPIO_FSEL_GPIO)
rsel = RP1_FSEL_SYS_RIO;
else if (func == GPIO_FSEL_NONE)
rsel = RP1_FSEL_NULL;
else
return;
rp1_gpio_get_bank(gpio, &bank, &offset);
if (func == GPIO_FSEL_INPUT)
rp1_gpio_set_dir(priv, gpio, DIR_INPUT);
else if (func == GPIO_FSEL_OUTPUT)
rp1_gpio_set_dir(priv, gpio, DIR_OUTPUT);
ctrl_reg = rp1_gpio_ctrl_read(base, bank, offset) & ~RP1_GPIO_CTRL_FSEL_MASK;
ctrl_reg |= rsel << RP1_GPIO_CTRL_FSEL_LSB;
rp1_gpio_ctrl_write(base, bank, offset, ctrl_reg);
pad_reg = rp1_gpio_pads_read(base, bank, offset);
old_pad_reg = pad_reg;
if (rsel == RP1_FSEL_NULL)
{
// Disable input
pad_reg &= ~RP1_PADS_IE_SET;
}
else
{
// Enable input
pad_reg |= RP1_PADS_IE_SET;
}
if (rsel != RP1_FSEL_NULL)
{
// Enable peripheral func output
pad_reg &= ~RP1_PADS_OD_SET;
}
else
{
// Disable peripheral func output
pad_reg |= RP1_PADS_OD_SET;
}
if (pad_reg != old_pad_reg)
rp1_gpio_pads_write(base, bank, offset, pad_reg);
}
static int rp1_gpio_get_level(void *priv, unsigned gpio)
{
volatile uint32_t *base = priv;
int bank, offset;
uint32_t pad_reg;
uint32_t reg;
int level;
rp1_gpio_get_bank(gpio, &bank, &offset);
pad_reg = rp1_gpio_pads_read(base, bank, offset);
if (!(pad_reg & RP1_PADS_IE_SET))
return -1;
reg = rp1_gpio_sys_rio_sync_in_read(base, bank, offset);
level = (reg & (1U << offset)) ? 1 : 0;
return level;
}
static void rp1_gpio_set_drive(void *priv, unsigned gpio, GPIO_DRIVE_T drv)
{
volatile uint32_t *base = priv;
int bank, offset;
rp1_gpio_get_bank(gpio, &bank, &offset);
if (drv == DRIVE_HIGH)
rp1_gpio_sys_rio_out_set(base, bank, offset);
else if (drv == DRIVE_LOW)
rp1_gpio_sys_rio_out_clr(base, bank, offset);
}
static void rp1_gpio_set_pull(void *priv, unsigned gpio, GPIO_PULL_T pull)
{
volatile uint32_t *base = priv;
uint32_t reg;
int bank, offset;
rp1_gpio_get_bank(gpio, &bank, &offset);
reg = rp1_gpio_pads_read(base, bank, offset);
reg &= ~(RP1_PADS_PDE_SET | RP1_PADS_PUE_SET);
if (pull == PULL_UP)
reg |= RP1_PADS_PUE_SET;
else if (pull == PULL_DOWN)
reg |= RP1_PADS_PDE_SET;
rp1_gpio_pads_write(base, bank, offset, reg);
}
static GPIO_PULL_T rp1_gpio_get_pull(void *priv, unsigned gpio)
{
volatile uint32_t *base = priv;
uint32_t reg;
GPIO_PULL_T pull = PULL_NONE;
int bank, offset;
rp1_gpio_get_bank(gpio, &bank, &offset);
reg = rp1_gpio_pads_read(base, bank, offset);
if (reg & RP1_PADS_PUE_SET)
pull = PULL_UP;
else if (reg & RP1_PADS_PDE_SET)
pull = PULL_DOWN;
return pull;
}
static GPIO_DRIVE_T rp1_gpio_get_drive(void *priv, unsigned gpio)
{
volatile uint32_t *base = priv;
uint32_t reg;
int bank, offset;
rp1_gpio_get_bank(gpio, &bank, &offset);
reg = rp1_gpio_sys_rio_out_read(base, bank, offset);
return (reg & (1U << offset)) ? DRIVE_HIGH : DRIVE_LOW;
}
static const char *rp1_gpio_get_name(void *priv, unsigned gpio)
{
static char name_buf[16];
UNUSED(priv);
if (gpio >= RP1_NUM_GPIOS)
return NULL;
sprintf(name_buf, "GPIO%d", gpio);
return name_buf;
}
static const char *rp1_gpio_get_fsel_name(void *priv, unsigned gpio, GPIO_FSEL_T fsel)
{
const char *name = NULL;
UNUSED(priv);
switch (fsel)
{
case GPIO_FSEL_GPIO:
name = "gpio";
break;
case GPIO_FSEL_INPUT:
name = "input";
break;
case GPIO_FSEL_OUTPUT:
name = "output";
break;
case GPIO_FSEL_NONE:
name = "none";
break;
case GPIO_FSEL_FUNC0:
case GPIO_FSEL_FUNC1:
case GPIO_FSEL_FUNC2:
case GPIO_FSEL_FUNC3:
case GPIO_FSEL_FUNC4:
case GPIO_FSEL_FUNC5:
case GPIO_FSEL_FUNC6:
case GPIO_FSEL_FUNC7:
case GPIO_FSEL_FUNC8:
if (gpio < RP1_NUM_GPIOS)
{
name = rp1_gpio_fsel_names[gpio][fsel - GPIO_FSEL_FUNC0];
if (!name)
name = "-";
}
break;
default:
return NULL;
}
return name;
}
static void *rp1_gpio_create_instance(const GPIO_CHIP_T *chip,
const char *dtnode)
{
UNUSED(dtnode);
return (void *)chip;
}
static int rp1_gpio_count(void *priv)
{
UNUSED(priv);
return RP1_NUM_GPIOS;
}
static void *rp1_gpio_probe_instance(void *priv, volatile uint32_t *base)
{
UNUSED(priv);
return (void *)base;
}
static const GPIO_CHIP_INTERFACE_T rp1_gpio_interface =
{
.gpio_create_instance = rp1_gpio_create_instance,
.gpio_count = rp1_gpio_count,
.gpio_probe_instance = rp1_gpio_probe_instance,
.gpio_get_fsel = rp1_gpio_get_fsel,
.gpio_set_fsel = rp1_gpio_set_fsel,
.gpio_set_drive = rp1_gpio_set_drive,
.gpio_set_dir = rp1_gpio_set_dir,
.gpio_get_dir = rp1_gpio_get_dir,
.gpio_get_level = rp1_gpio_get_level,
.gpio_get_drive = rp1_gpio_get_drive,
.gpio_get_pull = rp1_gpio_get_pull,
.gpio_set_pull = rp1_gpio_set_pull,
.gpio_get_name = rp1_gpio_get_name,
.gpio_get_fsel_name = rp1_gpio_get_fsel_name,
};
DECLARE_GPIO_CHIP(rp1, "raspberrypi,rp1-gpio",
&rp1_gpio_interface, 0x30000, 0);

707
cpp/hal/pinctrl/gpiolib.c Normal file
View File

@ -0,0 +1,707 @@
#define _FILE_OFFSET_BITS 64
#include <assert.h>
#include <errno.h>
#include <inttypes.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>
#include "gpiochip.h"
#include "util.h"
#define ARRAY_SIZE(_a) (sizeof(_a)/sizeof(_a[0]))
#define MAX_GPIO_CHIPS 8
typedef struct GPIO_CHIP_INSTANCE_
{
const GPIO_CHIP_T *chip;
const char *name;
const char *dtnode;
int mem_fd;
void *priv;
uint64_t phys_addr;
unsigned num_gpios;
uint32_t base;
} GPIO_CHIP_INSTANCE_T;
static unsigned num_gpio_chips;
static GPIO_CHIP_INSTANCE_T gpio_chips[MAX_GPIO_CHIPS];
static unsigned num_gpios;
static unsigned first_hdr_pin = GPIO_INVALID;
static unsigned last_hdr_pin = GPIO_INVALID;
static const char *gpio_names[MAX_GPIO_PINS];
static unsigned hdr_gpios[NUM_HDR_PINS + 1];
const char *pull_names[] = { "pn", "pd", "pu", "--" };
const char *drive_names[] = { "dl", "dh", "--" };
const char *fsel_names[] =
{
"a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7",
"a8", "??", "??", "??", "??", "??", "??", "??",
"ip", "op", "gp", "no"
};
void (*verbose_callback)(const char *);
static GPIO_CHIP_INSTANCE_T *gpio_create_instance(const GPIO_CHIP_T *chip,
uint64_t phys_addr,
const char *name,
const char *dtnode)
{
GPIO_CHIP_INSTANCE_T *inst = &gpio_chips[num_gpio_chips];
if (num_gpio_chips >= MAX_GPIO_CHIPS)
{
assert(0);
return NULL;
}
inst->chip = chip;
inst->name = name ? name : chip->name;
inst->dtnode = dtnode;
inst->phys_addr = phys_addr;
inst->priv = NULL;
inst->base = 0;
inst->priv = chip->interface->gpio_create_instance(chip, dtnode);
if (!inst->priv)
return NULL;
num_gpio_chips++;
return inst;
}
static int gpio_get_interface(unsigned gpio,
const GPIO_CHIP_INTERFACE_T **iface_ptr,
void **priv, unsigned *offset)
{
unsigned i;
*iface_ptr = NULL;
for (i = 0; i < num_gpio_chips; i++)
{
GPIO_CHIP_INSTANCE_T *inst = &gpio_chips[i];
const GPIO_CHIP_T *chip = inst->chip;
if (gpio >= inst->base && gpio < (inst->base + inst->num_gpios))
{
*iface_ptr = chip->interface;
*priv = inst->priv;
*offset = gpio - inst->base;
return 0;
}
}
return -1;
}
int gpio_num_is_valid(unsigned gpio)
{
return gpio < MAX_GPIO_PINS && !!gpio_names[gpio];
}
GPIO_DIR_T gpio_get_dir(unsigned gpio)
{
const GPIO_CHIP_INTERFACE_T *iface = NULL;
unsigned gpio_offset;
void *priv;
if (gpio_get_interface(gpio, &iface, &priv, &gpio_offset) == 0)
return iface->gpio_get_dir(priv, gpio_offset);
return DIR_MAX;
}
void gpio_set_dir(unsigned gpio, GPIO_DIR_T dir)
{
const GPIO_CHIP_INTERFACE_T *iface = NULL;
unsigned gpio_offset;
void *priv;
if (gpio_get_interface(gpio, &iface, &priv, &gpio_offset) == 0)
iface->gpio_set_dir(priv, gpio_offset, dir);
}
GPIO_FSEL_T gpio_get_fsel(unsigned gpio)
{
const GPIO_CHIP_INTERFACE_T *iface = NULL;
GPIO_FSEL_T fsel = GPIO_FSEL_MAX;
unsigned gpio_offset;
void *priv;
if (gpio_get_interface(gpio, &iface, &priv, &gpio_offset) == 0)
fsel = iface->gpio_get_fsel(priv, gpio_offset);
if (fsel == GPIO_FSEL_GPIO)
{
if (gpio_get_dir(gpio) == DIR_OUTPUT)
fsel = GPIO_FSEL_OUTPUT;
else
fsel = GPIO_FSEL_INPUT;
}
return fsel;
}
void gpio_set_fsel(unsigned gpio, const GPIO_FSEL_T func)
{
const GPIO_CHIP_INTERFACE_T *iface = NULL;
unsigned gpio_offset;
void *priv;
if (gpio_get_interface(gpio, &iface, &priv, &gpio_offset) == 0)
iface->gpio_set_fsel(priv, gpio_offset, func);
}
void gpio_set_drive(unsigned gpio, GPIO_DRIVE_T drv)
{
const GPIO_CHIP_INTERFACE_T *iface = NULL;
unsigned gpio_offset;
void *priv;
if (gpio_get_interface(gpio, &iface, &priv, &gpio_offset) == 0)
iface->gpio_set_drive(priv, gpio_offset, drv);
}
void gpio_set(unsigned gpio)
{
const GPIO_CHIP_INTERFACE_T *iface = NULL;
unsigned gpio_offset;
void *priv;
if (gpio_get_interface(gpio, &iface, &priv, &gpio_offset) == 0)
{
iface->gpio_set_drive(priv, gpio_offset, 1);
iface->gpio_set_dir(priv, gpio_offset, DIR_OUTPUT);
}
}
void gpio_clear(unsigned gpio)
{
const GPIO_CHIP_INTERFACE_T *iface = NULL;
unsigned gpio_offset;
void *priv;
if (gpio_get_interface(gpio, &iface, &priv, &gpio_offset) == 0)
{
iface->gpio_set_drive(priv, gpio_offset, 0);
iface->gpio_set_dir(priv, gpio_offset, DIR_OUTPUT);
}
}
int gpio_get_level(unsigned gpio)
{
const GPIO_CHIP_INTERFACE_T *iface = NULL;
unsigned gpio_offset;
void *priv;
if (gpio_get_interface(gpio, &iface, &priv, &gpio_offset) == 0)
return iface->gpio_get_level(priv, gpio_offset);
return 0;
}
GPIO_DRIVE_T gpio_get_drive(unsigned gpio)
{
const GPIO_CHIP_INTERFACE_T *iface = NULL;
unsigned gpio_offset;
void *priv;
if (gpio_get_interface(gpio, &iface, &priv, &gpio_offset) == 0)
return iface->gpio_get_drive(priv, gpio_offset);
return DRIVE_MAX;
}
GPIO_PULL_T gpio_get_pull(unsigned gpio)
{
const GPIO_CHIP_INTERFACE_T *iface = NULL;
unsigned gpio_offset;
void *priv;
if (gpio_get_interface(gpio, &iface, &priv, &gpio_offset) == 0)
return iface->gpio_get_pull(priv, gpio_offset);
return PULL_MAX;
}
void gpio_set_pull(unsigned gpio, GPIO_PULL_T pull)
{
const GPIO_CHIP_INTERFACE_T *iface = NULL;
unsigned gpio_offset;
void *priv;
if (gpio_get_interface(gpio, &iface, &priv, &gpio_offset) == 0)
iface->gpio_set_pull(priv, gpio_offset, pull);
}
void gpio_get_pin_range(unsigned *first, unsigned *last)
{
if (first_hdr_pin == GPIO_INVALID)
{
unsigned i;
for (i = 0; i < num_gpio_chips; i++)
{
if (!strncmp(gpio_chips[i].name, "bcm2", 4) ||
!strcmp(gpio_chips[i].name, "rp1"))
{
// Assume it's the standard RPi 40-pin header layout
uint32_t base = gpio_chips[i].base;
hdr_gpios[3] = base + 2;
hdr_gpios[5] = base + 3;
hdr_gpios[7] = base + 4;
hdr_gpios[8] = base + 14;
hdr_gpios[10] = base + 15;
hdr_gpios[11] = base + 17;
hdr_gpios[12] = base + 18;
hdr_gpios[13] = base + 27;
hdr_gpios[15] = base + 22;
hdr_gpios[16] = base + 23;
hdr_gpios[18] = base + 24;
hdr_gpios[19] = base + 10;
hdr_gpios[21] = base + 9;
hdr_gpios[18] = base + 24;
hdr_gpios[22] = base + 25;
hdr_gpios[23] = base + 11;
hdr_gpios[24] = base + 8;
hdr_gpios[26] = base + 7;
hdr_gpios[27] = base + 0;
hdr_gpios[28] = base + 1;
hdr_gpios[29] = base + 5;
hdr_gpios[31] = base + 6;
hdr_gpios[32] = base + 12;
hdr_gpios[33] = base + 13;
hdr_gpios[35] = base + 19;
hdr_gpios[36] = base + 16;
hdr_gpios[37] = base + 26;
hdr_gpios[38] = base + 20;
hdr_gpios[40] = base + 21;
first_hdr_pin = 1;
last_hdr_pin = 40;
break;
}
}
}
if (first)
*first = first_hdr_pin;
if (last)
*last = last_hdr_pin;
}
unsigned gpio_for_pin(int pin)
{
if (pin >= 1 && pin <= NUM_HDR_PINS)
return hdr_gpios[pin];
return GPIO_INVALID;
}
int gpio_to_pin(unsigned gpio)
{
int i;
for (i = 1; i <= NUM_HDR_PINS; i++)
{
if (hdr_gpios[i] == gpio)
return i;
}
return -1;
}
unsigned gpio_get_gpio_by_name(const char *name, int name_len)
{
unsigned gpio;
if (!name_len)
name_len = strlen(name);
for (gpio = 0; gpio < num_gpios; gpio++)
{
const char *gpio_name = gpio_names[gpio];
const char *p;
if (!gpio_name)
continue;
for (p = gpio_name; *p; )
{
int len = strcspn(p, "/");
if (len == name_len && memcmp(name, p, name_len) == 0)
return gpio;
p += len;
if (*p == '/')
p++;
}
}
return GPIO_INVALID;
}
const char *gpio_get_name(unsigned gpio)
{
if (gpio < num_gpios)
return gpio_names[gpio];
switch (gpio)
{
case GPIO_INVALID:
return "-";
case GPIO_GND:
return "gnd";
case GPIO_5V:
return "5v";
case GPIO_3V3:
return "3v3";
case GPIO_1V8:
return "1v8";
case GPIO_OTHER:
default:
return "???";
}
}
const char *gpio_get_gpio_fsel_name(unsigned gpio, GPIO_FSEL_T fsel)
{
const GPIO_CHIP_INTERFACE_T *iface = NULL;
unsigned gpio_offset;
void *priv;
if (gpio_get_interface(gpio, &iface, &priv, &gpio_offset) == 0)
return iface->gpio_get_fsel_name(priv, gpio_offset, fsel);
return NULL;
}
const char *gpio_get_fsel_name(GPIO_FSEL_T fsel)
{
if ((unsigned)fsel < ARRAY_SIZE(fsel_names))
return fsel_names[fsel];
return NULL;
}
const char *gpio_get_pull_name(GPIO_PULL_T pull)
{
if ((unsigned)pull < ARRAY_SIZE(pull_names))
return pull_names[pull];
return NULL;
}
const char *gpio_get_drive_name(GPIO_DRIVE_T drive)
{
if ((unsigned)drive < ARRAY_SIZE(drive_names))
return drive_names[drive];
return NULL;
}
static const GPIO_CHIP_T *gpio_find_chip(const char *name)
{
const GPIO_CHIP_T *chip;
for (chip = &__start_gpiochips; name && chip < &__stop_gpiochips; chip++) {
if (!strcmp(name, chip->name) ||
!strcmp(name, chip->compatible))
return chip;
}
return NULL;
}
int gpiolib_init(void)
{
const GPIO_CHIP_T *chip;
GPIO_CHIP_INSTANCE_T *inst;
char pathbuf[FILENAME_MAX];
char gpiomem_idx[4];
const char *dtpath = "/proc/device-tree";
const char *p;
char *alias = NULL, *names, *end, *compatible;
uint64_t phys_addr;
size_t names_len;
unsigned gpio_base;
unsigned pin, i;
for (pin = 0; pin <= NUM_HDR_PINS; pin++)
hdr_gpios[pin] = GPIO_INVALID;
// There is currently only one header layout
hdr_gpios[1] = GPIO_3V3;
hdr_gpios[17] = GPIO_3V3;
hdr_gpios[2] = GPIO_5V;
hdr_gpios[4] = GPIO_5V;
hdr_gpios[6] = GPIO_GND;
hdr_gpios[9] = GPIO_GND;
hdr_gpios[14] = GPIO_GND;
hdr_gpios[20] = GPIO_GND;
hdr_gpios[25] = GPIO_GND;
hdr_gpios[30] = GPIO_GND;
hdr_gpios[34] = GPIO_GND;
hdr_gpios[39] = GPIO_GND;
if (verbose_callback)
(*verbose_callback)("GPIO chips:\n");
dt_set_path(dtpath);
for (i = 0; ; i++)
{
sprintf(pathbuf, "gpio%d", i);
sprintf(gpiomem_idx, "%d", i);
alias = dt_read_prop("/aliases", pathbuf, NULL);
if (!alias && i == 0)
{
alias = dt_read_prop("/aliases", "gpio", NULL);
gpiomem_idx[0] = 0;
}
if (!alias)
break;
compatible = dt_read_prop(alias, "compatible", NULL);
if (!compatible)
{
sprintf(pathbuf, "%s/..", alias);
compatible = dt_read_prop(pathbuf, "compatible", NULL);
}
chip = gpio_find_chip(compatible);
dt_free(compatible);
if (!chip)
{
// Skip the unknown gpio chip
dt_free(alias);
continue;
}
phys_addr = dt_parse_addr(alias);
if (phys_addr == INVALID_ADDRESS)
{
dt_free(alias);
return -1;
}
inst = gpio_create_instance(chip, phys_addr, NULL, alias);
if (!inst)
{
dt_free(alias);
return -1;
}
sprintf(pathbuf, "/dev/gpiomem%s", gpiomem_idx);
inst->mem_fd = open(pathbuf, O_RDWR|O_SYNC);
}
gpio_base = 0;
num_gpios = 0;
for (i = 0; i < num_gpio_chips; i++)
{
unsigned gpio;
inst = &gpio_chips[i];
inst->base = gpio_base;
chip = inst->chip;
inst->num_gpios = chip->interface->gpio_count(inst->priv);
if (verbose_callback)
{
char msg_buf[100];
snprintf(msg_buf, sizeof(msg_buf), " %" PRIx64 ": %s (%d gpios)\n",
inst->phys_addr, chip->name, inst->num_gpios);
(*verbose_callback)(msg_buf);
}
if (!inst->num_gpios)
continue;
num_gpios = gpio_base + inst->num_gpios;
gpio_base = ROUND_UP(num_gpios, 100);
if (num_gpios > MAX_GPIO_PINS)
return -1;
names = dt_read_prop(inst->dtnode, "gpio-line-names", &names_len);
end = names + names_len;
for (gpio = 0, p = names; gpio < inst->num_gpios; gpio++)
{
static const char *names[] = { "ID_SD", "ID_SC" };
static const int pins[] = { 27, 28 };
char name_buf[32];
const char *name = "-", *arch_name;
if (p && p < end)
{
name = p;
p += strlen(p) + 1;
if (sscanf(name, "PIN%u", &pin) != 1 ||
pin < 1 || pin > NUM_HDR_PINS)
{
unsigned i;
pin = 0;
for (i = 0; i < ARRAY_SIZE(names); i++)
{
if (strcmp(name, names[i]) == 0)
{
pin = pins[i];
break;
}
}
}
if (pin >= 1)
{
hdr_gpios[pin] = inst->base + gpio;
if ((pin < first_hdr_pin) || (first_hdr_pin == GPIO_INVALID))
first_hdr_pin = pin;
if ((pin > last_hdr_pin) || (last_hdr_pin == GPIO_INVALID))
last_hdr_pin = pin;
}
}
arch_name = chip->interface->gpio_get_name(inst->priv, gpio);
if (!name[0] || !arch_name)
{
gpio_names[inst->base + gpio] = NULL;
continue;
}
if (strcmp(name, arch_name) != 0)
{
if (strcmp(name, "-") == 0)
{
name = arch_name;
}
else
{
if (snprintf(name_buf, sizeof(name_buf), "%s/%s",
name, arch_name) >= (int)sizeof(name_buf))
name_buf[sizeof(name_buf) - 1] = '\0';
name = name_buf;
}
}
gpio_names[inst->base + gpio] = strdup(name);
}
dt_free(names);
}
// On a board with PINs, show pins 1-40
if (first_hdr_pin == 3)
first_hdr_pin = 1;
return (int)num_gpios;
}
int gpiolib_init_by_name(const char *name)
{
const GPIO_CHIP_T *chip;
GPIO_CHIP_INSTANCE_T *inst;
unsigned gpio;
int pin;
for (pin = 0; pin <= NUM_HDR_PINS; pin++)
hdr_gpios[pin] = GPIO_INVALID;
if (verbose_callback)
(*verbose_callback)("GPIO chips:\n");
chip = gpio_find_chip(name);
if (!chip)
return 0;
inst = gpio_create_instance(chip, 0, NULL, NULL);
if (!inst)
return -1;
inst->num_gpios = chip->interface->gpio_count(inst->priv);
num_gpios = inst->num_gpios;
for (gpio = 0; gpio < inst->num_gpios; gpio++)
{
const char *name = chip->interface->gpio_get_name(inst->priv, gpio);
if (!name)
{
gpio_names[inst->base + gpio] = NULL;
continue;
}
gpio_names[gpio] = strdup(name);
}
if (inst->num_gpios && verbose_callback)
{
char msg_buf[100];
snprintf(msg_buf, sizeof(msg_buf), " %s (%d gpios)\n",
chip->name, inst->num_gpios);
(*verbose_callback)(msg_buf);
}
return (int)num_gpios;
}
int gpiolib_mmap(void)
{
int pagesize = getpagesize();
int mem_fd = -1;
unsigned i;
for (i = 0; i < num_gpio_chips; i++)
{
GPIO_CHIP_INSTANCE_T *inst;
const GPIO_CHIP_T *chip;
void *gpio_map;
void *new_priv;
unsigned align;
inst = &gpio_chips[i];
chip = inst->chip;
align = inst->phys_addr & (pagesize - 1);
if (inst->mem_fd >= 0)
{
gpio_map = mmap(
NULL, /* Any address in our space will do */
chip->size + align, /* Map length */
PROT_READ | PROT_WRITE, /* Enable reading & writing */
MAP_SHARED, /* Shared with other processes */
inst->mem_fd, /* File to map */
0 /* Offset to GPIO peripheral */
);
}
else
{
if (mem_fd < 0)
{
mem_fd = open("/dev/mem", O_RDWR|O_SYNC);
if (mem_fd < 0)
return errno;
}
gpio_map = mmap(
NULL, /* Any address in our space will do */
chip->size + align, /* Map length */
PROT_READ | PROT_WRITE, /* Enable reading & writing */
MAP_SHARED, /* Shared with other processes */
mem_fd, /* File to map */
inst->phys_addr - align /* Offset to GPIO peripheral */
);
}
if (gpio_map == MAP_FAILED)
return errno;
new_priv = chip->interface->gpio_probe_instance(inst->priv,
(void *)((char *)gpio_map + align));
if (!new_priv)
return -1;
inst->priv = new_priv;
}
return 0;
}
void gpiolib_set_verbose(void (*callback)(const char *))
{
verbose_callback = callback;
}

85
cpp/hal/pinctrl/gpiolib.h Normal file
View File

@ -0,0 +1,85 @@
#ifndef GPIOLIB_H
#define GPIOLIB_H
#include <stdint.h>
#define NUM_HDR_PINS 40
#define MAX_GPIO_PINS 300
#define GPIO_INVALID (~0U)
#define GPIO_GND (~1U)
#define GPIO_5V (~2U)
#define GPIO_3V3 (~3U)
#define GPIO_1V8 (~4U)
#define GPIO_OTHER (~5U)
typedef enum
{
GPIO_FSEL_FUNC0,
GPIO_FSEL_FUNC1,
GPIO_FSEL_FUNC2,
GPIO_FSEL_FUNC3,
GPIO_FSEL_FUNC4,
GPIO_FSEL_FUNC5,
GPIO_FSEL_FUNC6,
GPIO_FSEL_FUNC7,
GPIO_FSEL_FUNC8,
/* ... */
GPIO_FSEL_INPUT = 0x10,
GPIO_FSEL_OUTPUT,
GPIO_FSEL_GPIO, /* Preserves direction if possible, else input */
GPIO_FSEL_NONE, /* If possible, else input */
GPIO_FSEL_MAX
} GPIO_FSEL_T;
typedef enum
{
PULL_NONE,
PULL_DOWN,
PULL_UP,
PULL_MAX
} GPIO_PULL_T;
typedef enum
{
DIR_INPUT,
DIR_OUTPUT,
DIR_MAX,
} GPIO_DIR_T;
typedef enum
{
DRIVE_LOW,
DRIVE_HIGH,
DRIVE_MAX
} GPIO_DRIVE_T;
int gpiolib_init(void);
int gpiolib_init_by_name(const char *name);
int gpiolib_mmap(void);
void gpiolib_set_verbose(void (*callback)(const char *));
int gpio_num_is_valid(unsigned gpio);
GPIO_DIR_T gpio_get_dir(unsigned gpio);
void gpio_set_dir(unsigned gpio, GPIO_DIR_T dir);
GPIO_FSEL_T gpio_get_fsel(unsigned gpio);
void gpio_set_fsel(unsigned gpio, const GPIO_FSEL_T func);
void gpio_set_drive(unsigned gpio, GPIO_DRIVE_T drv);
void gpio_set(unsigned gpio);
void gpio_clear(unsigned gpio);
int gpio_get_level(unsigned gpio); /* The actual level observed */
GPIO_DRIVE_T gpio_get_drive(unsigned gpio); /* What it is being driven as */
GPIO_PULL_T gpio_get_pull(unsigned gpio);
void gpio_set_pull(unsigned gpio, GPIO_PULL_T pull);
void gpio_get_pin_range(unsigned *first, unsigned *last);
unsigned gpio_for_pin(int pin);
int gpio_to_pin(unsigned gpio);
unsigned gpio_get_gpio_by_name(const char *name, int namelen);
const char *gpio_get_name(unsigned gpio);
const char *gpio_get_gpio_fsel_name(unsigned gpio, GPIO_FSEL_T fsel);
const char *gpio_get_fsel_name(GPIO_FSEL_T fsel);
const char *gpio_get_pull_name(GPIO_PULL_T pull);
const char *gpio_get_drive_name(GPIO_DRIVE_T drive);
#endif

View File

@ -0,0 +1,83 @@
#!/usr/bin/env bash
_pinctrl ()
{
local cur prev words cword split cmd pins pincomp ALLPINS CHIPS chip pinmode=false prefix arg i func pull;
local opts="no op ip a0 a1 a2 a3 a4 a5 a6 a7 a8 dh dl pu pd pn ";
_init_completion -s || return;
i=1
while [[ $i -lt $cword && ${COMP_WORDS[$i]} =~ ^- ]]; do
arg=${COMP_WORDS[$i]}
if [[ "$arg" == "-c" ]]; then
chip=${COMP_WORDS[$((i + 1))]}
i=$((i + 2))
else
if [[ "$arg" == "-p" ]]; then
pinmode=true
fi
i=$((i + 1))
fi
done
if [[ $i -lt $cword && ${COMP_WORDS[$i]} =~ ^(get|set|funcs|poll|help) ]]; then
cmd=${COMP_WORDS[$i]}
i=$((i + 1))
elif [[ ${COMP_WORDS[$i]} =~ ^[A-Z0-9] ]]; then
cmd="set"
fi
if [[ "$cmd" != "" && $i -lt $cword ]]; then
pins=${COMP_WORDS[$i]};
i=$((i + 1))
while [[ $i -lt $cword && ${COMP_WORDS[$i]} =~ ^[-,] ]]; do
pins="$pins${COMP_WORDS[$i]}"
i=$((i + 1))
done
fi
if [[ "$pins" != "" ]]; then
while [[ $i -lt $cword ]]; do
arg=${COMP_WORDS[$i]}
if [[ $arg =~ ^(no|ip|op|a[0-8])$ ]]; then
func=$arg
opts=$(echo "$opts" | sed -E "s/(no|ip|op|a[0-8]) //g")
if [[ $func != "op" ]]; then
opts=$(echo "$opts" | sed -E "s/(dh|dl) //g")
fi
elif [[ $arg =~ ^(pu|pd|pn)$ ]]; then
pull=$arg
opts=$(echo "$opts" | sed -E "s/(pu|pd|pn) //g")
fi
i=$((i + 1))
done
fi
if [[ "$cmd" != "" && ( "$pins" == "" || "${cur}" =~ [-,] ) ]]; then
prefix=$(echo $cur | sed 's/[^-,]*$//')
if $pinmode; then
ALLPINS={1..40}
else
ALLPINS=($(pinctrl get | cut -d'/' -f4 | cut -d' ' -f1) $(pinctrl get | cut -d'/' -f3 | cut -d' ' -f2))
fi
pincomp="${ALLPINS[@]/#/$prefix}"
COMPREPLY+=($(compgen -W "$pincomp" -- $cur))
elif [[ "$cmd" != "" ]]; then
if [[ "$cmd" == "set" ]]; then
COMPREPLY+=($(compgen -W "$opts" -- $cur))
fi
else
if [[ "$prev" == "-c" ]]; then
CHIPS=($(pinctrl -v -p 0 | grep 'gpios)' | cut -d' ' -f4 | sort | uniq))
chips="${CHIPS[@]}"
COMPREPLY+=($(compgen -W "$chips" -- $cur))
elif [[ "$cur" =~ ^- ]]; then
COMPREPLY+=($(compgen -W "-p -h -v -c" -- $cur))
elif [[ "$chip" == "" ]]; then
COMPREPLY+=($(compgen -W "get set poll funcs help" -- $cur))
else
COMPREPLY+=($(compgen -W "funcs help" -- $cur))
fi
fi
}
complete -F _pinctrl pinctrl

711
cpp/hal/pinctrl/pinctrl.c Normal file
View File

@ -0,0 +1,711 @@
#define _GNU_SOURCE
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <inttypes.h>
#include <sys/time.h>
#include "gpiolib.h"
#define ARRAY_SIZE(_a) (sizeof(_a)/sizeof(_a[0]))
const char *program_name = "pinctrl";
static int pin_mode = 0;
static int verbose_mode = 0;
static unsigned num_gpios;
struct poll_gpio_state {
unsigned int num;
unsigned int gpio;
const char *name;
int level;
};
int num_poll_gpios;
struct poll_gpio_state *poll_gpios;
static void print_gpio_alts_info(unsigned gpio)
{
const char *name;
unsigned int num = gpio;
int fsel;
if (pin_mode)
gpio = gpio_for_pin(num);
if (!gpio_num_is_valid(gpio))
return;
name = gpio_get_name(gpio);
if (pin_mode && strchr(name, '/'))
name = strchr(name, '/') + 1;
printf("%d", num);
if (name[0])
printf(", %s", name);
for (fsel = GPIO_FSEL_FUNC0; ; fsel++)
{
name = gpio_get_gpio_fsel_name(gpio, fsel);
if (!name)
break;
printf(", %s", name);
}
printf("\n");
}
static void usage()
{
const char *name = program_name;
printf("\n");
printf("WARNING! %s set writes directly to the GPIO control registers\n", name);
printf("ignoring whatever else may be using them (such as Linux drivers) -\n");
printf("it is designed as a debug tool, only use it if you know what you\n");
printf("are doing and at your own risk!\n");
printf("\n");
printf("Running %s with the help argument prints this help.\n", name);
printf("%s can get and print the state of a GPIO (or all GPIOs)\n", name);
printf("and can be used to set the function, pulls and value of a GPIO.\n");
printf("%s must be run as root.\n", name);
printf("Use:\n");
printf(" %s [-p] [-v] get [GPIO]\n", name);
printf("OR\n");
printf(" %s [-p] [-v] [-e] set <GPIO> [options]\n", name);
printf("OR\n");
printf(" %s [-p] [-v] poll <GPIO>\n", name);
printf("OR\n");
printf(" %s [-p] [-v] funcs [GPIO]\n", name);
printf("OR\n");
printf(" %s [-p] [-v] lev [GPIO]\n", name);
printf("OR\n");
printf(" %s -c <chip> [funcs] [GPIO]\n", name);
printf("\n");
printf("GPIO is a comma-separated list of GPIO names, numbers or ranges (without\n");
printf("spaces), e.g. 4 or 18-21 or BT_ON,9-11\n");
printf("\n");
printf("Note that omitting [GPIO] from \"%s get\" prints all GPIOs.\n", name);
printf("If the -p option is given, GPIO numbers are replaced by pin numbers on the\n");
printf("40-way header. If the -v option is given, the output is more verbose. Including\n");
printf("the -e option in a \"set\" causes pinctrl to echo back the new pin states.\n");
printf("%s funcs will dump all the possible GPIO alt functions in CSV format\n", name);
printf("or if [GPIO] is specified the alternate funcs just for that specific GPIO.\n");
printf("The -c option allows the alt functions (and only the alt function) for a named\n");
printf("chip to be displayed, even if that chip is not present in the current system.\n");
printf("\n");
printf("Valid [options] for %s set are:\n", name);
printf(" ip set GPIO as input\n");
printf(" op set GPIO as output\n");
printf(" a0-a8 set GPIO to alt function in the range 0 to 8 (range varies by model)\n");
printf(" no set GPIO to no function (NONE)\n");
printf(" pu set GPIO in-pad pull up\n");
printf(" pd set GPIO pin-pad pull down\n");
printf(" pn set GPIO pull none (no pull)\n");
printf(" dh set GPIO to drive high (1) level (only valid if set to be an output)\n");
printf(" dl set GPIO to drive low (0) level (only valid if set to be an output)\n");
printf("Examples:\n");
printf(" %s get Prints state of all GPIOs one per line\n", name);
printf(" %s get 10 Prints state of GPIO10\n", name);
printf(" %s get 10,11 Prints state of GPIO10 and GPIO11\n", name);
printf(" %s set 10 a2 Set GPIO10 to fsel 2 function (nand_wen_clk)\n", name);
printf(" %s -e set 10 pu Enable GPIO10 ~50k in-pad pull up, echoing the result\n", name);
printf(" %s set 10 pd Enable GPIO10 ~50k in-pad pull down\n", name);
printf(" %s set 10 op Set GPIO10 to be an output\n", name);
printf(" %s set 10 dl Set GPIO10 to output low/zero (must already be set as an output)\n", name);
printf(" %s set 10 ip pd Set GPIO10 to input with pull down\n", name);
printf(" %s set 35 a1 pu Set GPIO35 to fsel 1 (jtag_2_clk) with pull up\n", name);
printf(" %s set 20 op pn dh Set GPIO20 to output with no pull and driving high\n", name);
printf(" %s lev 4 Prints the level (1 or 0) of GPIO4\n", name);
printf(" %s -c bcm2835 9-11 Display the alt functions for GPIOs 9-11 on bcm2835\n", name);
}
static int do_gpio_get(unsigned int gpio)
{
unsigned int num = gpio;
const char *name;
int fsel;
int level;
if (pin_mode)
{
gpio = gpio_for_pin(num);
switch (gpio)
{
case GPIO_INVALID:
case GPIO_GND:
case GPIO_5V:
case GPIO_3V3:
case GPIO_1V8:
case GPIO_OTHER:
name = gpio_get_name(gpio);
printf("%2d: %s\n", num, name);
return 0;
}
}
if (!gpio_num_is_valid(gpio))
return 1;
fsel = gpio_get_fsel(gpio);
printf("%2d: %2s ", num, gpio_get_fsel_name(fsel));
if (fsel == GPIO_FSEL_OUTPUT)
printf("%s", gpio_get_drive_name(gpio_get_drive(gpio)));
else
printf(" ");
name = gpio_get_name(gpio);
if (pin_mode && strchr(name, '/'))
name = strchr(name, '/') + 1;
level = gpio_get_level(gpio);
printf(" %s | %s // %s%s%s\n",
gpio_get_pull_name(gpio_get_pull(gpio)),
(level == 1) ? "hi" : (level == 0) ? "lo" : "--",
name ? name : "",
name ? " = " : "",
gpio_get_gpio_fsel_name(gpio, fsel));
return 0;
}
static int do_gpio_set(unsigned int gpio, int fsparam, int drive, int pull)
{
unsigned int num = gpio;
if (pin_mode)
{
gpio = gpio_for_pin(num);
if (gpio >= num_gpios)
{
printf("Pin %d cannot be set\n", num);
return 1;
}
}
if (!gpio_num_is_valid(gpio))
return 1;
if (fsparam != GPIO_FSEL_MAX)
gpio_set_fsel(gpio, fsparam);
else
fsparam = gpio_get_fsel(gpio);
if (drive != DRIVE_MAX)
{
if (fsparam == GPIO_FSEL_OUTPUT)
{
gpio_set_drive(gpio, drive);
}
else
{
printf("Can't set pin value, not an output\n");
return 1;
}
}
if (pull != PULL_MAX)
gpio_set_pull(gpio, pull);
return 0;
}
static int do_gpio_level(unsigned int gpio)
{
unsigned int num = gpio;
int level;
if (pin_mode)
{
gpio = gpio_for_pin(num);
switch (gpio)
{
case GPIO_INVALID:
return 1;
case GPIO_GND:
printf("G");
return 0;
case GPIO_5V:
printf("5");
return 0;
case GPIO_3V3:
printf("3");
return 0;
case GPIO_1V8:
printf("8");
return 0;
case GPIO_OTHER:
printf("?");
return 0;
}
}
if (!gpio_num_is_valid(gpio))
return 1;
level = gpio_get_level(gpio);
if (level >= 0)
printf("%d", level);
else
printf("-");
return 0;
}
static int do_gpio_poll_add(unsigned int gpio)
{
struct poll_gpio_state *new_gpio;
unsigned int num = gpio;
if (pin_mode)
gpio = gpio_for_pin(num);
if (!gpio_num_is_valid(gpio))
return 1;
poll_gpios = reallocarray(poll_gpios, num_poll_gpios + 1,
sizeof(*poll_gpios));
new_gpio = &poll_gpios[num_poll_gpios];
new_gpio->num = num;
new_gpio->gpio = gpio;
new_gpio->name = gpio_get_name(gpio);
new_gpio->level = -1; /* Unknown */
num_poll_gpios++;
return 0;
}
static void do_gpio_poll(void)
{
unsigned int idle_count = 0;
struct timeval idle_start;
int i;
while (num_poll_gpios)
{
int changed = 0;
for (i = 0; i < num_poll_gpios; i++)
{
struct poll_gpio_state *state = &poll_gpios[i];
int level = gpio_get_level(state->gpio);
if (level != state->level)
{
if (idle_count)
{
struct timeval now;
uint64_t interval_us;
gettimeofday(&now, NULL);
interval_us =
(uint64_t)(now.tv_sec - idle_start.tv_sec) * 1000000 +
(now.tv_usec - idle_start.tv_usec);
printf("+%" PRIu64 "us\n", interval_us);
idle_count = 0;
}
printf("%2d: %s // %s\n", state->num, level ? "hi" : "lo", state->name);
state->level = level;
changed = 1;
fflush(stdout);
}
}
if (!changed)
{
if (!idle_count)
gettimeofday(&idle_start, NULL);
idle_count++;
}
}
}
static void verbose_callback(const char *msg)
{
printf("%s", msg);
}
int main(int argc, char *argv[])
{
int ret;
/* arg parsing */
const char *named_chip = NULL;
int set = 0;
int get = 0;
int level = 0;
int poll = 0;
int funcs = 0;
int echo = 0;
int pull = PULL_MAX;
int infer_cmd = 0;
int fsparam = GPIO_FSEL_MAX;
int drive = DRIVE_MAX;
uint32_t gpiomask[(MAX_GPIO_PINS + 31)/32] = { 0 };
unsigned start_pin = GPIO_INVALID, end_pin, pin;
int first_pin = 1;
int i;
argv++;
argc--;
while (argc && (argv[0][0] == '-'))
{
const char *arg = *(argv++);
argc--;
if (strcmp(arg, "-h") == 0)
{
usage();
return 0;
}
else if (strcmp(arg, "-p") == 0)
{
pin_mode = 1;
}
else if (strcmp(arg, "-e") == 0)
{
echo = 1;
}
else if (strcmp(arg, "-v") == 0)
{
verbose_mode = 1;
}
else if (strcmp(arg, "-c") == 0)
{
if (!argc)
{
printf("* chip name expected - use 'pinctrl -h' for help\n");
return -1;
}
named_chip = *(argv++);
argc--;
}
else
{
printf("Unknown option '%s' - try \"%s help\"\n",
arg, program_name);
exit(1);
}
}
if (verbose_mode)
gpiolib_set_verbose(&verbose_callback);
if (named_chip)
ret = gpiolib_init_by_name(named_chip);
else
ret = gpiolib_init();
if (ret < 0)
{
printf("Failed to initialise gpiolib - %d\n", ret);
return -1;
}
num_gpios = ret;
if (!num_gpios)
{
printf("No GPIO chips found\n");
return -1;
}
if (argc)
{
const char *cmd = *(argv++);
argc--;
if (strcmp(cmd, "help") == 0)
{
usage();
return 0;
}
get = strcmp(cmd, "get") == 0;
set = strcmp(cmd, "set") == 0;
level = strcmp(cmd, "level") == 0 || strcmp(cmd, "lev") == 0;
poll = strcmp(cmd, "poll") == 0;
funcs = strcmp(cmd, "funcs") == 0;
if (!set && !get && !level && !poll && !funcs)
{
/* Back up in case we can decode this as a pin */
argv--;
argc++;
infer_cmd = 1;
}
}
else if (named_chip)
{
funcs = 1;
}
else
{
get = 1;
}
if (pin_mode)
{
gpio_get_pin_range(&start_pin, &end_pin);
if (start_pin == GPIO_INVALID)
{
printf("No PIN numbers declared in DT - pin mode disabled\n");
pin_mode = 0;
}
}
if (start_pin == GPIO_INVALID)
{
start_pin = 0;
end_pin = num_gpios - 1;
}
if (argc) /* expect pin number/name(s) next */
{
char *p = *(argv++);
argc--;
while (p)
{
unsigned gpio, gpio2;
int len, len2;
len = strcspn(p, "-,");
ret = sscanf(p, "%u%n", &gpio, &len2);
if (ret == 1 && len == len2 && gpio >= num_gpios)
break;
else if (ret != 1 || len != len2)
{
gpio = gpio_get_gpio_by_name(p, len);
if (gpio == GPIO_INVALID)
break;
if (pin_mode)
{
int pin = gpio_to_pin(gpio);
if (pin < 0)
{
printf("Signal \"%*s\" is not on a header pin\n",
len, p);
return 1;
}
gpio = (unsigned)pin;
}
}
p += len;
if (*p == '\0' && argc && (argv[0][0] == '-' || argv[0][0] == ','))
{
p = *(argv++);
argc--;
}
if (*p == '-')
{
p++;
len = strcspn(p, "-,");
ret = sscanf(p, "%u%n", &gpio2, &len2);
if (ret == 1 && len == len2 && gpio2 >= num_gpios)
break;
else if (ret != 1 || len != len2)
{
gpio2 = gpio_get_gpio_by_name(p, len);
if (gpio2 == GPIO_INVALID)
break;
if (pin_mode)
{
int pin = gpio_to_pin(gpio2);
if (pin < 0)
{
printf("Signal \"%*s\" is not on a header pin\n",
len, p);
return 1;
}
gpio2 = (unsigned)pin;
}
}
if (gpio2 < gpio)
{
int tmp = gpio2;
gpio2 = gpio;
gpio = tmp;
}
p += len;
}
else
{
gpio2 = gpio;
}
while (gpio <= gpio2)
{
gpiomask[gpio/32] |= (1 << (gpio % 32));
gpio++;
}
if (*p == '\0' && argc && argv[0][0] == ',')
{
p = *(argv++);
argc--;
}
if (*p == '\0')
{
p = NULL;
}
else
{
if (*p != ',')
break;
p++;
}
}
if (p)
{
if (infer_cmd && p == argv[-1])
printf("Unknown command \"%s\"\n", p);
else
printf("Unknown GPIO \"%s\"\n", p);
return 1;
}
}
else if (set)
{
printf("Need GPIO number to set\n");
return 1;
}
else if (poll)
{
printf("Need GPIO number to poll\n");
return 1;
}
if (set && !argc)
{
printf("Need a function or pull to set\n");
return 1;
}
if ((get || funcs) && argc)
{
printf("Too many arguments\n");
return 1;
}
if (infer_cmd)
{
if (named_chip)
funcs = 1;
else if (argc)
set = 1;
else
get = 1;
}
/* parse remaining args */
while (argc)
{
const char *arg = *(argv++);
argc--;
if (strcmp(arg, "dh") == 0)
drive = DRIVE_HIGH;
else if (strcmp(arg, "dl") == 0)
drive = DRIVE_LOW;
else if (strcmp(arg, "gp") == 0)
fsparam = GPIO_FSEL_GPIO;
else if (strcmp(arg, "ip") == 0)
fsparam = GPIO_FSEL_INPUT;
else if (strcmp(arg, "op") == 0)
fsparam = GPIO_FSEL_OUTPUT;
else if (strcmp(arg, "no") == 0)
fsparam = GPIO_FSEL_NONE;
else if (strcmp(arg, "a0") == 0)
fsparam = GPIO_FSEL_FUNC0;
else if (strcmp(arg, "a1") == 0)
fsparam = GPIO_FSEL_FUNC1;
else if (strcmp(arg, "a2") == 0)
fsparam = GPIO_FSEL_FUNC2;
else if (strcmp(arg, "a3") == 0)
fsparam = GPIO_FSEL_FUNC3;
else if (strcmp(arg, "a4") == 0)
fsparam = GPIO_FSEL_FUNC4;
else if (strcmp(arg, "a5") == 0)
fsparam = GPIO_FSEL_FUNC5;
else if (strcmp(arg, "a6") == 0)
fsparam = GPIO_FSEL_FUNC6;
else if (strcmp(arg, "a7") == 0)
fsparam = GPIO_FSEL_FUNC7;
else if (strcmp(arg, "a8") == 0)
fsparam = GPIO_FSEL_FUNC8;
else if (strcmp(arg, "pu") == 0)
pull = PULL_UP;
else if (strcmp(arg, "pd") == 0)
pull = PULL_DOWN;
else if (strcmp(arg, "pn") == 0)
pull = PULL_NONE;
else
{
printf("Unknown argument \"%s\"\n", arg);
return 1;
}
}
for (i = ARRAY_SIZE(gpiomask) - 1; i >= 0; i--)
{
if (gpiomask[i])
break;
}
if (i < 0)
memset(gpiomask, 0xff, sizeof(gpiomask));
if (!funcs)
{
ret = gpiolib_mmap();
if (ret)
{
if (ret == EACCES && geteuid())
printf("Must be root\n");
else
printf("Failed to mmap gpiolib - %s\n", strerror(ret));
return -1;
}
}
for (pin = start_pin; pin < end_pin + 1; pin++)
{
if (!(gpiomask[pin / 32] & (1 << (pin % 32))))
continue;
if (get)
do_gpio_get(pin);
if (set)
do_gpio_set(pin, fsparam, drive, pull);
if (level)
{
if (!first_pin)
printf(" ");
do_gpio_level(pin);
first_pin = 0;
}
if (poll)
do_gpio_poll_add(pin);
if (funcs)
print_gpio_alts_info(pin);
}
if (level)
printf("\n");
if (set && echo)
{
for (pin = start_pin; pin < end_pin + 1; pin++)
{
if (!(gpiomask[pin / 32] & (1 << (pin % 32))))
continue;
do_gpio_get(pin);
}
}
if (poll)
do_gpio_poll();
return 0;
}

255
cpp/hal/pinctrl/util.c Normal file
View File

@ -0,0 +1,255 @@
#include <assert.h>
#include <dirent.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "util.h"
// We're actually going to cheat and cast the pointers, but define
// a structure to keep the compiler happy.
struct dt_subnode_iter
{
DIR *dh;
};
const char *dtpath;
static void *do_read_file(const char *fname, const char *mode, size_t *plen)
{
FILE *fp = fopen(fname, mode);
void *buf;
long len;
if (fp == NULL)
return NULL;
fseek(fp, 0, SEEK_END);
len = ftell(fp);
if (plen)
*plen = len;
buf = malloc(len);
fseek(fp, 0, SEEK_SET);
if (buf)
{
if (fread(buf, 1, len, fp) != (size_t)len)
{
free(buf);
buf = NULL;
}
}
fclose(fp);
return (char *)buf;
}
char *read_text_file(const char *fname, size_t *plen)
{
return do_read_file(fname, "rt", plen);
}
void *read_file(const char *fname, size_t *plen)
{
return do_read_file(fname, "rb", plen);
}
void dt_set_path(const char *path)
{
dtpath = path;
}
char *dt_read_prop(const char *node, const char *prop, size_t *plen)
{
char filename[FILENAME_MAX];
size_t len;
len = snprintf(filename, sizeof(filename), "%s%s/%s", dtpath, node, prop);
if (len >= sizeof(filename))
{
assert(0);
return NULL;
}
filename[sizeof(filename) - 1] = '\0';
return read_file(filename, plen);
}
uint32_t *dt_read_cells(const char *node, const char *prop, unsigned *num_cells)
{
uint8_t *buf;
size_t len, i;
buf = (uint8_t *)dt_read_prop(node, prop, &len);
if (buf)
{
for (i = 0; i + 3 < len; i += 4)
{
*(uint32_t *)(buf + i) = (buf[i] << 24) + (buf[i + 1] << 16) +
(buf[i + 2] << 8) + (buf[i + 3] << 0);
}
*num_cells = i >> 2;
}
return (uint32_t *)buf;
}
uint64_t dt_extract_num(const uint32_t *cells, int size)
{
uint64_t val = 0;
int i;
/* PCIe uses 3 cells for an address, but we can ignore the first cell.
* In this case, the big-endian representation makes it easy because
* the unwanted portion is shifted off the top.
*/
for (i = 0; i < size; i++)
{
val = (val << 32) | cells[i];
}
return val;
}
uint64_t dt_read_num(const char *node, const char *prop, size_t size)
{
unsigned num_cells;
uint32_t *cells = dt_read_cells(node, prop, &num_cells);
uint64_t val = 0;
if (cells)
{
if (size <= num_cells)
val = dt_extract_num(cells, size);
dt_free(cells);
}
return val;
}
uint32_t dt_read_u32(const char *node, const char *prop)
{
return dt_read_num(node, prop, 1);
}
static uint64_t dt_translate_addr(const uint32_t *ranges, unsigned ranges_cells,
int npa, int nps, int nca, uint64_t addr)
{
/* Entries in the ranges table take the form:
* <child addr> <parent addr> <size>
* where the elements are big-endian numbers of lengths nca, npa and nps
* respectively.
*/
unsigned pos = 0;
while (pos + npa + nps + nca <= ranges_cells)
{
uint64_t ca, pa, ps;
ca = dt_extract_num(ranges + pos, nca);
pa = dt_extract_num(ranges + pos + nca, npa);
ps = dt_extract_num(ranges + pos + nca + npa, nps);
if (addr >= ca && addr <= ca + ps)
{
addr -= ca;
addr += pa;
break;
}
pos += npa + nps + nca;
}
return addr;
}
uint64_t dt_parse_addr(const char *node)
{
char buf1[FILENAME_MAX], buf2[FILENAME_MAX];
char *parent, *nextparent;
uint32_t *ranges = NULL;
unsigned ranges_cells = 0;
uint64_t addr = INVALID_ADDRESS;
unsigned npa, nps, nca = 0;
parent = buf1;
nextparent = buf2;
while (1)
{
char *tmp, *p;
strcpy(parent, node);
p = strrchr(parent, '/');
if (!p)
return INVALID_ADDRESS;
if (p == parent)
p[1] = '\0';
else
p[0] = '\0';
npa = dt_read_u32(parent, "#address-cells");
nps = dt_read_u32(parent, "#size-cells");
if (!npa || !nps)
{
addr = INVALID_ADDRESS;
break;
}
if (addr == INVALID_ADDRESS)
{
addr = dt_read_num(node, "reg", npa);
}
else if (ranges)
{
addr = dt_translate_addr(ranges, ranges_cells, npa, nps, nca, addr);
dt_free(ranges);
ranges = NULL;
}
if (parent[1] == '\0')
break;
ranges = dt_read_cells(parent, "ranges", &ranges_cells);
nca = npa;
node = parent;
/* Swap parent and nextparent */
tmp = parent;
parent = nextparent;
nextparent = tmp;
}
dt_free(ranges);
return addr;
}
void dt_free(void *value)
{
free(value);
}
DT_SUBNODE_HANDLE dt_open_subnodes(const char *node)
{
char dirpath[FILENAME_MAX];
size_t len;
len = snprintf(dirpath, sizeof(dirpath), "%s%s", dtpath, node);
if (len >= sizeof(dirpath))
{
assert(0);
return NULL;
}
return (DT_SUBNODE_HANDLE)opendir(dirpath);
}
const char *dt_next_subnode(DT_SUBNODE_HANDLE handle)
{
DIR *dirh = (DIR *)handle;
struct dirent *dent;
dent = readdir(dirh);
return dent ? dent->d_name : NULL;
}
void dt_close_subnodes(DT_SUBNODE_HANDLE handle)
{
DIR *dirh = (DIR *)handle;
closedir(dirh);
}

35
cpp/hal/pinctrl/util.h Normal file
View File

@ -0,0 +1,35 @@
#ifndef _UTIL_H
#define _UTIL_H
#include <stdint.h>
#define INVALID_ADDRESS ((uint64_t)~0)
#define ROUND_UP(n, d) ((((n) + (d) - 1) / (d)) * (d))
#define UNUSED(x) (void)(x)
typedef struct dt_subnode_iter *DT_SUBNODE_HANDLE;
char *read_text_file(const char *fname, size_t *plen);
void *read_file(const char *fname, size_t *plen);
void dt_set_path(const char *path);
char *dt_read_prop(const char *node, const char *prop, size_t *len);
uint32_t *dt_read_cells(const char *node, const char *prop, unsigned *num_cells);
uint64_t dt_extract_num(const uint32_t *cells, int size);
uint64_t dt_read_num(const char *node, const char *prop, size_t size);
uint32_t dt_read_u32(const char *node, const char *prop);
uint64_t dt_parse_addr(const char *node);
void dt_free(void *value);
DT_SUBNODE_HANDLE dt_open_subnodes(const char *node);
const char *dt_next_subnode(DT_SUBNODE_HANDLE handle);
void dt_close_subnodes(DT_SUBNODE_HANDLE handle);
#endif