Skip to content
Merged
34 changes: 32 additions & 2 deletions lib/pbio/drv/gpio/gpio_ev3.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@
#include <tiam1808/gpio.h>
#include <tiam1808/psc.h>

#include "../rproc/rproc.h"
#include "../rproc/rproc_ev3.h"

static uint32_t get_pin_index(const pbdrv_gpio_t *gpio) {
// TI API indexes pins from 1 to 144, so need to add 1.
pbdrv_gpio_ev3_mux_t *mux_info = gpio->bank;
Expand All @@ -31,13 +34,32 @@ static void pbdrv_gpio_alt_gpio(const pbdrv_gpio_t *gpio) {
pbdrv_gpio_alt(gpio, mux_info->gpio_mode);
}

// If the pin is not in banks 0/1, the PRU is not involved.
// Otherwise, if the PRU is initialized, do direction changes
// via the PRU in order to prevent race conditions.
// If the PRU is not initialized (i.e. doing direction changes
// during early boot), also set the direction directly via
// the ARM. The PRU code polls these direction change registers
// continuously and is not expected to block for too long,
// only on the order of microseconds.
#define PBDRV_GPIO_EV3_ARM_OWNS_GPIO_BANK(pin_index) \
((pin_index) > 32 || !pbdrv_rproc_is_ready())

static void gpio_write(const pbdrv_gpio_t *gpio, uint8_t value) {
if (!gpio) {
return;
}
pbdrv_gpio_alt_gpio(gpio);
uint32_t pin_index = get_pin_index(gpio);
GPIODirModeSet(SOC_GPIO_0_REGS, pin_index, GPIO_DIR_OUTPUT);
if (PBDRV_GPIO_EV3_ARM_OWNS_GPIO_BANK(pin_index)) {
GPIODirModeSet(SOC_GPIO_0_REGS, pin_index, GPIO_DIR_OUTPUT);
} else if (GPIODirModeGet(SOC_GPIO_0_REGS, pin_index) != GPIO_DIR_OUTPUT) {
Copy link
Member

@laurensvalk laurensvalk Aug 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this check go as the first if, so we don't re-set it for pins owned by the ARM either?

(I know it used to double-set it before as well, but now that we have the test we might as well cover both cases.)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I figured that code path was cheap enough (a single register write) that it wasn't worth bothering

uint32_t val = 1 << (pin_index - 1);
pbdrv_rproc_ev3_pru1_shared_ram.gpio_bank_01_dir_clr = val;
while (pbdrv_rproc_ev3_pru1_shared_ram.gpio_bank_01_dir_clr) {
// Wait for the PRU to process the command
}
}
GPIOPinWrite(SOC_GPIO_0_REGS, pin_index, value);
}

Expand All @@ -55,7 +77,15 @@ uint8_t pbdrv_gpio_input(const pbdrv_gpio_t *gpio) {
}
pbdrv_gpio_alt_gpio(gpio);
uint32_t pin_index = get_pin_index(gpio);
GPIODirModeSet(SOC_GPIO_0_REGS, pin_index, GPIO_DIR_INPUT);
if (PBDRV_GPIO_EV3_ARM_OWNS_GPIO_BANK(pin_index)) {
GPIODirModeSet(SOC_GPIO_0_REGS, pin_index, GPIO_DIR_INPUT);
} else if (GPIODirModeGet(SOC_GPIO_0_REGS, pin_index) != GPIO_DIR_INPUT) {
uint32_t val = 1 << (pin_index - 1);
pbdrv_rproc_ev3_pru1_shared_ram.gpio_bank_01_dir_set = val;
while (pbdrv_rproc_ev3_pru1_shared_ram.gpio_bank_01_dir_set) {
// Wait for the PRU to process the command
}
}
return GPIOPinRead(SOC_GPIO_0_REGS, pin_index) == GPIO_PIN_HIGH;
}

Expand Down
6 changes: 6 additions & 0 deletions lib/pbio/drv/rproc/rproc_ev3_pru1.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ typedef struct {
// PWM duty cycle for each channel, range [0-255]
uint8_t pwm_duty_cycle[PBDRV_RPROC_EV3_PRU1_NUM_PWM_CHANNELS];
};
// Because the PRU needs to manipulate the GPIO direction
// for doing I2C, and because these registers do *not*
// support atomic bit access, we give the PRU full ownership
// of them and route ARM accesses through the PRU.
uint32_t gpio_bank_01_dir_set;
uint32_t gpio_bank_01_dir_clr;
} pbdrv_rproc_ev3_pru1_shared_ram_t;

#endif // _INTERNAL_PBDRV_RPROC_EV3_PRU1_H_