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_GPIO_QMSI_SS) += drivers/gpio/qm_ss_gpio.o
|
||||||
obj-$(CONFIG_I2C_QMSI_SS) += drivers/i2c/qm_ss_i2c.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_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
|
installed. Make sure this option is properly set when QMSI_LIBRARY
|
||||||
is enabled otherwise the build will fail.
|
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
|
endif
|
||||||
|
|
|
@ -16,3 +16,9 @@ endif
|
||||||
ifdef CONFIG_SYS_POWER_DEEP_SLEEP
|
ifdef CONFIG_SYS_POWER_DEEP_SLEEP
|
||||||
KBUILD_CPPFLAGS +=-DENABLE_RESTORE_CONTEXT
|
KBUILD_CPPFLAGS +=-DENABLE_RESTORE_CONTEXT
|
||||||
endif
|
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 <x86intrin.h>
|
||||||
#include "qm_common.h"
|
#include "qm_common.h"
|
||||||
#include "qm_soc_regs.h"
|
#include "qm_soc_regs.h"
|
||||||
#include "qm_interrupt.h"
|
|
||||||
#include "soc_watch.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);
|
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. */
|
/* Log an event with one parameter. */
|
||||||
void soc_watch_log_event(soc_watch_event_t event_id, uintptr_t ev_data)
|
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;
|
static uint8_t record_rtc = 0;
|
||||||
const uint32_t *rtc_ctr = (uint32_t *)&QM_RTC->rtc_ccvr;
|
const uint32_t *rtc_ctr = (uint32_t *)&QM_RTC->rtc_ccvr;
|
||||||
const char *cp;
|
const char *cp;
|
||||||
|
unsigned int irq_flag = 0;
|
||||||
uint64_t tsc = __builtin_ia32_rdtsc(); /* Grab hi-res timestamp */
|
uint64_t tsc = __builtin_ia32_rdtsc(); /* Grab hi-res timestamp */
|
||||||
uint32_t rtc_val = *rtc_ctr;
|
uint32_t rtc_val = *rtc_ctr;
|
||||||
|
|
||||||
#define AVG_EVENT_SIZE 8 /* Size of a typical message in bytes. */
|
#define AVG_EVENT_SIZE 8 /* Size of a typical message in bytes. */
|
||||||
|
|
||||||
MLOG('[');
|
MLOG('[');
|
||||||
qm_irq_disable();
|
irq_flag = soc_watch_irq_lock();
|
||||||
/* TODO: We know exactly how many bytes of storage we need,
|
/* 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
|
* since we know the event code. So don't do an "AVG" size thing
|
||||||
* here--use the exact size!
|
* 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) {
|
if (event_id >= SOCW_EVENT_MAX) {
|
||||||
SOC_WATCH_TRACE("Unknown event id: 0x%x\n", event_id);
|
SOC_WATCH_TRACE("Unknown event id: 0x%x\n", event_id);
|
||||||
MLOG('?');
|
MLOG('?');
|
||||||
qm_irq_enable();
|
soc_watch_irq_unlock(irq_flag);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
cp = ev_strs[event_id]; /* Look up event string */
|
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(':');
|
||||||
MLOG_BYTE(soc_watch_event_buffer.eb_idx);
|
MLOG_BYTE(soc_watch_event_buffer.eb_idx);
|
||||||
qm_irq_enable();
|
soc_watch_irq_unlock(irq_flag);
|
||||||
MLOG(']');
|
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-y = main.o
|
||||||
|
obj-$(CONFIG_SOC_WATCH) += soc_watch_logger.o
|
|
@ -26,6 +26,7 @@
|
||||||
#include <power.h>
|
#include <power.h>
|
||||||
#include <soc_power.h>
|
#include <soc_power.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include "soc_watch_logger.h"
|
||||||
|
|
||||||
static enum power_states states_list[] = {
|
static enum power_states states_list[] = {
|
||||||
SYS_POWER_STATE_CPU_LPS,
|
SYS_POWER_STATE_CPU_LPS,
|
||||||
|
@ -386,6 +387,11 @@ void main(void)
|
||||||
|
|
||||||
build_suspend_device_list();
|
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
|
/* All our application does is putting the task to sleep so the kernel
|
||||||
* triggers the suspend operation.
|
* 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
|
build_only = true
|
||||||
tags = samples power
|
tags = samples power
|
||||||
filter = CONFIG_SOC_QUARK_SE_C1000
|
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