ext qmsi: Add config support and kernel events for enabling SoCWatch
tests: power_states app updated to support SoCWatch collection Add CONFIG_SOC_WATCH option for enabling the SoCWatch QMSI driver and associated instrumentation hooks. Bug fix for soc_watch.c to use local irq_lock/unlock This will be put into QMSI as well. JIRA: ZEP-1121 Change-Id: I0514324e81ca02c1d01ffc2d6cf4d31aee491544 Signed-off-by: Jon Moeller <jon.moeller@intel.com>
This commit is contained in:
parent
ff23cb58a8
commit
8358468fed
10 changed files with 257 additions and 4 deletions
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -36,7 +36,6 @@
|
|||
#include <x86intrin.h>
|
||||
#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(']');
|
||||
}
|
||||
|
||||
|
|
32
tests/power/power_states/prj_socwatch.conf
Normal file
32
tests/power/power_states/prj_socwatch.conf
Normal file
|
@ -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
|
|
@ -1 +1,2 @@
|
|||
obj-y = main.o
|
||||
obj-$(CONFIG_SOC_WATCH) += soc_watch_logger.o
|
|
@ -26,6 +26,7 @@
|
|||
#include <power.h>
|
||||
#include <soc_power.h>
|
||||
#include <string.h>
|
||||
#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.
|
||||
*/
|
||||
|
|
107
tests/power/power_states/src/soc_watch_logger.c
Normal file
107
tests/power/power_states/src/soc_watch_logger.c
Normal file
|
@ -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);
|
||||
}
|
31
tests/power/power_states/src/soc_watch_logger.h
Normal file
31
tests/power/power_states/src/soc_watch_logger.h
Normal file
|
@ -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 <zephyr.h>
|
||||
#include <misc/printk.h>
|
||||
#include <string.h>
|
||||
#include <misc/kernel_event_logger.h>
|
||||
#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);
|
|
@ -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
|
Loading…
Add table
Add a link
Reference in a new issue