summaryrefslogtreecommitdiff
path: root/chip/ish/aontaskfw
diff options
context:
space:
mode:
authorHu, Hebo <hebo.hu@intel.com>2019-03-08 15:34:21 +0800
committerchrome-bot <chrome-bot@chromium.org>2019-04-08 02:51:29 -0700
commit6a184d5019f0b45fe692da09a14e9ce7c853d68c (patch)
tree541192356148342f09bacebb75bea95027e98e7d /chip/ish/aontaskfw
parentd0a350e6691a9d93138051e2aa00e0d6c26151b5 (diff)
downloadchrome-ec-6a184d5019f0b45fe692da09a14e9ce7c853d68c.tar.gz
ish/ish5: implement AON low power management framework
AON PM framework including: 1: AON task skeleton 2: task switching between main FW and AON task 3: 'idlestats' console command for D0ix statistic information 4: D0ix entrance in idle task BUG=b:122364080 BRANCH=none TEST=tested on arcada Change-Id: Iefa9e067892d5c42d9f0c795275fe88e5a36115b Signed-off-by: Hu, Hebo <hebo.hu@intel.com> Reviewed-on: https://chromium-review.googlesource.com/1510518 Commit-Ready: Rushikesh S Kadam <rushikesh.s.kadam@intel.com> Commit-Ready: Hebo Hu <hebo.hu@intel.corp-partner.google.com> Tested-by: Jett Rink <jettrink@chromium.org> Reviewed-by: Jett Rink <jettrink@chromium.org> Reviewed-by: Hebo Hu <hebo.hu@intel.corp-partner.google.com>
Diffstat (limited to 'chip/ish/aontaskfw')
-rw-r--r--chip/ish/aontaskfw/ish_aon_share.h42
-rw-r--r--chip/ish/aontaskfw/ish_aontask.c383
-rw-r--r--chip/ish/aontaskfw/ish_aontask.ld.in68
3 files changed, 493 insertions, 0 deletions
diff --git a/chip/ish/aontaskfw/ish_aon_share.h b/chip/ish/aontaskfw/ish_aon_share.h
new file mode 100644
index 0000000000..3a5c1bd06b
--- /dev/null
+++ b/chip/ish/aontaskfw/ish_aon_share.h
@@ -0,0 +1,42 @@
+/* Copyright 2019 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef __CROS_EC_ISH_AON_SHARE_H
+#define __CROS_EC_ISH_AON_SHARE_H
+
+#include "ia_structs.h"
+
+/* magic ID for valid aontask image sanity check */
+#define AON_MAGIC_ID 0x544E4F41 /*"AONT"*/
+
+/* aontask error code */
+#define AON_SUCCESS 0
+#define AON_ERROR_NOT_SUPPORT_POWER_MODE 1
+#define AON_ERROR_DMA_FAILED 2
+
+
+/* shared data structure between main FW and aontask */
+struct ish_aon_share {
+ /* magic ID */
+ uint32_t magic_id;
+ /* last error */
+ /* error counter */
+ uint32_t error_count;
+ /* last error */
+ int last_error;
+ /* aontask's TSS segment entry */
+ struct tss_entry *aon_tss;
+ /* aontask's LDT start address */
+ ldt_entry *aon_ldt;
+ /* aontask's LDT's limit size */
+ uint32_t aon_ldt_size;
+ /* current power state, see chip/ish/power_mgt.h */
+ int pm_state;
+ /* for store/restore main FW's IDT */
+ struct idt_header main_fw_idt_hdr;
+
+} __packed;
+
+#endif /* __CROS_EC_ISH_AON_SHARE_H */
diff --git a/chip/ish/aontaskfw/ish_aontask.c b/chip/ish/aontaskfw/ish_aontask.c
new file mode 100644
index 0000000000..3c1095a157
--- /dev/null
+++ b/chip/ish/aontaskfw/ish_aontask.c
@@ -0,0 +1,383 @@
+/* Copyright 2019 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/**
+ * ISH aontask is a seprated very small program from main FW, not like main FW
+ * resides in main SRAM, aontask resides in a small AON memory (ISH3 has no
+ * seprated AON memory, reserved last 4KB of main SRAM for AON use, from ISH4,
+ * there is seprated AON memory, 4KB for ISH4, and 8KB for ISH5).
+ *
+ * When ISH entered into low power states, aontask may get switched and run,
+ * aontask managments the main SRAM and is responsible for store and restore
+ * main FW's running context, for example, when entering D0i2 state, put main
+ * SRAM into retention mode, when exit D0i2 state and before switch back to
+ * main FW, put main SRAM into normal access mode, when entering D0i3 state,
+ * at first stores main FW's writeable data into IMR DDR (read only code and
+ * data already have copies in IMR), then power off the main SRAM completely,
+ * when exit D0i3 state, at first power on the main SRAM, and restore main FW's
+ * code and data from IMR to main SRAM, then switch back to main FW.
+ *
+ * On ISH, except the hpet timer, also have other wakeup sources, peripheral
+ * events, such as gpio interrupt, uart interrupt, ipc interrupt, I2C and SPI
+ * access are also can wakeup ISH. ISH's PMU (power management unit HW) will
+ * manage these wakeup sources and transfer to a PMU wakeup interrupt which
+ * can wakeup aontask, and aontask will handle it, when aontask got up, and
+ * swiched back to main FW, main FW will receive the original wakeup source
+ * interrupt which triggered the PMU wakeup interrupt in aontask, then main FW
+ * handle the original interrupt normally.
+ *
+ * In most of the time, aontask is in halt state, and waiting for PMU wakeup
+ * interrupt to wakeup (reset prep interrupt also can wakeup aontask
+ * if CONFIG_ISH_PM_RESET_PREP defined), after wakeup, aontask will handle the
+ * low power states exit process and finaly switch back to main FW.
+ *
+ * aontask is running in the 32bit protection mode with flat memory segment
+ * settings, paging and cache are disabled (cache will be power gated).
+ *
+ * We use x86's hardware context switching mechanism for the switching of
+ * main FW and aontask.
+ * see https://wiki.osdev.org/Context_Switching
+ * https://en.wikipedia.org/wiki/Task_state_segment
+ *
+ */
+
+#include <common.h>
+#include <ia_structs.h>
+#include "power_mgt.h"
+#include "ish_aon_share.h"
+
+/**
+ * ISH aontask only need handle PMU wakeup interrupt and reset prep interrupt
+ * (if CONFIG_ISH_PM_RESET_PREP defined), before switch to aontask, all other
+ * interrupts should be masked. Since aontask is a seprated program from
+ * main FW, and the main SRAM will be power offed or will be put in in
+ * retention mode, aontask need its own IDT to handle PMU wakeup interrupt and
+ * reset prep interrupt (if CONFIG_ISH_PM_RESET_PREP defined)
+ *
+ * Due to very limit AON memory size (typically total 4KB), we don't want to
+ * define and allocate whole 256 entries for aontask'IDT, that almost need 2KB
+ * (256 * 8), so we just defined the only needed IDT entries:
+ * AON_IDT_ENTRY_VEC_FIRST ~ AON_IDT_ENTRY_VEC_LAST
+ */
+#define AON_IDT_ENTRY_VEC_FIRST ISH_PMU_WAKEUP_VEC
+
+#ifdef CONFIG_ISH_PM_RESET_PREP
+/**
+ * assume reset prep interrupt vector is greater than PMU wakeup interrupt
+ * vector, and also need handle reset prep interrupt
+ * (if CONFIG_ISH_PM_RESET_PREP defined)
+ */
+#define AON_IDT_ENTRY_VEC_LAST ISH_RESET_PREP_VEC
+#else
+/* only need handle single PMU wakeup interrupt */
+#define AON_IDT_ENTRY_VEC_LAST ISH_PMU_WAKEUP_VEC
+#endif
+
+static void handle_reset(void);
+
+/* ISR for PMU wakeup interrupt */
+static void pmu_wakeup_isr(void)
+{
+ /**
+ * Indicate completion of servicing the interrupt to IOAPIC first
+ * then indicate completion of servicing the interrupt to LAPIC
+ */
+ REG32(IOAPIC_EOI_REG) = ISH_PMU_WAKEUP_VEC;
+ REG32(LAPIC_EOI_REG) = 0x0;
+
+ __asm__ volatile ("iret;");
+
+ __builtin_unreachable();
+}
+
+#ifdef CONFIG_ISH_PM_RESET_PREP
+
+/* ISR for reset prep interrupt */
+static void reset_prep_isr(void)
+{
+ /* mask reset prep avail interrupt */
+ PMU_RST_PREP = PMU_RST_PREP_INT_MASK;
+
+ /**
+ * Indicate completion of servicing the interrupt to IOAPIC first
+ * then indicate completion of servicing the interrupt to LAPIC
+ */
+ REG32(IOAPIC_EOI_REG) = ISH_RESET_PREP_VEC;
+ REG32(LAPIC_EOI_REG) = 0x0;
+
+ handle_reset();
+
+ __builtin_unreachable();
+}
+
+#endif
+
+
+/**
+ * Use a static data array for aon IDT, and setting IDT header for IDTR
+ * register
+ *
+ * Due to very limit AON memory size (typically total 4KB), we don't want to
+ * define and allocate whole 256 entries for aontask'IDT, that almost need 2KB
+ * (256 * 8), so we just defined the only needed IDT entries:
+ * AON_IDT_ENTRY_VEC_FIRST ~ AON_IDT_ENTRY_VEC_LAST
+ *
+ * Since on x86, the IDT entry index (count from 0) is also the interrupt
+ * vector number, for IDT header, the 'start' filed still need to point to
+ * the entry 0, and 'size' must count from entry 0.
+ *
+ * We only allocated memory for entry AON_IDT_ENTRY_VEC_FIRST to
+ * AON_IDT_ENTRY_VEC_LAST, a little trick, but works well on ISH
+ *
+ * ------>---------------------------<----- aon_idt_hdr.start
+ * | | entry 0 |
+ * | +-------------------------+
+ * | | ... |
+ * | +-------------------------+<-----
+ * aon_idt_hdr.size | AON_IDT_ENTRY_VEC_FIRST | |
+ * | +-------------------------+ |
+ * | | ... | allocated memory in aon_idt
+ * | +-------------------------+ |
+ * | | AON_IDT_ENTRY_VEC_LAST | |
+ * ------>+-------------------------+<-----
+ * | ... |
+ * +-------------------------+
+ * | entry 255 |
+ * ---------------------------
+ */
+
+static struct idt_entry aon_idt[AON_IDT_ENTRY_VEC_LAST -
+ AON_IDT_ENTRY_VEC_FIRST + 1];
+
+static struct idt_header aon_idt_hdr = {
+
+ .limit = (sizeof(struct idt_entry) * (AON_IDT_ENTRY_VEC_LAST + 1)) - 1,
+ .entries = (struct idt_entry *)((uint32_t)&aon_idt -
+ (sizeof(struct idt_entry) * AON_IDT_ENTRY_VEC_FIRST))
+};
+
+/* aontask entry point function */
+void ish_aon_main(void);
+
+/**
+ * 8 bytes reserved on stack, just for GDB to show the correct stack
+ * information when doing source code level debuging
+ */
+#define AON_SP_RESERVED (8)
+
+/* TSS segment for aon task */
+static struct tss_entry aon_tss = {
+ .prev_task_link = 0,
+ .reserved1 = 0,
+ .esp0 = (uint8_t *)(CONFIG_ISH_AON_SRAM_ROM_START - AON_SP_RESERVED),
+ /* entry 1 in LDT for data segment */
+ .ss0 = 0xc,
+ .reserved2 = 0,
+ .esp1 = 0,
+ .ss1 = 0,
+ .reserved3 = 0,
+ .esp2 = 0,
+ .ss2 = 0,
+ .reserved4 = 0,
+ .cr3 = 0,
+ /* task excute entry point */
+ .eip = (uint32_t)&ish_aon_main,
+ .eflags = 0,
+ .eax = 0,
+ .ecx = 0,
+ .edx = 0,
+ .ebx = 0,
+ /* set stack top pointer at the end of usable aon memory */
+ .esp = CONFIG_ISH_AON_SRAM_ROM_START - AON_SP_RESERVED,
+ .ebp = AON_SP_RESERVED,
+ .esi = 0,
+ .edi = 0,
+ /* entry 1 in LDT for data segment */
+ .es = 0xc,
+ .reserved5 = 0,
+ /* entry 0 in LDT for code segment */
+ .cs = 0x4,
+ .reserved6 = 0,
+ /* entry 1 in LDT for data segment */
+ .ss = 0xc,
+ .reserved7 = 0,
+ /* entry 1 in LDT for data segment */
+ .ds = 0xc,
+ .reserved8 = 0,
+ /* entry 1 in LDT for data segment */
+ .fs = 0xc,
+ .reserved9 = 0,
+ /* entry 1 in LDT for data segment */
+ .gs = 0xc,
+ .reserved10 = 0,
+ .ldt_seg_selector = 0,
+ .reserved11 = 0,
+ .trap_debug = 0,
+
+ /**
+ * TSS's limit specified as 0x67, to allow the task has permission to
+ * access I/O port using IN/OUT instructions,'iomap_base_addr' field
+ * must be greater than or equal to TSS' limit
+ * see 'I/O port permissions' on
+ * https://en.wikipedia.org/wiki/Task_state_segment
+ */
+ .iomap_base_addr = GDT_DESC_TSS_LIMIT
+};
+
+/**
+ * define code and data LDT segements for aontask
+ * code : base = 0x0, limit = 0xFFFFFFFF, Present = 1, DPL = 0
+ * data : base = 0x0, limit = 0xFFFFFFFF, Present = 1, DPL = 0
+ */
+static ldt_entry aon_ldt[2] = {
+
+ /**
+ * entry 0 for code segment
+ * base: 0x0
+ * limit: 0xFFFFFFFF
+ * flag: 0x9B, Present = 1, DPL = 0, code segment
+ */
+ {
+ .dword_lo = GEN_GDT_DESC_LO(0x0, 0xFFFFFFFF,
+ GDT_DESC_CODE_FLAGS),
+
+ .dword_up = GEN_GDT_DESC_UP(0x0, 0xFFFFFFFF,
+ GDT_DESC_CODE_FLAGS)
+ },
+
+ /**
+ * entry 1 for data segment
+ * base: 0x0
+ * limit: 0xFFFFFFFF
+ * flag: 0x93, Present = 1, DPL = 0, data segment
+ */
+ {
+ .dword_lo = GEN_GDT_DESC_LO(0x0, 0xFFFFFFFF,
+ GDT_DESC_DATA_FLAGS),
+
+ .dword_up = GEN_GDT_DESC_UP(0x0, 0xFFFFFFFF,
+ GDT_DESC_DATA_FLAGS)
+ }
+};
+
+
+/* shared data structure between main FW and aon task */
+struct ish_aon_share aon_share = {
+ .magic_id = AON_MAGIC_ID,
+ .error_count = 0,
+ .last_error = AON_SUCCESS,
+ .aon_tss = &aon_tss,
+ .aon_ldt = &aon_ldt[0],
+ .aon_ldt_size = sizeof(aon_ldt),
+};
+
+static void handle_d0i2(void)
+{
+ /* TODO set main SRAM into retention mode*/
+
+ /* ish_halt(); */
+ /* wakeup from PMU interrupt */
+
+ /* TODO set main SRAM intto normal mode */
+}
+
+static void handle_d0i3(void)
+{
+ /* TODO store main FW 's context to IMR DDR from main SRAM */
+ /* TODO power off main SRAM */
+
+ /* ish_halt(); */
+ /* wakeup from PMU interrupt */
+
+ /* TODO power on main SRAM */
+ /* TODO restore main FW 's context to main SRAM from IMR DDR */
+}
+
+static void handle_d3(void)
+{
+ /* TODO store main FW 's context to IMR DDR from main sram */
+ /* TODO power off main SRAM */
+
+ /* TODO handle D3 */
+}
+
+static void handle_reset(void)
+{
+ /* TODO power off main SRAM */
+ /* TODO handle reset */
+}
+
+static void handle_unknown_state(void)
+{
+ aon_share.last_error = AON_ERROR_NOT_SUPPORT_POWER_MODE;
+ aon_share.error_count++;
+
+ /* switch back to main FW */
+}
+
+void ish_aon_main(void)
+{
+
+ /* set PMU wakeup interrupt gate using LDT code segment selector(0x4) */
+ aon_idt[0].dword_lo = GEN_IDT_DESC_LO(&pmu_wakeup_isr, 0x4,
+ IDT_DESC_FLAGS);
+
+ aon_idt[0].dword_up = GEN_IDT_DESC_UP(&pmu_wakeup_isr, 0x4,
+ IDT_DESC_FLAGS);
+#ifdef CONFIG_ISH_PM_RESET_PREP
+
+ /* set reset prep interrupt gate using LDT code segment selector(0x4) */
+ aon_idt[AON_IDT_ENTRY_VEC_LAST - AON_IDT_ENTRY_VEC_FIRST].dword_lo =
+ GEN_IDT_DESC_LO(&reset_prep_isr, 0x4, IDT_DESC_FLAGS);
+
+ aon_idt[AON_IDT_ENTRY_VEC_LAST - AON_IDT_ENTRY_VEC_FIRST].dword_up =
+ GEN_IDT_DESC_UP(&reset_prep_isr, 0x4, IDT_DESC_FLAGS);
+#endif
+
+ while (1) {
+
+ /**
+ * will start to run from here when switched to aontask from
+ * the second time
+ */
+
+ /* save main FW's IDT and load aontask's IDT */
+ __asm__ volatile (
+ "sidtl %0;\n"
+ "lidtl %1;\n"
+ :
+ : "m" (aon_share.main_fw_idt_hdr),
+ "m" (aon_idt_hdr)
+ );
+
+ aon_share.last_error = AON_SUCCESS;
+
+ switch (aon_share.pm_state) {
+ case ISH_PM_STATE_D0I2:
+ handle_d0i2();
+ break;
+ case ISH_PM_STATE_D0I3:
+ handle_d0i3();
+ break;
+ case ISH_PM_STATE_D3:
+ handle_d3();
+ break;
+ case ISH_PM_STATE_RESET_PREP:
+ handle_reset();
+ break;
+ default:
+ handle_unknown_state();
+ break;
+ }
+
+ /* restore main FW's IDT and switch back to main FW */
+ __asm__ volatile(
+ "lidtl %0;\n"
+ "iret;"
+ :
+ : "m" (aon_share.main_fw_idt_hdr)
+ );
+ }
+}
diff --git a/chip/ish/aontaskfw/ish_aontask.ld.in b/chip/ish/aontaskfw/ish_aontask.ld.in
new file mode 100644
index 0000000000..a45bbdb6a9
--- /dev/null
+++ b/chip/ish/aontaskfw/ish_aontask.ld.in
@@ -0,0 +1,68 @@
+/* Copyright 2019 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <config_chip.h>
+
+ENTRY(ish_aon_main);
+
+#define SRAM_START CONFIG_ISH_AON_SRAM_BASE_START
+#define SRAM_RW_LEN (CONFIG_ISH_AON_SRAM_SIZE - CONFIG_ISH_AON_SRAM_ROM_SIZE)
+
+/* reserved stack size */
+#define STACK_SIZE (256)
+
+/**
+ * resered 8 bytes for GDB showing correct stack
+ * information during source code level debuging
+ */
+#define RESERVED_GDB_SIZE (8)
+
+#define RAM_LEN (SRAM_RW_LEN - STACK_SIZE - RESERVED_GDB_SIZE)
+
+/**
+ * AON memory layout
+ * --------------+--------------+-------------------+-------------------+
+ * | RAM_LEN | STACK_SIZE | 8 Bytes for GDB | ROM(128 Bytes) |
+ * --------------+--------------+-------------------+-------------------+
+ */
+
+MEMORY
+{
+ /* leave STACK_SIZE bytes in the end of memory for stack */
+ RAM : ORIGIN = SRAM_START, LENGTH = RAM_LEN }
+
+SECTIONS
+{
+ /* AON parts visible to FW are linked to the beginning of the AON area */
+ .data.aon_share : AT(CONFIG_ISH_AON_SRAM_BASE_START)
+ {
+ KEEP(*(.data.aon_share))
+ } > RAM
+
+ .data :
+ {
+ *(.data)
+ *(.data*)
+ } > RAM
+
+ .text :
+ {
+ *(.text)
+ *(.text*)
+ } > RAM
+
+ .bss :
+ {
+ *(.bss)
+ *(.bss*)
+ } > RAM
+
+ .stack_tag :
+ {
+ KEEP(*(.stack_tag))
+ } > RAM
+
+
+}