ec_chip_mchp: Add MCHP chip folder

BRANCH=none
BUG=
TEST=Review only. Committing small pieces until
all code passes review.

Change-Id: I9d16f95314a7c97b11c4fe61602c6db2621e6024
Signed-off-by: Scott Worley <scott.worley@microchip.corp-partner.google.com>
This commit is contained in:
Scott Worley
2017-12-20 17:08:09 -05:00
committed by chrome-bot
parent c334f648bd
commit 4e9588ddcf
10 changed files with 4355 additions and 0 deletions

View File

@@ -40,6 +40,7 @@ COMMON_WARN = -Wall -Werror -Wundef -Wno-trigraphs -fno-strict-aliasing \
CFLAGS_WARN = $(COMMON_WARN) $(C_WARN)
CXXFLAGS_WARN = $(COMMON_WARN)
CFLAGS_DEBUG= -g
CFLAGS_DEBUG+=$(CFLAGS_DEBUG_CHIP)
CFLAGS_INCLUDE=$(foreach i,$(includes),-I$(i) ) -I.
CFLAGS_TEST=$(if $(TEST_BUILD),-DTEST_BUILD \
-DTEST_TASKFILE=$(PROJECT).tasklist,) \

92
chip/mchp/build.mk Normal file
View File

@@ -0,0 +1,92 @@
# -*- makefile -*-
# Copyright (c) 2013 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.
#
# Microchip(MCHP) MEC chip specific files build
#
# MCHP MEC SoC's have a Cortex-M4 ARM core
CORE:=cortex-m
# Allow the full Cortex-M4 instruction set
CFLAGS_CPU+=-march=armv7e-m -mcpu=cortex-m4
# JTAG debug with Keil ARM MDK debugger
# do not allow GCC dwarf debug extensions
#CFLAGS_DEBUG_EXTRA=-gdwarf-3 -gstrict-dwarf
LDFLAGS_EXTRA=
ifeq ($(CONFIG_LTO),y)
# Re-include the core's build.mk file so we can remove the lto flag.
include core/$(CORE)/build.mk
endif
# Required chip modules
chip-y=clock.o gpio.o hwtimer.o system.o uart.o port80.o tfdp.o
chip-$(CONFIG_ADC)+=adc.o
chip-$(CONFIG_DMA)+=dma.o
chip-$(CONFIG_ESPI)+=espi.o
chip-$(CONFIG_FANS)+=fan.o
chip-$(CONFIG_FLASH_PHYSICAL)+=flash.o
chip-$(CONFIG_I2C)+=i2c.o
chip-$(CONFIG_MEC_GPIO_EC_CMDS)+=gpio_cmds.o
chip-$(CONFIG_LPC)+=lpc.o
chip-$(CONFIG_MCHP_GPSPI)+=gpspi.o
chip-$(CONFIG_PWM)+=pwm.o
chip-$(CONFIG_SPI)+=spi.o qmspi.o
chip-$(CONFIG_TFDP)+=tfdp.o
chip-$(CONFIG_WATCHDOG)+=watchdog.o
chip-$(HAS_TASK_KEYSCAN)+=keyboard_raw.o
# location of the scripts and keys used to pack the SPI flash image
SCRIPTDIR:=./chip/${CHIP}/util
# Allow SPI size to be overridden by board specific size, default to 512KB
CHIP_SPI_SIZE_KB?=512
TEST_SPI=
ifeq ($(CONFIG_MCHP_LFW_DEBUG),y)
TEST_SPI=--test_spi
endif
# pack_ec.py creates SPI flash image for MEC
# _rw_size is CONFIG_RW_SIZE
# Commands to convert $^ to $@.tmp
cmd_obj_to_bin = $(OBJCOPY) --gap-fill=0xff -O binary $< $@.tmp1 ; \
${SCRIPTDIR}/pack_ec.py -o $@.tmp -i $@.tmp1 \
--loader_file $(chip-lfw-flat) ${TEST_SPI} \
--spi_size ${CHIP_SPI_SIZE_KB} \
--image_size $(_rw_size) ; rm -f $@.tmp1
chip-lfw = chip/${CHIP}/lfw/ec_lfw
chip-lfw-flat = $(out)/RW/$(chip-lfw)-lfw.flat
# build these specifically for lfw with -lfw suffix
objs_lfw = $(patsubst %, $(out)/RW/%-lfw.o, \
$(addprefix common/, util gpio) \
$(addprefix chip/$(CHIP)/, spi qmspi dma gpio clock hwtimer tfdp) \
core/$(CORE)/cpu $(chip-lfw))
# reuse version.o (and its dependencies) from main board
objs_lfw += $(out)/RW/common/version.o
dirs-y+=chip/$(CHIP)/lfw
# objs with -lfw suffix are to include lfw's gpio
$(out)/RW/%-lfw.o: private CC+=-I$(BDIR)/lfw -DLFW
# Remove the lto flag for the loader. It actually causes it to bloat in size.
ifeq ($(CONFIG_LTO),y)
$(out)/RW/%-lfw.o: private CFLAGS_CPU := $(filter-out -flto, $(CFLAGS_CPU))
endif
$(out)/RW/%-lfw.o: %.c
$(call quiet,c_to_o,CC )
# let lfw's elf link only with selected objects
$(out)/RW/%-lfw.elf: private objs = $(objs_lfw)
$(out)/RW/%-lfw.elf: override shlib :=
$(out)/RW/%-lfw.elf: %.ld $(objs_lfw)
$(call quiet,elf,LD )
# final image needs lfw loader
$(out)/$(PROJECT).bin: $(chip-lfw-flat)

763
chip/mchp/clock.c Normal file
View File

@@ -0,0 +1,763 @@
/* Copyright 2017 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.
*/
/* Clocks and power management settings */
#include "clock.h"
#include "common.h"
#include "console.h"
#include "cpu.h"
#include "hooks.h"
#include "hwtimer.h"
#include "pwm.h"
#include "pwm_chip.h"
#include "registers.h"
#include "shared_mem.h"
#include "system.h"
#include "task.h"
#include "timer.h"
#include "uart.h"
#include "util.h"
#include "tfdp_chip.h"
#include "vboot_hash.h"
/* Console output macros */
#define CPUTS(outstr) cputs(CC_CLOCK, outstr)
#define CPRINTS(format, args...) cprints(CC_CLOCK, format, ## args)
#ifdef CONFIG_LOW_POWER_IDLE
#define HTIMER_DIV_1_US_MAX (1998848)
#define HTIMER_DIV_1_1SEC (0x8012)
/* Recovery time for HvySlp2 is 0 usec */
#define HEAVY_SLEEP_RECOVER_TIME_USEC 75
#define SET_HTIMER_DELAY_USEC 200
static int idle_sleep_cnt;
static int idle_dsleep_cnt;
static uint64_t total_idle_dsleep_time_us;
#ifdef CONFIG_MCHP_DEEP_SLP_DEBUG
static uint32_t pcr_slp_en[MCHP_PCR_SLP_RST_REG_MAX];
static uint32_t pcr_clk_req[MCHP_PCR_SLP_RST_REG_MAX];
static uint32_t ecia_result[MCHP_INT_GIRQ_NUM];
#endif
/*
* Fixed amount of time to keep the console in use flag true after
* boot in order to give a permanent window in which the heavy sleep
* mode is not used.
*/
#define CONSOLE_IN_USE_ON_BOOT_TIME (15*SECOND)
static int console_in_use_timeout_sec = 60;
static timestamp_t console_expire_time;
#endif /*CONFIG_LOW_POWER_IDLE */
static int freq = 48000000;
void clock_wait_cycles(uint32_t cycles)
{
asm volatile("1: subs %0, #1\n"
" bne 1b\n" : "+r"(cycles));
}
int clock_get_freq(void)
{
return freq;
}
/** clock_init
* @note
* MCHP MEC implements 4 control bits in the VBAT Clock Enable register.
* It also implements an internal silicon 32KHz +/- 2% oscillator powered
* by VBAT.
* b[3] = XOSEL 0=parallel, 1=single-ended
* b[2] = 32KHZ_SOURCE specifies source of always-on clock domain
* 0=internal silicon oscillator
* 1=crystal XOSEL pin(s)
* b[1] = EXT_32K use always-on clock domain or external 32KHZ_IN pin
* 0=32K source is always-on clock domain
* 1=32K source is 32KHZ_IN pin (GPIO 0165)
* b[0] = 32K_SUPPRESS
* 0=32K clock domain stays enabled if VTR is off. Powered by VBAT
* 1=32K clock domain is disabled if VTR is off.
* Set b[3] based on CONFIG_CLOCK_CRYSTAL
* Set b[2:0] = 100b
* b[0]=0 32K clock domain always on (requires VBAT if VTR is off)
* b[1]=0 32K source is the 32K clock domain NOT the 32KHZ_IN pin
* b[2]=1 If activity detected on crystal pins switch 32K input from
* internal silicon oscillator to XOSEL pin(s) based on b[3].
*/
void clock_init(void)
{
int __attribute__((unused)) dummy;
trace0(0, MEC, 0, "Clock Init");
#ifdef CONFIG_CLOCK_CRYSTAL
/* XOSEL: 0 = Parallel resonant crystal */
MCHP_VBAT_CE &= ~(1ul << 3);
#else
/* XOSEL: 1 = Single ended clock source */
MCHP_VBAT_CE |= (1ul << 3);
#endif
/* 32K clock enable */
MCHP_VBAT_CE = (MCHP_VBAT_CE & ~(0x03)) | (1ul << 2);
#ifdef CONFIG_CLOCK_CRYSTAL
/* Wait for crystal to stabilize (OSC_LOCK == 1) */
while (!(MCHP_PCR_CHIP_OSC_ID & 0x100))
;
#endif
trace0(0, MEC, 0, "PLL OSC is Locked");
#ifndef LFW
dummy = shared_mem_size();
trace11(0, MEC, 0, "Shared Memory size = 0x%08x", (uint32_t)dummy);
#endif
}
/**
* Speed through boot + vboot hash calculation, dropping our processor
* clock only after vboot hashing is completed.
*/
static void clock_turbo_disable(void);
DECLARE_DEFERRED(clock_turbo_disable);
static void clock_turbo_disable(void)
{
#ifdef CONFIG_VBOOT_HASH
if (vboot_hash_in_progress())
hook_call_deferred(&clock_turbo_disable_data, 100 * MSEC);
else
#endif
/* Use 12 MHz processor clock for power savings */
MCHP_PCR_PROC_CLK_CTL = 4;
}
DECLARE_HOOK(HOOK_INIT,
clock_turbo_disable,
HOOK_PRIO_INIT_VBOOT_HASH + 1);
#ifdef CONFIG_LOW_POWER_IDLE
/**
* initialization of Hibernation timer0
* Clear PCR sleep enable.
* GIRQ=21, aggregator bit = 1, Direct NVIC = 112
* NVIC direct connect interrupts are used for all peripherals
* (exception GPIO's) then the MCHP_INT_BLK_EN GIRQ bit should not be
* set.
*/
static void htimer_init(void)
{
MCHP_PCR_SLP_DIS_DEV(MCHP_PCR_HTMR0);
MCHP_INT_ENABLE(MCHP_HTIMER_GIRQ) =
MCHP_HTIMER_GIRQ_BIT(0);
MCHP_HTIMER_PRELOAD(0) = 0; /* disable at beginning */
task_enable_irq(MCHP_IRQ_HTIMER0);
}
/**
* Use hibernate module to set up an htimer interrupt at a given
* time from now
*
* @param seconds Number of seconds before htimer interrupt
* @param microseconds Number of microseconds before htimer interrupt
* @note hibernation timer input clock is 32.768KHz and has two
* divider values.
* Control register bit[0] selects the divider.
* 0 is divide by 1 for 30.5us per LSB for a maximum of ~2 seconds.
* 1 is divide by 4096 for 0.125s per LSB for a maximum of ~2 hours.
*/
static void system_set_htimer_alarm(uint32_t seconds,
uint32_t microseconds)
{
uint32_t hcnt;
if (microseconds >= 1000000) {
seconds += microseconds / 1000000;
microseconds -= (microseconds / 1000000) * 1000000;
}
if (seconds || microseconds) {
/* if (seconds > 2) { */
if (seconds > 1) {
/* count from 2 sec to 2 hrs, mec1322 sec 18.10.2 */
ASSERT(seconds <= 0xffff / 8);
/* 0.125(=1/8) per clock */
MCHP_HTIMER_CONTROL(0) = 1;
/* (number of counts to be loaded)
* = seconds * ( 8 clocks per second )
* + microseconds / 125000
* ---> (0 if (microseconds < 125000)
*/
hcnt = (seconds * 8 + microseconds / 125000);
} else { /* count up to 2 sec. */
/* 30.5(= 2/61) usec */
MCHP_HTIMER_CONTROL(0) = 0;
/* (number of counts to be loaded)
* = (total microseconds) / 30.5;
*/
hcnt = (seconds * 1000000 + microseconds) *
2 / 61;
}
MCHP_HTIMER_PRELOAD(0) = hcnt;
}
}
/**
* return time slept in micro-seconds
*/
static timestamp_t system_get_htimer(void)
{
uint16_t count;
timestamp_t time;
count = MCHP_HTIMER_COUNT(0);
if (MCHP_HTIMER_CONTROL(0) == 1) /* if > 2 sec */
/* 0.125 sec per count */
time.le.lo = (uint32_t)(count * 125000);
else /* if < 2 sec */
/* 30.5(=61/2)usec per count */
time.le.lo = (uint32_t)(count * 61 / 2);
time.le.hi = 0;
return time; /* in uSec */
}
/**
* Disable and clear hibernation timer interrupt
*/
static void system_reset_htimer_alarm(void)
{
MCHP_HTIMER_PRELOAD(0) = 0;
MCHP_INT_SOURCE(MCHP_HTIMER_GIRQ) =
MCHP_HTIMER_GIRQ_BIT(0);
}
#ifdef CONFIG_MCHP_DEEP_SLP_DEBUG
static void print_pcr_regs(void)
{
int i;
trace0(0, MEC, 0, "Current PCR registers");
for (i = 0; i < 5; i++) {
trace12(0, MEC, 0, "REG SLP_EN[%d] = 0x%08X",
i, MCHP_PCR_SLP_EN(i));
trace12(0, MEC, 0, "REG CLK_REQ[%d] = 0x%08X",
i, MCHP_PCR_CLK_REQ(i));
}
}
static void print_ecia_regs(void)
{
int i;
trace0(0, MEC, 0, "Current GIRQn.Result registers");
for (i = MCHP_INT_GIRQ_FIRST;
i <= MCHP_INT_GIRQ_LAST; i++)
trace12(0, MEC, 0, "GIRQ[%d].Result = 0x%08X",
i, MCHP_INT_RESULT(i));
}
static void save_regs(void)
{
int i;
for (i = 0; i < MCHP_PCR_SLP_RST_REG_MAX; i++) {
pcr_slp_en[i] = MCHP_PCR_SLP_EN(i);
pcr_clk_req[i] = MCHP_PCR_CLK_REQ(i);
}
for (i = 0; i < MCHP_INT_GIRQ_NUM; i++)
ecia_result[i] =
MCHP_INT_RESULT(MCHP_INT_GIRQ_FIRST + i);
}
static void print_saved_regs(void)
{
int i;
trace0(0, BRD, 0, "Before sleep saved registers");
for (i = 0; i < MCHP_PCR_SLP_RST_REG_MAX; i++) {
trace12(0, BRD, 0, "PCR_SLP_EN[%d] = 0x%08X",
i, pcr_slp_en[i]);
trace12(0, BRD, 0, "PCR_CLK_REQ[%d] = 0x%08X",
i, pcr_clk_req[i]);
}
for (i = 0; i < MCHP_INT_GIRQ_NUM; i++)
trace12(0, BRD, 0, "GIRQ[%d].Result = 0x%08X",
(i+MCHP_INT_GIRQ_FIRST), ecia_result[i]);
}
#endif /* #ifdef CONFIG_MCHP_DEEP_SLP_DEBUG */
/**
* This is MCHP specific and equivalent to ARM Cortex's
* 'DeepSleep' via system control block register, CPU_SCB_SYSCTRL
* MCHP has new SLP_ALL feature.
* When SLP_ALL is enabled and HW sees sleep entry trigger from CPU.
* 1. HW saves PCR.SLP_EN registers
* 2. HW sets all PCR.SLP_EN bits to 1.
* 3. System sleeps
* 4. wake event wakes system
* 5. HW restores original values of all PCR.SLP_EN registers
* NOTE1: Current RTOS core (Cortex-Mx) does not use SysTick timer.
* We can leave code to disable it but do not re-enable on wake.
* NOTE2: Some peripherals will not sleep until outstanding transactions
* are complete: I2C, DMA, GPSPI, QMSPI, etc.
* NOTE3: Security blocks do not fully implement HW sleep therefore their
* sleep enables must be manually set/restored.
*
*/
static void prepare_for_deep_sleep(void)
{
trace0(0, MEC, 0, "Prepare for Deep Sleep");
/* sysTick timer */
CPU_NVIC_ST_CTRL &= ~ST_ENABLE;
CPU_NVIC_ST_CTRL &= ~ST_COUNTFLAG;
CPU_NVIC_ST_CTRL &= ~ST_TICKINT; /* SYS_TICK_INT_DISABLE */
/* Enable assertion of DeepSleep signals
* from the core when core enters sleep.
*/
CPU_SCB_SYSCTRL |= (1 << 2);
/* Stop timers */
MCHP_TMR32_CTL(0) &= ~1;
MCHP_TMR32_CTL(1) &= ~1;
MCHP_TMR16_CTL(0) &= ~1;
MCHP_INT_DISABLE(MCHP_TMR32_GIRQ) =
MCHP_TMR32_GIRQ_BIT(0) +
MCHP_TMR32_GIRQ_BIT(1);
MCHP_INT_SOURCE(MCHP_TMR32_GIRQ) =
MCHP_TMR32_GIRQ_BIT(0) +
MCHP_TMR32_GIRQ_BIT(1);
MCHP_INT_DISABLE(MCHP_TMR16_GIRQ) =
MCHP_TMR16_GIRQ_BIT(0);
MCHP_INT_SOURCE(MCHP_TMR16_GIRQ) =
MCHP_TMR16_GIRQ_BIT(0);
#ifdef CONFIG_WATCHDOG
/* Stop watchdog */
MCHP_WDG_CTL &= ~1;
#endif
#ifdef CONFIG_ESPI
#ifdef CONFIG_POWER_S0IX
MCHP_INT_SOURCE(22) = MCHP_INT22_WAKE_ONLY_ESPI;
MCHP_INT_ENABLE(22) = MCHP_INT22_WAKE_ONLY_ESPI;
#else
MCHP_ESPI_ACTIVATE &= ~1;
#endif
#else
#ifdef CONFIG_POWER_S0IX
MCHP_INT_SOURCE(22) = MCHP_INT22_WAKE_ONLY_LPC;
MCHP_INT_ENABLE(22) = MCHP_INT22_WAKE_ONLY_LPC;
#else
MCHP_LPC_ACT |= 1;
#endif
#endif
#ifdef CONFIG_ADC
/*
* Clear ADC activate bit. If a conversion is in progress the
* ADC block will not enter low power until the converstion is
* complete.
*/
MCHP_ADC_CTRL &= ~1;
#endif
/* stop Port80 capture timer */
MCHP_P80_ACTIVATE(0) = 0;
/*
* Clear SLP_EN bit(s) for wake sources.
* Currently only Hibernation timer 0.
* GPIO pins can always wake.
*/
MCHP_PCR_SLP_EN3 &= ~(MCHP_PCR_SLP_EN3_HTMR0);
#ifdef CONFIG_PWM
pwm_keep_awake(); /* clear sleep enables of active PWM's */
#else
/* Disable 100 Khz clock */
MCHP_PCR_SLOW_CLK_CTL &= 0xFFFFFC00;
#endif
#ifdef CONFIG_CHIPSET_DEBUG
/* Disable JTAG and preserve mode */
/* MCHP_EC_JTAG_EN &= ~(MCHP_JTAG_ENABLE); */
#endif
/* call board level */
#ifdef CONFIG_BOARD_DEEP_SLEEP
board_prepare_for_deep_sleep();
#endif
#ifdef CONFIG_MCHP_DEEP_SLP_DEBUG
save_regs();
#endif
}
static void resume_from_deep_sleep(void)
{
trace0(0, MEC, 0, "resume_from_deep_sleep");
MCHP_PCR_SYS_SLP_CTL = 0x00; /* default */
/* Disable assertion of DeepSleep signal when core executes WFI */
CPU_SCB_SYSCTRL &= ~(1 << 2);
#ifdef CONFIG_MCHP_DEEP_SLP_DEBUG
print_saved_regs();
print_pcr_regs();
print_ecia_regs();
#endif
#ifdef CONFIG_CHIPSET_DEBUG
MCHP_EC_JTAG_EN |= (MCHP_JTAG_ENABLE);
#endif
MCHP_PCR_SLOW_CLK_CTL |= 0x1e0;
/* call board level */
#ifdef CONFIG_BOARD_DEEP_SLEEP
board_resume_from_deep_sleep();
#endif
/*
* re-enable hibernation timer 0 PCR.SLP_EN to
* reduce power.
*/
MCHP_PCR_SLP_EN3 |= (MCHP_PCR_SLP_EN3_HTMR0);
#ifdef CONFIG_ESPI
#ifdef CONFIG_POWER_S0IX
MCHP_INT_DISABLE(22) = MCHP_INT22_WAKE_ONLY_ESPI;
MCHP_INT_SOURCE(22) = MCHP_INT22_WAKE_ONLY_ESPI;
#else
MCHP_ESPI_ACTIVATE |= 1;
#endif
#else
#ifdef CONFIG_POWER_S0IX
MCHP_INT_DISABLE(22) = MCHP_INT22_WAKE_ONLY_LPC;
MCHP_INT_SOURCE(22) = MCHP_INT22_WAKE_ONLY_LPC;
#else
MCHP_LPC_ACT |= 1;
#endif
#endif
/* re-enable Port80 capture */
MCHP_P80_ACTIVATE(0) = 1;
#ifdef CONFIG_ADC
MCHP_ADC_CTRL |= 1;
#endif
/* Enable timer */
MCHP_TMR32_CTL(0) |= 1;
MCHP_TMR32_CTL(1) |= 1;
MCHP_TMR16_CTL(0) |= 1;
MCHP_INT_ENABLE(MCHP_TMR32_GIRQ) =
MCHP_TMR32_GIRQ_BIT(0) +
MCHP_TMR32_GIRQ_BIT(1);
MCHP_INT_ENABLE(MCHP_TMR16_GIRQ) =
MCHP_TMR16_GIRQ_BIT(0);
/* Enable watchdog */
#ifdef CONFIG_WATCHDOG
#ifdef CONFIG_CHIPSET_DEBUG
/* enable WDG stall on active JTAG and do not start */
MCHP_WDG_CTL = (1 << 4);
#else
MCHP_WDG_CTL |= 1;
#endif
#endif
}
void clock_refresh_console_in_use(void)
{
disable_sleep(SLEEP_MASK_CONSOLE);
/* Set console in use expire time. */
console_expire_time = get_time();
console_expire_time.val += console_in_use_timeout_sec * SECOND;
}
/**
* Low power idle task. Executed when no tasks are ready to be scheduled.
*/
void __idle(void)
{
timestamp_t t0;
timestamp_t t1;
timestamp_t ht_t1;
uint32_t next_delay;
uint32_t max_sleep_time;
int time_for_dsleep;
int uart_ready_for_deepsleep;
htimer_init(); /* hibernation timer initialize */
disable_sleep(SLEEP_MASK_CONSOLE);
console_expire_time.val = get_time().val +
CONSOLE_IN_USE_ON_BOOT_TIME;
/*
* Print when the idle task starts. This is the lowest priority
* task, so this only starts once all other tasks have gotten a
* chance to do their task inits and have gone to sleep.
*/
CPRINTS("MEC1701 low power idle task started");
while (1) {
/* Disable interrupts */
interrupt_disable();
t0 = get_time(); /* uSec */
/* __hw_clock_event_get() is next programmed timer event */
next_delay = __hw_clock_event_get() - t0.le.lo;
time_for_dsleep = next_delay >
(HEAVY_SLEEP_RECOVER_TIME_USEC +
SET_HTIMER_DELAY_USEC);
max_sleep_time = next_delay -
HEAVY_SLEEP_RECOVER_TIME_USEC;
/* check if there enough time for deep sleep */
if (DEEP_SLEEP_ALLOWED && time_for_dsleep) {
trace0(0, MEC, 0, "Enough time for Deep Sleep");
/*
* Check if the console use has expired and
* console sleep is masked by GPIO(UART-RX)
* interrupt.
*/
if ((sleep_mask & SLEEP_MASK_CONSOLE) &&
t0.val > console_expire_time.val) {
/* allow console to sleep. */
enable_sleep(SLEEP_MASK_CONSOLE);
/*
* Wait one clock before checking if
* heavy sleep is allowed to give time
* for sleep mask to be updated.
*/
clock_wait_cycles(1);
if (LOW_SPEED_DEEP_SLEEP_ALLOWED)
CPRINTS("MEC1701 Disable console "
"in deepsleep");
}
/* UART is not being used */
uart_ready_for_deepsleep =
LOW_SPEED_DEEP_SLEEP_ALLOWED &&
!uart_tx_in_progress() &&
uart_buffer_empty();
/*
* Since MCHP's heavy sleep mode requires all
* blocks to be sleepable, UART/console's
* readiness is final decision factor of
* heavy sleep of EC.
*/
if (uart_ready_for_deepsleep) {
idle_dsleep_cnt++;
/*
* config UART Rx as GPIO wakeup
* interrupt source
*/
uart_enter_dsleep();
/* MCHP specific deep-sleep mode */
prepare_for_deep_sleep();
/*
* 'max_sleep_time' value should be big
* enough so that hibernation timer's
* interrupt triggers only after 'wfi'
* completes its excution.
*/
max_sleep_time -=
(get_time().le.lo - t0.le.lo);
/* setup/enable htimer wakeup interrupt */
system_set_htimer_alarm(0,
max_sleep_time);
/* set sleep all just before WFI */
MCHP_PCR_SYS_SLP_CTL |=
MCHP_PCR_SYS_SLP_HEAVY;
MCHP_PCR_SYS_SLP_CTL |=
MCHP_PCR_SYS_SLP_ALL;
} else {
idle_sleep_cnt++;
}
/* Wait for interrupt: goes into deep sleep. */
asm("dsb");
asm("wfi");
asm("isb");
asm("nop");
if (uart_ready_for_deepsleep) {
resume_from_deep_sleep();
/*
* Fast forward timer according to htimer
* counter:
* Since all blocks including timers
* will be in sleep mode, timers stops
* except hibernate timer.
* And system schedule timer should be
* corrected after wakeup by either
* hibernate timer or GPIO_UART_RX
* interrupt.
*/
ht_t1 = system_get_htimer();
/* disable/clear htimer wakeup interrupt */
system_reset_htimer_alarm();
t1.val = t0.val +
(uint64_t)(max_sleep_time -
ht_t1.le.lo);
force_time(t1);
/* re-eanble UART */
uart_exit_dsleep();
/* Record time spent in deep sleep. */
total_idle_dsleep_time_us +=
(uint64_t)(max_sleep_time -
ht_t1.le.lo);
}
} else { /* CPU 'Sleep' mode */
idle_sleep_cnt++;
asm("wfi");
}
interrupt_enable();
} /* while(1) */
}
#ifdef CONFIG_CMD_IDLE_STATS
/**
* Print low power idle statistics
*/
#ifdef CONFIG_MCHP_DEEP_SLP_DEBUG
static void print_pcr_regs(void)
{
int i;
ccprintf("PCR regs before WFI\n");
for (i = 0; i < 5; i++) {
ccprintf("PCR SLP_EN[%d] = 0x%08X\n", pcr_slp_en[i]);
ccprintf("PCR CLK_REQ[%d] = 0x%08X\n", pcr_clk_req[i]);
}
}
#endif
static int command_idle_stats(int argc, char **argv)
{
timestamp_t ts = get_time();
ccprintf("Num idle calls that sleep: %d\n",
idle_sleep_cnt);
ccprintf("Num idle calls that deep-sleep: %d\n",
idle_dsleep_cnt);
ccprintf("Total Time spent in deep-sleep(sec): %.6ld(s)\n",
total_idle_dsleep_time_us);
ccprintf("Total time on: %.6lds\n\n",
ts.val);
#ifdef CONFIG_MCHP_DEEP_SLP_DEBUG
print_pcr_regs(); /* debug */
#endif
return EC_SUCCESS;
}
DECLARE_CONSOLE_COMMAND(idlestats, command_idle_stats,
"",
"Print last idle stats");
#endif /* defined(CONFIG_CMD_IDLE_STATS) */
/**
* Configure deep sleep clock settings.
*/
static int command_dsleep(int argc, char **argv)
{
int v;
if (argc > 1) {
if (parse_bool(argv[1], &v)) {
/*
* Force deep sleep not to use heavy sleep mode or
* allow it to use the heavy sleep mode.
*/
if (v) /* 'on' */
disable_sleep(
SLEEP_MASK_FORCE_NO_LOW_SPEED);
else /* 'off' */
enable_sleep(
SLEEP_MASK_FORCE_NO_LOW_SPEED);
} else {
/* Set console in use timeout. */
char *e;
v = strtoi(argv[1], &e, 10);
if (*e)
return EC_ERROR_PARAM1;
console_in_use_timeout_sec = v;
/* Refresh console in use to use new timeout. */
clock_refresh_console_in_use();
}
}
ccprintf("Sleep mask: %08x\n", sleep_mask);
ccprintf("Console in use timeout: %d sec\n",
console_in_use_timeout_sec);
return EC_SUCCESS;
}
DECLARE_CONSOLE_COMMAND(dsleep, command_dsleep,
"[ on | off | <timeout> sec]",
"Deep sleep clock settings:\nUse 'on' to force deep "
"sleep NOT to enter heavysleep mode.\nUse 'off' to "
"allow deepsleep to use heavysleep whenever conditions "
"allow.\n"
"Give a timeout value for the console in use timeout.\n"
"See also 'sleepmask'.");
#endif /* CONFIG_LOW_POWER_IDLE */

191
chip/mchp/config_chip.h Normal file
View File

@@ -0,0 +1,191 @@
/* Copyright 2017 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_CONFIG_CHIP_H
#define __CROS_EC_CONFIG_CHIP_H
/* CPU core BFD configuration */
#include "core/cortex-m/config_core.h"
/* Number of IRQ vectors on the NVIC */
#define CONFIG_IRQ_COUNT 157
/* Use a bigger console output buffer */
#undef CONFIG_UART_TX_BUF_SIZE
#define CONFIG_UART_TX_BUF_SIZE 2048
/* Interval between HOOK_TICK notifications */
#define HOOK_TICK_INTERVAL_MS 250
#define HOOK_TICK_INTERVAL (HOOK_TICK_INTERVAL_MS * MSEC)
/*
* Enable chip_pre_init called from main
* Used for configuring peripheral block
* sleep enables.
*/
#define CONFIG_CHIP_PRE_INIT
/*
* MCHP EC's have I2C master/slave
* controllers and multiple I2C ports. Any
* port may be mapped to any controller.
* Enable multi-port controller feature.
* Board level configuration determines
* how many controllers/ports are used and
* the mapping of port(s) to controller(s).
* NOTE: Some MCHP reduced pin packages
* may not implement all 11 I2C ports.
*/
#define CONFIG_I2C_MULTI_PORT_CONTROLLER
/*
* MCHP I2C controller is master-slave capable and requires
* a slave address be programmed even if used as master only.
* Each I2C controller can respond to two slave address.
* Define dummy slave addresses that aren't used on the I2C port(s)
* connected to each controller.
*/
#define CONFIG_MCHP_I2C0_SLAVE_ADDRS 0xE3E1
#define CONFIG_MCHP_I2C1_SLAVE_ADDRS 0xE3E1
#define CONFIG_MCHP_I2C2_SLAVE_ADDRS 0xE3E1
#define CONFIG_MCHP_I2C3_SLAVE_ADDRS 0xE3E1
/************************************************************************/
/* Memory mapping */
/*
* MEC1701H has a total of 256KB SRAM.
* CODE at 0xE0000 - 0x117FFF, DATA at 0x118000 - 0x11FFFF
* MCHP MEC can fetch code from data or data from code.
*/
/************************************************************************/
/* Define our RAM layout. */
#define CONFIG_MEC_SRAM_BASE_START 0x000E0000
#define CONFIG_MEC_SRAM_BASE_END 0x00120000
#define CONFIG_MEC_SRAM_SIZE (CONFIG_MEC_SRAM_BASE_END - \
CONFIG_MEC_SRAM_BASE_START)
/* 32k RAM for RO / RW / loader */
#define CONFIG_RAM_SIZE 0x00008000
#define CONFIG_RAM_BASE (CONFIG_MEC_SRAM_BASE_END - \
CONFIG_RAM_SIZE)
/* System stack size */
#define CONFIG_STACK_SIZE 1024
/* non-standard task stack sizes */
#define IDLE_TASK_STACK_SIZE 512
#define LARGER_TASK_STACK_SIZE 640
#define VENTI_TASK_STACK_SIZE 768
#define CHARGER_TASK_STACK_SIZE 640
#define HOOKS_TASK_STACK_SIZE 640
#define CONSOLE_TASK_STACK_SIZE 640
#define HOST_CMD_TASK_STACK_SIZE 640
/*
* TODO: Large stack consumption
* https://code.google.com/p/chrome-os-partner/issues/detail?id=49245
*/
#define PD_TASK_STACK_SIZE 800
/* Default task stack size */
#define TASK_STACK_SIZE 512
/************************************************************************/
/* Define our flash layout. */
/* Protect bank size 4K bytes */
#define CONFIG_FLASH_BANK_SIZE 0x00001000
/* Sector erase size 4K bytes */
#define CONFIG_FLASH_ERASE_SIZE 0x00001000
/* Minimum write size */
#define CONFIG_FLASH_WRITE_SIZE 0x00000004
/* One page size for write */
#define CONFIG_FLASH_WRITE_IDEAL_SIZE 256
/* Program memory base address */
#define CONFIG_PROGRAM_MEMORY_BASE 0x000E0000
#include "config_flash_layout.h"
/************************************************************************/
/* Customize the build */
/* Optional features present on this chip */
#define CONFIG_ADC
#define CONFIG_DMA
#define CONFIG_LPC
#define CONFIG_SPI
#define CONFIG_SWITCH
/*
* Enable configuration after ESPI_RESET# de-asserts
*/
#undef CONFIG_MCHP_ESPI_RESET_DEASSERT_INIT
/*
* Enable CPRINT in chip eSPI module
* Define at board level.
*/
#undef CONFIG_MCHP_ESPI_DEBUG
/*
* Enable EC UART commands in eSPI module useful for debugging.
*/
#undef CONFIG_MCHP_ESPI_EC_CMD
/*
* Define this to use MEC1701 ROM SPI read API
* in little firmware module instead of SPI code
* from this module
*/
#undef CONFIG_CHIP_LFW_USE_ROM_SPI
/*
* Use DMA when transmitting commands & data
* with GPSPI controllers.
*/
#define CONFIG_MCHP_GPSPI_TX_DMA
/*
* Use DMA when transmitting command & data of length
* greater than QMSPI TX FIFO size.
*/
#define CONFIG_MCHP_QMSPI_TX_DMA
/*
* Board level gpio.inc is using MCHP data sheet GPIO pin
* numbers which are octal.
* MCHP has 6 banks/ports each containing 32 GPIO's.
* Each bank/port is connected to a GIRQ.
* Port numbering:
* GPIO_015 = 13 decimal. Port = 13/32 = 0, bit = 13 % 32 = 13
* GPIO_0123 = 83 decimal. Port 83/32 = 2, bit = 83 % 32 = 19
* OR port = 0123 >> 5, bit = 0123 & 037(0x1F) = 023 = 19 decimal.
* You must use octal GPIO numbers in PIN(gpio_num) macro in
* gpio.inc files.
* Example: GPIO 211 in documentation 0211 = 137 = 0x89
* GPIO(PCH_SLP_S0_L, PIN(0211), GPIO_INPUT | GPIO_PULL_DOWN)
* OR
* GPIO(PCH_SLP_S0_L, PIN(0x89), GPIO_INPUT | GPIO_PULL_DOWN)
*/
#define GPIO_BANK(index) ((index) >> 5)
#define GPIO_BANK_MASK(index) (1ul << ((index) & 0x1F))
#define GPIO_PIN(index) GPIO_BANK(index), GPIO_BANK_MASK(index)
#define GPIO_PIN_MASK(bank, mask) (bank), (mask)
#ifndef __ASSEMBLER__
#endif /* #ifndef __ASSEMBLER__ */
#endif /* __CROS_EC_CONFIG_CHIP_H */

419
chip/mchp/gpio.c Normal file
View File

@@ -0,0 +1,419 @@
/* Copyright 2017 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.
*/
/* GPIO module for MCHP MEC */
#include "common.h"
#include "gpio.h"
#include "hooks.h"
#include "registers.h"
#include "system.h"
#include "task.h"
#include "timer.h"
#include "util.h"
#include "lpc_chip.h"
#include "tfdp_chip.h"
/* Console output macros */
#define CPUTS(outstr) cputs(CC_LPC, outstr)
#define CPRINTS(format, args...) cprints(CC_LPC, format, ## args)
struct gpio_int_mapping {
int8_t girq_id;
int8_t port_offset;
};
/* TODO will this mapping change for other MEC chips?
* Mapping from GPIO port to GIRQ info
* MEC1701 each bank contains 32 GPIO's. Pin Id is the bit position [0:31]
* Bank GPIO's GIRQ
* 0 0000 - 0036 11
* 1 0040 - 0076 10
* 2 0100 - 0135 9
* 3 0140 - 0175 8
* 4 0200 - 0235 12
* 5 0240 - 0276 26
*/
static const struct gpio_int_mapping int_map[6] = {
{ 11, 0 }, { 10, 1 }, { 9, 2 },
{ 8, 3 }, { 12, 4 }, { 26, 5 }
};
/*
* NOTE: GCC __builtin_ffs(val) returns (index + 1) of least significant
* 1-bit of val or if val == 0 returns 0
*/
void gpio_set_alternate_function(uint32_t port, uint32_t mask, int func)
{
int i;
uint32_t val;
while (mask) {
i = __builtin_ffs(mask) - 1;
val = MCHP_GPIO_CTL(port, i);
val &= ~((1 << 12) | (1 << 13));
/* mux_control = 0 indicates GPIO */
if (func > 0)
val |= (func & 0x3) << 12;
MCHP_GPIO_CTL(port, i) = val;
mask &= ~(1 << i);
}
}
test_mockable int gpio_get_level(enum gpio_signal signal)
{
uint32_t mask = gpio_list[signal].mask;
int i;
uint32_t val;
if (mask == 0)
return 0;
i = GPIO_MASK_TO_NUM(mask);
val = MCHP_GPIO_CTL(gpio_list[signal].port, i);
return (val & (1 << 24)) ? 1 : 0;
}
void gpio_set_level(enum gpio_signal signal, int value)
{
uint32_t mask = gpio_list[signal].mask;
int i;
if (mask == 0)
return;
i = GPIO_MASK_TO_NUM(mask);
if (value)
MCHP_GPIO_CTL(gpio_list[signal].port, i) |= (1 << 16);
else
MCHP_GPIO_CTL(gpio_list[signal].port, i) &= ~(1 << 16);
}
/*
* Add support for new #ifdef CONFIG_CMD_GPIO_POWER_DOWN.
* If GPIO_POWER_DONW flag is set force GPIO Control to
* GPIO input, interrupt detect disabled, power control field
* in bits[3:2]=10b.
* NOTE: if interrupt detect is enabled when pin is powered down
* then a false edge may be detected.
*
*/
void gpio_set_flags_by_mask(uint32_t port, uint32_t mask, uint32_t flags)
{
int i;
uint32_t val;
while (mask) {
i = GPIO_MASK_TO_NUM(mask);
mask &= ~(1 << i);
val = MCHP_GPIO_CTL(port, i);
#ifdef CONFIG_GPIO_POWER_DOWN
if (flags & GPIO_POWER_DOWN) {
val = (MCHP_GPIO_CTRL_PWR_OFF +
MCHP_GPIO_INTDET_DISABLED);
MCHP_GPIO_CTL(port, i) = val;
continue;
}
#endif
val &= ~(MCHP_GPIO_CTRL_PWR_MASK);
val |= MCHP_GPIO_CTRL_PWR_VTR;
/*
* Select open drain first, so that we don't
* glitch the signal when changing the line to
* an output.
*/
if (flags & GPIO_OPEN_DRAIN)
val |= (MCHP_GPIO_OPEN_DRAIN);
else
val &= ~(MCHP_GPIO_OPEN_DRAIN);
if (flags & GPIO_OUTPUT) {
val |= (MCHP_GPIO_OUTPUT);
val &= ~(MCHP_GPIO_OUTSEL_PAR);
} else {
val &= ~(MCHP_GPIO_OUTPUT);
val |= (MCHP_GPIO_OUTSEL_PAR);
}
/* Handle pull-up / pull-down */
val &= ~(MCHP_GPIO_CTRL_PUD_MASK);
if (flags & GPIO_PULL_UP)
val |= MCHP_GPIO_CTRL_PUD_PU;
else if (flags & GPIO_PULL_DOWN)
val |= MCHP_GPIO_CTRL_PUD_PD;
else
val |= MCHP_GPIO_CTRL_PUD_NONE;
/* Set up interrupt */
val &= ~(MCHP_GPIO_INTDET_MASK);
switch (flags & GPIO_INT_ANY) {
case GPIO_INT_F_RISING:
val |= MCHP_GPIO_INTDET_EDGE_RIS;
break;
case GPIO_INT_F_FALLING:
val |= MCHP_GPIO_INTDET_EDGE_FALL;
break;
case GPIO_INT_BOTH: /* both edges */
val |= MCHP_GPIO_INTDET_EDGE_BOTH;
break;
case GPIO_INT_F_LOW:
val |= MCHP_GPIO_INTDET_LVL_LO;
break;
case GPIO_INT_F_HIGH:
val |= MCHP_GPIO_INTDET_LVL_HI;
break;
default:
val |= MCHP_GPIO_INTDET_DISABLED;
break;
}
/* Set up level */
if (flags & GPIO_HIGH)
val |= (MCHP_GPIO_CTRL_OUT_LVL);
else if (flags & GPIO_LOW)
val &= ~(MCHP_GPIO_CTRL_OUT_LVL);
MCHP_GPIO_CTL(port, i) = val;
}
}
void gpio_power_off_by_mask(uint32_t port, uint32_t mask)
{
int i;
while (mask) {
i = GPIO_MASK_TO_NUM(mask);
mask &= ~(1 << i);
MCHP_GPIO_CTL(port, i) = (MCHP_GPIO_CTRL_PWR_OFF +
MCHP_GPIO_INTDET_DISABLED);
}
}
int gpio_power_off(enum gpio_signal signal)
{
int i, port;
if (gpio_list[signal].mask == 0)
return EC_ERROR_INVAL;
i = GPIO_MASK_TO_NUM(gpio_list[signal].mask);
port = gpio_list[signal].port;
MCHP_GPIO_CTL(port, i) = (MCHP_GPIO_CTRL_PWR_OFF +
MCHP_GPIO_INTDET_DISABLED);
return EC_SUCCESS;
}
/*
* gpio_list[signal].port = [0, 6] each port contains up to 32 pins
* gpio_list[signal].mask = bit mask in 32-bit port
* NOTE: MCHP GPIO are always aggregated not direct connected to NVIC.
* GPIO's are aggregated into banks of 32 pins.
* Each bank/port are connected to a GIRQ.
* int_map[port].girq_id is the GIRQ ID
* The bit number in the GIRQ registers is the same as the bit number
* in the GPIO bank.
*/
int gpio_enable_interrupt(enum gpio_signal signal)
{
int i, port, girq_id;
if (gpio_list[signal].mask == 0)
return EC_SUCCESS;
i = GPIO_MASK_TO_NUM(gpio_list[signal].mask);
port = gpio_list[signal].port;
girq_id = int_map[port].girq_id;
MCHP_INT_ENABLE(girq_id) = (1 << i);
MCHP_INT_BLK_EN |= (1 << girq_id);
return EC_SUCCESS;
}
int gpio_disable_interrupt(enum gpio_signal signal)
{
int i, port, girq_id;
if (gpio_list[signal].mask == 0)
return EC_SUCCESS;
i = GPIO_MASK_TO_NUM(gpio_list[signal].mask);
port = gpio_list[signal].port;
girq_id = int_map[port].girq_id;
MCHP_INT_DISABLE(girq_id) = (1 << i);
return EC_SUCCESS;
}
/*
* MCHP Interrupt Source is R/W1C no need for read-modify-write.
* GPIO's are aggregated meaning the NVIC Pending bit may be
* set for another GPIO in the GIRQ. You can clear NVIC pending
* and the hardware should re-assert it within one Cortex-M4 clock.
* If the Cortex-M4 is clocked slower than AHB then the Cortex-M4
* will take longer to register the interrupt. Not clearing NVIC
* pending leave a pending status if only the GPIO this routine
* clears is pending.
* NVIC (system control) register space is strongly-ordered
* Interrupt Aggregator is in Device space (system bus connected
* to AHB) with the Cortex-M4 write buffer.
* We need to insure the write to aggregator register in device
* AHB space completes before NVIC pending is cleared.
* The Cortex-M4 memory ordering rules imply Device access
* comes before strongly ordered access. Cortex-M4 will not re-order
* the writes. Due to the presence of the write buffer a DSB will
* not guarantee the clearing of the device status completes. Add
* a read back before clearing NVIC pending.
* GIRQ 8, 9, 10, 11, 12, 26 map to NVIC inputs 0, 1, 2, 3, 4, and 18.
*/
int gpio_clear_pending_interrupt(enum gpio_signal signal)
{
int i, port, girq_id;
if (gpio_list[signal].mask == 0)
return EC_SUCCESS;
i = GPIO_MASK_TO_NUM(gpio_list[signal].mask);
port = gpio_list[signal].port;
girq_id = int_map[port].girq_id;
/* Clear interrupt source sticky status bit even if not enabled */
MCHP_INT_SOURCE(girq_id) = (1 << i);
i = MCHP_INT_SOURCE(girq_id);
task_clear_pending_irq(girq_id - 8);
return EC_SUCCESS;
}
/*
* MCHP NOTE - called from main before scheduler started
*/
void gpio_pre_init(void)
{
int i;
int flags;
int is_warm = system_is_reboot_warm();
const struct gpio_info *g = gpio_list;
for (i = 0; i < GPIO_COUNT; i++, g++) {
flags = g->flags;
if (flags & GPIO_DEFAULT)
continue;
/*
* If this is a warm reboot, don't set the output levels or
* we'll shut off the AP.
*/
if (is_warm)
flags &= ~(GPIO_LOW | GPIO_HIGH);
gpio_set_flags_by_mask(g->port, g->mask, flags);
/* Use as GPIO, not alternate function */
gpio_set_alternate_function(g->port, g->mask, -1);
}
}
/* Clear any interrupt flags before enabling GPIO interrupt
* Original code has flaws.
* Writing result register to source only clears bits that have their
* enable and sources bits set.
* We must clear the NVIC pending R/W bit before setting NVIC enable.
* NVIC Pending is only cleared by the NVIC HW on ISR entry.
* Modifications are:
* 1. Clear all status bits in each GPIO GIRQ. This assumes any edges
* will occur after gpio_init. The old code is also making this
* assumption for the GPIO's that have been enabled.
* 2. Clear NVIC pending to prevent ISR firing on false edge.
*/
#define ENABLE_GPIO_GIRQ(x) \
do { \
MCHP_INT_SOURCE(x) = 0xfffffffful; \
task_clear_pending_irq(MCHP_IRQ_GIRQ ## x); \
task_enable_irq(MCHP_IRQ_GIRQ ## x); \
} while (0)
static void gpio_init(void)
{
ENABLE_GPIO_GIRQ(8);
ENABLE_GPIO_GIRQ(9);
ENABLE_GPIO_GIRQ(10);
ENABLE_GPIO_GIRQ(11);
ENABLE_GPIO_GIRQ(12);
ENABLE_GPIO_GIRQ(26);
}
DECLARE_HOOK(HOOK_INIT, gpio_init, HOOK_PRIO_DEFAULT);
/************************************************************************/
/* Interrupt handlers */
/**
* Handler for each GIRQ interrupt. This reads and clears the interrupt
* bits for the GIRQ interrupt, then finds and calls the corresponding
* GPIO interrupt handlers.
*
* @param girq GIRQ index
* @param port_offset GPIO port offset for the given GIRQ
*/
static void gpio_interrupt(int girq)
{
int i, bit;
const struct gpio_info *g = gpio_list;
uint32_t sts = MCHP_INT_RESULT(girq);
/* CPRINTS("MEC1701 GPIO GIRQ %d result = 0x%08x", girq, sts); */
trace12(0, GPIO, 0, "GPIO GIRQ %d result = 0x%08x", girq, sts);
/* RW1C, no need for read-modify-write */
MCHP_INT_SOURCE(girq) = sts;
for (i = 0; i < GPIO_IH_COUNT && sts; ++i, ++g) {
bit = __builtin_ffs(g->mask) - 1;
if (sts & (1 << bit))
gpio_irq_handlers[i](i);
sts &= ~(1 << bit);
}
}
#define GPIO_IRQ_FUNC(irqfunc, girq) \
void irqfunc(void) \
{ \
gpio_interrupt(girq); \
}
GPIO_IRQ_FUNC(__girq_8_interrupt, 8);
GPIO_IRQ_FUNC(__girq_9_interrupt, 9);
GPIO_IRQ_FUNC(__girq_10_interrupt, 10);
GPIO_IRQ_FUNC(__girq_11_interrupt, 11);
GPIO_IRQ_FUNC(__girq_12_interrupt, 12);
GPIO_IRQ_FUNC(__girq_26_interrupt, 26);
#undef GPIO_IRQ_FUNC
/*
* Declare IRQs. Nesting this macro inside the GPIO_IRQ_FUNC macro works
* poorly because DECLARE_IRQ() stringizes its inputs.
*/
DECLARE_IRQ(MCHP_IRQ_GIRQ8, __girq_8_interrupt, 1);
DECLARE_IRQ(MCHP_IRQ_GIRQ9, __girq_9_interrupt, 1);
DECLARE_IRQ(MCHP_IRQ_GIRQ10, __girq_10_interrupt, 1);
DECLARE_IRQ(MCHP_IRQ_GIRQ11, __girq_11_interrupt, 1);
DECLARE_IRQ(MCHP_IRQ_GIRQ12, __girq_12_interrupt, 1);
DECLARE_IRQ(MCHP_IRQ_GIRQ26, __girq_26_interrupt, 1);

38
chip/mchp/gpio_chip.h Normal file
View File

@@ -0,0 +1,38 @@
/* Copyright 2017 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.
*
* Register map for MCHP MEC processor
*/
/** @file gpio_chip.h
*MEC GPIO module
*/
/** @defgroup MEC gpio
*/
#ifndef _GPIO_CHIP_H
#define _GPIO_CHIP_H
#include <stdint.h>
#include <stddef.h>
#include "gpio.h"
#ifdef __cplusplus
extern "C" {
#endif
/* Place any C interfaces here */
int gpio_power_off(enum gpio_signal signal);
void gpio_power_off_by_mask(uint32_t port, uint32_t mask);
#ifdef __cplusplus
}
#endif
#endif /* #ifndef _GPIO_CHIP_H */
/** @}
*/

97
chip/mchp/gpio_cmds.c Normal file
View File

@@ -0,0 +1,97 @@
/* Copyright 2017 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.
*/
/* MCHP MEC GPIO module EC UART commands */
#include "common.h"
#include "console.h"
#include "gpio.h"
#include "hooks.h"
#include "registers.h"
#include "system.h"
#include "util.h"
#include "gpio_chip.h"
#include "tfdp_chip.h"
/* Console output macros */
#define CPUTS(outstr) cputs(CC_LPC, outstr)
#define CPRINTS(format, args...) cprints(CC_LPC, format, ## args)
static int cmd_gp_get_config(int argc, char **argv)
{
char *e;
int i;
uint32_t gctrl;
/* If a signal is specified, print only that one */
if (argc == 2) {
i = strtoi(argv[1], &e, 0);
if (*e)
return EC_ERROR_PARAM1;
if (!gpio_is_implemented(i))
return EC_ERROR_PARAM1;
gctrl = MCHP_GPIO_CTRL(i);
ccprintf(" GPIO[0x%X].Ctrl = 0x%08X\n", i, gctrl);
} else { /* Otherwise print them all */
for (i = 0; i < GPIO_COUNT; i++) {
if (!gpio_is_implemented(i))
continue; /* Skip unsupported signals */
gctrl = MCHP_GPIO_CTRL(i);
ccprintf(" GPIO[0x%X].Ctrl = 0x%08X\n", i, gctrl);
}
}
/* Flush console to avoid truncating output */
cflush();
return EC_SUCCESS;
}
DECLARE_CONSOLE_COMMAND(gpgetcfg, cmd_gp_get_config,
"[number]",
"Read GPIO config");
static int cmd_gp_set_config(int argc, char **argv)
{
char *e;
int i;
uint32_t gctrl;
/* If a signal is specified, print only that one */
if (argc > 2) {
i = strtoi(argv[1], &e, 0);
if (*e)
return EC_ERROR_PARAM1;
if (!gpio_is_implemented(i))
return EC_ERROR_PARAM1;
gctrl = (uint32_t)strtoi(argv[2], &e, 0);
if (*e)
return EC_ERROR_PARAM2;
MCHP_GPIO_CTRL(i) = gctrl;
gctrl = MCHP_GPIO_CTRL(i);
ccprintf(" GPIO[0x%X].Ctrl = 0x%08X\n", i, gctrl);
} else {
ccprintf(" Requires two parameters: GPIO num and new config");
}
/* Flush console to avoid truncating output */
cflush();
return EC_SUCCESS;
}
DECLARE_CONSOLE_COMMAND(gpsetcfg, cmd_gp_set_config,
"gp_num val",
"Set GPIO config");

2226
chip/mchp/registers.h Normal file

File diff suppressed because it is too large Load Diff

490
chip/mchp/system.c Normal file
View File

@@ -0,0 +1,490 @@
/* Copyright 2017 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.
*/
/* System module for Chrome EC : MCHP hardware specific implementation */
#include "clock.h"
#include "common.h"
#include "console.h"
#include "cpu.h"
#include "gpio.h"
#include "host_command.h"
#include "registers.h"
#include "shared_mem.h"
#include "system.h"
#include "hooks.h"
#include "task.h"
#include "timer.h"
#include "util.h"
#include "spi.h"
#include "lpc_chip.h"
#include "tfdp_chip.h"
#define CPUTS(outstr) cputs(CC_LPC, outstr)
#define CPRINTS(format, args...) cprints(CC_LPC, format, ## args)
/* Indices for hibernate data registers (RAM backed by VBAT) */
enum hibdata_index {
HIBDATA_INDEX_SCRATCHPAD = 0, /* General-purpose scratchpad */
HIBDATA_INDEX_SAVED_RESET_FLAGS, /* Saved reset flags */
HIBDATA_INDEX_PD0, /* USB-PD0 saved port state */
HIBDATA_INDEX_PD1, /* USB-PD1 saved port state */
};
static void check_reset_cause(void)
{
uint32_t status = MCHP_VBAT_STS;
uint32_t flags = 0;
uint32_t rst_sts = MCHP_PCR_PWR_RST_STS &
(MCHP_PWR_RST_STS_VTR |
MCHP_PWR_RST_STS_VBAT);
trace12(0, MEC, 0,
"check_reset_cause: VBAT_PFR = 0x%08X PCR PWRST = 0x%08X",
status, rst_sts);
/* Clear the reset causes now that we've read them */
MCHP_VBAT_STS |= status;
MCHP_PCR_PWR_RST_STS |= rst_sts;
trace0(0, MEC, 0, "check_reset_cause: after clear");
trace11(0, MEC, 0, " VBAT_PFR = 0x%08X", MCHP_VBAT_STS);
trace11(0, MEC, 0, " PCR PWRST = 0x%08X", MCHP_PCR_PWR_RST_STS);
/*
* BIT[6] determine VTR reset
*/
if (rst_sts & MCHP_PWR_RST_STS_VTR)
flags |= RESET_FLAG_RESET_PIN;
flags |= MCHP_VBAT_RAM(HIBDATA_INDEX_SAVED_RESET_FLAGS);
MCHP_VBAT_RAM(HIBDATA_INDEX_SAVED_RESET_FLAGS) = 0;
if ((status & MCHP_VBAT_STS_WDT) && !(flags & (RESET_FLAG_SOFT |
RESET_FLAG_HARD |
RESET_FLAG_HIBERNATE)))
flags |= RESET_FLAG_WATCHDOG;
trace11(0, MEC, 0, "check_reset_cause: EC reset flags = 0x%08x", flags);
system_set_reset_flags(flags);
}
int system_is_reboot_warm(void)
{
uint32_t reset_flags;
/*
* Check reset cause here,
* gpio_pre_init is executed faster than system_pre_init
*/
check_reset_cause();
reset_flags = system_get_reset_flags();
if ((reset_flags & RESET_FLAG_RESET_PIN) ||
(reset_flags & RESET_FLAG_POWER_ON) ||
(reset_flags & RESET_FLAG_WATCHDOG) ||
(reset_flags & RESET_FLAG_HARD) ||
(reset_flags & RESET_FLAG_SOFT))
return 0;
else
return 1;
}
/*
* Sleep unused blocks to reduce power.
* Drivers/modules will unsleep their blocks.
* Keep sleep enables cleared for required blocks:
* ECIA, PMC, CPU, ECS and optionally JTAG.
* SLEEP_ALL feature will set these upon sleep entry.
* Based on CONFIG_CHIPSET_DEBUG enable or disable JTAG.
* JTAG mode (4-pin or 2-pin SWD + 1-pin SWV) was set
* by Boot-ROM. We can override Boot-ROM JTAG mode
* using
* CONFIG_MCHP_JTAG_MODE
*/
static void chip_periph_sleep_control(void)
{
uint32_t d;
d = MCHP_PCR_SLP_EN0_SLEEP;
#ifdef CONFIG_CHIPSET_DEBUG
d &= ~(MCHP_PCR_SLP_EN0_JTAG);
#ifdef CONFIG_MCHP_JTAG_MODE
MCHP_EC_JTAG_EN = CONFIG_MCHP_JTAG_MODE;
#else
MCHP_EC_JTAG_EN |= 0x01;
#endif
#else
MCHP_EC_JTAG_EN &= ~0x01;
#endif
MCHP_PCR_SLP_EN0 = d;
MCHP_PCR_SLP_EN1 = MCHP_PCR_SLP_EN1_UNUSED_BLOCKS;
MCHP_PCR_SLP_EN2 = MCHP_PCR_SLP_EN2_SLEEP;
MCHP_PCR_SLP_EN3 = MCHP_PCR_SLP_EN3_SLEEP;
MCHP_PCR_SLP_EN4 = MCHP_PCR_SLP_EN4_SLEEP;
}
#ifdef CONFIG_CHIP_PRE_INIT
void chip_pre_init(void)
{
chip_periph_sleep_control();
}
#endif
void system_pre_init(void)
{
#ifdef CONFIG_MCHP_TFDP
uint8_t imgtype;
#endif
/*
* Make sure AHB Error capture is enabled.
* Signals bus fault to Cortex-M4 core if an address presented
* to AHB is not claimed by any HW block.
*/
MCHP_EC_AHB_ERR = 0; /* write any value to clear */
MCHP_EC_AHB_ERR_EN = 0; /* enable capture of address on error */
#ifdef CONFIG_ESPI
MCHP_EC_GPIO_BANK_PWR |= MCHP_EC_GPIO_BANK_PWR_VTR3_18;
#endif
#ifndef CONFIG_CHIP_PRE_INIT
chip_periph_sleep_control();
#endif
/* Enable direct NVIC */
MCHP_EC_INT_CTRL |= 1;
/* Disable ARM TRACE debug port */
MCHP_EC_TRACE_EN &= ~1;
/*
* Enable aggregated only interrupt GIRQ's
* Make sure direct mode interrupt sources aggregated outputs
* are not enabled.
* Aggregated only GIRQ's 8,9,10,11,12,22,24,25,26
* Direct GIRQ's = 13,14,15,16,17,18,19,21,23
* These bits only need to be touched again on RESET_SYS.
* NOTE: GIRQ22 wake for AHB peripherals not processor.
*/
MCHP_INT_BLK_DIS = 0xfffffffful;
MCHP_INT_BLK_EN = (0x1Ful << 8) + (0x07ul << 24);
spi_enable(CONFIG_SPI_FLASH_PORT, 1);
#ifdef CONFIG_MCHP_TFDP
/*
* MCHP Enable TFDP for fast debug messages
* If not defined then traceN() and TRACEN() macros are empty
*/
tfdp_power(1);
tfdp_enable(1, 1);
imgtype = MCHP_VBAT_RAM(MCHP_IMAGETYPE_IDX);
CPRINTS("system_pre_init. Image type = 0x%02x", imgtype);
trace1(0, MEC, 0, "System pre-init. Image type = 0x%02x", imgtype);
/* Debug: dump some signals */
imgtype = gpio_get_level(GPIO_PCH_RSMRST_L);
trace1(0, MEC, 0, "PCH_RSMRST_L = %d", imgtype);
imgtype = gpio_get_level(GPIO_RSMRST_L_PGOOD);
trace1(0, MEC, 0, "RSMRST_L_PGOOD = %d", imgtype);
imgtype = gpio_get_level(GPIO_POWER_BUTTON_L);
trace1(0, MEC, 0, "POWER_BUTTON_L = %d", imgtype);
imgtype = gpio_get_level(GPIO_PMIC_DPWROK);
trace1(0, MEC, 0, "PMIC_DPWROK = %d", imgtype);
imgtype = gpio_get_level(GPIO_ALL_SYS_PWRGD);
trace1(0, MEC, 0, "ALL_SYS_PWRGD = %d", imgtype);
imgtype = gpio_get_level(GPIO_AC_PRESENT);
trace1(0, MEC, 0, "AC_PRESENT = %d", imgtype);
imgtype = gpio_get_level(GPIO_PCH_SLP_SUS_L);
trace1(0, MEC, 0, "PCH_SLP_SUS_L = %d", imgtype);
imgtype = gpio_get_level(GPIO_PMIC_INT_L);
trace1(0, MEC, 0, "PCH_PMIC_INT_L = %d", imgtype);
#endif
}
void chip_save_reset_flags(int flags)
{
MCHP_VBAT_RAM(HIBDATA_INDEX_SAVED_RESET_FLAGS) = flags;
}
void __attribute__((noreturn)) _system_reset(int flags,
int wake_from_hibernate)
{
uint32_t save_flags = 0;
/* Disable interrupts to avoid task swaps during reboot */
interrupt_disable();
trace12(0, MEC, 0,
"_system_reset: flags=0x%08X wake_from_hibernate=%d",
flags, wake_from_hibernate);
/* Save current reset reasons if necessary */
if (flags & SYSTEM_RESET_PRESERVE_FLAGS)
save_flags = system_get_reset_flags() | RESET_FLAG_PRESERVED;
if (flags & SYSTEM_RESET_LEAVE_AP_OFF)
save_flags |= RESET_FLAG_AP_OFF;
if (wake_from_hibernate)
save_flags |= RESET_FLAG_HIBERNATE;
else if (flags & SYSTEM_RESET_HARD)
save_flags |= RESET_FLAG_HARD;
else
save_flags |= RESET_FLAG_SOFT;
chip_save_reset_flags(save_flags);
trace11(0, MEC, 0, "_system_reset: save_flags=0x%08X", save_flags);
/*
* Trigger chip reset
*/
#ifndef CONFIG_CHIPSET_DEBUG
MCHP_PCR_SYS_RST |= MCHP_PCR_SYS_SOFT_RESET;
#endif
/* Spin and wait for reboot; should never return */
while (1)
;
}
void system_reset(int flags)
{
_system_reset(flags, 0);
}
const char *system_get_chip_vendor(void)
{
return "mchp";
}
/*
* MEC1701H Chip ID = 0x2D
* Rev = 0x82
*/
const char *system_get_chip_name(void)
{
switch (MCHP_CHIP_DEV_ID) {
case 0x2D:
return "mec1701";
default:
return "unknown";
}
}
static char to_hex(int x)
{
if (x >= 0 && x <= 9)
return '0' + x;
return 'a' + x - 10;
}
const char *system_get_chip_revision(void)
{
static char buf[3];
uint8_t rev = MCHP_CHIP_DEV_REV;
buf[0] = to_hex(rev / 16);
buf[1] = to_hex(rev & 0xf);
buf[2] = '\0';
return buf;
}
static int bbram_idx_lookup(enum system_bbram_idx idx)
{
switch (idx) {
#ifdef CONFIG_USB_PD_DUAL_ROLE
case SYSTEM_BBRAM_IDX_PD0:
return HIBDATA_INDEX_PD0;
case SYSTEM_BBRAM_IDX_PD1:
return HIBDATA_INDEX_PD1;
#endif
default:
return 1;
}
}
int system_get_bbram(enum system_bbram_idx idx, uint8_t *value)
{
int hibdata = bbram_idx_lookup(idx);
if (hibdata < 0)
return EC_ERROR_UNIMPLEMENTED;
*value = MCHP_VBAT_RAM(hibdata);
return EC_SUCCESS;
}
int system_set_bbram(enum system_bbram_idx idx, uint8_t value)
{
int hibdata = bbram_idx_lookup(idx);
if (hibdata < 0)
return EC_ERROR_UNIMPLEMENTED;
MCHP_VBAT_RAM(hibdata) = value;
return EC_SUCCESS;
}
int system_set_scratchpad(uint32_t value)
{
MCHP_VBAT_RAM(HIBDATA_INDEX_SCRATCHPAD) = value;
return EC_SUCCESS;
}
uint32_t system_get_scratchpad(void)
{
return MCHP_VBAT_RAM(HIBDATA_INDEX_SCRATCHPAD);
}
void system_hibernate(uint32_t seconds, uint32_t microseconds)
{
int i;
#ifdef CONFIG_HOSTCMD_PD
/* Inform the PD MCU that we are going to hibernate. */
host_command_pd_request_hibernate();
/* Wait to ensure exchange with PD before hibernating. */
msleep(100);
#endif
cflush();
if (board_hibernate)
board_hibernate();
/* Disable interrupts */
interrupt_disable();
for (i = 0; i <= 92; ++i) {
task_disable_irq(i);
task_clear_pending_irq(i);
}
for (i = 8; i <= 26; ++i)
MCHP_INT_DISABLE(i) = 0xffffffff;
MCHP_INT_BLK_DIS |= 0xffff00;
/* Disable UART */
MCHP_UART_ACT(0) &= ~0x1;
MCHP_LPC_ACT &= ~0x1;
/* Disable JTAG */
MCHP_EC_JTAG_EN &= ~1;
/* Disable 32KHz clock */
MCHP_VBAT_CE &= ~0x2;
/* Stop watchdog */
MCHP_WDG_CTL &= ~1;
/* Stop timers */
MCHP_TMR32_CTL(0) &= ~1;
MCHP_TMR32_CTL(1) &= ~1;
MCHP_TMR16_CTL(0) &= ~1;
/* Power down ADC */
/*
* If ADC is in middle of acquisition it will continue until finished
*/
MCHP_ADC_CTRL &= ~1;
/* Disable blocks */
MCHP_PCR_SLOW_CLK_CTL &= ~(MCHP_PCR_SLOW_CLK_CTL_MASK);
/*
* Set sleep state
* arm sleep state to trigger on next WFI
*/
CPU_SCB_SYSCTRL |= 0x4;
MCHP_PCR_SYS_SLP_CTL = MCHP_PCR_SYS_SLP_HEAVY;
MCHP_PCR_SYS_SLP_CTL = MCHP_PCR_SYS_SLP_ALL;
/* Setup GPIOs for hibernate */
if (board_hibernate_late)
board_hibernate_late();
if (hibernate_wake_pins_used > 0) {
for (i = 0; i < hibernate_wake_pins_used; ++i) {
const enum gpio_signal pin = hibernate_wake_pins[i];
gpio_reset(pin);
gpio_enable_interrupt(pin);
}
interrupt_enable();
task_enable_irq(MCHP_IRQ_GIRQ8);
task_enable_irq(MCHP_IRQ_GIRQ9);
task_enable_irq(MCHP_IRQ_GIRQ10);
task_enable_irq(MCHP_IRQ_GIRQ11);
task_enable_irq(MCHP_IRQ_GIRQ12);
}
if (seconds || microseconds) {
/*
* Not needed when using direct mode interrupts.
* MCHP_INT_BLK_EN |= 1 << MCHP_HTIMER_GIRQ;
*/
MCHP_INT_ENABLE(MCHP_HTIMER_GIRQ) =
MCHP_HTIMER_GIRQ_BIT(0);
interrupt_enable();
task_enable_irq(MCHP_IRQ_HTIMER0);
if (seconds > 2) {
ASSERT(seconds <= 0xffff / 8);
MCHP_HTIMER_CONTROL(0) = 1;
MCHP_HTIMER_PRELOAD(0) =
(seconds * 8 + microseconds / 125000);
} else {
MCHP_HTIMER_CONTROL(0) = 0;
MCHP_HTIMER_PRELOAD(0) =
(seconds * 1000000 + microseconds) * 2 / 71;
}
}
asm("wfi");
/* Use 48MHz clock to speed through wake-up */
MCHP_PCR_PROC_CLK_CTL = 1;
/* Reboot */
_system_reset(0, 1);
/* We should never get here. */
while (1)
;
}
void htimer_interrupt(void)
{
/* Time to wake up */
}
DECLARE_IRQ(MCHP_IRQ_HTIMER0, htimer_interrupt, 1);
enum system_image_copy_t system_get_shrspi_image_copy(void)
{
return MCHP_VBAT_RAM(MCHP_IMAGETYPE_IDX);
}
uint32_t system_get_lfw_address(void)
{
uint32_t * const lfw_vector =
(uint32_t * const)CONFIG_PROGRAM_MEMORY_BASE;
return *(lfw_vector + 1);
}
void system_set_image_copy(enum system_image_copy_t copy)
{
MCHP_VBAT_RAM(MCHP_IMAGETYPE_IDX) = (copy == SYSTEM_IMAGE_RW) ?
SYSTEM_IMAGE_RW : SYSTEM_IMAGE_RO;
}

View File

@@ -1067,6 +1067,32 @@
/* Use Virtual Wire signals instead of GPIO with eSPI interface */
#undef CONFIG_ESPI_VW_SIGNALS
/* MCHP next two items are EC eSPI slave configuration */
/* Maximum clock frequence eSPI EC slave advertises
* Values in MHz are 20, 25, 33, 50, and 66
*/
#undef CONFIG_ESPI_EC_MAX_FREQ
/* EC eSPI slave advertises IO lanes
* 0 = Single
* 1 = Single and Dual
* 2 = Single and Quad
* 3 = Single, Dual, and Quad
*/
#undef CONFIG_ESPI_EC_MODE
/* Bit map of eSPI channels EC advertises
* bit[0] = 1 Peripheral channel
* bit[1] = 1 Virtual Wire channel
* bit[2] = 1 OOB channel
* bit[3] = 1 Flash channel
*/
#undef CONFIG_ESPI_EC_CHAN_BITMAP
/* Use Virtual Wire for Platform Reset instead of a sideband signal */
#undef CONFIG_ESPI_PLTRST_IS_VWIRE
/* Include code for handling external power */
#define CONFIG_EXTPOWER
@@ -2277,6 +2303,13 @@
* SPI master port's hardwired CS pin. */
#undef CONFIG_SPI_MASTER_NO_CS_GPIOS
/* Support MCHP MEC family GP-SPI master(s)
* Define to 0x01 for GPSPI0 only.
* Define to 0x02 for GPSPI1 only.
* Define to 0x03 for both controllers.
*/
#undef CONFIG_MCHP_GPSPI
/* Support testing SPI slave controller driver. */
#undef CONFIG_SPS_TEST
@@ -2319,6 +2352,11 @@
*/
#undef CONFIG_TABLET_MODE_SWITCH
/*
* Microchip Trace FIFO Debug Port
*/
#undef CONFIG_MCHP_TFDP
/*****************************************************************************/
/* Task config */