This is an automated email from Gerrit. "Steve Marple <stevemar...@googlemail.com>" just uploaded a new patch set to Gerrit, which you can find at https://review.openocd.org/c/openocd/+/7130
-- gerrit commit 6cb9a12565c68446c3cdb1fc37fab6a285a054ba Author: Steve Marple <stevemar...@googlemail.com> Date: Tue Aug 16 23:37:47 2022 +0100 drivers/bcm2835gpio: Implement srst and power sensing Signed-off-by: Steve Marple <stevemar...@googlemail.com> Change-Id: Ib3904278737350b0f74bd95e4abf487e19fa646f diff --git a/doc/openocd.texi b/doc/openocd.texi index 5b37ad1299..26c7bd6075 100644 --- a/doc/openocd.texi +++ b/doc/openocd.texi @@ -3299,8 +3299,12 @@ configuration on exit. GPIO numbers >= 32 can't be used for performance reasons. GPIO configuration is handled by the generic command @ref{adapter gpio, @command{adapter gpio}}. -See @file{interface/raspberrypi-native.cfg} for a sample config and -pinout. +The driver supports the @command{adapter gpio pwr_ctrl}, @command{adapter gpio +pwr_sense}, and @command{adapter gpio srst_sense} commands. When sensing inputs +the GPIO peripheral should not generate interrupts as this may cause Linux to +hang so be sure to include the line @samp{dtoverlay=gpio-no-irq} in the file +@file{/boot/config.txt}. See @file{interface/raspberrypi-native.cfg} for a +sample config and pinout. @deffn {Config Command} {bcm2835gpio speed_coeffs} @var{speed_coeff} @var{speed_offset} Set SPEED_COEFF and SPEED_OFFSET for delay calculations. If unspecified, diff --git a/src/jtag/drivers/bcm2835gpio.c b/src/jtag/drivers/bcm2835gpio.c index 51f737251b..16aaaa2098 100644 --- a/src/jtag/drivers/bcm2835gpio.c +++ b/src/jtag/drivers/bcm2835gpio.c @@ -40,10 +40,24 @@ uint32_t bcm2835_peri_base = 0x20000000; #define GPIO_SET (*(pio_base+7)) /* sets bits which are 1, ignores bits which are 0 */ #define GPIO_CLR (*(pio_base+10)) /* clears bits which are 1, ignores bits which are 0 */ #define GPIO_LEV (*(pio_base+13)) /* current level of the pin */ - -static int dev_mem_fd; -static volatile uint32_t *pio_base = MAP_FAILED; -static volatile uint32_t *pads_base = MAP_FAILED; +#define GPIO_GPEDS0 (*(pio_base+16)) /* event detect status 0 */ +#define GPIO_GPREN0 (*(pio_base+19)) /* rising-edge detect enable 0 */ +#define GPIO_GPFEN0 (*(pio_base+22)) /* falling-edge detect enable 0 */ +#define GPIO_GPHEN0 (*(pio_base+25)) /* high detect enable 0 */ +#define GPIO_GPLEN0 (*(pio_base+28)) /* low detect enable 0 */ +#define GPIO_GPAREN0 (*(pio_base+31)) /* asynchronous rising-edge detection (0) */ +#define GPIO_GPAFEN0 (*(pio_base+34)) /* asynchronous falling-edge detection (0) */ + +/* There are various options for sensing: level detection, edge detection, and + * asynchronous edge detection. Try asynchronous detection since it should catch + * narrow glitches that the pattern-matching edge-detection choices might + * reject. + */ +#define GPIO_SENSE_LOW GPIO_GPAFEN0 +#define GPIO_SENSE_HIGH GPIO_GPAREN0 + +static int dev_mem_fd; static volatile uint32_t *pio_base = MAP_FAILED; static +volatile uint32_t *pads_base = MAP_FAILED; /* Transition delay coefficients */ static int speed_coeff = 113714; @@ -107,6 +121,20 @@ static void restore_gpio(enum adapter_gpio_config_index idx) else GPIO_CLR = 1 << adapter_gpio_config[idx].gpio_num; } + + /* TODO: restore edge and level detection settings for sense inputs? Is + * it worthwhile? + */ + if (idx == ADAPTER_GPIO_IDX_SRST_SENSE || idx == ADAPTER_GPIO_IDX_PWR_SENSE) { + uint32_t mask = (1 << adapter_gpio_config[idx].gpio_num); + /* Clear all edge and level detection for this GPIO. */ + GPIO_GPREN0 &= ~mask; + GPIO_GPFEN0 &= ~mask; + GPIO_GPHEN0 &= ~mask; + GPIO_GPLEN0 &= ~mask; + GPIO_GPAREN0 &= ~mask; + GPIO_GPAFEN0 &= ~mask; + } } } @@ -139,6 +167,29 @@ static void initialize_gpio(enum adapter_gpio_config_index idx) break; } + if (idx == ADAPTER_GPIO_IDX_SRST_SENSE || idx == ADAPTER_GPIO_IDX_PWR_SENSE) { + /* Enabling edge detection can lead to unwanted interactions with the OS + * (almost certainly Linux) that can result in Linux hanging. The + * solution is to disable interrupts from the GPIO peripheral when + * sensing srst or power dropout; do so by including + * "dtoverlay=gpio-no-irq" in /boot/config.txt + */ + uint32_t mask = (1 << adapter_gpio_config[idx].gpio_num); /* Clear all + edge and level detection for this GPIO. */ + GPIO_GPREN0 &= ~mask; + GPIO_GPFEN0 &= ~mask; + GPIO_GPHEN0 &= ~mask; + GPIO_GPLEN0 &= ~mask; + GPIO_GPAREN0 &= ~mask; + GPIO_GPAFEN0 &= ~mask; + + if (adapter_gpio_config[idx].active_low) + GPIO_SENSE_LOW |= mask; + else + GPIO_SENSE_HIGH |= mask; + GPIO_GPEDS0 = mask; /* Clear any existing event for this GPIO */ + } + /* Direction for non push-pull is already set by set_gpio_value() */ if (adapter_gpio_config[idx].drive == ADAPTER_GPIO_DRIVE_MODE_PUSH_PULL) OUT_GPIO(adapter_gpio_config[idx].gpio_num); @@ -471,6 +522,8 @@ static int bcm2835gpio_init(void) initialize_gpio(ADAPTER_GPIO_IDX_SRST); initialize_gpio(ADAPTER_GPIO_IDX_LED); + initialize_gpio(ADAPTER_GPIO_IDX_SRST_SENSE); + initialize_gpio(ADAPTER_GPIO_IDX_PWR_SENSE); return ERROR_OK; } @@ -499,6 +552,8 @@ static int bcm2835gpio_quit(void) restore_gpio(ADAPTER_GPIO_IDX_SRST); restore_gpio(ADAPTER_GPIO_IDX_LED); + restore_gpio(ADAPTER_GPIO_IDX_SRST_SENSE); + restore_gpio(ADAPTER_GPIO_IDX_PWR_SENSE); /* Keep powered until last possible moment */ restore_gpio(ADAPTER_GPIO_IDX_PWR_CTRL); @@ -508,6 +563,36 @@ static int bcm2835gpio_quit(void) return ERROR_OK; } +static int bcm2835gpio_input_sense(enum adapter_gpio_config_index idx, int *sensed) +{ + if (!is_gpio_config_valid(idx)) { + *sensed = 0; + return ERROR_OK; + } + + /* Check if any events occurred since last time; read out all. */ + uint32_t mask = (1 << adapter_gpio_config[idx].gpio_num); + if (GPIO_GPEDS0 & mask) { + *sensed = 1; + GPIO_GPEDS0 = mask; + LOG_ERROR("BCM2835: %s asserted", adapter_gpio_get_name(idx)); + } else { + *sensed = 0; + } + + return ERROR_OK; +} + +static int bcm2835gpio_power_dropout(int *power_dropout) +{ + return bcm2835gpio_input_sense(ADAPTER_GPIO_IDX_PWR_SENSE, power_dropout); +} + +static int bcm2835gpio_srst_asserted(int *srst_asserted) +{ + return bcm2835gpio_input_sense(ADAPTER_GPIO_IDX_SRST_SENSE, srst_asserted); +} + struct adapter_driver bcm2835gpio_adapter_driver = { .name = "bcm2835gpio", .transports = bcm2835_transports, @@ -519,6 +604,8 @@ struct adapter_driver bcm2835gpio_adapter_driver = { .speed = bcm2835gpio_speed, .khz = bcm2835gpio_khz, .speed_div = bcm2835gpio_speed_div, + .power_dropout = bcm2835gpio_power_dropout, + .srst_asserted = bcm2835gpio_srst_asserted, .jtag_ops = &bcm2835gpio_interface, .swd_ops = &bitbang_swd, --