diff options
author | Peter Marheine <pmarheine@chromium.org> | 2023-02-16 00:14:21 +0000 |
---|---|---|
committer | Peter Marheine <pmarheine@chromium.org> | 2023-02-16 00:14:21 +0000 |
commit | f43d6df6f2860dbe8ec15aebdc28890771ad7897 (patch) | |
tree | 6877eff7016f9979e46017ea5221a16d36344304 /board/hyperdebug/gpio.c | |
parent | dce247e97903a2464ff8884106bffa43587c1dcb (diff) | |
parent | 0849542c72359150a357945c010dca6b5a213633 (diff) | |
download | chrome-ec-f43d6df6f2860dbe8ec15aebdc28890771ad7897.tar.gz |
Merge remote-tracking branch cros/main into firmware-nissa-15217.B-mainfirmware-nissa-15217.126.B-main
Generated by: ./util/update_release_branch.py --zephyr --board nissa firmware-
nissa-15217.B-main
Relevant changes:
git log --oneline dce247e979..0849542c72 -- zephyr/program/nissa
util/getversion.sh
0849542c72 zephyr: add sleep property for keyboard factory test
5825d894b9 nissa: disable PLATFORM_EC_TCPC_INTERRUPT
25a400690f yavikso: Update fan table version 3
9263f14536 zephyr: remove shi node and add references to shi0
b40c6970d3 Craask: Charger limit for 65w adapter
6fd2d3ea6c zephyr: add comments for tcpc driver
00986ffb1c zephyr: move usbc interrupt handler to shim
2508094b14 yaviks: reduce RW image size (64KB)
55b6aaf52f yaviks: enable keyboard factory test
4786d1eac6 nissa: Add ocpc init function to Nissa boards
2ae1083638 zephyr: cros_kb: set KSI/KSO kbs mode by pinctrl driver
d415f4665a yaviks: Fix OCPC Vsys overshooting
93f9d42a7c zephyr: config: drop few more redundant options
BRANCH=None
BUG=b:254148652 b:260762509 b:265220075 b:268273712 b:265763662
BUG=b:253557900 b:254148652 b:269212593 b:267404783 b:262352202
TEST=`make -j buildall`
Force-Relevant-Builds: all
Change-Id: I195dd82af114180b611f9afd1465a69f7f43d417
Signed-off-by: Peter Marheine <pmarheine@chromium.org>
Diffstat (limited to 'board/hyperdebug/gpio.c')
-rw-r--r-- | board/hyperdebug/gpio.c | 670 |
1 files changed, 670 insertions, 0 deletions
diff --git a/board/hyperdebug/gpio.c b/board/hyperdebug/gpio.c index 59f224571a..7dbc063237 100644 --- a/board/hyperdebug/gpio.c +++ b/board/hyperdebug/gpio.c @@ -4,11 +4,246 @@ */ /* HyperDebug GPIO logic and console commands */ +#include "atomic.h" +#include "builtin/assert.h" #include "common.h" #include "console.h" +#include "cpu.h" #include "gpio.h" +#include "hooks.h" +#include "registers.h" +#include "shared_mem.h" +#include "task.h" +#include "timer.h" #include "util.h" +/* + * 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 + * signal that changed (the direction of change is not explicitly recorded). + * + * So conceptually the buffer entries are pairs of (diff: u64, signal_no: u8). + * These entries are encoded as bytes in the following way: First the timestamp + * diff is shifted left by signal_bits, and the signal_no value put into the + * lower bits freed up this way. Now we have a single u64, which often will be + * a small value (or at least, when the edges happen rapidly, and the need to + * store many of them the highest, then the u64 will be a small value). This + * u64 is then stored 7 bits at a time in successive bytes, with the most + * significant bit indicating whether more bytes belong to the same entry. + * + * The chain of relative timestamps are resolved by keeping two absolute + * timestamps: head_time is the time of the most recently inserted event, and is + * accessed and updated only by the interrupt handler. tail_time is the past + * timestamp on which the diff of the oldest record in the buffer is based (the + * timestamp of the last record to be removed from the buffer), it is accessed + * and updated only from the non-interrupt code that removes records from the + * buffer. + * + * In a similar fashion, the signal level is recorded "at both ends" in for each + * monitored signal by head_level and tail_level, the former only accessed from + * the interrupt handler, and the latter only accessed from non-interrupt code. + */ +struct cyclic_buffer_header_t { + /* Number of signals being monitored in this buffer. */ + uint8_t num_signals; + /* The number of bits required to represent 0..num_signals-1. */ + uint8_t signal_bits; + /* Sticky bit recording if overflow occurred. */ + volatile uint8_t overflow; + /* Time of the most recent event, updated from interrupt context. */ + volatile timestamp_t head_time; + /* Time base that the oldest event is relative to. */ + timestamp_t tail_time; + /* Index at which new records are placed, updated from interrupt. */ + volatile uint32_t head; + /* Index af oldest record. */ + uint32_t tail; + /* + * Size of cyclic byte buffer, determined at runtime, not necessarily + * power of two. Head and tail wrap to zero here. + */ + uint32_t size; + /* Data contents */ + uint8_t data[]; +}; + +/* + * The STM32L5 has 16 edge detection circuits. Each pin can only be used with + * one of them. That is, detector 0 can take its input from one of pins A0, + * B0, C0, ..., while detector 1 can choose between A1, B1, etc. + * + * Information about the current use of each detection circuit is stored in 16 + * "slots" below. + */ +struct monitoring_slot_t { + /* Link to buffer recording edges of this signal. */ + struct cyclic_buffer_header_t *buffer; + /* EC enum id of the signal used by this detection slot. */ + int gpio_signal; + /* The index of the signal as used in the recording buffer. */ + uint8_t signal_no; + /* Most recently recorded level of the signal. */ + volatile uint8_t head_level; + /* Level as of the current oldest end (tail) of the recording. */ + uint8_t tail_level; +}; +struct monitoring_slot_t monitoring_slots[16]; + +/* + * Counts unacknowledged buffer overflows. Whenever non-zero, the red LED + * will flash. + */ +atomic_t num_cur_error_conditions; + +/* + * Counts the number of cyclic buffers currently in existence, the green LED + * will flash whenever this is non-zero, indicating the monitoring activity. + */ +int num_cur_monitoring = 0; + +static __attribute__((noinline)) void overflow(struct monitoring_slot_t *slot) +{ + struct cyclic_buffer_header_t *buffer_header = slot->buffer; + gpio_disable_interrupt(slot->gpio_signal); + buffer_header->overflow = 1; + atomic_add(&num_cur_error_conditions, 1); +} + +void gpio_edge(enum gpio_signal signal) +{ + /* + * Hardware has detected one or more edges since last time. We + * process by looking at the current level of the signal. If opposite + * the most recent level, we record one edge, if the same as most + * recent level, we record two edges, that is, a zero-width pulse. + * This is useful for tests trying to verify e.g. that there are no + * glitches on a particular signal, and want to know about any pulses, + * however narrow. + */ + timestamp_t now = get_time(); + int gpio_num = GPIO_MASK_TO_NUM(gpio_list[signal].mask); + struct monitoring_slot_t *slot = monitoring_slots + gpio_num; + int current_level = gpio_get_level(signal); + struct cyclic_buffer_header_t *buffer_header = slot->buffer; + uint8_t *buffer_data = buffer_header->data; + uint32_t tail = buffer_header->tail, head = buffer_header->head, + size = buffer_header->size; + uint64_t diff = now.val - buffer_header->head_time.val; + + uint8_t signal_bits = buffer_header->signal_bits; + + /* + * Race condition here! If three or more edges happen in + * rapid succession, we may fail to record some of them, but + * we should never over-report edges. + * + * Since the edge interrupts pending bit has been cleared before the + * "current_level" was polled, if an edge happened between the two, then + * an interrupt is currently pending, and when handled after this method + * returns, the logic below would wrongly conclude that the signal must + * have seen two transitions, in order to end up at the same level as + * before. In order to avoid such over-reporting, we clear "pending" + * interrupt bit below, but only for the direction that goes "towards" + * the level measured above. + */ + if (current_level) + STM32_EXTI_RPR = BIT(gpio_num); + else + STM32_EXTI_FPR = BIT(gpio_num); + + /* + * Insert an entry recording the time since last event, and which + * signal changed (the direction of the edge is not explicitly + * recorded, as it can be inferred from the initial level). + * + * The time difference and pin index are encoded in `diff`, which will + * be a small integer if the event arrive rapidly. 7 bits of this + * integer is then put into one byte at a time, using the high bit of + * each byte to indicate if more are to come. This encoding will use + * only one byte per event, in the best case, allowing tens of + * thousands of events to be buffered. + */ + diff <<= signal_bits; + diff |= slot->signal_no; + do { + buffer_data[head++] = ((diff >= 0x80) ? 0x80 : 0x00) | + (diff & 0x7F); + diff >>= 7; + if (head == size) + head = 0; + if (head == tail) { + /* + * The new head will not be persisted, maintaining the + * invatiant that head and tail are equal only when the + * buffer is empty. + */ + return overflow(slot); + } + } while (diff); + + /* + * If current level equals the previous level, then record an + * additional edge 0ms after the previous. + */ + if (!!current_level == !!slot->head_level) { + /* + * Add a record with zero diff, and the same signal no. (Will + * always fit in one byte, as signal_no never uses more than 7 + * bits.) + */ + buffer_data[head++] = slot->signal_no; + if (head == size) + head = 0; + if (head == tail) { + /* + * The new head will not be persisted, maintaining the + * invatiant that head and tail are equal only when the + * buffer is empty. + */ + return overflow(slot); + } + } else { + slot->head_level = current_level; + } + buffer_header->head = head; + buffer_header->head_time = now; +} + +static void board_gpio_init(void) +{ + /* Mark every slot as unused. */ + for (int i = 0; i < ARRAY_SIZE(monitoring_slots); i++) + monitoring_slots[i].gpio_signal = GPIO_COUNT; +} +DECLARE_HOOK(HOOK_INIT, board_gpio_init, HOOK_PRIO_DEFAULT); + +static void stop_all_gpio_monitoring(void) +{ + struct monitoring_slot_t *slot; + struct cyclic_buffer_header_t *buffer_header; + for (int i = 0; i < ARRAY_SIZE(monitoring_slots); i++) { + slot = monitoring_slots + i; + if (!slot->buffer) + continue; + + /* Disable interrupts for all signals feeding into the same + * cyclic buffer. */ + buffer_header = slot->buffer; + for (int j = i; j < ARRAY_SIZE(monitoring_slots); j++) { + slot = monitoring_slots + i; + if (slot->buffer != buffer_header) + continue; + gpio_disable_interrupt(slot->gpio_signal); + slot->gpio_signal = GPIO_COUNT; + } + /* Deallocate this one cyclic buffer. */ + num_cur_monitoring--; + if (buffer_header->overflow) + atomic_sub(&num_cur_error_conditions, 1); + shared_mem_release((char *)buffer_header); + } +} + /** * Find a GPIO signal by name. * @@ -102,3 +337,438 @@ static int command_gpio_pull_mode(int argc, const char **argv) DECLARE_CONSOLE_COMMAND_FLAGS(gpiopullmode, command_gpio_pull_mode, "name <none | up | down>", "Set a GPIO weak pull mode", CMD_FLAG_RESTRICTED); + +/* + * Set multiple aspects of a GPIO pin simultaneously, that is, can switch output + * level, opendrain/pushpull, and pullup simultaneously, eliminating the risk of + * glitches. + */ +static int command_gpio_multiset(int argc, const char **argv) +{ + int gpio; + int flags; + + if (argc < 4) + return EC_ERROR_PARAM_COUNT; + + gpio = find_signal_by_name(argv[2]); + if (gpio == GPIO_COUNT) + return EC_ERROR_PARAM2; + flags = gpio_get_flags(gpio); + + if (argc > 3 && strcasecmp(argv[3], "-") != 0) { + flags = flags & ~(GPIO_LOW | GPIO_HIGH); + if (strcasecmp(argv[3], "0") == 0) + flags |= GPIO_LOW; + else if (strcasecmp(argv[3], "1") == 0) + flags |= GPIO_HIGH; + else + return EC_ERROR_PARAM3; + } + + if (argc > 4 && strcasecmp(argv[4], "-") != 0) { + flags = flags & ~(GPIO_INPUT | GPIO_OUTPUT | GPIO_OPEN_DRAIN); + if (strcasecmp(argv[4], "input") == 0) + flags |= GPIO_INPUT; + else if (strcasecmp(argv[4], "opendrain") == 0) + flags |= GPIO_OUTPUT | GPIO_OPEN_DRAIN; + else if (strcasecmp(argv[4], "pushpull") == 0) + flags |= GPIO_OUTPUT; + else if (strcasecmp(argv[4], "alternate") == 0) + flags |= GPIO_ALTERNATE; + else + return EC_ERROR_PARAM4; + } + + if (argc > 5 && strcasecmp(argv[5], "-") != 0) { + flags = flags & ~(GPIO_PULL_UP | GPIO_PULL_DOWN); + if (strcasecmp(argv[5], "none") == 0) + ; + else if (strcasecmp(argv[5], "up") == 0) + flags |= GPIO_PULL_UP; + else if (strcasecmp(argv[5], "down") == 0) + flags |= GPIO_PULL_DOWN; + else + return EC_ERROR_PARAM5; + } + + /* Update GPIO flags. */ + gpio_set_flags(gpio, flags); + return EC_SUCCESS; +} + +static int command_gpio_monitoring_start(int argc, const char **argv) +{ + BUILD_ASSERT(STM32_IRQ_EXTI15 < 32); + int gpios[16]; + int gpio_num = argc - 3; + int i; + timestamp_t now; + int rv; + uint32_t nvic_mask; + size_t cyclic_buffer_size = 8192; /* Maybe configurable by parameter */ + struct cyclic_buffer_header_t *buf; + struct monitoring_slot_t *slot; + + if (gpio_num <= 0 || gpio_num > 16) + return EC_ERROR_PARAM_COUNT; + + for (i = 0; i < gpio_num; i++) { + gpios[i] = find_signal_by_name(argv[3 + i]); + if (gpios[i] == GPIO_COUNT) { + rv = EC_ERROR_PARAM3 + i; + goto out_partial_cleanup; + } + slot = monitoring_slots + + GPIO_MASK_TO_NUM(gpio_list[gpios[i]].mask); + if (slot->gpio_signal != GPIO_COUNT) { + ccprintf("Error: Monitoring of %s conflicts with %s\n", + argv[3 + i], + gpio_list[slot->gpio_signal].name); + rv = EC_ERROR_PARAM3 + i; + goto out_partial_cleanup; + } + slot->gpio_signal = gpios[i]; + } + + /* + * All the requested signals were available for monitoring, and their + * slots have been marked as reserved for the respective signal. + */ + rv = shared_mem_acquire(sizeof(struct cyclic_buffer_header_t) + + cyclic_buffer_size, + (char **)&buf); + if (rv != EC_SUCCESS) + goto out_cleanup; + + buf->head = buf->tail = 0; + buf->size = cyclic_buffer_size; + buf->overflow = 0; + buf->num_signals = gpio_num; + buf->signal_bits = 0; + /* Compute how many bits are required to represent 0..gpio_num-1. */ + while ((gpio_num - 1) >> buf->signal_bits) + buf->signal_bits++; + + for (i = 0; i < gpio_num; i++) { + slot = monitoring_slots + + GPIO_MASK_TO_NUM(gpio_list[gpios[i]].mask); + slot->buffer = buf; + slot->signal_no = i; + } + + /* + * The code relies on all EXTIn interrupts belonging to the same 32-bit + * NVIC register, so that multiple interrupts can be "unleashed" + * simultaneously. + */ + nvic_mask = 0; + + /* + * Disable interrupts in GPIO/EXTI detection circuits (should be + * disabled already, but disabled and clear pending bit to be on the + * safe side). + */ + for (i = 0; i < gpio_num; i++) { + int gpio_num = GPIO_MASK_TO_NUM(gpio_list[gpios[i]].mask); + gpio_disable_interrupt(gpios[i]); + gpio_clear_pending_interrupt(gpios[i]); + nvic_mask |= BIT(STM32_IRQ_EXTI0 + gpio_num); + } + /* Also disable interrupts at NVIC (interrupt controller) level. */ + CPU_NVIC_UNPEND(0) = nvic_mask; + CPU_NVIC_DIS(0) = nvic_mask; + + for (i = 0; i < gpio_num; i++) { + int gpio_num = GPIO_MASK_TO_NUM(gpio_list[gpios[i]].mask); + slot = monitoring_slots + gpio_num; + /* + * Tell the GPIO block to start detecting rising and falling + * edges, and latch them in STM32_EXTI_RPR and STM32_EXTI_FPR + * respectively. Interrupts are still disabled in the NVIC, + * meaning that the execution will not be interrupted, yet, even + * if the GPIO block requests interrupt. + */ + gpio_enable_interrupt(gpios[i]); + slot->tail_level = slot->head_level = gpio_get_level(gpios[i]); + /* + * Race condition here! If three or more edges happen in + * rapid succession, we may fail to record some of them, but + * we should never over-report edges. + * + * Since edge detection was enabled before the "head_level" + * was polled, if an edge happened between the two, then an + * interrupt is currently pending, and when handled after this + * loop, the logic in the gpio_edge interrupt handler would + * wrongly conclude that the signal must have seen two + * transitions, in order to end up at the same level as before. + * In order to avoid such over-reporting, we clear "pending" + * interrupt bit below, but only for the direction that goes + * "towards" the level measured above. + */ + if (slot->head_level) + STM32_EXTI_RPR = BIT(gpio_num); + else + STM32_EXTI_FPR = BIT(gpio_num); + } + /* + * Now enable the handling of the set of interrupts. + */ + now = get_time(); + buf->head_time = now; + CPU_NVIC_EN(0) = nvic_mask; + + buf->tail_time = now; + num_cur_monitoring++; + ccprintf(" @%lld\n", buf->tail_time.val); + + /* + * Dump the initial level of each input, for the convenience of the + * caller. (Allow makes monitoring useful, even if a signal has no + * transitions during the monitoring period. + */ + for (i = 0; i < gpio_num; i++) { + slot = monitoring_slots + + GPIO_MASK_TO_NUM(gpio_list[gpios[i]].mask); + ccprintf(" %d %s %d\n", i, gpio_list[gpios[i]].name, + slot->tail_level); + } + + return EC_SUCCESS; + +out_cleanup: + i = gpio_num; +out_partial_cleanup: + while (i-- > 0) { + monitoring_slots[GPIO_MASK_TO_NUM(gpio_list[gpios[i]].mask)] + .gpio_signal = GPIO_COUNT; + } + return rv; +} + +static int command_gpio_monitoring_read(int argc, const char **argv) +{ + int gpios[16]; + int gpio_num = argc - 3; + int i; + struct cyclic_buffer_header_t *buf = NULL; + struct monitoring_slot_t *slot; + int gpio_signals_by_no[16]; + uint8_t signal_bits; + uint32_t tail, head; + timestamp_t tail_time, now; + + if (gpio_num <= 0 || gpio_num > 16) + return EC_ERROR_PARAM_COUNT; + + for (i = 0; i < gpio_num; i++) { + gpios[i] = find_signal_by_name(argv[3 + i]); + if (gpios[i] == GPIO_COUNT) + return EC_ERROR_PARAM3 + i; /* May overflow */ + slot = monitoring_slots + + GPIO_MASK_TO_NUM(gpio_list[gpios[i]].mask); + if (slot->gpio_signal != gpios[i]) { + ccprintf("Error: Not monitoring %s\n", + gpio_list[gpios[i]].name); + return EC_ERROR_PARAM3 + i; + } + if (buf == NULL) { + buf = slot->buffer; + } else if (buf != slot->buffer) { + ccprintf( + "Error: Not monitoring %s as part of same groups as %s\n", + gpio_list[gpios[i]].name, + gpio_list[gpios[0]].name); + return EC_ERROR_PARAM3 + i; + } + gpio_signals_by_no[slot->signal_no] = gpios[i]; + } + if (gpio_num != buf->num_signals) { + ccprintf("Error: Not full set of signals monitored\n"); + return EC_ERROR_INVAL; + } + + /* + * We read current time, before taking a snapshot of the head pointer as + * set by the interrupt handler. This way, we can guarantee that the + * transcript will include any edge happening at or before the `now` + * timestamp. If an interrupt happens between the two lines below, + * causing our head pointer to include an event that happened after + * "now", then it will be skipped in the loop below, and kept for the + * next invocation of `gpio monitoring read`. + */ + now = get_time(); + head = buf->head; + + ccprintf(" @%lld\n", now.val); + signal_bits = buf->signal_bits; + tail = buf->tail; + tail_time = buf->tail_time; + while (tail != head) { + uint8_t *buffer = buf->data; + timestamp_t diff; + uint8_t byte; + uint8_t signal_no; + int shift = 0; + uint32_t tentative_tail = tail; + struct monitoring_slot_t *slot; + diff.val = 0; + do { + byte = buffer[tentative_tail++]; + if (tentative_tail == buf->size) + tentative_tail = 0; + diff.val |= (byte & 0x7F) << shift; + shift += 7; + } while (byte & 0x80); + signal_no = diff.val & (0xFF >> (8 - signal_bits)); + diff.val >>= signal_bits; + if (tail_time.val + diff.val > now.val) { + /* + * Do not consume this or subsequent records, which + * apparently happened after our "now" timestamp from + * earlier in the execution of this method. + */ + break; + } + tail = tentative_tail; + tail_time.val += diff.val; + slot = monitoring_slots + + GPIO_MASK_TO_NUM( + gpio_list[gpio_signals_by_no[signal_no]].mask); + /* To conserve bandwidth, timestamps are relative to `now`. */ + ccprintf(" %d %lld %s\n", signal_no, tail_time.val - now.val, + slot->tail_level ? "F" : "R"); + slot->tail_level = !slot->tail_level; + } + buf->tail = tail; + buf->tail_time = tail_time; + if (buf->overflow) { + ccprintf("Error: Buffer overflow\n"); + } + + return EC_SUCCESS; +} + +static int command_gpio_monitoring_stop(int argc, const char **argv) +{ + int gpios[16]; + int gpio_num = argc - 3; + int i; + struct cyclic_buffer_header_t *buf = NULL; + struct monitoring_slot_t *slot; + + if (gpio_num <= 0 || gpio_num > 16) + return EC_ERROR_PARAM_COUNT; + + for (i = 0; i < gpio_num; i++) { + gpios[i] = find_signal_by_name(argv[3 + i]); + if (gpios[i] == GPIO_COUNT) + return EC_ERROR_PARAM3 + i; /* May overflow */ + slot = monitoring_slots + + GPIO_MASK_TO_NUM(gpio_list[gpios[i]].mask); + if (slot->gpio_signal != gpios[i]) { + ccprintf("Error: Not monitoring %s\n", + gpio_list[gpios[i]].name); + return EC_ERROR_PARAM3 + i; + } + if (buf == NULL) { + buf = slot->buffer; + } else if (buf != slot->buffer) { + ccprintf( + "Error: Not monitoring %s as part of same groups as %s\n", + gpio_list[gpios[i]].name, + gpio_list[gpios[0]].name); + return EC_ERROR_PARAM3 + i; + } + } + if (gpio_num != buf->num_signals) { + ccprintf("Error: Not full set of signals monitored\n"); + return EC_ERROR_INVAL; + } + + for (i = 0; i < gpio_num; i++) { + gpio_disable_interrupt(gpios[i]); + } + + /* + * With no more interrupts modifying the buffer, it can be deallocated. + */ + num_cur_monitoring--; + for (i = 0; i < gpio_num; i++) { + slot = monitoring_slots + + GPIO_MASK_TO_NUM(gpio_list[gpios[i]].mask); + slot->gpio_signal = GPIO_COUNT; + slot->buffer = NULL; + } + + if (buf->overflow) + atomic_sub(&num_cur_error_conditions, 1); + + shared_mem_release((char *)buf); + return EC_SUCCESS; +} + +static int command_gpio_monitoring(int argc, const char **argv) +{ + if (argc < 3) + return EC_ERROR_PARAM_COUNT; + if (!strcasecmp(argv[2], "start")) + return command_gpio_monitoring_start(argc, argv); + if (!strcasecmp(argv[2], "read")) + return command_gpio_monitoring_read(argc, argv); + if (!strcasecmp(argv[2], "stop")) + return command_gpio_monitoring_stop(argc, argv); + return EC_ERROR_PARAM2; +} + +static int command_gpio(int argc, const char **argv) +{ + if (argc < 2) + return EC_ERROR_PARAM_COUNT; + if (!strcasecmp(argv[1], "monitoring")) + return command_gpio_monitoring(argc, argv); + if (!strcasecmp(argv[1], "multiset")) + return command_gpio_multiset(argc, argv); + return EC_ERROR_PARAM1; +} +DECLARE_CONSOLE_COMMAND_FLAGS(gpio, command_gpio, + "multiset PIN [level] [mode] [pullmode]" + "\nmonitoring start PIN" + "\nmonitoring read PIN" + "\nmonitoring stop PIN", + "GPIO manipulation", CMD_FLAG_RESTRICTED); + +static int command_reinit(int argc, const char **argv) +{ + stop_all_gpio_monitoring(); + return EC_SUCCESS; +} +DECLARE_CONSOLE_COMMAND_FLAGS(reinit, command_reinit, "", + "Stop any ongoing operation", + CMD_FLAG_RESTRICTED); + +static void led_tick(void) +{ + /* Indicate ongoing GPIO monitoring by flashing the green LED. */ + if (num_cur_monitoring) + gpio_set_level(GPIO_NUCLEO_LED1, + !gpio_get_level(GPIO_NUCLEO_LED1)); + else { + /* + * If not flashing, leave the green LED on, to indicate that + * HyperDebug firmware is running and ready. + */ + gpio_set_level(GPIO_NUCLEO_LED1, 1); + } + /* Indicate error conditions by flashing red LED. */ + if (atomic_add(&num_cur_error_conditions, 0)) + gpio_set_level(GPIO_NUCLEO_LED3, + !gpio_get_level(GPIO_NUCLEO_LED3)); + else { + /* + * If not flashing, leave the red LED off. + */ + gpio_set_level(GPIO_NUCLEO_LED3, 0); + } +} +DECLARE_HOOK(HOOK_TICK, led_tick, HOOK_PRIO_DEFAULT); |