From a46e9d233b0d094256459f613101070f84133a6a Mon Sep 17 00:00:00 2001 From: "Jes B. Klinke" Date: Mon, 8 May 2023 13:18:37 -0700 Subject: board/hyperdebug: Properly enter alternate mode HyperDebug allows host software to change the mode of most pins. Some of them have pre-defined alternate modes, such as UART, SPI or I2C. The I2C pins in particular must operate as open-drain, when in their "alternate" mode. The EC codebase usually does this as part of gpio_config_module() when all pins used by a particular module are put into alternate mode, based on the flags field in ALTERNATE() declarations in gpio.inc. HyperDebug however, operates on a single pin at a time using gpio_set_flags() which does not look at ALTERNATE() declarations. This CL introduces a helper method to find out which additional flags need to be applied, when switching a particular pin into alternate mode. BUG=none TEST=HyperDebug can continue I2C communication after `transport init` Change-Id: I8e7c9ed081635c6166e9487cff02f0b0e3d059cc Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/4513887 Tested-by: Jes Klinke Reviewed-by: Jett Rink Commit-Queue: Jes Klinke --- board/hyperdebug/gpio.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 57 insertions(+), 2 deletions(-) diff --git a/board/hyperdebug/gpio.c b/board/hyperdebug/gpio.c index 5bcabe226d..a12b424032 100644 --- a/board/hyperdebug/gpio.c +++ b/board/hyperdebug/gpio.c @@ -27,6 +27,34 @@ const struct dac_t dac_channels[GPIO_COUNT] = { [GPIO_CN7_10] = { STM32_DAC_CR_EN2, &STM32_DAC_DHR12R2 }, }; +/* + * GPIO structure for keeping extra flags such as GPIO_OPEN_DRAIN, to be applied + * whenever the pin is switched into "alternate" mode. + */ +struct gpio_alt_flags { + /* Port base address */ + uint32_t port; + + /* Bitmask on that port (multiple bits allowed) */ + uint32_t mask; + + /* Flags (GPIO_*; see above). */ + uint32_t flags; +}; + +/* + * Construct the gpio_alt_flags array, this really is just a subset of the + * columns in the gpio_alt_funcs array in common/gpio.c (which is not accessible + * from here). This array is used by extra_alternate_flags(). + */ +#define ALTERNATE(pinmask, function, module, flagz) \ + { GPIO_##pinmask, .flags = (flagz) }, + +static __const_data const struct gpio_alt_flags gpio_alt_flags[] = { +#include "gpio.wrap" +}; +#undef ALTERNATE + /* * A cyclic buffer is used to record events (edges) of one or more GPIO * signals. Each event records the time since the previous event, and the @@ -284,6 +312,30 @@ static void stop_all_gpio_monitoring(void) } } +/* + * Return GPIO_OPEN_DRAIN or any other special flags to apply when the given + * signal is in "alternate" mode. + */ +static uint32_t extra_alternate_flags(enum gpio_signal signal) +{ + const struct gpio_info *g = gpio_list + signal; + const struct gpio_alt_flags *af; + + /* Find the first ALTERNATE() declaration for the given pin. */ + for (af = gpio_alt_flags; + af < gpio_alt_flags + ARRAY_SIZE(gpio_alt_flags); af++) { + if (af->port != g->port) + continue; + + if (af->mask & g->mask) { + return af->flags; + } + } + + /* No ALTERNATE() declaration mention the given pin. */ + return 0; +} + /** * Find a GPIO signal by name. * @@ -344,7 +396,7 @@ static int command_gpio_mode(int argc, const char **argv) /* Disable digital output, when DAC is overriding. */ flags |= GPIO_INPUT; } else if (strcasecmp(argv[2], "alternate") == 0) - flags |= GPIO_ALTERNATE; + flags |= GPIO_ALTERNATE | extra_alternate_flags(gpio); else return EC_ERROR_PARAM2; @@ -484,7 +536,7 @@ static int command_gpio_multiset(int argc, const char **argv) /* Disable digital output, when DAC is overriding. */ flags |= GPIO_INPUT; } else if (strcasecmp(argv[4], "alternate") == 0) - flags |= GPIO_ALTERNATE; + flags |= GPIO_ALTERNATE | extra_alternate_flags(gpio); else return EC_ERROR_PARAM4; } @@ -872,6 +924,9 @@ static int command_reinit(int argc, const char **argv) if (flags & GPIO_DEFAULT) continue; + if (flags & GPIO_ALTERNATE) + flags |= extra_alternate_flags(i); + /* Set up GPIO based on flags */ gpio_set_flags_by_mask(g->port, g->mask, flags); } -- cgit v1.2.1