diff --git a/arch/arc/include/swap_macros.h b/arch/arc/include/swap_macros.h index f3e9f7b3886..c8859d5ed59 100644 --- a/arch/arc/include/swap_macros.h +++ b/arch/arc/include/swap_macros.h @@ -73,6 +73,10 @@ extern "C" { .endm +.macro _discard_callee_saved_regs + add_s sp, sp, ___callee_saved_stack_t_SIZEOF +.endm + /* * Must be called with interrupts locked or in P0. * Upon exit, sp will be pointing to the stack frame. diff --git a/arch/arc/soc/quark_se_c1000_ss/Kbuild b/arch/arc/soc/quark_se_c1000_ss/Kbuild index 5ae78ce56c3..0945b739fec 100644 --- a/arch/arc/soc/quark_se_c1000_ss/Kbuild +++ b/arch/arc/soc/quark_se_c1000_ss/Kbuild @@ -1,4 +1,3 @@ -ccflags-y +=-I$(srctree)/arch/x86/soc/intel_quark ccflags-y +=-I$(srctree)/include ccflags-y +=-I$(srctree)/include/drivers ccflags-y +=-I$(srctree)/drivers diff --git a/arch/arc/soc/quark_se_c1000_ss/Makefile b/arch/arc/soc/quark_se_c1000_ss/Makefile index 09a05bc3086..77df187115e 100644 --- a/arch/arc/soc/quark_se_c1000_ss/Makefile +++ b/arch/arc/soc/quark_se_c1000_ss/Makefile @@ -4,6 +4,7 @@ soc-cflags = $(call cc-option,-mcpu=quarkse_em) \ soc-aflags = $(soc-cflags) soc-cxxflags = $(soc-cflags) soc-cflags += -DQM_SENSOR=1 +soc-cflags += -I$(srctree)/arch/x86/soc/intel_quark ## FIXME SOC_SERIES = quark_se diff --git a/arch/arc/soc/quark_se_c1000_ss/power.c b/arch/arc/soc/quark_se_c1000_ss/power.c index 2f4df3deff6..3d70383bcfb 100644 --- a/arch/arc/soc/quark_se_c1000_ss/power.c +++ b/arch/arc/soc/quark_se_c1000_ss/power.c @@ -11,6 +11,7 @@ #include #include #include +#include #include "power_states.h" #include "ss_power_states.h" @@ -19,6 +20,7 @@ #if (defined(CONFIG_SYS_POWER_DEEP_SLEEP)) extern void _power_soc_sleep(void); extern void _power_soc_deep_sleep(void); +extern void _power_soc_deep_sleep_2(void); static void _deep_sleep(enum power_states state) { @@ -75,6 +77,11 @@ void _sys_soc_set_power_state(enum power_states state) case SYS_POWER_STATE_DEEP_SLEEP_1: _deep_sleep(state); break; + case SYS_POWER_STATE_DEEP_SLEEP_2: + ss_power_soc_lpss_enable(); + power_soc_set_ss_restore_flag(); + _power_soc_deep_sleep_2(); + break; #endif default: break; @@ -97,6 +104,22 @@ void _sys_soc_power_state_post_ops(enum power_states state) case SYS_POWER_STATE_DEEP_SLEEP_1: __builtin_arc_seti(0); break; + case SYS_POWER_STATE_DEEP_SLEEP_2: + ss_power_soc_lpss_disable(); + + /* If flag is cleared it means the system entered in + * sleep state while we were in LPS. In that case, we + * must set ARC_READY flag so x86 core can continue + * its execution. + */ + if ((QM_SCSS_GP->gp0 & GP0_BIT_SLEEP_READY) == 0) { + _quark_se_ss_ready(); + __builtin_arc_seti(0); + } else { + QM_SCSS_GP->gp0 &= ~GP0_BIT_SLEEP_READY; + QM_SCSS_GP->gps0 &= ~QM_GPS0_BIT_SENSOR_WAKEUP; + } + break; default: break; } diff --git a/arch/arc/soc/quark_se_c1000_ss/soc.c b/arch/arc/soc/quark_se_c1000_ss/soc.c index 3986556f2ad..68a3df3de09 100644 --- a/arch/arc/soc/quark_se_c1000_ss/soc.c +++ b/arch/arc/soc/quark_se_c1000_ss/soc.c @@ -14,7 +14,6 @@ #include #include "soc.h" #include -#include /** * @brief perform basic hardware initialization @@ -25,7 +24,8 @@ static int quark_se_arc_init(struct device *arg) { ARG_UNUSED(arg); - shared_data->flags |= ARC_READY; + _quark_se_ss_ready(); + return 0; } diff --git a/arch/arc/soc/quark_se_c1000_ss/soc.h b/arch/arc/soc/quark_se_c1000_ss/soc.h index 3c35970bf23..e6247f14b95 100644 --- a/arch/arc/soc/quark_se_c1000_ss/soc.h +++ b/arch/arc/soc/quark_se_c1000_ss/soc.h @@ -106,6 +106,7 @@ #include #include +#include #define INT_ENABLE_ARC ~(0x00000001 << 8) #define INT_ENABLE_ARC_BIT_POS (8) @@ -184,6 +185,11 @@ #define SPI_DW_IRQ_FLAGS 0 +static inline void _quark_se_ss_ready(void) +{ + shared_data->flags |= ARC_READY; +} + #endif /* !_ASMLANGUAGE */ #endif /* _BOARD__H_ */ diff --git a/arch/arc/soc/quark_se_c1000_ss/soc_power.S b/arch/arc/soc/quark_se_c1000_ss/soc_power.S index 0b9e3356342..88720bf666e 100644 --- a/arch/arc/soc/quark_se_c1000_ss/soc_power.S +++ b/arch/arc/soc/quark_se_c1000_ss/soc_power.S @@ -15,9 +15,14 @@ GTEXT(_sys_soc_resume_from_deep_sleep) GTEXT(_power_restore_cpu_context) GTEXT(_power_soc_sleep) GTEXT(_power_soc_deep_sleep) +GTEXT(_power_soc_deep_sleep_2) #define GPS0_REGISTER 0xb0800100 +#define GP0_REGISTER 0xb0800114 +#define GP0_BIT_SLEEP_READY 0 #define RESTORE_SS_BIT 2 +#define SLEEP_INTR_ENABLED_BIT 4 +#define SLEEP_MODE_RTC_ENABLED_BIT 5 SECTION_FUNC(TEXT, _sys_soc_resume_from_deep_sleep) /* Check is this wakeup after sleep event. */ @@ -68,6 +73,47 @@ SECTION_FUNC(TEXT, _power_soc_deep_sleep) j @power_soc_deep_sleep /* Does not return */ +SECTION_FUNC(TEXT, _power_soc_deep_sleep_2) + /* + * Setup 'sleep' instruction operand. + */ + + /* Get interrupt priority from status32 registers. */ + lr r0, [_ARC_V2_STATUS32] + lsr r0, r0 + and r0, r0, 0xF + + /* Enable interrupts */ + bset r0, r0, SLEEP_INTR_ENABLED_BIT + + /* Set 'sleep' mode corresponding to SS2 state i.e. core disabled, + * timers disabled, RTC enabled. + */ + bset r0, r0, SLEEP_MODE_RTC_ENABLED_BIT + + /* + * Save the return address. + * The restore function will pop this and jump + * back to the caller. + */ + push_s blink + + jl @save_cpu_context + + ld r1, [GP0_REGISTER] + bset r1, r1, GP0_BIT_SLEEP_READY + st r1, [GP0_REGISTER] + sleep r0 + + /* If we reach this code it means the x86 core didn't put the + * system in SYS_POWER_STATE_DEEP_SLEEP_2 state while we were + * in LPS. Then discard saved context. + */ + _discard_callee_saved_regs + + pop_s blink + j_s [blink] + SECTION_FUNC(TEXT, _sys_soc_restore_cpu_context) mov_s r1, _kernel ld_s r2, [r1, _kernel_offset_to_current] diff --git a/arch/arc/soc/quark_se_c1000_ss/soc_power.h b/arch/arc/soc/quark_se_c1000_ss/soc_power.h index 5c8e763cfc0..d7bc5440cbd 100644 --- a/arch/arc/soc/quark_se_c1000_ss/soc_power.h +++ b/arch/arc/soc/quark_se_c1000_ss/soc_power.h @@ -11,12 +11,24 @@ extern "C" { #endif +/* + * Bit 0 from GP0 register is used internally by the kernel + * to handle PM multicore support. Any change on QMSI and/or + * bootloader which affects this bit should take it in + * consideration. + */ +#define GP0_BIT_SLEEP_READY BIT(0) + enum power_states { SYS_POWER_STATE_CPU_LPS, /* SS2 with LPSS enabled state */ SYS_POWER_STATE_CPU_LPS_1, /* SS2 state */ SYS_POWER_STATE_CPU_LPS_2, /* SS1 state with Timer ON */ SYS_POWER_STATE_DEEP_SLEEP, /* DEEP SLEEP state */ SYS_POWER_STATE_DEEP_SLEEP_1, /* SLEEP state */ + SYS_POWER_STATE_DEEP_SLEEP_2, /* Multicore DEEP SLEEP state. + * Execution context is saved + * and core enters in LPS state. + */ SYS_POWER_STATE_MAX }; @@ -42,6 +54,9 @@ enum power_states { * SYS_POWER_STATE_DEEP_SLEEP_1: Only Always-On peripherals can wake up * the SoC. This consists of the Counter, RTC, GPIO 1 and AIO Comparator. * + * SYS_POWER_STATE_DEEP_SLEEP_2: Only Always-On peripherals can wake up + * the SoC. This consists of the Counter, RTC, GPIO 1 and AIO Comparator. + * * Considerations around SYS_POWER_STATE_CPU_LPS (LPSS state): * ----------------------------------------------------------- * diff --git a/arch/x86/soc/intel_quark/quark_se/power.c b/arch/x86/soc/intel_quark/quark_se/power.c index b158f0269bb..db328e67d01 100644 --- a/arch/x86/soc/intel_quark/quark_se/power.c +++ b/arch/x86/soc/intel_quark/quark_se/power.c @@ -9,6 +9,7 @@ #include #include #include +#include #include "power_states.h" @@ -47,6 +48,7 @@ static void _deep_sleep(enum power_states state) _power_soc_sleep(); break; case SYS_POWER_STATE_DEEP_SLEEP: + case SYS_POWER_STATE_DEEP_SLEEP_2: _power_soc_deep_sleep(); break; default: @@ -70,6 +72,7 @@ void _sys_soc_set_power_state(enum power_states state) #if (defined(CONFIG_SYS_POWER_DEEP_SLEEP)) case SYS_POWER_STATE_DEEP_SLEEP: case SYS_POWER_STATE_DEEP_SLEEP_1: + case SYS_POWER_STATE_DEEP_SLEEP_2: _deep_sleep(state); break; #endif @@ -87,6 +90,11 @@ void _sys_soc_power_state_post_ops(enum power_states state) __asm__ volatile("sti"); break; #if (defined(CONFIG_SYS_POWER_DEEP_SLEEP)) + case SYS_POWER_STATE_DEEP_SLEEP_2: +#ifdef CONFIG_ARC_INIT + _arc_init(NULL); +#endif /* CONFIG_ARC_INIT */ + /* Fallthrough */ case SYS_POWER_STATE_DEEP_SLEEP: case SYS_POWER_STATE_DEEP_SLEEP_1: __asm__ volatile("sti"); @@ -96,3 +104,8 @@ void _sys_soc_power_state_post_ops(enum power_states state) break; } } + +bool _sys_soc_power_state_is_arc_ready(void) +{ + return QM_SCSS_GP->gp0 & GP0_BIT_SLEEP_READY ? true : false; +} diff --git a/arch/x86/soc/intel_quark/quark_se/soc.c b/arch/x86/soc/intel_quark/quark_se/soc.c index 15bf131925a..557aef8ba8f 100644 --- a/arch/x86/soc/intel_quark/quark_se/soc.c +++ b/arch/x86/soc/intel_quark/quark_se/soc.c @@ -37,7 +37,8 @@ * starts the ARC processor. * @return N/A */ -static int arc_init(struct device *arg) +/* This function is also called at deep sleep resume. */ +int _arc_init(struct device *arg) { uint32_t *reset_vector; @@ -83,7 +84,7 @@ skip_arc_init: return 0; } -SYS_INIT(arc_init, POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT); +SYS_INIT(_arc_init, POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT); #endif /*CONFIG_ARC_INIT*/ diff --git a/arch/x86/soc/intel_quark/quark_se/soc.h b/arch/x86/soc/intel_quark/quark_se/soc.h index e3931dfbf88..eb53059904a 100644 --- a/arch/x86/soc/intel_quark/quark_se/soc.h +++ b/arch/x86/soc/intel_quark/quark_se/soc.h @@ -64,4 +64,8 @@ #endif /* _ASMLANGUAGE */ +#ifdef CONFIG_ARC_INIT +int _arc_init(struct device *arg); +#endif /* CONFIG_ARC_INIT */ + #endif /* __SOC_H_ */ diff --git a/arch/x86/soc/intel_quark/quark_se/soc_power.h b/arch/x86/soc/intel_quark/quark_se/soc_power.h index 1562d1ea025..acd0c8bc098 100644 --- a/arch/x86/soc/intel_quark/quark_se/soc_power.h +++ b/arch/x86/soc/intel_quark/quark_se/soc_power.h @@ -11,12 +11,23 @@ extern "C" { #endif +/* + * Bit 0 from GP0 register is used internally by the kernel + * to handle PM multicore support. Any change on QMSI and/or + * bootloader which affects this bit should take it in + * consideration. + */ +#define GP0_BIT_SLEEP_READY BIT(0) + enum power_states { SYS_POWER_STATE_CPU_LPS, /* C2LP state */ SYS_POWER_STATE_CPU_LPS_1, /* C2 state */ SYS_POWER_STATE_CPU_LPS_2, /* C1 state */ SYS_POWER_STATE_DEEP_SLEEP, /* DEEP SLEEP state */ SYS_POWER_STATE_DEEP_SLEEP_1, /* SLEEP state */ + SYS_POWER_STATE_DEEP_SLEEP_2, /* Multicore support for + * DEEP_SLEEP state. + */ SYS_POWER_STATE_MAX }; @@ -42,6 +53,9 @@ enum power_states { * * SYS_POWER_STATE_DEEP_SLEEP_1: Only Always-On peripherals can wake up * the SoC. This consists of the Counter, RTC, GPIO 1 and AIO Comparator. + * + * SYS_POWER_STATE_DEEP_SLEEP_2: Only Always-On peripherals can wake up + * the SoC. This consists of the Counter, RTC, GPIO 1 and AIO Comparator. */ void _sys_soc_set_power_state(enum power_states state); @@ -55,6 +69,14 @@ void _sys_soc_set_power_state(enum power_states state); */ void _sys_soc_power_state_post_ops(enum power_states state); +/** + * @brief Check if ARC core is ready to enter in DEEP_SLEEP states. + * + * @retval true If ARC is ready. + * @retval false Otherwise. + */ +bool _sys_soc_power_state_is_arc_ready(void); + #ifdef __cplusplus } #endif diff --git a/tests/power/multicore/README.rst b/tests/power/multicore/README.rst new file mode 100644 index 00000000000..9f2fc7129f9 --- /dev/null +++ b/tests/power/multicore/README.rst @@ -0,0 +1,140 @@ +Multicore Test App +################## + +These applications aim to provide an easy way to test the PM multicore support +from Quark SE. It implements two common flows: 1) LMT core is idle while ARC +core is busy so the system is not put on DEEP SLEEP mode, and 2) LMT core is +idle when ARC core is also idle so the system enters in DEEP SLEEP mode. + +These applications implement a master/slave approach where LMT application +plays the master role while ARC application plays the slave. The master is the +one which controls the wakeup device (in this example it is the RTC) and +actually puts the system in deep sleep mode. + +To keep the synchronization logic simple and demonstrate the multi-core +coordination, we do not handle any events in ARC other than the notification +IPM from LMT. Handling events in ARC itself may need more complex communication +and synchronization logic between the applications of the the 2 cores. For +example, such an implementation should be able to handle the corner case of +ARC getting woken up by some event when LMT is in the process of putting the +SOC in deep sleep. + +In the following section the working mechanism from both ARC and LMT +applications are described, and building and wiring instructions are +provided. + +test/power/multicore/arc +************************ + +The ARC application is very simple, it keeps the system bouncing between 'busy' +and 'idle' states. The application keeps the system busy for 10 seconds and +then idle until woken up by IPM event from LMT. When system goes into idle, +the application puts the system in SYS_POWER_STATE_DEEP_SLEEP_2 state which +allows LMT core to put the system in sleep mode. + +The application uses UART_0 as console output device so, in order to be able +to see ARC output messages, make sure you have attached a serial cable to +UART_0. In 'quark_se_c1000_ss_devboard', UART_0 pins are in J14 header. The +table below shows the wiring instructions. + ++---------+------------------+ +| J14 PIN | SERIAL CABLE PIN | ++=========+==================+ +| 3 | RXD | ++---------+------------------+ +| 5 | TXD | ++---------+------------------+ +| 11 | GND | ++---------+------------------+ + +If your wiring is correct, you should see the following output on console: + +:: + + ARC: Quark SE PM Multicore Demo + ARC: busy + ARC: idle + ARC: busy + ARC: idle + ARC: busy + ARC: idle + ARC: busy + ... + +To build the ARC application, run the following commands: + +:: + + $ cd tests/power/multicore/arc/ + $ make + +test/power/multicore/lmt +************************ + +The LMT application is very simple and also keeps the system bouncing between +'busy' and 'idle'. When the system goes into idle, the application tries to +put the system in DEEP_SLEEP state. If ARC core is busy, it fails. If ARC core +is idle, it succeeds. + +When 'TEST_CASE=sleep-success', the application will be busy for 15 seconds +and idle for 5. This means that ARC core will be idle when LMT core tries to +enter in DEEP_SLEEP, and it will succeed. In this case, the output on your +console should look like this: + +:: + + LMT: Quark SE PM Multicore Demo + LMT: busy + LMT: idle + LMT: Try to put the system in SYS_POWER_STATE_DEEP_SLEEP_2 state + LMT: Succeed. + LMT: busy + LMT: idle + LMT: Try to put the system in SYS_POWER_STATE_DEEP_SLEEP_2 state + LMT: Succeed. + LMT: busy + LMT: idle + LMT: Try to put the system in SYS_POWER_STATE_DEEP_SLEEP_2 state + LMT: Succeed. + ... + +To build the LMT application which tests the "success" path, run the following +commands: + +:: + + $ cd tests/power/multicore/lmt TEST_CASE=sleep-success + $ make + +When 'TEST_CASE=sleep-fail', application is busy for 5 seconds and idle for 15 +seconds. This means that ARC core will be busy when LMT core tries to enter in +DEEP_SLEEP, and it will fail. In this case the output on your console should +look like this: + +:: + + LMT: Quark SE PM Multicore Demo + LMT: busy + LMT: idle + LMT: Try to put the system in SYS_POWER_STATE_DEEP_SLEEP_2 state + LMT: Failed. ARC is busy. + LMT: busy + LMT: idle + LMT: Try to put the system in SYS_POWER_STATE_DEEP_SLEEP_2 state + LMT: Failed. ARC is busy. + LMT: busy + LMT: idle + LMT: Try to put the system in SYS_POWER_STATE_DEEP_SLEEP_2 state + LMT: Failed. ARC is busy. + ... + +To build the LMT application which tests the "failure" path, run the following +commands: + +:: + + $ cd tests/power/multicore/lmt + $ make TEST_CASE=sleep-fail + +The application uses UART_1 device as console output device, which is the +default console device. diff --git a/tests/power/multicore/arc/Makefile b/tests/power/multicore/arc/Makefile new file mode 100644 index 00000000000..1fec44f4a23 --- /dev/null +++ b/tests/power/multicore/arc/Makefile @@ -0,0 +1,4 @@ +BOARD = quark_se_c1000_ss_devboard +CONF_FILE = prj.conf + +include ${ZEPHYR_BASE}/Makefile.inc diff --git a/tests/power/multicore/arc/prj.conf b/tests/power/multicore/arc/prj.conf new file mode 100644 index 00000000000..bfa6b9419aa --- /dev/null +++ b/tests/power/multicore/arc/prj.conf @@ -0,0 +1,9 @@ +CONFIG_SYS_POWER_MANAGEMENT=y +CONFIG_SYS_POWER_DEEP_SLEEP=y +CONFIG_SYS_POWER_LOW_POWER_STATE=y +CONFIG_DEVICE_POWER_MANAGEMENT=y +CONFIG_TICKLESS_IDLE=y +CONFIG_UART_1=n +CONFIG_UART_CONSOLE_ON_DEV_NAME="UART_0" +CONFIG_IPM=y +CONFIG_IPM_QUARK_SE=y diff --git a/tests/power/multicore/arc/src/Makefile b/tests/power/multicore/arc/src/Makefile new file mode 100644 index 00000000000..cc7bd339f3d --- /dev/null +++ b/tests/power/multicore/arc/src/Makefile @@ -0,0 +1,3 @@ +ccflags-y += -I$(ZEPHYR_BASE)/drivers + +obj-y = main.o diff --git a/tests/power/multicore/arc/src/main.c b/tests/power/multicore/arc/src/main.c new file mode 100644 index 00000000000..06944ce222e --- /dev/null +++ b/tests/power/multicore/arc/src/main.c @@ -0,0 +1,135 @@ +/* + * 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 +#include +#include +#include +#include +#include + +#define TASK_TIME_IN_SEC 10 +#define MAX_SUSPEND_DEVICE_COUNT 15 + +static struct device *suspended_devices[MAX_SUSPEND_DEVICE_COUNT]; +static int suspend_device_count; +static struct k_fifo fifo; +static int post_ops_done; + +QUARK_SE_IPM_DEFINE(alarm_notification, 0, QUARK_SE_IPM_INBOUND); + +static void suspend_devices(void) +{ + for (int i = suspend_device_count - 1; i >= 0; i--) { + device_set_power_state(suspended_devices[i], + DEVICE_PM_SUSPEND_STATE); + } +} + +static void resume_devices(void) +{ + for (int i = 0; i < suspend_device_count; i++) { + device_set_power_state(suspended_devices[i], + DEVICE_PM_ACTIVE_STATE); + } +} + +int _sys_soc_suspend(int32_t ticks) +{ + post_ops_done = 0; + + suspend_devices(); + + _sys_soc_set_power_state(SYS_POWER_STATE_DEEP_SLEEP_2); + + if (!post_ops_done) { + post_ops_done = 1; + resume_devices(); + _sys_soc_power_state_post_ops(SYS_POWER_STATE_DEEP_SLEEP_2); + } + + return SYS_PM_DEEP_SLEEP; +} + +void _sys_soc_resume(void) +{ + if (!post_ops_done) { + post_ops_done = 1; + _sys_soc_power_state_post_ops(SYS_POWER_STATE_DEEP_SLEEP_2); + resume_devices(); + } +} + +static void build_suspend_device_list(void) +{ + int i, devcount; + struct device *devices; + + device_list_get(&devices, &devcount); + if (devcount > MAX_SUSPEND_DEVICE_COUNT) { + printk("Error: List of devices exceeds what we can track " + "for suspend. Built: %d, Max: %d\n", + devcount, MAX_SUSPEND_DEVICE_COUNT); + return; + } + + suspend_device_count = 2; + for (i = 0; i < devcount; i++) { + if (!strcmp(devices[i].config->name, "arc_v2_irq_unit")) { + suspended_devices[0] = &devices[i]; + } else if (!strcmp(devices[i].config->name, "sys_clock")) { + suspended_devices[1] = &devices[i]; + } else { + suspended_devices[suspend_device_count++] = &devices[i]; + } + } +} + +void alarm_notification_handler(void *context, uint32_t id, + volatile void *data) +{ + /* Unblock ARC application thread. */ + k_fifo_put(&fifo, NULL); +} + +void main(void) +{ + struct device *ipm; + + printk("ARC: Quark SE PM Multicore Demo\n"); + + build_suspend_device_list(); + + k_fifo_init(&fifo); + + ipm = device_get_binding("alarm_notification"); + ipm_register_callback(ipm, alarm_notification_handler, NULL); + ipm_set_enabled(ipm, 1); + + while (1) { + /* Simulate some task handling by busy waiting. */ + printk("ARC: busy\n"); + k_busy_wait(TASK_TIME_IN_SEC * 1000 * 1000); + + printk("ARC: idle\n"); + k_fifo_get(&fifo, K_FOREVER); + } +} diff --git a/tests/power/multicore/lmt/Makefile b/tests/power/multicore/lmt/Makefile new file mode 100644 index 00000000000..723e9581ff3 --- /dev/null +++ b/tests/power/multicore/lmt/Makefile @@ -0,0 +1,4 @@ +BOARD = quark_se_c1000_devboard +CONF_FILE = prj.conf + +include ${ZEPHYR_BASE}/Makefile.inc diff --git a/tests/power/multicore/lmt/prj.conf b/tests/power/multicore/lmt/prj.conf new file mode 100644 index 00000000000..b9ff02930a2 --- /dev/null +++ b/tests/power/multicore/lmt/prj.conf @@ -0,0 +1,12 @@ +CONFIG_SYS_POWER_MANAGEMENT=y +CONFIG_SYS_POWER_DEEP_SLEEP=y +CONFIG_SYS_POWER_LOW_POWER_STATE=y +CONFIG_DEVICE_POWER_MANAGEMENT=y +CONFIG_TICKLESS_IDLE=y +CONFIG_ARC_INIT=y +CONFIG_UART_0=n +CONFIG_RTC=y +CONFIG_IPM=y +CONFIG_IPM_QUARK_SE=y +CONFIG_IPM_QUARK_SE_MASTER=y + diff --git a/tests/power/multicore/lmt/src/Makefile b/tests/power/multicore/lmt/src/Makefile new file mode 100644 index 00000000000..3a31db7df90 --- /dev/null +++ b/tests/power/multicore/lmt/src/Makefile @@ -0,0 +1,9 @@ +TEST_CASE ?= sleep-success + +ifeq ($(TEST_CASE), sleep-success) +ccflags-y += -DTEST_CASE_SLEEP_SUCCESS +endif + +ccflags-y +=-I$(ZEPHYR_BASE)/drivers + +obj-y = main.o diff --git a/tests/power/multicore/lmt/src/main.c b/tests/power/multicore/lmt/src/main.c new file mode 100644 index 00000000000..f5cbe3a35bf --- /dev/null +++ b/tests/power/multicore/lmt/src/main.c @@ -0,0 +1,168 @@ +/* + * 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 +#include +#include +#include +#include +#include + +#ifdef TEST_CASE_SLEEP_SUCCESS +#define TASK_TIME_IN_SEC 15 +#define IDLE_TIME_IN_SEC 5 +#else +#define TASK_TIME_IN_SEC 5 +#define IDLE_TIME_IN_SEC 15 +#endif /* TEST_CASE_SLEEP_SUCCESS */ + +#define MAX_SUSPEND_DEVICE_COUNT 15 + +static struct device *suspended_devices[MAX_SUSPEND_DEVICE_COUNT]; +static int suspend_device_count; +static struct k_fifo fifo; +static struct device *ipm; + +QUARK_SE_IPM_DEFINE(alarm_notification, 0, QUARK_SE_IPM_OUTBOUND); + +static void suspend_devices(void) +{ + for (int i = suspend_device_count - 1; i >= 0; i--) { + device_set_power_state(suspended_devices[i], + DEVICE_PM_SUSPEND_STATE); + } +} + +static void resume_devices(void) +{ + for (int i = 0; i < suspend_device_count; i++) { + device_set_power_state(suspended_devices[i], + DEVICE_PM_ACTIVE_STATE); + } +} + +int _sys_soc_suspend(int32_t ticks) +{ + printk("LMT: Try to put the system in SYS_POWER_STATE_DEEP_SLEEP_2" + " state\n"); + + if (!_sys_soc_power_state_is_arc_ready()) { + printk("LMT: Failed. ARC is busy.\n"); + return SYS_PM_NOT_HANDLED; + } + + suspend_devices(); + + _sys_soc_set_power_state(SYS_POWER_STATE_DEEP_SLEEP_2); + + resume_devices(); + + printk("LMT: Succeed.\n"); + + _sys_soc_power_state_post_ops(SYS_POWER_STATE_DEEP_SLEEP_2); + + return SYS_PM_DEEP_SLEEP; +} + +static void build_suspend_device_list(void) +{ + int i, devcount; + struct device *devices; + + device_list_get(&devices, &devcount); + if (devcount > MAX_SUSPEND_DEVICE_COUNT) { + printk("Error: List of devices exceeds what we can track " + "for suspend. Built: %d, Max: %d\n", + devcount, MAX_SUSPEND_DEVICE_COUNT); + return; + } + + suspend_device_count = 3; + for (i = 0; i < devcount; i++) { + if (!strcmp(devices[i].config->name, "loapic")) { + suspended_devices[0] = &devices[i]; + } else if (!strcmp(devices[i].config->name, "ioapic")) { + suspended_devices[1] = &devices[i]; + } else if (!strcmp(devices[i].config->name, + CONFIG_UART_CONSOLE_ON_DEV_NAME)) { + suspended_devices[2] = &devices[i]; + } else { + suspended_devices[suspend_device_count++] = &devices[i]; + } + } +} + +static void alarm_handler(struct device *dev) +{ + /* Unblock LMT application thread. */ + k_fifo_put(&fifo, NULL); + + /* Send a dummy message to ARC so the ARC application + * thread can be unblocked. + */ + ipm_send(ipm, 0, 0, NULL, 0); +} + +void main(void) +{ + struct device *rtc_dev; + struct rtc_config config; + uint32_t now; + + printk("LMT: Quark SE PM Multicore Demo\n"); + + k_fifo_init(&fifo); + + build_suspend_device_list(); + + ipm = device_get_binding("alarm_notification"); + if (!ipm) { + printk("Error: Failed to get IPM device\n"); + return; + } + + rtc_dev = device_get_binding("RTC_0"); + if (!rtc_dev) { + printk("Error: Failed to get RTC device\n"); + return; + } + + rtc_enable(rtc_dev); + + config.init_val = 0; + config.alarm_enable = 0; + config.alarm_val = 0; + config.cb_fn = alarm_handler; + rtc_set_config(rtc_dev, &config); + + while (1) { + /* Simulate some task handling by busy waiting. */ + printk("LMT: busy\n"); + k_busy_wait(TASK_TIME_IN_SEC * 1000 * 1000); + + now = rtc_read(rtc_dev); + rtc_set_alarm(rtc_dev, + now + (RTC_ALARM_SECOND * IDLE_TIME_IN_SEC)); + + printk("LMT: idle\n"); + k_fifo_get(&fifo, K_FOREVER); + } +}