/////////////////////////////////////////////////////////////////////////////////////// // File RP1.xs // Description: XS module for HiPi::GPIO -on Pi5 with RP1 // Copyright: Copyright (c) 2023 Mark Dootson // License: This is free software; you can redistribute it and/or modify it under // the same terms as the Perl 5 programming language system itself. // // RP1 use details from pinctrl util // https://github.com/raspberrypi/utils/blob/master/pinctrl/gpiochip_rp1.c /////////////////////////////////////////////////////////////////////////////////////// #include "EXTERN.h" #include "perl.h" #include "XSUB.h" #include "mylib/include/ppport.h" #include #include #include #define PI_INIT_FAILED -1 /* gpio: 0-53 */ #define PI_MIN_GPIO 0 #define PI_MAX_GPIO 53 #define PI_PIN_ERROR -1 #define PI_EDGE_ERROR -1 /* level: 0-1 */ #define PI_OFF 0 #define PI_ON 1 #define PI_CLEAR 0 #define PI_SET 1 #define PI_LOW 0 #define PI_HIGH 1 /* mode: 0-11 */ #define PI_MAX_MODE 11 /* pud: 0-2 */ #define PI_PUD_OFF 0 #define PI_PUD_DOWN 1 #define PI_PUD_UP 2 #define PI_PUD_UNSET 0x08 /* FSEL MAPING */ #define HIPI_MODE_INPUT 0x00 #define HIPI_MODE_OUTPUT 0x01 #define HIPI_MODE_ALT0 0x04 #define HIPI_MODE_ALT1 0x05 #define HIPI_MODE_ALT2 0x06 #define HIPI_MODE_ALT3 0x07 #define HIPI_MODE_ALT4 0x03 #define HIPI_MODE_ALT5 0x02 #define HIPI_MODE_ALT6 0x08 #define HIPI_MODE_ALT7 0x09 #define HIPI_MODE_ALT8 0x0a #define HIPI_MODE_NONE 0x0b /* locations and offsets */ #define GPIO_BASE 0x200000 #define GPIO_LEN 0x030000 /* Just hard code this so we can output */ /* It has no use. */ #define PERI_BASE_ADDRESS 0x1f000d0000 static int fdMem = -1; static volatile uint32_t * gpio_register = MAP_FAILED; // pre-declares required static int do_initialise(void); static void do_uninitialise(void); static void send_module_error( char * error ); #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) // 5 bits #define RP1_GPIO_CTRL_F_M_LSB 5 #define RP1_GPIO_CTRL_F_M_MASK (0x7f << RP1_GPIO_CTRL_F_M_LSB) // 7 bits #define RP1_GPIO_CTRL_OUTOVER_LSB 12 #define RP1_GPIO_CTRL_OUTOVER_MASK (0x03 << RP1_GPIO_CTRL_OUTOVER_LSB) // 2 bits #define RP1_GPIO_CTRL_OEOVER_LSB 14 #define RP1_GPIO_CTRL_OEOVER_MASK (0x03 << RP1_GPIO_CTRL_OEOVER_LSB) // 2 bits #define RP1_GPIO_CTRL_INOVER_LSB 16 #define RP1_GPIO_CTRL_INOVER_MASK (0x03 << RP1_GPIO_CTRL_INOVER_LSB) // 2 bits #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_PADS_SCHMITT_SET (1 << 1) #define RP1_PADS_SLEW_SET (1 << 0) #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; 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; typedef enum { SCHMITT_OFF, SCHMITT_ON, SCHMITT_MAX, } GPIO_SCHMITT_T; typedef enum { SLEW_SLOW, SLEW_FAST, SLEW_MAX, } GPIO_SLEW_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" , }, // 0 { "SPI0_SIO2" , "DPI_DE" , "RXD1" , "SCL0" , 0 , "SYS_RIO01" , "PROC_RIO01" , "PIO1" , "SPI2_SIO1", }, // 1 { "SPI0_CE3" , "DPI_VSYNC" , "CTS1" , "SDA1" , "IR_RX0" , "SYS_RIO02" , "PROC_RIO02" , "PIO2" , "SPI2_SIO0", }, // 2 { "SPI0_CE2" , "DPI_HSYNC" , "RTS1" , "SCL1" , "IR_TX0" , "SYS_RIO03" , "PROC_RIO03" , "PIO3" , "SPI2_SCLK", }, // 3 { "GPCLK0" , "DPI_D0" , "TXD2" , "SDA2" , "RI0" , "SYS_RIO04" , "PROC_RIO04" , "PIO4" , "SPI3_CE0" , }, // 4 { "GPCLK1" , "DPI_D1" , "RXD2" , "SCL2" , "DTR0" , "SYS_RIO05" , "PROC_RIO05" , "PIO5" , "SPI3_SIO1", }, // 5 { "GPCLK2" , "DPI_D2" , "CTS2" , "SDA3" , "DCD0" , "SYS_RIO06" , "PROC_RIO06" , "PIO6" , "SPI3_SIO0", }, // 6 { "SPI0_CE1" , "DPI_D3" , "RTS2" , "SCL3" , "DSR0" , "SYS_RIO07" , "PROC_RIO07" , "PIO7" , "SPI3_SCLK", }, // 7 { "SPI0_CE0" , "DPI_D4" , "TXD3" , "SDA0" , 0 , "SYS_RIO08" , "PROC_RIO08" , "PIO8" , "SPI4_CE0" , }, // 8 { "SPI0_MISO" , "DPI_D5" , "RXD3" , "SCL0" , 0 , "SYS_RIO09" , "PROC_RIO09" , "PIO9" , "SPI4_SIO0", }, // 9 { "SPI0_MOSI" , "DPI_D6" , "CTS3" , "SDA1" , 0 , "SYS_RIO010", "PROC_RIO010", "PIO10" , "SPI4_SIO1", }, // 10 { "SPI0_SCLK" , "DPI_D7" , "RTS3" , "SCL1" , 0 , "SYS_RIO011", "PROC_RIO011", "PIO11" , "SPI4_SCLK", }, // 11 { "PWM0_CHAN0", "DPI_D8" , "TXD4" , "SDA2" , "AAUD_LEFT" , "SYS_RIO012", "PROC_RIO012", "PIO12" , "SPI5_CE0" , }, // 12 { "PWM0_CHAN1", "DPI_D9" , "RXD4" , "SCL2" , "AAUD_RIGHT" , "SYS_RIO013", "PROC_RIO013", "PIO13" , "SPI5_SIO1", }, // 13 { "PWM0_CHAN2", "DPI_D10" , "CTS4" , "SDA3" , "TXD0" , "SYS_RIO014", "PROC_RIO014", "PIO14" , "SPI5_SIO0", }, // 14 { "PWM0_CHAN3", "DPI_D11" , "RTS4" , "SCL3" , "RXD0" , "SYS_RIO015", "PROC_RIO015", "PIO15" , "SPI5_SCLK", }, // 15 { "SPI1_CE2" , "DPI_D12" , "DSI0_TE_EXT" , 0 , "CTS0" , "SYS_RIO016", "PROC_RIO016", "PIO16" , }, // 16 { "SPI1_CE1" , "DPI_D13" , "DSI1_TE_EXT" , 0 , "RTS0" , "SYS_RIO017", "PROC_RIO017", "PIO17" , }, // 17 { "SPI1_CE0" , "DPI_D14" , "I2S0_SCLK" , "PWM0_CHAN2" , "I2S1_SCLK" , "SYS_RIO018", "PROC_RIO018", "PIO18" , "GPCLK1", }, // 18 { "SPI1_MISO" , "DPI_D15" , "I2S0_WS" , "PWM0_CHAN3" , "I2S1_WS" , "SYS_RIO019", "PROC_RIO019", "PIO19" , }, // 19 { "SPI1_MOSI" , "DPI_D16" , "I2S0_SDI0" , "GPCLK0" , "I2S1_SDI0" , "SYS_RIO020", "PROC_RIO020", "PIO20" , }, // 20 { "SPI1_SCLK" , "DPI_D17" , "I2S0_SDO0" , "GPCLK1" , "I2S1_SDO0" , "SYS_RIO021", "PROC_RIO021", "PIO21" , }, // 21 { "SD0CLK" , "DPI_D18" , "I2S0_SDI1" , "SDA3" , "I2S1_SDI1" , "SYS_RIO022", "PROC_RIO022", "PIO22" , }, // 22 { "SD0_CMD" , "DPI_D19" , "I2S0_SDO1" , "SCL3" , "I2S1_SDO1" , "SYS_RIO023", "PROC_RIO023", "PIO23" , }, // 23 { "SD0_DAT0" , "DPI_D20" , "I2S0_SDI2" , 0 , "I2S1_SDI2" , "SYS_RIO024", "PROC_RIO024", "PIO24" , "SPI2_CE1" , }, // 24 { "SD0_DAT1" , "DPI_D21" , "I2S0_SDO2" , "MIC_CLK" , "I2S1_SDO2" , "SYS_RIO025", "PROC_RIO025", "PIO25" , "SPI3_CE1" , }, // 25 { "SD0_DAT2" , "DPI_D22" , "I2S0_SDI3" , "MIC_DAT0" , "I2S1_SDI3" , "SYS_RIO026", "PROC_RIO026", "PIO26" , "SPI5_CE1" , }, // 26 { "SD0_DAT3" , "DPI_D23" , "I2S0_SDO3" , "MIC_DAT1" , "I2S1_SDO3" , "SYS_RIO027", "PROC_RIO027", "PIO27" , "SPI1_CE1" , }, // 27 { "SD1CLK" , "SDA4" , "I2S2_SCLK" , "SPI6_MISO" , "VBUS_EN0" , "SYS_RIO10" , "PROC_RIO10" , }, // 28 { "SD1_CMD" , "SCL4" , "I2S2_WS" , "SPI6_MOSI" , "VBUS_OC0" , "SYS_RIO11" , "PROC_RIO11" , }, // 29 { "SD1_DAT0" , "SDA5" , "I2S2_SDI0" , "SPI6_SCLK" , "TXD5" , "SYS_RIO12" , "PROC_RIO12" , }, // 30 { "SD1_DAT1" , "SCL5" , "I2S2_SDO0" , "SPI6_CE0" , "RXD5" , "SYS_RIO13" , "PROC_RIO13" , }, // 31 { "SD1_DAT2" , "GPCLK3" , "I2S2_SDI1" , "SPI6_CE1" , "CTS5" , "SYS_RIO14" , "PROC_RIO14" , }, // 32 { "SD1_DAT3" , "GPCLK4" , "I2S2_SDO1" , "SPI6_CE2" , "RTS5" , "SYS_RIO15" , "PROC_RIO15" , }, // 33 { "PWM1_CHAN2", "GPCLK3" , "VBUS_EN0" , "SDA4" , "MIC_CLK" , "SYS_RIO20" , "PROC_RIO20" , }, // 34 { "SPI8_CE1" , "PWM1_CHAN0" , "VBUS_OC0" , "SCL4" , "MIC_DAT0" , "SYS_RIO21" , "PROC_RIO21" , }, // 35 { "SPI8_CE0" , "TXD5" , "PCIE_CLKREQ_N", "SDA5" , "MIC_DAT1" , "SYS_RIO22" , "PROC_RIO22" , }, // 36 { "SPI8_MISO" , "RXD5" , "MIC_CLK" , "SCL5" , "PCIE_CLKREQ_N", "SYS_RIO23" , "PROC_RIO23" , }, // 37 { "SPI8_MOSI" , "RTS5" , "MIC_DAT0" , "SDA6" , "AAUD_LEFT" , "SYS_RIO24" , "PROC_RIO24" , "DSI0_TE_EXT", }, // 38 { "SPI8_SCLK" , "CTS5" , "MIC_DAT1" , "SCL6" , "AAUD_RIGHT" , "SYS_RIO25" , "PROC_RIO25" , "DSI1_TE_EXT", }, // 39 { "PWM1_CHAN1", "TXD5" , "SDA4" , "SPI6_MISO" , "AAUD_LEFT" , "SYS_RIO26" , "PROC_RIO26" , }, // 40 { "PWM1_CHAN2", "RXD5" , "SCL4" , "SPI6_MOSI" , "AAUD_RIGHT" , "SYS_RIO27" , "PROC_RIO27" , }, // 41 { "GPCLK5" , "RTS5" , "VBUS_EN1" , "SPI6_SCLK" , "I2S2_SCLK" , "SYS_RIO28" , "PROC_RIO28" , }, // 42 { "GPCLK4" , "CTS5" , "VBUS_OC1" , "SPI6_CE0" , "I2S2_WS" , "SYS_RIO29" , "PROC_RIO29" , }, // 43 { "GPCLK5" , "SDA5" , "PWM1_CHAN0" , "SPI6_CE1" , "I2S2_SDI0" , "SYS_RIO210", "PROC_RIO210", }, // 44 { "PWM1_CHAN3", "SCL5" , "SPI7_CE0" , "SPI6_CE2" , "I2S2_SDO0" , "SYS_RIO211", "PROC_RIO211", }, // 45 { "GPCLK3" , "SDA4" , "SPI7_MOSI" , "MIC_CLK" , "I2S2_SDI1" , "SYS_RIO212", "PROC_RIO212", "DSI0_TE_EXT", }, // 46 { "GPCLK5" , "SCL4" , "SPI7_MISO" , "MIC_DAT0" , "I2S2_SDO1" , "SYS_RIO213", "PROC_RIO213", "DSI1_TE_EXT", }, // 47 { "PWM1_CHAN0", "PCIE_CLKREQ_N", "SPI7_SCLK" , "MIC_DAT1" , "TXD5" , "SYS_RIO214", "PROC_RIO214", }, // 48 { "SPI8_SCLK" , "SPI7_SCLK" , "SDA5" , "AAUD_LEFT" , "RXD5" , "SYS_RIO215", "PROC_RIO215", }, // 49 { "SPI8_MISO" , "SPI7_MOSI" , "SCL5" , "AAUD_RIGHT" , "VBUS_EN2" , "SYS_RIO216", "PROC_RIO216", }, // 50 { "SPI8_MOSI" , "SPI7_MISO" , "SDA6" , "AAUD_LEFT" , "VBUS_OC2" , "SYS_RIO217", "PROC_RIO217", }, // 51 { "SPI8_CE0" , 0 , "SCL6" , "AAUD_RIGHT" , "VBUS_EN3" , "SYS_RIO218", "PROC_RIO218", }, // 52 { "SPI8_CE1" , "SPI7_CE0" , 0 , "PCIE_CLKREQ_N", "VBUS_OC3" , "SYS_RIO219", "PROC_RIO219", }, // 53 }; 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( int bank, int offset) { return rp1_gpio_read32(gpio_register, gpio_state.io[bank], RP1_GPIO_IO_REG_CTRL_OFFSET(offset)); } static void rp1_gpio_ctrl_write( int bank, int offset, uint32_t value) { rp1_gpio_write32(gpio_register, gpio_state.io[bank], RP1_GPIO_IO_REG_CTRL_OFFSET(offset), value); } static uint32_t rp1_gpio_pads_read( int bank, int offset) { return rp1_gpio_read32(gpio_register, gpio_state.pads[bank], RP1_GPIO_PADS_REG_OFFSET(offset)); } static void rp1_gpio_pads_write( int bank, int offset, uint32_t value) { rp1_gpio_write32(gpio_register, gpio_state.pads[bank], RP1_GPIO_PADS_REG_OFFSET(offset), value); } static uint32_t rp1_gpio_sys_rio_out_read( int bank ) { return rp1_gpio_read32(gpio_register, gpio_state.sys_rio[bank], RP1_GPIO_SYS_RIO_REG_OUT_OFFSET); } static uint32_t rp1_gpio_sys_rio_sync_in_read( int bank ) { return rp1_gpio_read32(gpio_register, gpio_state.sys_rio[bank], RP1_GPIO_SYS_RIO_REG_SYNC_IN_OFFSET); } static void rp1_gpio_sys_rio_out_write( int bank, uint32_t value) { rp1_gpio_write32(gpio_register, gpio_state.sys_rio[bank], RP1_GPIO_SYS_RIO_REG_OUT_OFFSET, value); } static void rp1_gpio_sys_rio_out_clr( int bank, int offset ) { rp1_gpio_write32(gpio_register, gpio_state.sys_rio[bank], RP1_GPIO_SYS_RIO_REG_OUT_OFFSET + RP1_CLR_OFFSET, 1U << offset); } static void rp1_gpio_sys_rio_out_set( int bank, int offset ) { rp1_gpio_write32(gpio_register, gpio_state.sys_rio[bank], RP1_GPIO_SYS_RIO_REG_OUT_OFFSET + RP1_SET_OFFSET, 1U << offset); } static uint32_t rp1_gpio_sys_rio_oe_read(int bank) { return rp1_gpio_read32(gpio_register, gpio_state.sys_rio[bank], RP1_GPIO_SYS_RIO_REG_OE_OFFSET); } // set direction INPUT static void rp1_gpio_sys_rio_oe_clr( int bank, int offset) { rp1_gpio_write32(gpio_register, gpio_state.sys_rio[bank], RP1_GPIO_SYS_RIO_REG_OE_OFFSET + RP1_CLR_OFFSET, 1U << offset); } // set direction OUTPUT static void rp1_gpio_sys_rio_oe_set( int bank, int offset) { rp1_gpio_write32(gpio_register, gpio_state.sys_rio[bank], RP1_GPIO_SYS_RIO_REG_OE_OFFSET + RP1_SET_OFFSET, 1U << offset); } static void rp1_gpio_set_dir(uint32_t gpio, GPIO_DIR_T dir) { int bank, offset; rp1_gpio_get_bank(gpio, &bank, &offset); if (dir == DIR_INPUT) rp1_gpio_sys_rio_oe_clr(bank, offset); else if (dir == DIR_OUTPUT) rp1_gpio_sys_rio_oe_set(bank, offset); else assert(0); } static GPIO_DIR_T rp1_gpio_get_dir(unsigned gpio) { int bank, offset; GPIO_DIR_T dir; uint32_t reg; rp1_gpio_get_bank(gpio, &bank, &offset); reg = rp1_gpio_sys_rio_oe_read(bank); dir = (reg & (1U << offset)) ? DIR_OUTPUT : DIR_INPUT; return dir; } static GPIO_FSEL_T rp1_gpio_get_fsel(unsigned gpio) { 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( 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(unsigned gpio, const GPIO_FSEL_T func) { 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(gpio, DIR_INPUT); else if (func == GPIO_FSEL_OUTPUT) rp1_gpio_set_dir(gpio, DIR_OUTPUT); ctrl_reg = rp1_gpio_ctrl_read(bank, offset) & ~RP1_GPIO_CTRL_FSEL_MASK; ctrl_reg |= rsel << RP1_GPIO_CTRL_FSEL_LSB; rp1_gpio_ctrl_write(bank, offset, ctrl_reg); pad_reg = rp1_gpio_pads_read(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(bank, offset, pad_reg); } static int rp1_gpio_get_level(unsigned gpio) { 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(bank, offset); if (!(pad_reg & RP1_PADS_IE_SET)) return -1; reg = rp1_gpio_sys_rio_sync_in_read(bank); level = (reg & (1U << offset)) ? 1 : 0; return level; } static void rp1_gpio_set_drive(unsigned gpio, GPIO_DRIVE_T drv) { uint32_t reg; int bank, offset; rp1_gpio_get_bank(gpio, &bank, &offset); //reg = rp1_gpio_sys_rio_out_read(bank); //if (drv == DRIVE_HIGH) // reg |= (1U << offset); //else if (drv == DRIVE_LOW) // reg &= ~(1U << offset); //rp1_gpio_sys_rio_out_write(bank, reg); if (drv == DRIVE_HIGH) rp1_gpio_sys_rio_out_set(bank, offset); else if (drv == DRIVE_LOW) rp1_gpio_sys_rio_out_clr(bank, offset); else assert(0); } static void rp1_gpio_set_pull( unsigned gpio, GPIO_PULL_T pull) { uint32_t reg; int bank, offset; rp1_gpio_get_bank(gpio, &bank, &offset); reg = rp1_gpio_pads_read(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(bank, offset, reg); } static GPIO_PULL_T rp1_gpio_get_pull(unsigned gpio) { uint32_t reg; GPIO_PULL_T pull = PULL_NONE; int bank, offset; rp1_gpio_get_bank(gpio, &bank, &offset); reg = rp1_gpio_pads_read(bank, offset); if (reg & RP1_PADS_PUE_SET) pull = PULL_UP; else if (reg & RP1_PADS_PDE_SET) pull = PULL_DOWN; return pull; } static void rp1_gpio_set_schmitt( unsigned gpio, GPIO_SCHMITT_T schmitt ) { uint32_t reg; int bank, offset; rp1_gpio_get_bank(gpio, &bank, &offset); reg = rp1_gpio_pads_read(bank, offset); reg &= ~RP1_PADS_SCHMITT_SET; if( schmitt == SCHMITT_ON) { reg |= RP1_PADS_SCHMITT_SET; } rp1_gpio_pads_write(bank, offset, reg); } static GPIO_SCHMITT_T rp1_gpio_get_schmitt(unsigned gpio) { uint32_t reg; int bank, offset; rp1_gpio_get_bank(gpio, &bank, &offset); reg = rp1_gpio_pads_read(bank, offset); return ( reg & RP1_PADS_SCHMITT_SET ) ? SCHMITT_ON : SCHMITT_OFF; } static void rp1_gpio_set_slew( unsigned gpio, GPIO_SLEW_T slew ) { uint32_t reg; int bank, offset; rp1_gpio_get_bank(gpio, &bank, &offset); reg = rp1_gpio_pads_read(bank, offset); reg &= ~RP1_PADS_SLEW_SET; if( slew == SLEW_FAST) { reg |= RP1_PADS_SLEW_SET; } rp1_gpio_pads_write(bank, offset, reg); } static GPIO_SLEW_T rp1_gpio_get_slew(unsigned gpio) { uint32_t reg; int bank, offset; rp1_gpio_get_bank(gpio, &bank, &offset); reg = rp1_gpio_pads_read(bank, offset); return ( reg & RP1_PADS_SLEW_SET ) ? SLEW_FAST : SLEW_SLOW; } static GPIO_DRIVE_T rp1_gpio_get_drive(unsigned gpio) { uint32_t reg; int bank, offset; rp1_gpio_get_bank(gpio, &bank, &offset); reg = rp1_gpio_sys_rio_out_read(bank); return (reg & (1U << offset)) ? DRIVE_HIGH : DRIVE_LOW; } static const char *rp1_gpio_get_name(unsigned gpio) { static char name_buf[16]; if (gpio >= RP1_NUM_GPIOS) return NULL; sprintf(name_buf, "GPIO%d", gpio); return name_buf; } static const char *rp1_gpio_get_fsel_name(unsigned gpio, GPIO_FSEL_T fsel) { const char *name = NULL; 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 int rp1_gpio_count() { return RP1_NUM_GPIOS; } // map a memory block static uint32_t * map_gpiomem(int fd, uint32_t addr, uint32_t len) { return (uint32_t *) mmap(0, len, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_SHARED|MAP_LOCKED, fd, addr); } // open /dev/gpiomem static int open_gpiomen(void) { if ((fdMem = open("/dev/gpiomem0", O_RDWR | O_SYNC) ) < 0) { return PI_INIT_FAILED; } return 0; } // initialise lib static int do_initialise(void) { if(open_gpiomen() == PI_INIT_FAILED) { send_module_error("HiPi::GPIO failed to open memory device /dev/gpiomem0"); do_uninitialise(); return 0; } gpio_register = map_gpiomem(fdMem, GPIO_BASE, GPIO_LEN); if (gpio_register == MAP_FAILED) { send_module_error("HiPi::GPIO failed to map gpio memory block"); do_uninitialise(); return 0; } return 1; } // uninitialise lib static void do_uninitialise(void) { if (gpio_register != MAP_FAILED) munmap((void *)gpio_register, GPIO_LEN); gpio_register == MAP_FAILED; if (fdMem != -1) { close(fdMem); fdMem = -1; } } // delay microseconds static void delay_microseconds(uint32_t inputmicros) { struct timespec ts, rem; int seconds = inputmicros / 1000000; int micros = inputmicros % 1000000; ts.tv_sec = seconds; ts.tv_nsec = micros * 1000; while ( clock_nanosleep(CLOCK_REALTIME, 0, &ts, &rem) ) { ts = rem; } } // set pud mode static void do_gpio_set_pud(unsigned gpio, unsigned pud) { if( pud == PI_PUD_UNSET ) pud = PI_PUD_OFF; rp1_gpio_set_pull( gpio, pud ); } // get pud mode int do_gpio_get_pud(unsigned gpio) { return rp1_gpio_get_pull( gpio ); } // set pin mode static void do_gpio_set_mode(unsigned gpio, unsigned mode) { rp1_gpio_set_fsel( gpio, mode ); } // get pin mode int do_gpio_get_mode(unsigned gpio) { GPIO_DIR_T dir; int mode = rp1_gpio_get_fsel( gpio ); if( mode == GPIO_FSEL_GPIO ) { dir = rp1_gpio_get_dir( gpio ); if( dir == DIR_OUTPUT ) { return GPIO_FSEL_OUTPUT; } else { return GPIO_FSEL_INPUT; } } else { return mode; } } // read pin value static int do_gpio_read(unsigned gpio) { return rp1_gpio_get_level( gpio ); } // write pin value static void do_gpio_write(unsigned gpio, unsigned level) { rp1_gpio_set_drive( gpio, level ); } // get schmitt value static int do_gpio_get_schmitt(unsigned gpio) { return rp1_gpio_get_schmitt( gpio ); } // set schmitt value static void do_gpio_set_schmitt(unsigned gpio, unsigned schmitt) { rp1_gpio_set_schmitt( gpio, schmitt ); } // get slew value static int do_gpio_get_slew(unsigned gpio) { return rp1_gpio_get_slew( gpio ); } // set slew value static void do_gpio_set_slew(unsigned gpio, unsigned slew) { rp1_gpio_set_slew( gpio, slew ); } // send error to module static void send_module_error( char * error ) { dSP; ENTER; SAVETMPS; PUSHMARK(SP); EXTEND(SP, 1); PUSHs(sv_2mortal(newSVpv(error, 0))); PUTBACK; call_pv("HiPi::GPIO::error_report", G_DISCARD); FREETMPS; LEAVE; } static uint32_t map_fsel_rp1_to_hipi( GPIO_FSEL_T fsel ) { int hipi_mode = HIPI_MODE_NONE; switch (fsel) { case GPIO_FSEL_INPUT: hipi_mode = HIPI_MODE_INPUT; break; case GPIO_FSEL_OUTPUT: hipi_mode = HIPI_MODE_OUTPUT; break; case GPIO_FSEL_GPIO: hipi_mode = HIPI_MODE_INPUT; break; case GPIO_FSEL_FUNC0: hipi_mode = HIPI_MODE_ALT0; break; case GPIO_FSEL_FUNC1: hipi_mode = HIPI_MODE_ALT1; break; case GPIO_FSEL_FUNC2: hipi_mode = HIPI_MODE_ALT2; break; case GPIO_FSEL_FUNC3: hipi_mode = HIPI_MODE_ALT3; break; case GPIO_FSEL_FUNC4: hipi_mode = HIPI_MODE_ALT4; break; case GPIO_FSEL_FUNC5: hipi_mode = HIPI_MODE_ALT5; break; case GPIO_FSEL_FUNC6: hipi_mode = HIPI_MODE_ALT6; break; case GPIO_FSEL_FUNC7: hipi_mode = HIPI_MODE_ALT7; break; case GPIO_FSEL_FUNC8: hipi_mode = HIPI_MODE_ALT8; break; default: hipi_mode = HIPI_MODE_NONE; } return hipi_mode; } static GPIO_FSEL_T map_fsel_hipi_to_rp1( int fsel ) { GPIO_FSEL_T rp1_mode = GPIO_FSEL_NONE; switch (fsel) { case HIPI_MODE_INPUT: rp1_mode = GPIO_FSEL_INPUT; break; case HIPI_MODE_OUTPUT: rp1_mode = GPIO_FSEL_OUTPUT; break; case HIPI_MODE_ALT0: rp1_mode = GPIO_FSEL_FUNC0; break; case HIPI_MODE_ALT1: rp1_mode = GPIO_FSEL_FUNC1; break; case HIPI_MODE_ALT2: rp1_mode = GPIO_FSEL_FUNC2; break; case HIPI_MODE_ALT3: rp1_mode = GPIO_FSEL_FUNC3; break; case HIPI_MODE_ALT4: rp1_mode = GPIO_FSEL_FUNC4; break; case HIPI_MODE_ALT5: rp1_mode = GPIO_FSEL_FUNC5; break; case HIPI_MODE_ALT6: rp1_mode = GPIO_FSEL_FUNC6; break; case HIPI_MODE_ALT7: rp1_mode = GPIO_FSEL_FUNC7; break; case HIPI_MODE_ALT8: rp1_mode = GPIO_FSEL_FUNC8; break; default: rp1_mode = GPIO_FSEL_NONE; } return rp1_mode; } MODULE = HiPi::GPIO::RP1 PACKAGE = HiPi::GPIO int xs_initialise_gpio_block() CODE: RETVAL = do_initialise(); OUTPUT: RETVAL void xs_release_gpio_block() CODE: do_uninitialise(); int xs_gpio_write( gpio, level ) unsigned gpio unsigned level CODE: if (gpio > PI_MAX_GPIO) { send_module_error("bad gpio number specified"); RETVAL = PI_PIN_ERROR; } else if (level > PI_ON) { send_module_error("bad level specified"); RETVAL = PI_PIN_ERROR; } else { do_gpio_write( gpio, level ); RETVAL = (int)level; } OUTPUT: RETVAL int xs_gpio_read( gpio ) unsigned gpio CODE: if (gpio > PI_MAX_GPIO) { send_module_error("bad gpio number specified"); RETVAL = PI_PIN_ERROR; } else { RETVAL = do_gpio_read(gpio); } OUTPUT: RETVAL int xs_gpio_set_mode( gpio, mode ) unsigned gpio unsigned mode CODE: if (gpio > PI_MAX_GPIO) { send_module_error("bad gpio number specified"); RETVAL = PI_PIN_ERROR; } else if( mode > PI_MAX_MODE ){ send_module_error("bad mode specified"); RETVAL = PI_PIN_ERROR; } else { do_gpio_set_mode( gpio, map_fsel_hipi_to_rp1( mode )); RETVAL = (int)mode; } OUTPUT: RETVAL int xs_gpio_get_mode( gpio ) unsigned gpio CODE: if (gpio > PI_MAX_GPIO) { send_module_error("bad gpio number specified"); RETVAL = PI_PIN_ERROR; } else { RETVAL = map_fsel_rp1_to_hipi(do_gpio_get_mode( gpio )); } OUTPUT: RETVAL void xs_gpio_get_mode_name( gpio, mode ) unsigned gpio int mode PPCODE: XPUSHs(sv_2mortal(newSVpv(rp1_gpio_get_fsel_name( gpio, map_fsel_hipi_to_rp1( mode ) ), 0))); void xs_gpio_get_current_mode_name( gpio ) unsigned gpio PPCODE: XPUSHs(sv_2mortal(newSVpv(rp1_gpio_get_fsel_name( gpio, do_gpio_get_mode( gpio ) ), 0))); int xs_gpio_set_pud( gpio, pud ) unsigned gpio unsigned pud CODE: if (gpio > PI_MAX_GPIO) { send_module_error("bad gpio number specified"); RETVAL = PI_PIN_ERROR; } else if( pud > PI_PUD_UP ){ send_module_error("bad pud action specified"); RETVAL = PI_PIN_ERROR; } else { // set the pud do_gpio_set_pud( gpio, pud); RETVAL = (int)pud; } OUTPUT: RETVAL int xs_gpio_get_pud( gpio ) unsigned gpio CODE: if (gpio > PI_MAX_GPIO) { send_module_error("bad gpio number specified"); RETVAL = PI_PIN_ERROR; } else { // get the pud RETVAL = do_gpio_get_pud( gpio ); } OUTPUT: RETVAL int xs_gpio_set_schmitt( gpio, schmitt ) unsigned gpio unsigned schmitt CODE: if (gpio > PI_MAX_GPIO) { send_module_error("bad gpio number specified"); RETVAL = PI_PIN_ERROR; } else if( schmitt >= SCHMITT_MAX ){ send_module_error("bad schmitt value specified"); RETVAL = PI_PIN_ERROR; } else { do_gpio_set_schmitt( gpio, schmitt); RETVAL = (int)schmitt; } OUTPUT: RETVAL int xs_gpio_get_schmitt( gpio ) unsigned gpio CODE: if (gpio > PI_MAX_GPIO) { send_module_error("bad gpio number specified"); RETVAL = PI_PIN_ERROR; } else { RETVAL = do_gpio_get_schmitt( gpio ); } OUTPUT: RETVAL int xs_gpio_set_slew( gpio, slew ) unsigned gpio unsigned slew CODE: if (gpio > PI_MAX_GPIO) { send_module_error("bad gpio number specified"); RETVAL = PI_PIN_ERROR; } else if( slew >= SLEW_MAX ){ send_module_error("bad slew value specified"); RETVAL = PI_PIN_ERROR; } else { do_gpio_set_slew( gpio, slew); RETVAL = (int)slew; } OUTPUT: RETVAL int xs_gpio_get_slew( gpio ) unsigned gpio CODE: if (gpio > PI_MAX_GPIO) { send_module_error("bad gpio number specified"); RETVAL = PI_PIN_ERROR; } else { RETVAL = do_gpio_get_slew( gpio ); } OUTPUT: RETVAL uint64_t xs_gpio_get_peripheral_base_address() CODE: RETVAL = PERI_BASE_ADDRESS; OUTPUT: RETVAL