diff options
-rw-r--r-- | MAINTAINERS | 7 | ||||
-rw-r--r-- | board/Marvell/octeon_nic23/board.c | 197 | ||||
-rw-r--r-- | cmd/Kconfig | 16 | ||||
-rw-r--r-- | cmd/Makefile | 1 | ||||
-rw-r--r-- | cmd/cyclic.c | 84 | ||||
-rw-r--r-- | common/Kconfig | 20 | ||||
-rw-r--r-- | common/Makefile | 1 | ||||
-rw-r--r-- | common/board_f.c | 2 | ||||
-rw-r--r-- | common/board_r.c | 2 | ||||
-rw-r--r-- | common/cyclic.c | 123 | ||||
-rw-r--r-- | configs/octeon_nic23_defconfig | 3 | ||||
-rw-r--r-- | doc/develop/cyclic.rst | 50 | ||||
-rw-r--r-- | doc/develop/index.rst | 1 | ||||
-rw-r--r-- | doc/usage/cmd/cyclic.rst | 45 | ||||
-rw-r--r-- | doc/usage/index.rst | 1 | ||||
-rw-r--r-- | fs/cramfs/uncompress.c | 2 | ||||
-rw-r--r-- | include/asm-generic/global_data.h | 7 | ||||
-rw-r--r-- | include/cyclic.h | 138 | ||||
-rw-r--r-- | include/time.h | 19 | ||||
-rw-r--r-- | include/watchdog.h | 23 | ||||
-rw-r--r-- | test/common/Makefile | 1 | ||||
-rw-r--r-- | test/common/cyclic.c | 35 | ||||
-rw-r--r-- | test/test-main.c | 3 |
23 files changed, 777 insertions, 4 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index 1ebcd368a6..25a11a06c6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -795,6 +795,13 @@ T: git https://source.denx.de/u-boot/custodians/u-boot-coldfire.git F: arch/m68k/ F: doc/arch/m68k.rst +CYCLIC +M: Stefan Roese <sr@denx.de> +S: Maintained +F: cmd/cyclic.c +F: common/cyclic.c +F: include/cyclic.h + DFU M: Lukasz Majewski <lukma@denx.de> S: Maintained diff --git a/board/Marvell/octeon_nic23/board.c b/board/Marvell/octeon_nic23/board.c index 3e2c544443..08b1aa4b6e 100644 --- a/board/Marvell/octeon_nic23/board.c +++ b/board/Marvell/octeon_nic23/board.c @@ -3,8 +3,10 @@ * Copyright (C) 2021-2022 Stefan Roese <sr@denx.de> */ +#include <cyclic.h> #include <dm.h> #include <ram.h> +#include <time.h> #include <asm/gpio.h> #include <mach/octeon_ddr.h> @@ -15,11 +17,90 @@ #include <mach/cvmx-helper-cfg.h> #include <mach/cvmx-helper-util.h> #include <mach/cvmx-bgxx-defs.h> +#include <mach/cvmx-dtx-defs.h> #include "board_ddr.h" +/** + * cvmx_spem#_cfg_rd + * + * This register allows read access to the configuration in the PCIe core. + * + */ +union cvmx_spemx_cfg_rd { + u64 u64; + struct cvmx_spemx_cfg_rd_s { + u64 data : 32; + u64 addr : 32; + } s; + struct cvmx_spemx_cfg_rd_s cn73xx; +}; + +/** + * cvmx_spem#_cfg_wr + * + * This register allows write access to the configuration in the PCIe core. + * + */ +union cvmx_spemx_cfg_wr { + u64 u64; + struct cvmx_spemx_cfg_wr_s { + u64 data : 32; + u64 addr : 32; + } s; + struct cvmx_spemx_cfg_wr_s cn73xx; +}; + +/** + * cvmx_spem#_flr_pf_stopreq + * + * PF function level reset stop outbound requests register. + * Hardware automatically sets the STOPREQ bit for the PF when it enters a + * function level reset (FLR). Software is responsible for clearing the STOPREQ + * bit but must not do so prior to hardware taking down the FLR, which could be + * as long as 100ms. It may be appropriate for software to wait longer before clearing + * STOPREQ, software may need to drain deep DPI queues for example. + * Whenever SPEM receives a PF or child VF request mastered by CNXXXX over S2M (i.e. P or NP), + * when STOPREQ is set for the function, SPEM will discard the outgoing request + * before sending it to the PCIe core. If a NP, SPEM will schedule an immediate + * SWI_RSP_ERROR completion for the request - no timeout is required. + * In both cases, SPEM()_DBG_PF()_INFO[P()_BMD_E] will be set and a error + * interrupt is generated. + * + * STOPREQ mimics the behavior of PCIEEP()_CFG001[ME] for outbound requests that will + * master the PCIe bus (P and NP). + * + * STOPREQ will have no effect on completions returned by CNXXXX over the S2M, + * nor on M2S traffic. + * + * When a PF()_STOPREQ is set, none of the associated + * PEM()_FLR_PF()_VF_STOPREQ[VF_STOPREQ] will be set. + * + * STOPREQ is reset when the MAC is reset, and is not reset after a chip soft reset. + */ +union cvmx_spemx_flr_pf_stopreq { + u64 u64; + struct cvmx_spemx_flr_pf_stopreq_s { + u64 reserved_3_63 : 61; + u64 pf2_stopreq : 1; + u64 pf1_stopreq : 1; + u64 pf0_stopreq : 1; + } s; + struct cvmx_spemx_flr_pf_stopreq_s cn73xx; +}; + +#define CVMX_SPEMX_CFG_WR(offset) 0x00011800C0000028ull +#define CVMX_SPEMX_CFG_RD(offset) 0x00011800C0000030ull +#define CVMX_SPEMX_FLR_PF_STOPREQ(offset) 0x00011800C0000218ull + +#define DTX_SELECT_LTSSM 0x0 +#define DTX_SELECT_LTSSM_ENA 0x3ff +#define LTSSM_L0 0x11 + #define NIC23_DEF_DRAM_FREQ 800 +static u32 pci_cfgspace_reg0[2] = { 0, 0 }; + static u8 octeon_nic23_cfg0_spd_values[512] = { OCTEON_NIC23_CFG0_SPD_VALUES }; @@ -145,8 +226,118 @@ void board_configure_qlms(void) cvmx_qlm_measure_clock(4), cvmx_qlm_measure_clock(5)); } +/** + * If there is a PF FLR then the PCI EEPROM is not re-read. In this case + * we need to re-program the vendor and device ID immediately after hardware + * completes FLR. + * + * PCI spec requires FLR to be completed within 100ms. The user who triggered + * FLR expects hardware to finish FLR within 100ms, otherwise the user will + * end up reading DEVICE_ID incorrectly from the reset value. + * CN23XX exits FLR at any point between 66 and 99ms, so U-Boot has to wait + * 99ms to let hardware finish its part, then finish reprogramming the + * correct device ID before the end of 100ms. + * + * Note: this solution only works properly when there is no other activity + * within U-Boot for 100ms from the time FLR is triggered. + * + * This function gets called every 100usec. If FLR happens during any + * other activity like bootloader/image update then it is possible that + * this function does not get called for more than the FLR period which will + * cause the PF device ID restore to happen after whoever initiated the FLR to + * read the incorrect device ID 0x9700 (reset value) instead of 0x9702 + * (restored value). + */ +static void octeon_board_restore_pf(void *ctx) +{ + union cvmx_spemx_flr_pf_stopreq stopreq; + static bool start_initialized[2] = {false, false}; + bool pf0_flag, pf1_flag; + u64 ltssm_bits; + const u64 pf_flr_wait_usecs = 99700; + u64 elapsed_usecs; + union cvmx_spemx_cfg_wr cfg_wr; + union cvmx_spemx_cfg_rd cfg_rd; + static u64 start_us[2]; + int pf_num; + + csr_wr(CVMX_DTX_SPEM_SELX(0), DTX_SELECT_LTSSM); + csr_rd(CVMX_DTX_SPEM_SELX(0)); + csr_wr(CVMX_DTX_SPEM_ENAX(0), DTX_SELECT_LTSSM_ENA); + csr_rd(CVMX_DTX_SPEM_ENAX(0)); + ltssm_bits = csr_rd(CVMX_DTX_SPEM_DATX(0)); + if (((ltssm_bits >> 3) & 0x3f) != LTSSM_L0) + return; + + stopreq.u64 = csr_rd(CVMX_SPEMX_FLR_PF_STOPREQ(0)); + pf0_flag = stopreq.s.pf0_stopreq; + pf1_flag = stopreq.s.pf1_stopreq; + /* See if PF interrupt happened */ + if (!(pf0_flag || pf1_flag)) + return; + + if (pf0_flag && !start_initialized[0]) { + start_initialized[0] = true; + start_us[0] = get_timer_us(0); + } + + /* Store programmed PCIe DevID SPEM0 PF0 */ + if (pf0_flag && !pci_cfgspace_reg0[0]) { + cfg_rd.s.addr = (0 << 24) | 0x0; + csr_wr(CVMX_SPEMX_CFG_RD(0), cfg_rd.u64); + cfg_rd.u64 = csr_rd(CVMX_SPEMX_CFG_RD(0)); + pci_cfgspace_reg0[0] = cfg_rd.s.data; + } + + if (pf1_flag && !start_initialized[1]) { + start_initialized[1] = true; + start_us[1] = get_timer_us(0); + } + + /* Store programmed PCIe DevID SPEM0 PF1 */ + if (pf1_flag && !pci_cfgspace_reg0[1]) { + cfg_rd.s.addr = (1 << 24) | 0x0; + csr_wr(CVMX_SPEMX_CFG_RD(0), cfg_rd.u64); + cfg_rd.u64 = csr_rd(CVMX_SPEMX_CFG_RD(0)); + pci_cfgspace_reg0[1] = cfg_rd.s.data; + } + + /* For PF, rewrite pci config space reg 0 */ + for (pf_num = 0; pf_num < 2; pf_num++) { + if (!start_initialized[pf_num]) + continue; + + elapsed_usecs = get_timer_us(0) - start_us[pf_num]; + + if (elapsed_usecs > pf_flr_wait_usecs) { + /* Here, our measured FLR duration has passed; + * check if device ID has been reset, + * which indicates FLR completion (per MA team). + */ + cfg_rd.s.addr = (pf_num << 24) | 0x0; + csr_wr(CVMX_SPEMX_CFG_RD(0), cfg_rd.u64); + cfg_rd.u64 = csr_rd(CVMX_SPEMX_CFG_RD(0)); + /* if DevID has NOT been reset, FLR is not yet + * complete + */ + if (cfg_rd.s.data != pci_cfgspace_reg0[pf_num]) { + stopreq.s.pf0_stopreq = (pf_num == 0) ? 1 : 0; + stopreq.s.pf1_stopreq = (pf_num == 1) ? 1 : 0; + csr_wr(CVMX_SPEMX_FLR_PF_STOPREQ(0), stopreq.u64); + + cfg_wr.u64 = 0; + cfg_wr.s.addr = (pf_num << 24) | 0; + cfg_wr.s.data = pci_cfgspace_reg0[pf_num]; + csr_wr(CVMX_SPEMX_CFG_WR(0), cfg_wr.u64); + start_initialized[pf_num] = false; + } + } + } +} + int board_late_init(void) { + struct cyclic_info *cyclic; struct gpio_desc gpio = {}; ofnode node; @@ -164,6 +355,12 @@ int board_late_init(void) board_configure_qlms(); + /* Register cyclic function for PCIe FLR fixup */ + cyclic = cyclic_register(octeon_board_restore_pf, 100, + "pcie_flr_fix", NULL); + if (!cyclic) + printf("Registering of cyclic function failed\n"); + return 0; } diff --git a/cmd/Kconfig b/cmd/Kconfig index 67c7d2512f..7d574d4ab7 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -2511,6 +2511,22 @@ config CMD_CBSYSINFO memory by coreboot before jumping to U-Boot. It can be useful for debugging the beaaviour of coreboot or U-Boot. +config CMD_CYCLIC + bool "cyclic - Show information about cyclic functions" + depends on CYCLIC + default y + help + This enables the 'cyclic' command which provides information about + cyclic execution functions. This infrastructure allows registering + functions to be executed cyclically, e.g. every 100ms. These commands + are supported: + + cyclic list - list cyclic functions + cyclic cyclic demo <cycletime_ms> <delay_us> - register cyclic + demo function + + See doc/develop/cyclic.rst for more details. + config CMD_DIAG bool "diag - Board diagnostics" help diff --git a/cmd/Makefile b/cmd/Makefile index 7abe1d3630..b2df59baef 100644 --- a/cmd/Makefile +++ b/cmd/Makefile @@ -56,6 +56,7 @@ obj-$(CONFIG_CMD_DIAG) += diag.o endif obj-$(CONFIG_CMD_ADTIMG) += adtimg.o obj-$(CONFIG_CMD_ABOOTIMG) += abootimg.o +obj-$(CONFIG_CMD_CYCLIC) += cyclic.o obj-$(CONFIG_CMD_EVENT) += event.o obj-$(CONFIG_CMD_EXTENSION) += extension_board.o obj-$(CONFIG_CMD_ECHO) += echo.o diff --git a/cmd/cyclic.c b/cmd/cyclic.c new file mode 100644 index 0000000000..c1bc556aad --- /dev/null +++ b/cmd/cyclic.c @@ -0,0 +1,84 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * A general-purpose cyclic execution infrastructure, to allow "small" + * (run-time wise) functions to be executed at a specified frequency. + * Things like LED blinking or watchdog triggering are examples for such + * tasks. + * + * Copyright (C) 2022 Stefan Roese <sr@denx.de> + */ + +#include <common.h> +#include <command.h> +#include <cyclic.h> +#include <div64.h> +#include <malloc.h> +#include <linux/delay.h> + +struct cyclic_demo_info { + uint delay_us; +}; + +static void cyclic_demo(void *ctx) +{ + struct cyclic_demo_info *info = ctx; + + /* Just a small dummy delay here */ + udelay(info->delay_us); +} + +static int do_cyclic_demo(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct cyclic_demo_info *info; + struct cyclic_info *cyclic; + uint time_ms; + + if (argc < 3) + return CMD_RET_USAGE; + + info = malloc(sizeof(struct cyclic_demo_info)); + if (!info) { + printf("out of memory\n"); + return CMD_RET_FAILURE; + } + + time_ms = simple_strtoul(argv[1], NULL, 0); + info->delay_us = simple_strtoul(argv[2], NULL, 0); + + /* Register demo cyclic function */ + cyclic = cyclic_register(cyclic_demo, time_ms * 1000, "cyclic_demo", + info); + if (!cyclic) + printf("Registering of cyclic_demo failed\n"); + + printf("Registered function \"%s\" to be executed all %dms\n", + "cyclic_demo", time_ms); + + return 0; +} + +static int do_cyclic_list(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct cyclic_info *cyclic, *tmp; + u64 cnt, freq; + + list_for_each_entry_safe(cyclic, tmp, cyclic_get_list(), list) { + cnt = cyclic->run_cnt * 1000000ULL * 100ULL; + freq = lldiv(cnt, timer_get_us() - cyclic->start_time_us); + printf("function: %s, cpu-time: %lld us, frequency: %lld.%02d times/s\n", + cyclic->name, cyclic->cpu_time_us, + lldiv(freq, 100), do_div(freq, 100)); + } + + return 0; +} + +static char cyclic_help_text[] = + "cyclic demo <cycletime_ms> <delay_us> - register cyclic demo function\n" + "cyclic list - list cyclic functions\n"; + +U_BOOT_CMD_WITH_SUBCMDS(cyclic, "Cyclic", cyclic_help_text, + U_BOOT_SUBCMD_MKENT(demo, 3, 1, do_cyclic_demo), + U_BOOT_SUBCMD_MKENT(list, 1, 1, do_cyclic_list)); diff --git a/common/Kconfig b/common/Kconfig index 2c3f7f4274..3e44acd2be 100644 --- a/common/Kconfig +++ b/common/Kconfig @@ -545,6 +545,26 @@ config DISPLAY_BOARDINFO_LATE menu "Start-up hooks" +config CYCLIC + bool "General-purpose cyclic execution mechanism" + help + This enables a general-purpose cyclic execution infrastructure, + to allow "small" (run-time wise) functions to be executed at + a specified frequency. Things like LED blinking or watchdog + triggering are examples for such tasks. + +if CYCLIC + +config CYCLIC_MAX_CPU_TIME_US + int "Sets the max allowed time for a cyclic function in us" + default 1000 + help + The max allowed time for a cyclic function in us. If a functions + takes longer than this duration this function will get unregistered + automatically. + +endif # CYCLIC + config EVENT bool "General-purpose event-handling mechanism" default y if SANDBOX diff --git a/common/Makefile b/common/Makefile index 2ed8672c3a..1d56c9f289 100644 --- a/common/Makefile +++ b/common/Makefile @@ -84,6 +84,7 @@ obj-y += malloc_simple.o endif endif +obj-$(CONFIG_CYCLIC) += cyclic.o obj-$(CONFIG_$(SPL_TPL_)EVENT) += event.o obj-$(CONFIG_$(SPL_TPL_)HASH) += hash.o diff --git a/common/board_f.c b/common/board_f.c index 18e2246733..deb46be182 100644 --- a/common/board_f.c +++ b/common/board_f.c @@ -16,6 +16,7 @@ #include <console.h> #include <cpu.h> #include <cpu_func.h> +#include <cyclic.h> #include <display_options.h> #include <dm.h> #include <env.h> @@ -828,6 +829,7 @@ static const init_fnc_t init_sequence_f[] = { initf_malloc, log_init, initf_bootstage, /* uses its own timer, so does not need DM */ + cyclic_init, event_init, #ifdef CONFIG_BLOBLIST bloblist_init, diff --git a/common/board_r.c b/common/board_r.c index 56eb60fa27..062bc3e688 100644 --- a/common/board_r.c +++ b/common/board_r.c @@ -13,6 +13,7 @@ #include <api.h> #include <bootstage.h> #include <cpu_func.h> +#include <cyclic.h> #include <display_options.h> #include <exports.h> #ifdef CONFIG_MTD_NOR_FLASH @@ -611,6 +612,7 @@ static init_fnc_t init_sequence_r[] = { #endif initr_barrier, initr_malloc, + cyclic_init, log_init, initr_bootstage, /* Needs malloc() but has its own timer */ #if defined(CONFIG_CONSOLE_RECORD) diff --git a/common/cyclic.c b/common/cyclic.c new file mode 100644 index 0000000000..cd5dcb1f2b --- /dev/null +++ b/common/cyclic.c @@ -0,0 +1,123 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * A general-purpose cyclic execution infrastructure, to allow "small" + * (run-time wise) functions to be executed at a specified frequency. + * Things like LED blinking or watchdog triggering are examples for such + * tasks. + * + * Copyright (C) 2022 Stefan Roese <sr@denx.de> + */ + +#include <cyclic.h> +#include <log.h> +#include <malloc.h> +#include <time.h> +#include <linux/errno.h> +#include <linux/list.h> +#include <asm/global_data.h> + +DECLARE_GLOBAL_DATA_PTR; + +struct list_head *cyclic_get_list(void) +{ + return &gd->cyclic->cyclic_list; +} + +struct cyclic_info *cyclic_register(cyclic_func_t func, uint64_t delay_us, + const char *name, void *ctx) +{ + struct cyclic_info *cyclic; + + if (!gd->cyclic->cyclic_ready) { + pr_debug("Cyclic IF not ready yet\n"); + return NULL; + } + + cyclic = calloc(1, sizeof(struct cyclic_info)); + if (!cyclic) { + pr_debug("Memory allocation error\n"); + return NULL; + } + + /* Store values in struct */ + cyclic->func = func; + cyclic->ctx = ctx; + cyclic->name = strdup(name); + cyclic->delay_us = delay_us; + cyclic->start_time_us = timer_get_us(); + list_add_tail(&cyclic->list, &gd->cyclic->cyclic_list); + + return cyclic; +} + +int cyclic_unregister(struct cyclic_info *cyclic) +{ + list_del(&cyclic->list); + free(cyclic); + + return 0; +} + +void cyclic_run(void) +{ + struct cyclic_info *cyclic, *tmp; + uint64_t now, cpu_time; + + /* Prevent recursion */ + if (gd->cyclic->cyclic_running) + return; + + gd->cyclic->cyclic_running = true; + list_for_each_entry_safe(cyclic, tmp, &gd->cyclic->cyclic_list, list) { + /* + * Check if this cyclic function needs to get called, e.g. + * do not call the cyclic func too often + */ + now = timer_get_us(); + if (time_after_eq64(now, cyclic->next_call)) { + /* Call cyclic function and account it's cpu-time */ + cyclic->next_call = now + cyclic->delay_us; + cyclic->func(cyclic->ctx); + cyclic->run_cnt++; + cpu_time = timer_get_us() - now; + cyclic->cpu_time_us += cpu_time; + + /* Check if cpu-time exceeds max allowed time */ + if (cpu_time > CONFIG_CYCLIC_MAX_CPU_TIME_US) { + pr_err("cyclic function %s took too long: %lldus vs %dus max, disabling\n", + cyclic->name, cpu_time, + CONFIG_CYCLIC_MAX_CPU_TIME_US); + + /* Unregister this cyclic function */ + cyclic_unregister(cyclic); + } + } + } + gd->cyclic->cyclic_running = false; +} + +int cyclic_uninit(void) +{ + struct cyclic_info *cyclic, *tmp; + + list_for_each_entry_safe(cyclic, tmp, &gd->cyclic->cyclic_list, list) + cyclic_unregister(cyclic); + gd->cyclic->cyclic_ready = false; + + return 0; +} + +int cyclic_init(void) +{ + int size = sizeof(struct cyclic_drv); + + gd->cyclic = (struct cyclic_drv *)malloc(size); + if (!gd->cyclic) + return -ENOMEM; + + memset(gd->cyclic, '\0', size); + INIT_LIST_HEAD(&gd->cyclic->cyclic_list); + gd->cyclic->cyclic_ready = true; + + return 0; +} diff --git a/configs/octeon_nic23_defconfig b/configs/octeon_nic23_defconfig index 95e98c1161..098831e0b1 100644 --- a/configs/octeon_nic23_defconfig +++ b/configs/octeon_nic23_defconfig @@ -20,6 +20,8 @@ CONFIG_AHCI=y CONFIG_OF_BOARD_FIXUP=y CONFIG_SYS_CONSOLE_ENV_OVERWRITE=y # CONFIG_SYS_DEVICE_NULLDEV is not set +CONFIG_CYCLIC=y +CONFIG_CYCLIC_MAX_CPU_TIME_US=5000 CONFIG_ARCH_MISC_INIT=y CONFIG_BOARD_EARLY_INIT_F=y CONFIG_BOARD_LATE_INIT=y @@ -41,6 +43,7 @@ CONFIG_CMD_TIME=y CONFIG_CMD_EXT4=y CONFIG_CMD_FAT=y CONFIG_CMD_FS_GENERIC=y +CONFIG_CMD_CYCLIC=y CONFIG_EFI_PARTITION=y CONFIG_ENV_IS_IN_SPI_FLASH=y CONFIG_TFTP_TSIZE=y diff --git a/doc/develop/cyclic.rst b/doc/develop/cyclic.rst new file mode 100644 index 0000000000..43bedacb9f --- /dev/null +++ b/doc/develop/cyclic.rst @@ -0,0 +1,50 @@ +.. SPDX-License-Identifier: GPL-2.0+ + +Cyclic functions +================ + +The cyclic function execution infrastruture provides a way to periodically +execute code, e.g. every 100ms. Examples for such functions might be LED +blinking etc. The functions that are hooked into this cyclic list should +be small timewise as otherwise the execution of the other code that relies +on a high frequent polling (e.g. UART rx char ready check) might be +delayed too much. To detect cyclic functions with a too long execution +time, the Kconfig option `CONFIG_CYCLIC_MAX_CPU_TIME_US` is introduced, +which configures the max allowed time for such a cyclic function. If it's +execution time exceeds this time, this cyclic function will get removed +from the cyclic list. + +Registering a cyclic function +----------------------------- + +To register a cyclic function, use something like this:: + + static void cyclic_demo(void *ctx) + { + /* Just a small dummy delay here */ + udelay(10); + } + + int board_init(void) + { + struct cyclic_info *cyclic; + + /* Register demo cyclic function */ + cyclic = cyclic_register(cyclic_demo, 10 * 1000, "cyclic_demo", NULL); + if (!cyclic) + printf("Registering of cyclic_demo failed\n"); + + return 0; + } + +This will register the function `cyclic_demo()` to be periodically +executed all 10ms. + +How is this cyclic functionality integrated / executed? +-------------------------------------------------------- + +The cyclic infrastructure integrates the main function responsible for +calling all registered cyclic functions cyclic_run() into the common +WATCHDOG_RESET macro. This guarantees that cyclic_run() is executed +very often, which is necessary for the cyclic functions to get scheduled +and executed at their configured periods. diff --git a/doc/develop/index.rst b/doc/develop/index.rst index f7ee09db24..ce6b38e576 100644 --- a/doc/develop/index.rst +++ b/doc/develop/index.rst @@ -27,6 +27,7 @@ Implementation ci_testing commands config_binding + cyclic devicetree/index distro driver-model/index diff --git a/doc/usage/cmd/cyclic.rst b/doc/usage/cmd/cyclic.rst new file mode 100644 index 0000000000..3085cc7204 --- /dev/null +++ b/doc/usage/cmd/cyclic.rst @@ -0,0 +1,45 @@ +.. SPDX-License-Identifier: GPL-2.0+ + +cyclic command +============== + +Synopsis +-------- + +:: + + cyclic list + +Description +----------- + +The cyclic list command provides a list of the currently registered +cyclic functions. + +This shows the following information: + +Function + Function name + +cpu-time + Total time spent in this cyclic function. + +Frequency + Frequency of execution of this function, e.g. 100 times/s for a + pediod of 10ms. + + +See :doc:`../../develop/cyclic` for more information on cyclic functions. + +Example +------- + +:: + + => cyclic list + function: cyclic_demo, cpu-time: 52906 us, frequency: 99.20 times/s + +Configuration +------------- + +The cyclic command is only available if CONFIG_CMD_CYCLIC=y. diff --git a/doc/usage/index.rst b/doc/usage/index.rst index 5170fa8b4a..10313f83dd 100644 --- a/doc/usage/index.rst +++ b/doc/usage/index.rst @@ -33,6 +33,7 @@ Shell commands cmd/bootz cmd/cbsysinfo cmd/conitrace + cmd/cyclic cmd/dm cmd/echo cmd/env diff --git a/fs/cramfs/uncompress.c b/fs/cramfs/uncompress.c index f431cc46c1..38e10e2e44 100644 --- a/fs/cramfs/uncompress.c +++ b/fs/cramfs/uncompress.c @@ -62,7 +62,7 @@ int cramfs_uncompress_init (void) stream.avail_in = 0; #if defined(CONFIG_HW_WATCHDOG) || defined(CONFIG_WATCHDOG) - stream.outcb = (cb_func) WATCHDOG_RESET; + stream.outcb = (cb_func)watchdog_reset_func; #else stream.outcb = Z_NULL; #endif /* CONFIG_HW_WATCHDOG */ diff --git a/include/asm-generic/global_data.h b/include/asm-generic/global_data.h index 805a6fd679..9006c76927 100644 --- a/include/asm-generic/global_data.h +++ b/include/asm-generic/global_data.h @@ -20,6 +20,7 @@ */ #ifndef __ASSEMBLY__ +#include <cyclic.h> #include <event_internal.h> #include <fdtdec.h> #include <membuff.h> @@ -474,6 +475,12 @@ struct global_data { */ struct event_state event_state; #endif +#ifdef CONFIG_CYCLIC + /** + * @cyclic: cyclic driver data + */ + struct cyclic_drv *cyclic; +#endif /** * @dmtag_list: List of DM tags */ diff --git a/include/cyclic.h b/include/cyclic.h new file mode 100644 index 0000000000..23902234cc --- /dev/null +++ b/include/cyclic.h @@ -0,0 +1,138 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * A general-purpose cyclic execution infrastructure, to allow "small" + * (run-time wise) functions to be executed at a specified frequency. + * Things like LED blinking or watchdog triggering are examples for such + * tasks. + * + * Copyright (C) 2022 Stefan Roese <sr@denx.de> + */ + +#ifndef __cyclic_h +#define __cyclic_h + +#include <linux/list.h> +#include <asm/types.h> + +/** + * struct cyclic_drv - Cyclic driver internal data + * + * @cyclic_list: Cylic list node + * @cyclic_ready: Flag if cyclic infrastructure is ready + * @cyclic_running: Flag if cyclic infrastructure is running + */ +struct cyclic_drv { + struct list_head cyclic_list; + bool cyclic_ready; + bool cyclic_running; +}; + +/** + * struct cyclic_info - Information about cyclic execution function + * + * @func: Function to call periodically + * @ctx: Context pointer to get passed to this function + * @name: Name of the cyclic function, e.g. shown in the commands + * @delay_ns: Delay is ns after which this function shall get executed + * @start_time_us: Start time in us, when this function started its execution + * @cpu_time_us: Total CPU time of this function + * @run_cnt: Counter of executions occurances + * @next_call: Next time in us, when the function shall be executed again + * @list: List node + */ +struct cyclic_info { + void (*func)(void *ctx); + void *ctx; + char *name; + uint64_t delay_us; + uint64_t start_time_us; + uint64_t cpu_time_us; + uint64_t run_cnt; + uint64_t next_call; + struct list_head list; +}; + +/** Function type for cyclic functions */ +typedef void (*cyclic_func_t)(void *ctx); + +#if defined(CONFIG_CYCLIC) +/** + * cyclic_register - Register a new cyclic function + * + * @func: Function to call periodically + * @delay_us: Delay is us after which this function shall get executed + * @name: Cyclic function name/id + * @ctx: Context to pass to the function + * @return: pointer to cyclic_struct if OK, NULL on error + */ +struct cyclic_info *cyclic_register(cyclic_func_t func, uint64_t delay_us, + const char *name, void *ctx); + +/** + * cyclic_unregister - Unregister a cyclic function + * + * @cyclic: Pointer to cyclic_struct of the function that shall be removed + * @return: 0 if OK, -ve on error + */ +int cyclic_unregister(struct cyclic_info *cyclic); + +/** + * cyclic_init() - Set up cyclic functions + * + * Init a list of cyclic functions, so that these can be added as needed + */ +int cyclic_init(void); + +/** + * cyclic_uninit() - Clean up cyclic functions + * + * This removes all cyclic functions + */ +int cyclic_uninit(void); + +/** + * cyclic_get_list() - Get cyclic list pointer + * + * Return the cyclic list pointer + * + * @return: pointer to cyclic_list + */ +struct list_head *cyclic_get_list(void); + +/** + * cyclic_run() - Interate over all registered cyclic functions + * + * Interate over all registered cyclic functions and if the it's function + * needs to be executed, then call into these registered functions. + */ +void cyclic_run(void); +#else +static inline struct cyclic_info *cyclic_register(cyclic_func_t func, + uint64_t delay_us, + const char *name, + void *ctx) +{ + return NULL; +} + +static inline int cyclic_unregister(struct cyclic_info *cyclic) +{ + return 0; +} + +static inline void cyclic_run(void) +{ +} + +static inline int cyclic_init(void) +{ + return 0; +} + +static inline int cyclic_uninit(void) +{ + return 0; +} +#endif + +#endif diff --git a/include/time.h b/include/time.h index 9deb2cf61c..3b2ba09124 100644 --- a/include/time.h +++ b/include/time.h @@ -83,6 +83,25 @@ uint64_t usec_to_tick(unsigned long usec); (time_after_eq(a,b) && \ time_before(a,c)) +/* Same as above, but does so with platform independent 64bit types. + * These must be used when utilizing jiffies_64 (i.e. return value of + * get_jiffies_64() */ +#define time_after64(a,b) \ + (typecheck(__u64, a) && \ + typecheck(__u64, b) && \ + ((__s64)((b) - (a)) < 0)) +#define time_before64(a,b) time_after64(b,a) + +#define time_after_eq64(a,b) \ + (typecheck(__u64, a) && \ + typecheck(__u64, b) && \ + ((__s64)((a) - (b)) >= 0)) +#define time_before_eq64(a,b) time_after_eq64(b,a) + +#define time_in_range64(a, b, c) \ + (time_after_eq64(a, b) && \ + time_before_eq64(a, c)) + /** * usec2ticks() - Convert microseconds to internal ticks * diff --git a/include/watchdog.h b/include/watchdog.h index 813cc8f2a5..0a9777edcb 100644 --- a/include/watchdog.h +++ b/include/watchdog.h @@ -11,6 +11,8 @@ #define _WATCHDOG_H_ #if !defined(__ASSEMBLY__) +#include <cyclic.h> + /* * Reset the watchdog timer, always returns 0 * @@ -60,11 +62,16 @@ int init_func_watchdog_reset(void); /* Don't require the watchdog to be enabled in SPL */ #if defined(CONFIG_SPL_BUILD) && \ !defined(CONFIG_SPL_WATCHDOG) - #define WATCHDOG_RESET() {} + #define WATCHDOG_RESET() { \ + cyclic_run(); \ + } #else extern void watchdog_reset(void); - #define WATCHDOG_RESET watchdog_reset + #define WATCHDOG_RESET() { \ + watchdog_reset(); \ + cyclic_run(); \ + } #endif #endif #else @@ -74,11 +81,21 @@ int init_func_watchdog_reset(void); #if defined(__ASSEMBLY__) #define WATCHDOG_RESET /*XXX DO_NOT_DEL_THIS_COMMENT*/ #else - #define WATCHDOG_RESET() {} + #define WATCHDOG_RESET() { \ + cyclic_run(); \ + } #endif /* __ASSEMBLY__ */ #endif /* CONFIG_WATCHDOG && !__ASSEMBLY__ */ #endif /* CONFIG_HW_WATCHDOG */ +#if !defined(__ASSEMBLY__) +/* Currently only needed for fs/cramfs/uncompress.c */ +static inline void watchdog_reset_func(void) +{ + WATCHDOG_RESET(); +} +#endif + /* * Prototypes from $(CPU)/cpu.c. */ diff --git a/test/common/Makefile b/test/common/Makefile index 9087788ba6..cc918f64e5 100644 --- a/test/common/Makefile +++ b/test/common/Makefile @@ -1,4 +1,5 @@ # SPDX-License-Identifier: GPL-2.0+ obj-y += cmd_ut_common.o obj-$(CONFIG_AUTOBOOT) += test_autoboot.o +obj-$(CONFIG_CYCLIC) += cyclic.o obj-$(CONFIG_EVENT) += event.o diff --git a/test/common/cyclic.c b/test/common/cyclic.c new file mode 100644 index 0000000000..a5c4e78989 --- /dev/null +++ b/test/common/cyclic.c @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2022 Stefan Roese <sr@denx.de> + */ + +#include <common.h> +#include <cyclic.h> +#include <dm.h> +#include <test/common.h> +#include <test/test.h> +#include <test/ut.h> +#include <watchdog.h> +#include <linux/delay.h> + +/* Test that cyclic function is called */ +static bool cyclic_active = false; + +static void cyclic_test(void *ctx) +{ + cyclic_active = true; +} + +static int dm_test_cyclic_running(struct unit_test_state *uts) +{ + cyclic_active = false; + ut_assertnonnull(cyclic_register(cyclic_test, 10 * 1000, "cyclic_demo", + NULL)); + + /* Execute all registered cyclic functions */ + WATCHDOG_RESET(); + ut_asserteq(true, cyclic_active); + + return 0; +} +COMMON_TEST(dm_test_cyclic_running, 0); diff --git a/test/test-main.c b/test/test-main.c index 4f1c54b0f9..ae34002a3d 100644 --- a/test/test-main.c +++ b/test/test-main.c @@ -6,6 +6,7 @@ #include <common.h> #include <console.h> +#include <cyclic.h> #include <dm.h> #include <event.h> #include <dm/root.h> @@ -220,6 +221,7 @@ static int dm_test_restore(struct device_node *of_root) static int test_pre_run(struct unit_test_state *uts, struct unit_test *test) { ut_assertok(event_init()); + ut_assertok(cyclic_init()); if (test->flags & UT_TESTF_DM) ut_assertok(dm_test_pre_run(uts)); @@ -265,6 +267,7 @@ static int test_post_run(struct unit_test_state *uts, struct unit_test *test) ut_unsilence_console(uts); if (test->flags & UT_TESTF_DM) ut_assertok(dm_test_post_run(uts)); + ut_assertok(cyclic_uninit()); ut_assertok(event_uninit()); return 0; |