Skip to content

Commit 0c9eca1

Browse files
ArcaneNibbledlech
authored andcommitted
pbio/drv/gpio/gpio_ev3.c: Route via PRU
Give the PRU complete control over GPIO banks 0/1. The PRU needs to be able to change GPIO pin directions for I2C. However, if the ARM and the PRU both attempt read-modify-write operations on this register, they can corrupt each other's access. Solve this by giving the PRU complete control over these banks once the PRU has booted.
1 parent d2bd04a commit 0c9eca1

2 files changed

Lines changed: 38 additions & 2 deletions

File tree

lib/pbio/drv/gpio/gpio_ev3.c

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717
#include <tiam1808/gpio.h>
1818
#include <tiam1808/psc.h>
1919

20+
#include "../rproc/rproc.h"
21+
#include "../rproc/rproc_ev3.h"
22+
2023
static uint32_t get_pin_index(const pbdrv_gpio_t *gpio) {
2124
// TI API indexes pins from 1 to 144, so need to add 1.
2225
pbdrv_gpio_ev3_mux_t *mux_info = gpio->bank;
@@ -31,13 +34,32 @@ static void pbdrv_gpio_alt_gpio(const pbdrv_gpio_t *gpio) {
3134
pbdrv_gpio_alt(gpio, mux_info->gpio_mode);
3235
}
3336

37+
// If the pin is not in banks 0/1, the PRU is not involved.
38+
// Otherwise, if the PRU is initialized, do direction changes
39+
// via the PRU in order to prevent race conditions.
40+
// If the PRU is not initialized (i.e. doing direction changes
41+
// during early boot), also set the direction directly via
42+
// the ARM. The PRU code polls these direction change registers
43+
// continuously and is not expected to block for too long,
44+
// only on the order of microseconds.
45+
#define PBDRV_GPIO_EV3_ARM_OWNS_GPIO_BANK(pin_index) \
46+
((pin_index) > 32 || !pbdrv_rproc_is_ready())
47+
3448
static void gpio_write(const pbdrv_gpio_t *gpio, uint8_t value) {
3549
if (!gpio) {
3650
return;
3751
}
3852
pbdrv_gpio_alt_gpio(gpio);
3953
uint32_t pin_index = get_pin_index(gpio);
40-
GPIODirModeSet(SOC_GPIO_0_REGS, pin_index, GPIO_DIR_OUTPUT);
54+
if (PBDRV_GPIO_EV3_ARM_OWNS_GPIO_BANK(pin_index)) {
55+
GPIODirModeSet(SOC_GPIO_0_REGS, pin_index, GPIO_DIR_OUTPUT);
56+
} else if (GPIODirModeGet(SOC_GPIO_0_REGS, pin_index) != GPIO_DIR_OUTPUT) {
57+
uint32_t val = 1 << (pin_index - 1);
58+
pbdrv_rproc_ev3_pru1_shared_ram.gpio_bank_01_dir_clr = val;
59+
while (pbdrv_rproc_ev3_pru1_shared_ram.gpio_bank_01_dir_clr) {
60+
// Wait for the PRU to process the command
61+
}
62+
}
4163
GPIOPinWrite(SOC_GPIO_0_REGS, pin_index, value);
4264
}
4365

@@ -55,7 +77,15 @@ uint8_t pbdrv_gpio_input(const pbdrv_gpio_t *gpio) {
5577
}
5678
pbdrv_gpio_alt_gpio(gpio);
5779
uint32_t pin_index = get_pin_index(gpio);
58-
GPIODirModeSet(SOC_GPIO_0_REGS, pin_index, GPIO_DIR_INPUT);
80+
if (PBDRV_GPIO_EV3_ARM_OWNS_GPIO_BANK(pin_index)) {
81+
GPIODirModeSet(SOC_GPIO_0_REGS, pin_index, GPIO_DIR_INPUT);
82+
} else if (GPIODirModeGet(SOC_GPIO_0_REGS, pin_index) != GPIO_DIR_INPUT) {
83+
uint32_t val = 1 << (pin_index - 1);
84+
pbdrv_rproc_ev3_pru1_shared_ram.gpio_bank_01_dir_set = val;
85+
while (pbdrv_rproc_ev3_pru1_shared_ram.gpio_bank_01_dir_set) {
86+
// Wait for the PRU to process the command
87+
}
88+
}
5989
return GPIOPinRead(SOC_GPIO_0_REGS, pin_index) == GPIO_PIN_HIGH;
6090
}
6191

lib/pbio/drv/rproc/rproc_ev3_pru1.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,12 @@ typedef struct {
1919
// PWM duty cycle for each channel, range [0-255]
2020
uint8_t pwm_duty_cycle[PBDRV_RPROC_EV3_PRU1_NUM_PWM_CHANNELS];
2121
};
22+
// Because the PRU needs to manipulate the GPIO direction
23+
// for doing I2C, and because these registers do *not*
24+
// support atomic bit access, we give the PRU full ownership
25+
// of them and route ARM accesses through the PRU.
26+
uint32_t gpio_bank_01_dir_set;
27+
uint32_t gpio_bank_01_dir_clr;
2228
} pbdrv_rproc_ev3_pru1_shared_ram_t;
2329

2430
#endif // _INTERNAL_PBDRV_RPROC_EV3_PRU1_H_

0 commit comments

Comments
 (0)