mirror of
https://github.com/akuker/RASCSI.git
synced 2024-10-19 12:24:04 +00:00
Import Raspberry Pi foundation pinctl utility
This commit is contained in:
parent
13cfc6f3a0
commit
e1bf397515
12
cpp/hal/pinctrl/CMakeLists.txt
Normal file
12
cpp/hal/pinctrl/CMakeLists.txt
Normal 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
36
cpp/hal/pinctrl/README.md
Normal 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)
|
42
cpp/hal/pinctrl/gpiochip.h
Normal file
42
cpp/hal/pinctrl/gpiochip.h
Normal 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
|
875
cpp/hal/pinctrl/gpiochip_bcm2712.c
Normal file
875
cpp/hal/pinctrl/gpiochip_bcm2712.c
Normal 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", ®_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);
|
462
cpp/hal/pinctrl/gpiochip_bcm2835.c
Normal file
462
cpp/hal/pinctrl/gpiochip_bcm2835.c
Normal 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);
|
504
cpp/hal/pinctrl/gpiochip_rp1.c
Normal file
504
cpp/hal/pinctrl/gpiochip_rp1.c
Normal 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
707
cpp/hal/pinctrl/gpiolib.c
Normal 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
85
cpp/hal/pinctrl/gpiolib.h
Normal 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
|
83
cpp/hal/pinctrl/pinctrl-completion.bash
Normal file
83
cpp/hal/pinctrl/pinctrl-completion.bash
Normal 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
711
cpp/hal/pinctrl/pinctrl.c
Normal 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
255
cpp/hal/pinctrl/util.c
Normal 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
35
cpp/hal/pinctrl/util.h
Normal 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
|
Loading…
Reference in New Issue
Block a user