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:
Jon Moeller 2016-10-25 08:41:43 -05:00 committed by Anas Nashif
commit 8358468fed
10 changed files with 257 additions and 4 deletions

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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(']');
}

View 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

View file

@ -1 +1,2 @@
obj-y = main.o
obj-$(CONFIG_SOC_WATCH) += soc_watch_logger.o

View file

@ -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.
*/

View 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);
}

View 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);

View file

@ -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