diff --git a/ext/hal/qmsi/Kbuild b/ext/hal/qmsi/Kbuild index 251ef65eb83..92c9d7f2725 100644 --- a/ext/hal/qmsi/Kbuild +++ b/ext/hal/qmsi/Kbuild @@ -25,3 +25,4 @@ obj-$(CONFIG_SPI_QMSI_SS) += drivers/spi/qm_ss_spi.o obj-$(CONFIG_GPIO_QMSI_SS) += drivers/gpio/qm_ss_gpio.o obj-$(CONFIG_I2C_QMSI_SS) += drivers/i2c/qm_ss_i2c.o obj-$(CONFIG_ADC_QMSI_SS) += drivers/adc/qm_ss_adc.o +obj-$(CONFIG_SOC_WATCH) += drivers/soc_watch.o \ No newline at end of file diff --git a/ext/hal/qmsi/Kconfig b/ext/hal/qmsi/Kconfig index 1974283ae60..1d43dcd2476 100644 --- a/ext/hal/qmsi/Kconfig +++ b/ext/hal/qmsi/Kconfig @@ -54,4 +54,11 @@ config QMSI_INSTALL_PATH installed. Make sure this option is properly set when QMSI_LIBRARY is enabled otherwise the build will fail. +config SOC_WATCH + bool "Enable SoCWatch drivers and related instrumentation" + default n + depends on KERNEL_EVENT_LOGGER + help + This option enables the SoCWatch driver and related instrumentation. + endif diff --git a/ext/hal/qmsi/Makefile b/ext/hal/qmsi/Makefile index bded4a47449..0d5d2f93735 100644 --- a/ext/hal/qmsi/Makefile +++ b/ext/hal/qmsi/Makefile @@ -16,3 +16,9 @@ endif ifdef CONFIG_SYS_POWER_DEEP_SLEEP KBUILD_CPPFLAGS +=-DENABLE_RESTORE_CONTEXT endif + +SOC_WATCH_ENABLE ?= 0 +ifeq ($(CONFIG_SOC_WATCH),y) +SOC_WATCH_ENABLE := 1 +CFLAGS += -DSOC_WATCH_ENABLE +endif \ No newline at end of file diff --git a/ext/hal/qmsi/drivers/soc_watch.c b/ext/hal/qmsi/drivers/soc_watch.c index 7f931dbbf46..f48dcffca31 100644 --- a/ext/hal/qmsi/drivers/soc_watch.c +++ b/ext/hal/qmsi/drivers/soc_watch.c @@ -36,7 +36,6 @@ #include #include "qm_common.h" #include "qm_soc_regs.h" -#include "qm_interrupt.h" #include "soc_watch.h" /* @@ -238,6 +237,62 @@ static void eb_write_uint32(uint32_t *data) soc_watch_event_buffer.eb_idx += sizeof(uint32_t); } +/* x86 CPU FLAGS.IF register field (Interrupt enable Flag, bit 9), indicating + * whether or not CPU interrupts are enabled. + */ +#define X86_FLAGS_IF BIT(9) + +/** + * Save interrupt state and disable all interrupts on the CPU. + * Defined locally for modularity when used in other contexts (i.e. RTOS) + * + * @return An architecture-dependent lock-out key representing the "interrupt + * disable state" prior to the call. + */ +static inline unsigned int soc_watch_irq_lock(void) +{ + unsigned int key = 0; + + /* + * Store the CPU FLAGS register into the variable `key' and disable + * interrupt delivery to the core. + */ + __asm__ __volatile__("pushfl;\n\t" + "cli;\n\t" + "popl %0;\n\t" + : "=g"(key) + : + : "memory"); + + return key; +} + +/** + * + * Restore previous interrupt state on the CPU saved via soc_watch_irq_lock(). + * Defined locally for modularity when used in other contexts (i.e. RTOS) + * + * @param[in] key architecture-dependent lock-out key returned by a previous + * invocation of soc_watch_irq_lock(). + */ +static inline void soc_watch_irq_unlock(unsigned int key) +{ + /* + * `key' holds the CPU FLAGS register content at the time when + * soc_watch_irq_lock() was called. + */ + if (!(key & X86_FLAGS_IF)) { + /* + * Interrupts were disabled when soc_watch_irq_lock() was invoked: + * do not re-enable interrupts. + */ + return; + } + + /* Enable interrupts */ + __asm__ __volatile__("sti;\n\t" : :); +} + /* Log an event with one parameter. */ void soc_watch_log_event(soc_watch_event_t event_id, uintptr_t ev_data) { @@ -255,13 +310,14 @@ void soc_watch_log_app_event(soc_watch_event_t event_id, uint8_t ev_subtype, static uint8_t record_rtc = 0; const uint32_t *rtc_ctr = (uint32_t *)&QM_RTC->rtc_ccvr; const char *cp; + unsigned int irq_flag = 0; uint64_t tsc = __builtin_ia32_rdtsc(); /* Grab hi-res timestamp */ uint32_t rtc_val = *rtc_ctr; #define AVG_EVENT_SIZE 8 /* Size of a typical message in bytes. */ MLOG('['); - qm_irq_disable(); + irq_flag = soc_watch_irq_lock(); /* TODO: We know exactly how many bytes of storage we need, * since we know the event code. So don't do an "AVG" size thing * here--use the exact size! @@ -289,7 +345,7 @@ void soc_watch_log_app_event(soc_watch_event_t event_id, uint8_t ev_subtype, if (event_id >= SOCW_EVENT_MAX) { SOC_WATCH_TRACE("Unknown event id: 0x%x\n", event_id); MLOG('?'); - qm_irq_enable(); + soc_watch_irq_unlock(irq_flag); return; } cp = ev_strs[event_id]; /* Look up event string */ @@ -348,7 +404,7 @@ void soc_watch_log_app_event(soc_watch_event_t event_id, uint8_t ev_subtype, } MLOG(':'); MLOG_BYTE(soc_watch_event_buffer.eb_idx); - qm_irq_enable(); + soc_watch_irq_unlock(irq_flag); MLOG(']'); } diff --git a/tests/power/power_states/prj_socwatch.conf b/tests/power/power_states/prj_socwatch.conf new file mode 100644 index 00000000000..3efe6f0ec23 --- /dev/null +++ b/tests/power/power_states/prj_socwatch.conf @@ -0,0 +1,32 @@ +CONFIG_ARC_INIT=n +CONFIG_SYS_POWER_MANAGEMENT=y +CONFIG_SYS_POWER_DEEP_SLEEP=n +CONFIG_SYS_POWER_LOW_POWER_STATE=y +CONFIG_DEVICE_POWER_MANAGEMENT=y +CONFIG_TICKLESS_IDLE=y + +# RTC Wake Event +CONFIG_RTC=y + +# COUNTER Wake Event +CONFIG_COUNTER=n + +# GPIO 1 Wake Event +CONFIG_GPIO=n +CONFIG_GPIO_QMSI=n +CONFIG_GPIO_QMSI_1=n +CONFIG_GPIO_QMSI_1_NAME="GPIO_1" +CONFIG_GPIO_QMSI_1_PRI=2 + +# Comparator Wake Event +CONFIG_AIO_COMPARATOR=n + +# Enable SoCWatch power event tracing +CONFIG_SOC_WATCH=y +CONFIG_RING_BUFFER=y +CONFIG_NANO_TIMEOUTS=y +CONFIG_IDLE_STACK_SIZE=512 +CONFIG_KERNEL_EVENT_LOGGER=y +CONFIG_KERNEL_EVENT_LOGGER_BUFFER_SIZE=16 +CONFIG_KERNEL_EVENT_LOGGER_CONTEXT_SWITCH=y +CONFIG_KERNEL_EVENT_LOGGER_INTERRUPT=y diff --git a/tests/power/power_states/src/Makefile b/tests/power/power_states/src/Makefile index 00066e15678..fd6e46c3c03 100644 --- a/tests/power/power_states/src/Makefile +++ b/tests/power/power_states/src/Makefile @@ -1 +1,2 @@ obj-y = main.o +obj-$(CONFIG_SOC_WATCH) += soc_watch_logger.o \ No newline at end of file diff --git a/tests/power/power_states/src/main.c b/tests/power/power_states/src/main.c index 5e57dfbedb3..101b42a22d1 100644 --- a/tests/power/power_states/src/main.c +++ b/tests/power/power_states/src/main.c @@ -26,6 +26,7 @@ #include #include #include +#include "soc_watch_logger.h" static enum power_states states_list[] = { SYS_POWER_STATE_CPU_LPS, @@ -386,6 +387,11 @@ void main(void) build_suspend_device_list(); +#ifdef CONFIG_SOC_WATCH + /* Start the event monitoring thread */ + soc_watch_logger_thread_start(); +#endif + /* All our application does is putting the task to sleep so the kernel * triggers the suspend operation. */ diff --git a/tests/power/power_states/src/soc_watch_logger.c b/tests/power/power_states/src/soc_watch_logger.c new file mode 100644 index 00000000000..a4d4e638fc6 --- /dev/null +++ b/tests/power/power_states/src/soc_watch_logger.c @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2016 Intel Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "soc_watch_logger.h" + +#define STSIZE 512 +char __stack soc_watch_event_logger_stack[1][STSIZE]; + +/** + * @brief soc_watch data collector thread + * + * @details Collect the kernel event messages and pass them to + * soc_watch + * + * @return No return value. + */ +void soc_watch_data_collector(void) +{ +#ifdef CONFIG_SOC_WATCH + int res; + uint32_t data[4]; + uint8_t dropped_count; + uint16_t event_id; + + /* We register the thread as collector to avoid this thread generating a + * context switch event every time it collects the data + */ + sys_k_event_logger_register_as_collector(); + + while (1) { + /* collect the data */ + uint8_t data_length = SIZE32_OF(data); + + res = sys_k_event_logger_get_wait(&event_id, &dropped_count, + data, &data_length); + + if (res > 0) { + + /* process the data */ + switch (event_id) { +#ifdef CONFIG_KERNEL_EVENT_LOGGER_CONTEXT_SWITCH + case KERNEL_EVENT_LOGGER_CONTEXT_SWITCH_EVENT_ID: + if (data_length != 2) { + PRINTF("\x1b[13;1HError in context switch message. " + "event_id = %d, Expected %d, received %d\n", + event_id, 2, data_length); + } else { + /* Log context switch event for SoCWatch */ + SOC_WATCH_LOG_APP_EVENT(SOCW_EVENT_APP, + event_id, data[1]); + } + break; +#endif +#ifdef CONFIG_KERNEL_EVENT_LOGGER_INTERRUPT + case KERNEL_EVENT_LOGGER_INTERRUPT_EVENT_ID: + if (data_length != 2) { + PRINTF("\x1b[13;1HError in interrupt message. " + "event_id = %d, Expected %d, received %d\n", + event_id, 2, data_length); + } else { + /* Log interrupt event for SoCWatch */ + SOC_WATCH_LOG_EVENT(SOCW_EVENT_INTERRUPT, data[1]); + } + break; +#endif + default: + PRINTF("unrecognized event id %d", event_id); + } + } else { + /* This error should never happen */ + if (res == -EMSGSIZE) { + PRINTF("FATAL ERROR. The buffer provided to collect the " + "profiling events is too small\n"); + } + } + } +#endif +} + +/** + * @brief Start the soc_watch logger thread + * + * @details Start the soc_watch data collector thread + * + * @return No return value. + */ +void soc_watch_logger_thread_start(void) +{ + PRINTF("\x1b[2J\x1b[15;1H"); + + k_thread_spawn(&soc_watch_event_logger_stack[0][0], STSIZE, + (k_thread_entry_t) soc_watch_data_collector, 0, 0, 0, + 6, 0, 0); +} diff --git a/tests/power/power_states/src/soc_watch_logger.h b/tests/power/power_states/src/soc_watch_logger.h new file mode 100644 index 00000000000..a5c91dc427e --- /dev/null +++ b/tests/power/power_states/src/soc_watch_logger.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2016 Intel Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include "soc_watch.h" + +#if defined(CONFIG_STDOUT_CONSOLE) +#define PRINTF(...) { char output[256]; \ + sprintf(output, __VA_ARGS__); puts(output); } +#else +#define PRINTF(...) printk(__VA_ARGS__) +#endif + +void soc_watch_data_collector(void); +void soc_watch_logger_thread_start(void); diff --git a/tests/power/power_states/testcase.ini b/tests/power/power_states/testcase.ini index 20e36fe52e6..c6794b1074a 100644 --- a/tests/power/power_states/testcase.ini +++ b/tests/power/power_states/testcase.ini @@ -2,3 +2,9 @@ build_only = true tags = samples power filter = CONFIG_SOC_QUARK_SE_C1000 + +[test_socwatch] +extra_args = CONF_FILE="prj_socwatch.conf" +build_only = true +tags = samples power +filter = CONFIG_SOC_QUARK_SE_C1000 \ No newline at end of file