diff options
46 files changed, 9069 insertions, 4 deletions
diff --git a/Makefile.rules b/Makefile.rules index b532173e28..4dd8b8a723 100644 --- a/Makefile.rules +++ b/Makefile.rules @@ -239,6 +239,27 @@ $(out)/TAGS: $(out)/cscope.files $(out)/tags: $(out)/cscope.files $(call quiet,ctags,CTAGS ) +# TODO: optional make rules for PROJECT_EXTRA +$(npcx-flash-fw-bin): + $(if $(V),,@echo ' EXTBIN ' $(subst $(out)/,,$@) ; ) + -@ mkdir -p $(@D) + -@ $(CC) $(CFLAGS) -MMD -MF $(out)/$(npcx-lfw).d -c $(npcx-flash-fw).c \ + -o $(out)/$(npcx-flash-fw).o + -@ $(LD) $(out)/$(npcx-flash-fw).o $(LDFLAGS) \ + -o $(out)/$(npcx-flash-fw).elf -T $(npcx-flash-fw).ld \ + -Map $(out)/$(npcx-flash-fw).map + -@ $(OBJCOPY) -O binary $(out)/$(npcx-flash-fw).elf $@ + +# TODO: optional make rules for PROJECT_EXTRA +$(npcx-lfw-bin): + $(if $(V),,@echo ' EXTBIN ' $(subst $(out)/,,$@) ; ) + -@ mkdir -p $(@D) + -@ $(CC) $(CFLAGS) -MMD -MF $(out)/$(npcx-lfw).d -c $(npcx-lfw).c \ + -o $(out)/$(npcx-lfw).o + -@ $(LD) $(out)/$(npcx-lfw).o $(LDFLAGS) -o $(out)/$(npcx-lfw).elf \ + -T $(npcx-lfw).ld -Map $(out)/$(npcx-lfw).map + -@ $(OBJCOPY) -O binary $(out)/$(npcx-lfw).elf $@ + .PHONY: xrefs xrefs: $(call targ_if_prog,etags,$(out)/TAGS) \ $(call targ_if_prog,ctags,$(out)/tags) diff --git a/board/npcx_evb/board.c b/board/npcx_evb/board.c new file mode 100644 index 0000000000..890c3b2f1f --- /dev/null +++ b/board/npcx_evb/board.c @@ -0,0 +1,122 @@ +/* Copyright (c) 2014 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. + */ +/* EC for Nuvoton M4 EB configuration */ + +#include "adc.h" +#include "adc_chip.h" +#include "backlight.h" +#include "chipset.h" +#include "common.h" +#include "driver/temp_sensor/tmp006.h" +#include "extpower.h" +#include "fan.h" +#include "fan_chip.h" +#include "gpio.h" +#include "i2c.h" +#include "keyboard_scan.h" +#include "lid_switch.h" +#include "peci.h" +#include "power.h" +#include "power_button.h" +#include "pwm.h" +#include "pwm_chip.h" +#include "registers.h" +#include "switch.h" +#include "temp_sensor.h" +#include "temp_sensor_chip.h" +#include "timer.h" +#include "thermal.h" +#include "util.h" + +#include "gpio_list.h" + +/******************************************************************************/ +/* ADC channels. Must be in the exactly same order as in enum adc_channel. */ +const struct adc_t adc_channels[] = { + [ADC_CH_0] = {"ADC0", NPCX_ADC_INPUT_CH0, ADC_MAX_VOLT, + ADC_READ_MAX+1, 0}, + [ADC_CH_1] = {"ADC1", NPCX_ADC_INPUT_CH1, ADC_MAX_VOLT, + ADC_READ_MAX+1, 0}, + [ADC_CH_2] = {"ADC2", NPCX_ADC_INPUT_CH2, ADC_MAX_VOLT, + ADC_READ_MAX+1, 0}, +}; +BUILD_ASSERT(ARRAY_SIZE(adc_channels) == ADC_CH_COUNT); + +/******************************************************************************/ +/* PWM channels. Must be in the exactly same order as in enum pwm_channel. */ +const struct pwm_t pwm_channels[] = { + [PWM_CH_FAN] = { + .channel = 0, + .flags = 0, +#ifdef CONFIG_PWM_INPUT_LFCLK + .freq = 20000, /* Need <= mft freq */ +#else + .freq = 3000000, +#endif + /* 0xEA60=3000000*60/2/1500,0x190=20000*60/2/1500 */ + .cycle_pulses = 0x190, + }, + [PWM_CH_KBLIGHT] = { + .channel = 1, + .flags = 0, + .freq = 10000, + .cycle_pulses = 100, + }, +}; +BUILD_ASSERT(ARRAY_SIZE(pwm_channels) == PWM_CH_COUNT); + +/******************************************************************************/ +/* Physical fans. These are logically separate from pwm_channels. */ +const struct fan_t fans[] = { + [FAN_CH_0] = { + .flags = FAN_USE_RPM_MODE, + .rpm_min = 1500, + .rpm_start = 1500, + .rpm_max = 8190, + .ch = 0,/* Use PWM/MFT to control fan */ + .pgood_gpio = GPIO_PGOOD_FAN, + .enable_gpio = -1, + }, +};/*TODO: (Benson_TBD_1) rpm_min/rpm_max not confirm */ +BUILD_ASSERT(ARRAY_SIZE(fans) == FAN_CH_COUNT); + +/******************************************************************************/ +/* MFT channels. These are logically separate from mft_channels. */ +const struct mft_t mft_channels[] = { + [MFT_CH_0] = { + .module = NPCX_MFT_MODULE_1, + .port = NPCX_MFT_MODULE_PORT_TA, + .default_count = 0xFFFF, +#ifdef CONFIG_MFT_INPUT_LFCLK + .freq = 32768, +#else + .freq = 2000000, +#endif + }, +}; +BUILD_ASSERT(ARRAY_SIZE(mft_channels) == MFT_CH_COUNT); + +/******************************************************************************/ +/* I2C ports */ +const struct i2c_port_t i2c_ports[] = { + {"master", I2C_PORT_MASTER, 100, + GPIO_MASTER_I2C_SCL, GPIO_MASTER_I2C_SDA}, +}; +const unsigned int i2c_ports_used = ARRAY_SIZE(i2c_ports); + +/******************************************************************************/ +/* Keyboard scan setting */ +struct keyboard_scan_config keyscan_config = { + .output_settle_us = 40, + .debounce_down_us = 6 * MSEC, + .debounce_up_us = 30 * MSEC, + .scan_period_us = 1500, + .min_post_scan_delay_us = 1000, + .poll_timeout_us = SECOND, + .actual_key_mask = { + 0x14, 0xff, 0xff, 0xff, 0xff, 0xf5, 0xff, + 0xa4, 0xff, 0xf6, 0x55, 0xfa, 0xc8 /* full set */ + }, +}; diff --git a/board/npcx_evb/board.h b/board/npcx_evb/board.h new file mode 100644 index 0000000000..c8dd5f6139 --- /dev/null +++ b/board/npcx_evb/board.h @@ -0,0 +1,87 @@ +/* Copyright (c) 2014 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. + */ + +/* Configuration for Nuvoton M4 EB */ + +#ifndef __BOARD_H +#define __BOARD_H + +/* Support Code RAM architecture (Run code in RAM) */ +#define CONFIG_CODERAM_ARCH + +/* Optional modules */ +#define CONFIG_ADC +#define CONFIG_PECI +#define CONFIG_PWM +#define CONFIG_SPI + +/* Optional features */ +#define CONFIG_SYSTEM_UNLOCKED /* Allow dangerous commands for testing */ +#define CONFIG_SPI_FLASH +#define CONFIG_KEYBOARD_BOARD_CONFIG +#define CONFIG_KEYBOARD_PROTOCOL_8042 +#define CONFIG_POWER_BUTTON +#define CONFIG_VBOOT_HASH +#define CONFIG_PWM_KBLIGHT +#define CONFIG_BOARD_VERSION + +/* Optional features for test commands */ +#define CONFIG_CMD_TASKREADY +#define CONFIG_CMD_STACKOVERFLOW +#define CONFIG_CMD_JUMPTAGS +#define CONFIG_CMD_FLASH +#define CONFIG_CMD_SPI_FLASH +#define CONFIG_CMD_SCRATCHPAD +#define CONFIG_CMD_I2CWEDGE + +#define CONFIG_UART_HOST 0 +#define CONFIG_FANS 1 +#define CONFIG_SPI_FLASH_SIZE 0x00800000 /* 8MB spi flash */ + +/* Optional feature - used by nuvoton */ +#define CONFIG_PWM_INPUT_LFCLK /* PWM use LFCLK for input clock */ +#define CONFIG_MFT_INPUT_LFCLK /* MFT use LFCLK for input clock */ + +/* Optional for testing */ +#undef CONFIG_PSTORE +#define CONFIG_LOW_POWER_IDLE /* Deep Sleep Support */ + +/* Single I2C port, where the EC is the master. */ +#define I2C_PORT_MASTER 0 +#define I2C_PORT_HOST 0 + +#ifndef __ASSEMBLER__ + +enum adc_channel { + ADC_CH_0 = 0, + ADC_CH_1, + ADC_CH_2, + ADC_CH_COUNT +}; + +enum pwm_channel { + PWM_CH_FAN, + PWM_CH_KBLIGHT, + /* Number of PWM channels */ + PWM_CH_COUNT +}; + +enum fan_channel { + FAN_CH_0, + /* Number of FAN channels */ + FAN_CH_COUNT +}; + +enum mft_channel { + MFT_CH_0, + /* Number of MFT channels */ + MFT_CH_COUNT +}; + +#include "gpio_signal.h" + +#endif /* !__ASSEMBLER__ */ + +#endif /* __BOARD_H */ diff --git a/board/npcx_evb/build.mk b/board/npcx_evb/build.mk new file mode 100644 index 0000000000..8689f927bc --- /dev/null +++ b/board/npcx_evb/build.mk @@ -0,0 +1,12 @@ +# -*- makefile -*- +# Copyright (c) 2014 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. +# +# Board specific files build +# + +# the IC is Nuvoton M-Series EC +CHIP:=npcx + +board-y=board.o diff --git a/board/npcx_evb/ec.tasklist b/board/npcx_evb/ec.tasklist new file mode 100644 index 0000000000..f0a9acff85 --- /dev/null +++ b/board/npcx_evb/ec.tasklist @@ -0,0 +1,24 @@ +/* Copyright (c) 2014 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. + */ + +/** + * List of enabled tasks in the priority order + * + * The first one has the lowest priority. + * + * For each task, use the macro TASK_ALWAYS(n, r, d, s) for base tasks and + * TASK_NOTEST(n, r, d, s) for tasks that can be excluded in test binaries, + * where : + * 'n' is the name of the task + * 'r' is the main routine of the task + * 'd' is an opaque parameter passed to the routine at startup + * 's' is the stack size in bytes; must be a multiple of 8 + */ +#define CONFIG_TASK_LIST \ + TASK_ALWAYS(HOOKS, hook_task, NULL, LARGER_TASK_STACK_SIZE) \ + TASK_NOTEST(KEYPROTO, keyboard_protocol_task, NULL, TASK_STACK_SIZE) \ + TASK_ALWAYS(HOSTCMD, host_command_task, NULL, TASK_STACK_SIZE) \ + TASK_ALWAYS(CONSOLE, console_task, NULL, LARGER_TASK_STACK_SIZE) \ + TASK_NOTEST(KEYSCAN, keyboard_scan_task, NULL, TASK_STACK_SIZE) diff --git a/board/npcx_evb/gpio.inc b/board/npcx_evb/gpio.inc new file mode 100644 index 0000000000..08837dfea1 --- /dev/null +++ b/board/npcx_evb/gpio.inc @@ -0,0 +1,45 @@ +/* -*- mode:c -*- + * + * Copyright (c) 2014 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. + */ + +/* TODO: Redefine debug 2 inputs */ +GPIO(RECOVERY_L, 0, 0, GPIO_PULL_UP | GPIO_INT_BOTH, switch_interrupt) /* Recovery signal from servo */ +GPIO(WP_L, 0, 1, GPIO_PULL_DOWN | GPIO_INT_BOTH, switch_interrupt) /* Write protect input */ + +/* For testing 8042 commands, we need the following GPIOs */ +/* TODO: Redefine 4 inputs */ +GPIO(POWER_BUTTON_L, 0, 2, GPIO_PULL_UP | GPIO_INT_BOTH, power_button_interrupt) /* Power button */ +GPIO(LID_OPEN, 3, 3, GPIO_PULL_DOWN | GPIO_INT_BOTH, lid_interrupt) /* Lid switch */ +GPIO(ENTERING_RW, 3, 6, GPIO_OUT_LOW, NULL) /* Indicate when EC is entering RW code */ +GPIO(PCH_WAKE_L, 5, 0, GPIO_OUT_HIGH, NULL) /* Wake signal output to PCH */ + +/* Used for module testing */ +GPIO(PGOOD_FAN, C, 7, GPIO_PULL_UP | GPIO_INPUT, NULL) /* Power Good for FAN test */ +GPIO(SPI_CS_L, A, 5, GPIO_OUT_HIGH, NULL) /* SPI_CS Ready, Low Active. */ + +/* + * I2C pins should be configured as inputs until I2C module is + * initialized. This will avoid driving the lines unintentionally. + */ +GPIO(MASTER_I2C_SCL, B, 5, GPIO_INPUT, NULL) +GPIO(MASTER_I2C_SDA, B, 4, GPIO_INPUT, NULL) + +/* Used for board version command */ +GPIO(BOARD_VERSION1, 6, 4, GPIO_INPUT, NULL) /* Board version stuffing resistor 1 */ +GPIO(BOARD_VERSION2, 6, 5, GPIO_INPUT, NULL) /* Board version stuffing resistor 2 */ +GPIO(BOARD_VERSION3, 6, 6, GPIO_INPUT, NULL) /* Board version stuffing resistor 3 */ + +/* Alternate pins for UART/I2C/ADC/SPI/PWM/MFT */ +ALTERNATE(1, 0x03, 1, MODULE_UART, 0) /* CR_SIN/SOUT GPIO10/11 */ +ALTERNATE(B, 0x30, 1, MODULE_I2C, 0) /* I2C0SDA/I2C0SCL GPIOB4/B5 */ +ALTERNATE(8, 0x80, 1, MODULE_I2C, 0) /* I2C1SDA GPIO87 */ +ALTERNATE(9, 0x07, 1, MODULE_I2C, 0) /* I2C1SCL/I2C2SDA/I2C2SCL GPIO90/91/92 */ +ALTERNATE(4, 0x38, 1, MODULE_ADC, 0) /* ADC GPIO45/44/43 */ +ALTERNATE(A, 0x0A, 1, MODULE_SPI, 0) /* SPIP_MOSI/SPIP_SCLK GPIOA3/A1 */ +ALTERNATE(9, 0x20, 1, MODULE_SPI, 0) /* SPIP_MISO GPIO95 */ +ALTERNATE(C, 0x04, 1, MODULE_PWM_KBLIGHT, 0) /* PWM1 for PWM/KBLIGHT Test GPIOC2 */ +ALTERNATE(C, 0x08, 1, MODULE_PWM_FAN, 0) /* PWM0 for PWM/FAN Test GPIOC3 */ +ALTERNATE(4, 0x01, 1, MODULE_PWM_FAN, 0) /* MFT-1/TA1_TACH1 for FAN Test GPIOD3 */ diff --git a/chip/npcx/adc.c b/chip/npcx/adc.c new file mode 100644 index 0000000000..a715b08bfb --- /dev/null +++ b/chip/npcx/adc.c @@ -0,0 +1,241 @@ +/* Copyright (c) 2014 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. + */ + +/* NPCX-specific ADC module for Chrome EC */ + +#include "adc.h" +#include "adc_chip.h" +#include "atomic.h" +#include "clock.h" +#include "clock_chip.h" +#include "console.h" +#include "common.h" +#include "gpio.h" +#include "hooks.h" +#include "registers.h" +#include "task.h" +#include "timer.h" +#include "util.h" + +/* Maximum time we allow for an ADC conversion */ +#define ADC_TIMEOUT_US SECOND +#define ADC_CLK 2000000 +#define ADC_REGULAR_DLY 0x11 +#define ADC_REGULAR_ADCCNF2 0x8B07 +#define ADC_REGULAR_GENDLY 0x0100 +#define ADC_REGULAR_MEAST 0x0001 + +/* ADC conversion mode */ +enum npcx_adc_conversion_mode { + ADC_CHN_CONVERSION_MODE = 0, + ADC_SCAN_CONVERSION_MODE = 1 +}; + +/* ADC repetitive mode */ +enum npcx_adc_repetitive_mode { + ADC_ONE_SHOT_CONVERSION_TYPE = 0, + ADC_REPETITIVE_CONVERSION_TYPE = 1 +}; + + +/* Global variables */ +static task_id_t task_waiting; + +/** + * Preset ADC operation clock. + * + * @param none + * @return none + * @notes changed when initial or HOOK_FREQ_CHANGE command + */ +void adc_freq_changed(void) +{ + uint8_t prescaler_divider = 0; + + /* Set clock prescaler divider to ADC module*/ + prescaler_divider = (uint8_t)(clock_get_apb1_freq() / ADC_CLK); + if (prescaler_divider >= 1) + prescaler_divider = prescaler_divider - 1; + if (prescaler_divider > 0x3F) + prescaler_divider = 0x3F; + + /* Set Core Clock Division Factor in order to obtain the ADC clock */ + NPCX_ATCTL = (NPCX_ATCTL & (~(((1<<6)-1)<<NPCX_ATCTL_SCLKDIV))) + |(prescaler_divider<<NPCX_ATCTL_SCLKDIV); +} +DECLARE_HOOK(HOOK_FREQ_CHANGE, adc_freq_changed, HOOK_PRIO_DEFAULT); + +/** + * Get current voltage data of the specified channel. + * + * @param input_ch npcx input channel to read + * @return ADC channel voltage data.(Range: 0~1023) + */ +static int get_channel_data(enum npcx_adc_input_channel input_ch) +{ + return (NPCX_CHNDAT(input_ch)>>NPCX_CHNDAT_CHDAT) & ((1<<10)-1); +} + +/** + * Flush an ADC sequencer and initiate a read. + * + * @param input_ch operation channel + * @param timeout preset timeout + * @return TRUE/FALSE success/fail + * @notes set SW-triggered interrupt conversion and one-shot mode in npcx chip + */ +static int start_single_and_wait(enum npcx_adc_input_channel input_ch + , int timeout) +{ + int event; + + task_waiting = task_get_current(); + + /* Set ADC conversion code to SW conversion mode */ + NPCX_ADCCNF = (NPCX_ADCCNF & (~(((1<<2)-1)<<NPCX_ADCCNF_ADCMD))) + |(ADC_CHN_CONVERSION_MODE<<NPCX_ADCCNF_ADCMD); + + /* Set conversion type to one-shot type */ + NPCX_ADCCNF = (NPCX_ADCCNF & (~(((1<<1)-1)<<NPCX_ADCCNF_ADCRPTC))) + |(ADC_ONE_SHOT_CONVERSION_TYPE<<NPCX_ADCCNF_ADCRPTC); + + /* Update number of channel to be converted */ + NPCX_ASCADD = (NPCX_ASCADD & (~(((1<<5)-1)<<NPCX_ASCADD_SADDR))) + |(input_ch<<NPCX_ASCADD_SADDR); + + /* Clear End-of-Conversion Event status */ + SET_BIT(NPCX_ADCSTS, NPCX_ADCSTS_EOCEV); + + /* Enable ADC End-of-Conversion Interrupt if applicable */ + SET_BIT(NPCX_ADCCNF, NPCX_ADCCNF_INTECEN); + + /* Start conversion */ + SET_BIT(NPCX_ADCCNF, NPCX_ADCCNF_START); + + /* Wait for interrupt */ + event = task_wait_event(timeout); + + task_waiting = TASK_ID_INVALID; + + return event != TASK_EVENT_TIMER; + +} + +/** + * ADC read specific channel. + * + * @param ch operation channel + * @return ADC converted voltage or error message + */ +int adc_read_channel(enum adc_channel ch) +{ + const struct adc_t *adc = adc_channels + ch; + static struct mutex adc_lock; + int value; + + mutex_lock(&adc_lock); + + /* Enable ADC clock (bit4 mask = 0x10) */ + clock_enable_peripheral(CGC_OFFSET_ADC, CGC_ADC_MASK, + CGC_MODE_RUN | CGC_MODE_SLEEP); + + if (start_single_and_wait(adc->input_ch, ADC_TIMEOUT_US)) { + if ((adc->input_ch == + ((NPCX_ASCADD>>NPCX_ASCADD_SADDR)&((1<<5)-1))) + && (IS_BIT_SET(NPCX_CHNDAT(adc->input_ch), + NPCX_CHNDAT_NEW))) { + value = get_channel_data(adc->input_ch) * + adc->factor_mul / adc->factor_div + adc->shift; + } else { + value = ADC_READ_ERROR; + } + } else { + value = ADC_READ_ERROR; + } + /* Disable ADC clock (bit4 mask = 0x10) */ + clock_disable_peripheral(CGC_OFFSET_ADC, CGC_ADC_MASK, + CGC_MODE_RUN | CGC_MODE_SLEEP); + + mutex_unlock(&adc_lock); + + return value; +} + + +/** + * ADC read all channels. + * + * @param data all ADC converted voltage + * @return ADC converted error message + */ +int adc_read_all_channels(int *data) +{ + int i; + + for (i = 0; i < ADC_CH_COUNT; ++i) { + data[i] = adc_read_channel(i); + if (ADC_READ_ERROR == data[i]) + return EC_ERROR_UNKNOWN; + } + return EC_SUCCESS; +} + +/** + * ADC interrupt handler + * + * @param none + * @return none + * @notes Only handle SW-triggered conversion in npcx chip + */ +void adc_interrupt(void) +{ + if (IS_BIT_SET(NPCX_ADCSTS, NPCX_ADCSTS_EOCEV)) { + /* Disable End-of-Conversion Interrupt */ + CLEAR_BIT(NPCX_ADCCNF, NPCX_ADCCNF_INTECEN); + + /* Stop conversion */ + SET_BIT(NPCX_ADCCNF, NPCX_ADCCNF_STOP); + + /* Clear End-of-Conversion Event status */ + SET_BIT(NPCX_ADCSTS, NPCX_ADCSTS_EOCEV); + + /* Wake up the task which was waiting for the interrupt */ + if (task_waiting != TASK_ID_INVALID) + task_wake(task_waiting); + } +} +DECLARE_IRQ(NPCX_IRQ_ADC, adc_interrupt, 2); + +/** + * ADC initial. + * + * @param none + * @return none + */ +static void adc_init(void) +{ + /* Configure pins from GPIOs to ADCs */ + gpio_config_module(MODULE_ADC, 1); + + /* Enable ADC */ + SET_BIT(NPCX_ADCCNF, NPCX_ADCCNF_ADCEN); + + /* Set Core Clock Division Factor in order to obtain the ADC clock */ + adc_freq_changed(); + + /* Set regular speed */ + NPCX_ATCTL = (NPCX_ATCTL & (~(((1<<3)-1)<<NPCX_ATCTL_DLY))) + |((ADC_REGULAR_DLY - 1)<<NPCX_ATCTL_DLY); + NPCX_ADCCNF2 = ADC_REGULAR_ADCCNF2; + NPCX_GENDLY = ADC_REGULAR_GENDLY; + NPCX_MEAST = ADC_REGULAR_MEAST; + + task_waiting = TASK_ID_INVALID; + + /* Enable IRQs */ + task_enable_irq(NPCX_IRQ_ADC); + +} +DECLARE_HOOK(HOOK_INIT, adc_init, HOOK_PRIO_DEFAULT); diff --git a/chip/npcx/adc_chip.h b/chip/npcx/adc_chip.h new file mode 100644 index 0000000000..fda98a7d74 --- /dev/null +++ b/chip/npcx/adc_chip.h @@ -0,0 +1,39 @@ +/* Copyright (c) 2014 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. + */ + +/* NPCX-specific ADC module for Chrome EC */ + +#ifndef __CROS_EC_ADC_CHIP_H +#define __CROS_EC_ADC_CHIP_H + +/* Minimum and maximum values returned by raw ADC read. */ +#define ADC_READ_MIN 0 +#define ADC_READ_MAX 1023 +#define ADC_MAX_VOLT 3260 + +/* ADC input channel select */ +enum npcx_adc_input_channel { + NPCX_ADC_INPUT_CH0 = 0, + NPCX_ADC_INPUT_CH1, + NPCX_ADC_INPUT_CH2, + NPCX_ADC_INPUT_CH_COUNT +}; + +/* Data structure to define ADC channels. */ +struct adc_t { + const char *name; + enum npcx_adc_input_channel input_ch; + int factor_mul; + int factor_div; + int shift; +}; + +/* + * Boards must provide this list of ADC channel definitions. This must match + * the enum adc_channel list provided by the board. + */ +extern const struct adc_t adc_channels[]; + +#endif /* __CROS_EC_ADC_CHIP_H */ diff --git a/chip/npcx/build.mk b/chip/npcx/build.mk new file mode 100644 index 0000000000..c3f9a1c3de --- /dev/null +++ b/chip/npcx/build.mk @@ -0,0 +1,38 @@ +# -*- makefile -*- +# Copyright (c) 2014 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. +# +# NPCX chip specific files build +# + +# NPCX SoC has a Cortex-M4F ARM core +CORE:=cortex-m +# Allow the full Cortex-M4 instruction set +CFLAGS_CPU+=-march=armv7e-m -mcpu=cortex-m4 + +# Required chip modules +chip-y=clock.o gpio.o hwtimer.o jtag.o system.o uart.o + +# Optional chip modules +chip-$(CONFIG_ADC)+=adc.o chip_temp_sensor.o +chip-$(CONFIG_FANS)+=fan.o +chip-$(CONFIG_FLASH)+=flash.o +chip-$(CONFIG_I2C)+=i2c.o +chip-$(CONFIG_LPC)+=lpc.o +chip-$(CONFIG_PECI)+=peci.o +# pwm functions are implemented with the fan functions +chip-$(CONFIG_PWM)+=pwm.o fan.o +chip-$(CONFIG_SPI)+=spi.o +chip-$(CONFIG_WATCHDOG)+=watchdog.o +chip-$(HAS_TASK_KEYSCAN)+=keyboard_raw.o + +# little FW for booting +npcx-lfw=chip/npcx/lfw/ec_lfw +npcx-lfw-bin=${out}/$(npcx-lfw).bin +PROJECT_EXTRA+=${npcx-lfw-bin} + +# spi flash program fw for openocd +npcx-flash-fw=chip/npcx/spiflashfw/ec_npcxflash +npcx-flash-fw-bin=${out}/$(npcx-flash-fw).bin +PROJECT_EXTRA+=${npcx-flash-fw-bin} diff --git a/chip/npcx/chip_temp_sensor.c b/chip/npcx/chip_temp_sensor.c new file mode 100644 index 0000000000..7d2df97dbc --- /dev/null +++ b/chip/npcx/chip_temp_sensor.c @@ -0,0 +1,32 @@ +/* Copyright (c) 2014 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. + */ + +/* Temperature sensor module for Chrome EC */ + +#include "adc.h" +#include "adc_chip.h" +#include "common.h" +#include "hooks.h" + +/* Initialize temperature reading to a sane value (27 C) */ +static int last_val = C_TO_K(27); + +static void chip_temp_sensor_poll(void) +{ +#ifdef CONFIG_CMD_ECTEMP + last_val = adc_read_channel(ADC_CH_EC_TEMP); +#endif +} +DECLARE_HOOK(HOOK_SECOND, chip_temp_sensor_poll, HOOK_PRIO_TEMP_SENSOR); + +int chip_temp_sensor_get_val(int idx, int *temp_ptr) +{ + if (last_val == ADC_READ_ERROR) + return EC_ERROR_UNKNOWN; + + *temp_ptr = last_val; + + return EC_SUCCESS; +} diff --git a/chip/npcx/clock.c b/chip/npcx/clock.c new file mode 100644 index 0000000000..c96261c543 --- /dev/null +++ b/chip/npcx/clock.c @@ -0,0 +1,379 @@ +/* Copyright (c) 2014 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 "clock_chip.h" +#include "common.h" +#include "console.h" +#include "cpu.h" +#include "gpio.h" +#include "hooks.h" +#include "hwtimer.h" +#include "hwtimer_chip.h" +#include "registers.h" +#include "system.h" +#include "task.h" +#include "timer.h" +#include "uart.h" +#include "util.h" +#include "watchdog.h" + +/* Console output macros */ +#define CPUTS(outstr) cputs(CC_CLOCK, outstr) +#define CPRINTS(format, args...) cprints(CC_CLOCK, format, ## args) + +#define OSC_CLK 48000000 /* Default is 40MHz (target is 48MHz) */ +#define WAKE_INTERVAL 61 /* Unit: 61 usec */ +#define IDLE_PARAMS 0x7 /* Support deep idle, instant wake-up */ + +/* + * Frequency multiplier values definition according to the requested + * PLL_CLOCK Clock Frequency + */ +#define HFCGN 0x02 +#if (OSC_CLK == 50000000) +#define HFCGMH 0x0B +#define HFCGML 0xEC +#elif (OSC_CLK == 48000000) +#define HFCGMH 0x0B +#define HFCGML 0x72 +#elif (OSC_CLK == 40000000) +#define HFCGMH 0x09 +#define HFCGML 0x89 +#elif (OSC_CLK == 33000000) +#define HFCGMH 0x07 +#define HFCGML 0xDE +#else +#error "Unsupported FMCLK Clock Frequency" +#endif + +/* Low power idle statistics */ +#ifdef CONFIG_LOW_POWER_IDLE +static int idle_sleep_cnt; +static int idle_dsleep_cnt; +static uint64_t idle_dsleep_time_us; +/* + * Fixed amount of time to keep the console in use flag true after boot in + * order to give a permanent window in which the low speed clock is not used. + */ +#define CONSOLE_IN_USE_ON_BOOT_TIME (15*SECOND) +static int console_in_use_timeout_sec = 15; +static timestamp_t console_expire_time; +#endif + + +static int freq; + +/* Low power idle statistics */ + +/** + * Enable clock to peripheral by setting the CGC register pertaining + * to run, sleep, and/or deep sleep modes. + * + * @param offset Offset of the peripheral. See enum clock_gate_offsets. + * @param mask Bit mask of the bits within CGC reg to set. + * @param mode no used + */ +void clock_enable_peripheral(uint32_t offset, uint32_t mask, uint32_t mode) +{ + /* Don't support for different mode */ + uint8_t reg_mask = mask & 0xff; + + /* Set PD bit to 0 */ + NPCX_PWDWN_CTL(offset) &= ~reg_mask; + /* Wait for clock change to take affect. */ + clock_wait_cycles(3); +} + +/** + * Disable clock to peripheral by setting the CGC register pertaining + * to run, sleep, and/or deep sleep modes. + * + * @param offset Offset of the peripheral. See enum clock_gate_offsets. + * @param mask Bit mask of the bits within CGC reg to clear. + * @param mode no used + */ +void clock_disable_peripheral(uint32_t offset, uint32_t mask, uint32_t mode) +{ + /* Don't support for different mode */ + uint8_t reg_mask = mask & 0xff; + + /* Set PD bit to 1 */ + NPCX_PWDWN_CTL(offset) |= reg_mask; + +} + +/*****************************************************************************/ +/* IC specific low-level driver */ + +/** + * Set the CPU clocks and PLLs. + */ +void clock_init(void) +{ + /* + * Configure Frequency multiplier values according to the requested + * FMCLK Clock Frequency + */ + NPCX_HFCGN = HFCGN; + NPCX_HFCGML = HFCGML; + NPCX_HFCGMH = HFCGMH; + + /* Load M and N values into the frequency multiplier */ + SET_BIT(NPCX_HFCGCTRL, NPCX_HFCGCTRL_LOAD); + + /* Wait for stable */ + while (IS_BIT_SET(NPCX_HFCGCTRL, NPCX_HFCGCTRL_CLK_CHNG)) + ; + + /* Keep Core CLK & FMCLK are the same */ + NPCX_HFCGP = 0x00; + + freq = OSC_CLK; + + /* Notify modules of frequency change */ + hook_notify(HOOK_FREQ_CHANGE); +} + +/** + * Return the current clock frequency in Hz. + */ +int clock_get_freq(void) +{ + return freq; +} + +/** + * Return the current APB1 clock frequency in Hz. + */ +int clock_get_apb1_freq(void) +{ + int apb1_div = (NPCX_HFCBCD & 0x03) + 1; + return freq/apb1_div; +} + +/** + * Return the current APB2 clock frequency in Hz. + */ +int clock_get_apb2_freq(void) +{ + int apb2_div = ((NPCX_HFCBCD>>2) & 0x03) + 1; + return freq/apb2_div; +} + +/** + * Wait for a number of clock cycles. + * + * Simple busy waiting for use before clocks/timers are initialized. + * + * @param cycles Number of cycles to wait. + */ +void clock_wait_cycles(uint32_t cycles) +{ + asm("1: subs %0, #1\n" + " bne 1b\n" :: "r"(cycles)); +} + +#ifdef CONFIG_LOW_POWER_IDLE +void clock_refresh_console_in_use(void) +{ + /* Set console in use expire time. */ + console_expire_time = get_time(); + console_expire_time.val += console_in_use_timeout_sec * SECOND; + return; +} + +void clock_uart2gpio(void) +{ + /* Is pimux to UART? */ + if (IS_BIT_SET(NPCX_DEVALT(0x0A), NPCX_DEVALTA_UART_SL)) { + /* Change pinmux to GPIO and disable UART IRQ */ + task_disable_irq(NPCX_IRQ_UART); + CLEAR_BIT(NPCX_DEVALT(0x0A), NPCX_DEVALTA_UART_SL); + + /*Enable MIWU for GPIO (UARTRX) */ + SET_BIT(NPCX_WKEN(1, 1), 0); + /* Clear Pending bit of GPIO (UARTRX) */ + if (IS_BIT_SET(NPCX_WKPND(1, 1), 0)) + SET_BIT(NPCX_WKPCL(1, 1), 0); + /* Disable MIWU IRQ */ + task_disable_irq(NPCX_IRQ_WKINTB_1); + } +} + +void clock_gpio2uart(void) +{ + /* Is Pending bit of GPIO (UARTRX) */ + if (IS_BIT_SET(NPCX_WKPND(1, 1), 0)) { + /* Clear Pending bit of GPIO (UARTRX) */ + SET_BIT(NPCX_WKPCL(1, 1), 0); + /* Refresh console in-use timer */ + clock_refresh_console_in_use(); + /* Disable MIWU & IRQ for GPIO (UARTRX) */ + CLEAR_BIT(NPCX_WKEN(1, 1), 0); + /* Enable MIWU IRQ */ + task_enable_irq(NPCX_IRQ_WKINTB_1); + /* Go back CR_SIN*/ + SET_BIT(NPCX_DEVALT(0x0A), NPCX_DEVALTA_UART_SL); + /* Enable uart again */ + task_enable_irq(NPCX_IRQ_UART); + } +} + +/* Idle task. Executed when no tasks are ready to be scheduled. */ +void __idle(void) +{ +#ifdef SUPPORT_JTAG + while (1) { + /* + * TODO:(ML) JTAG bug: if debugger is connected, + * CPU can't enter wfi. Rev.B will fix it. + */ + ; + }; +#else + + timestamp_t t0, t1; + uint32_t next_evt_us; + + /* + * Initialize console in use to true and specify the console expire + * time in order to give a fixed window on boot in which the low speed + * clock will not be used in idle. + */ + console_expire_time.val = get_time().val + CONSOLE_IN_USE_ON_BOOT_TIME; + + while (1) { + /* + * Disable interrupts before going to deep sleep in order to + * calculate the appropriate time to wake up. Note: the wfi + * instruction waits until an interrupt is pending, so it + * will still wake up even with interrupts disabled. + */ + interrupt_disable(); + + /* Compute event delay */ + t0 = get_time(); + next_evt_us = __hw_clock_event_get() - t0.le.lo; + + /* Do we have enough time before next event to deep sleep. */ + if (DEEP_SLEEP_ALLOWED && (next_evt_us > WAKE_INTERVAL) + /* Make sure it's over console expired time */ + && (t0.val > console_expire_time.val)) { +#if DEBUG_CLK + /* Use GPIO to indicate SLEEP mode */ + CLEAR_BIT(NPCX_PDOUT(0), 0); +#endif + idle_dsleep_cnt++; + /* Set instant wake up mode */ + SET_BIT(NPCX_ENIDL_CTL, NPCX_ENIDL_CTL_LP_WK_CTL); + + /* Set deep idle - instant wake-up mode */ + NPCX_PMCSR = IDLE_PARAMS; + /* UART-rx(console) become to GPIO (NONE INT mode) */ + clock_uart2gpio(); + /* Enter deep idle */ + asm("wfi"); + /* GPIO back to UART-rx (console) */ + clock_gpio2uart(); + + /* Get time delay cause of deep idle */ + next_evt_us = __hw_clock_get_sleep_time(); + /* Fast forward timer according to wake-up timer. */ + t1.val = t0.val + next_evt_us; + /* Record time spent in deep sleep. */ + idle_dsleep_time_us += next_evt_us; + force_time(t1); + } else { +#if DEBUG_CLK + /* Use GPIO to indicate NORMAL mode */ + SET_BIT(NPCX_PDOUT(0), 0); +#endif + idle_sleep_cnt++; + /* normal idle : wait for interrupt */ + asm("wfi"); + } + + /* + * Restore interrupt + * RTOS will leave idle task to handle ISR which wakes up EC + */ + interrupt_enable(); + } +#endif +} +#endif /* CONFIG_LOW_POWER_IDLE */ + + +#ifdef CONFIG_LOW_POWER_IDLE +/** + * Print low power idle statistics + */ +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("Time spent in deep-sleep: %.6lds\n", + idle_dsleep_time_us); + ccprintf("Total time on: %.6lds\n", ts.val); + return EC_SUCCESS; +} +DECLARE_CONSOLE_COMMAND(idlestats, command_idle_stats, + "", + "Print last idle stats", + NULL); + +/** + * 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 low speed clock or + * allow it to use the low speed clock. + */ + if (v) + disable_sleep(SLEEP_MASK_FORCE_NO_LOW_SPEED); + else + 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); + ccprintf("PMCSR register: 0x%02x\n", NPCX_PMCSR); + + 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 use low speed clock.\nUse 'off' to " + "allow deep sleep to auto-select using the low speed " + "clock.\n" + "Give a timeout value for the console in use timeout.\n" + "See also 'sleepmask'.", + NULL); +#endif /* CONFIG_LOW_POWER_IDLE */ diff --git a/chip/npcx/clock_chip.h b/chip/npcx/clock_chip.h new file mode 100644 index 0000000000..ffc5958869 --- /dev/null +++ b/chip/npcx/clock_chip.h @@ -0,0 +1,21 @@ +/* Copyright (c) 2014 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. + */ + +/* NPCX-specific clock module for Chrome EC */ + +#ifndef CLOCK_CHIP_H_ +#define CLOCK_CHIP_H_ + +/** + * Return the current APB1 clock frequency in Hz. + */ +int clock_get_apb1_freq(void); + +/** + * Return the current APB2 clock frequency in Hz. + */ +int clock_get_apb2_freq(void); + +#endif /* CLOCK_CHIP_H_ */ diff --git a/chip/npcx/config_chip.h b/chip/npcx/config_chip.h new file mode 100644 index 0000000000..88afba0266 --- /dev/null +++ b/chip/npcx/config_chip.h @@ -0,0 +1,140 @@ +/* Copyright (c) 2014 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" +#define CONFIG_PSTATE_AT_END + +/* 32k hz internal oscillator frequency (FRCLK) */ +#define INT_32K_CLOCK 32768 + +/* Number of IRQ vectors on the NVIC */ +#define CONFIG_IRQ_COUNT 64 + +/* Use a bigger console output buffer */ +#undef CONFIG_UART_TX_BUF_SIZE +#define CONFIG_UART_TX_BUF_SIZE 8192 + +/* Interval between HOOK_TICK notifications */ +#define HOOK_TICK_INTERVAL_MS 250 +#define HOOK_TICK_INTERVAL (HOOK_TICK_INTERVAL_MS * MSEC) + +/* Maximum number of deferrable functions */ +#define DEFERRABLE_MAX_COUNT 8 + +/* Number of I2C ports */ +#define I2C_PORT_COUNT 4 + +/* Number of PWM ports */ +#define PWM_COUNT 8 + +/*****************************************************************************/ +/* Memory mapping */ +#define CONFIG_RAM_BASE 0x200C0000 /* memory map address of data ram */ +#define CONFIG_RAM_SIZE 0x00008000 /* 32KB data ram */ +#define CONFIG_CDRAM_BASE 0x10088000 /* memory map address of code ram */ +#define CONFIG_CDRAM_SIZE 0x00020000 /* 128KB code ram */ +#define CONFIG_FLASH_BASE 0x64000000 /* memory address of spi-flash */ +#define CONFIG_LPRAM_BASE 0x40001600 /* memory address of low power ram */ +#define CONFIG_LPRAM_SIZE 0x00000620 /* 1568B low power ram */ + +/* System stack size */ +#define CONFIG_STACK_SIZE 4096 + +/* non-standard task stack sizes */ +#define IDLE_TASK_STACK_SIZE 512 +#define LARGER_TASK_STACK_SIZE 768 +#define SMALLER_TASK_STACK_SIZE 384 + +/* Default task stack size */ +#define TASK_STACK_SIZE 512 + +/* SPI Flash Spec of W25Q20CV */ + +#define CONFIG_FLASH_BANK_SIZE 0x00001000 /* protect bank size 4K bytes */ +#define CONFIG_FLASH_ERASE_SIZE 0x00001000 /* sector erase size 4K bytes */ +#define CONFIG_FLASH_WRITE_SIZE 0x00000001 /* minimum write size */ + +#define CONFIG_FLASH_WRITE_IDEAL_SIZE 256 /* one page size for write */ +#define CONFIG_FLASH_PHYSICAL_SIZE 0x00040000 /* 256KB Flash used for EC */ + +/****************************************************************************/ +/* Define our flash layout. */ +/* Size of one firmware image in flash */ +#ifndef CONFIG_FW_IMAGE_SIZE +#define CONFIG_FW_IMAGE_SIZE (CONFIG_FLASH_PHYSICAL_SIZE / 2) +#endif + +/* RO firmware offset of flash */ +#define CONFIG_FW_RO_OFF 0 + +/* + * The EC uses the one bank of flash to emulate a SPI-like write protect + * register with persistent state. + */ +#define CONFIG_FW_PSTATE_SIZE CONFIG_FLASH_BANK_SIZE + +#ifdef CONFIG_PSTATE_AT_END +/* PSTATE is at end of flash */ +#define CONFIG_FW_RO_SIZE CONFIG_FW_IMAGE_SIZE +#define CONFIG_FW_PSTATE_OFF (CONFIG_FLASH_PHYSICAL_SIZE \ + - CONFIG_FW_PSTATE_SIZE) +/* Don't claim PSTATE is part of flash */ +#define CONFIG_FLASH_SIZE CONFIG_FW_PSTATE_OFF + +#else +/* PSTATE immediately follows RO, in the first half of flash */ +#define CONFIG_FW_RO_SIZE (CONFIG_FW_IMAGE_SIZE \ + - CONFIG_FW_PSTATE_SIZE) +#define CONFIG_FW_PSTATE_OFF CONFIG_FW_RO_SIZE +#define CONFIG_FLASH_SIZE CONFIG_FLASH_PHYSICAL_SIZE +#endif + +/* Either way, RW firmware is one firmware image offset from the start */ +#define CONFIG_FW_RW_OFF CONFIG_FW_IMAGE_SIZE +#define CONFIG_FW_RW_SIZE CONFIG_FW_IMAGE_SIZE + +/* TODO(crosbug.com/p/23796): why 2 sets of configs with the same numbers? */ +#define CONFIG_FW_WP_RO_OFF CONFIG_FW_RO_OFF +#define CONFIG_FW_WP_RO_SIZE CONFIG_FW_RO_SIZE + +/* + * The offset from top of flash wich used by booter + * the main funcationality to copy iamge from spi-flash to code ram + */ +#define CONFIG_LFW_OFFSET 0x1000 + +/****************************************************************************/ +/* Lock the boot configuration to prevent brickage. */ + +/* + * No GPIO trigger for ROM bootloader. + * Keep JTAG debugging enabled. + * Use 0xA442 flash write key. + * Lock it this way. + */ +#define CONFIG_BOOTCFG_VALUE 0x7ffffffe + +/****************************************************************************/ +/* Customize the build */ + +/* Optional features present on this chip */ +#define CONFIG_ADC +#define CONFIG_FPU +#define CONFIG_I2C +#define CONFIG_LPC +#define CONFIG_PECI +#define CONFIG_SWITCH +#define CONFIG_MPU +#define CONFIG_SPI + + +/* Compile for running from RAM instead of flash */ +/* #define COMPILE_FOR_RAM */ + +#endif /* __CROS_EC_CONFIG_CHIP_H */ diff --git a/chip/npcx/fan.c b/chip/npcx/fan.c new file mode 100644 index 0000000000..924f7716f4 --- /dev/null +++ b/chip/npcx/fan.c @@ -0,0 +1,611 @@ +/* Copyright (c) 2014 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. + */ + +/* NPCX fan control module. */ + +#include "clock.h" +#include "clock_chip.h" +#include "fan.h" +#include "fan_chip.h" +#include "gpio.h" +#include "hooks.h" +#include "registers.h" +#include "util.h" +#include "pwm.h" +#include "pwm_chip.h" +#include "console.h" +#include "timer.h" +#include "task.h" + +#if !(DEBUG_FAN) +#define CPRINTS(...) +#else +#define CPRINTS(format, args...) cprints(CC_PWM, format, ## args) +#endif + +/* Fan operation module */ +enum npcx_fan_op_module { + NPCX_FAN_OP_PWM, + NPCX_FAN_OP_MFT, + /* Number of FAN module operations */ + NPCX_FAN_OP_COUNT +}; + +/* MFT model select */ +enum npcx_mft_mdsel { + NPCX_MFT_MDSEL_1, + NPCX_MFT_MDSEL_2, + NPCX_MFT_MDSEL_3, + NPCX_MFT_MDSEL_4, + NPCX_MFT_MDSEL_5, + /* Number of MFT modes */ + NPCX_MFT_MDSEL_COUNT +}; + +/* MFT clock source */ +enum npcx_mft_clk_src { + TCKC_NOCLK = 0, + TCKC_PRESCALE_APB1_CLK, + TCKC_EXTERNAL, + TCKC_PULSE_ACC, + TCKC_LFCLK +}; + +#define RPM_SCALE 1 /* Fan RPM is multiplier of actual RPM */ +#define RPM_EDGES 1 /* Fan number of edges - 1 */ +/* + * RPM = (n - 1) * m * f * 60 / poles / TACH + * n = Fan number of edges = (RPM_EDGES + 1) + * m = Fan multiplier defined by RANGE + * f = PWM and MFT freq + * poles = 2 + */ +#define RPM_TO_TACH(pwm_channel, rpm) \ + MIN(((uint32_t)(pwm_channels[pwm_channel].freq)*30*RPM_EDGES*RPM_SCALE \ + /MAX((rpm), 1)), (pwm_channels[pwm_channel].cycle_pulses)) + +#define TACH_TO_RPM(mft_channel, tach) \ + ((mft_channels[mft_channel].freq)*30*RPM_EDGES*RPM_SCALE \ + /MAX((tach), 1)) + +/* Global variables */ +static volatile struct tacho_status_t tacho_status; +static int rpm_target; +static int rpm_actual = -1; +static int fan_init_ch; +/** + * Select fan operation channel by module. + * + * @param none + * @param op_module npcx operation module + * @return npcx operation channel by module + * @notes Fan is controlled by PWM/MFT module in npcx chip + */ +static int fan_op_ch(int ch, enum npcx_fan_op_module op_module) +{ + uint8_t op_ch; + + switch (ch) { + case 0: + if (op_module == NPCX_FAN_OP_PWM) + op_ch = PWM_CH_FAN; + else + op_ch = MFT_CH_0; + break; + default: + op_ch = 0; + break; + } + return op_ch; +} + +/** + * MFT start measure. + * + * @param ch operation channel + * @return none + */ +static void mft_startmeasure(int ch) +{ + int mft_ch = fan_op_ch(ch, NPCX_FAN_OP_MFT); + + /* Start measurement */ +#ifdef CONFIG_MFT_INPUT_LFCLK + /* Set the LFCLK clock. */ + if (NPCX_MFT_MODULE_PORT_TB == mft_channels[mft_ch].port) + NPCX_TCKC(mft_channels[mft_ch].module) = + (NPCX_TCKC(mft_channels[mft_ch].module) + &(~(((1<<3)-1)<<NPCX_TCKC_C2CSEL))) + |(TCKC_LFCLK<<NPCX_TCKC_C2CSEL); + else + NPCX_TCKC(mft_channels[mft_ch].module) = + (NPCX_TCKC(mft_channels[mft_ch].module) + &(~(((1<<3)-1)<<NPCX_TCKC_C1CSEL))) + |(TCKC_LFCLK<<NPCX_TCKC_C1CSEL); +#else + /* Set the core clock. */ + if (NPCX_MFT_MODULE_PORT_TB == mft_channels[mft_ch].port) + NPCX_TCKC(mft_channels[mft_ch].module) = + (NPCX_TCKC(mft_channels[mft_ch].module) + &(~(((1<<3)-1)<<NPCX_TCKC_C2CSEL))) + |(TCKC_PRESCALE_APB1_CLK<<NPCX_TCKC_C2CSEL); + else + NPCX_TCKC(mft_channels[mft_ch].module) = + (NPCX_TCKC(mft_channels[mft_ch].module) + &(~(((1<<3)-1)<<NPCX_TCKC_C1CSEL))) + |(TCKC_PRESCALE_APB1_CLK<<NPCX_TCKC_C1CSEL); +#endif +} + +/** + * MFT stop measure. + * + * @param ch operation channel + * @return none + */ +static void mft_stopmeasure(int ch) +{ + int mft_ch = fan_op_ch(ch, NPCX_FAN_OP_MFT); + + /* Clear all pending flag */ + NPCX_TECLR(mft_channels[mft_ch].module) = + NPCX_TECTRL(mft_channels[mft_ch].module); + + /* Stop the timer */ + if (NPCX_MFT_MODULE_PORT_TB == mft_channels[mft_ch].port) + NPCX_TCKC(mft_channels[mft_ch].module) = + (NPCX_TCKC(mft_channels[mft_ch].module) + &(~(((1<<3)-1)<<NPCX_TCKC_C2CSEL))) + |(TCKC_NOCLK<<NPCX_TCKC_C2CSEL); + else + NPCX_TCKC(mft_channels[mft_ch].module) = + (NPCX_TCKC(mft_channels[mft_ch].module) + &(~(((1<<3)-1)<<NPCX_TCKC_C1CSEL))) + |(TCKC_NOCLK<<NPCX_TCKC_C1CSEL); +} + +/** + * MFT final measure. + * + * @param ch operation channel + * @return none + */ +static void mft_finalmeasure(int ch) +{ + int mft_ch = fan_op_ch(ch, NPCX_FAN_OP_MFT); + + /* + * Start of the last tacho cycle is detected - + * calculated tacho cycle duration + */ + if (NPCX_MFT_MODULE_PORT_TB == mft_channels[mft_ch].port) + tacho_status.edge_interval = + (uint32_t)(mft_channels[mft_ch].default_count + - NPCX_TCRB(mft_channels[mft_ch].module)); + else + tacho_status.edge_interval = + (uint32_t)(mft_channels[mft_ch].default_count + - NPCX_TCRA(mft_channels[mft_ch].module)); +} + +/** + * Preset fan operation clock. + * + * @param none + * @return none + * @notes changed when initial or HOOK_FREQ_CHANGE command + */ +#ifndef CONFIG_MFT_INPUT_LFCLK +void mft_freq_changed(void) +{ + uint16_t prescaler_divider = 0; + int mft_ch = fan_op_ch(fan_init_ch, NPCX_FAN_OP_MFT); + + /* Set clock prescaler divider to MFT module*/ + prescaler_divider = (uint16_t)(clock_get_apb1_freq() + /mft_channels[mft_ch].freq); + if (prescaler_divider >= 1) + prescaler_divider = prescaler_divider - 1; + if (prescaler_divider > 0xFF) + prescaler_divider = 0xFF; + + NPCX_TPRSC(mft_channels[mft_ch].module) = (uint8_t)prescaler_divider; +} +DECLARE_HOOK(HOOK_FREQ_CHANGE, mft_freq_changed, HOOK_PRIO_DEFAULT); +#endif + +/** + * Fan configuration. + * + * @param ch operation channel + * @param enable_mft_read_rpm FAN_USE_RPM_MODE enable flag + * @return none + */ +static void fan_config(int ch, int enable_mft_read_rpm) +{ + int pwm_ch = fan_op_ch(ch, NPCX_FAN_OP_PWM); + int mft_ch = fan_op_ch(ch, NPCX_FAN_OP_MFT); + + fan_init_ch = ch; + pwm_config(pwm_ch); + + /* Mux mft */ + CLEAR_BIT(NPCX_DEVALT(3), NPCX_DEVALT3_TB1_TACH2_SL1); + /* Configure pins from GPIOs to FAN */ + gpio_config_module(MODULE_PWM_FAN, 1); + + if (enable_mft_read_rpm) { + /* Set mode 5 to MFT module*/ + NPCX_TMCTRL(mft_channels[mft_ch].module) = + (NPCX_TMCTRL(mft_channels[mft_ch].module) + & (~(((1<<3)-1)<<NPCX_TMCTRL_MDSEL))) + | (NPCX_MFT_MDSEL_5<<NPCX_TMCTRL_MDSEL); + +#ifndef CONFIG_MFT_INPUT_LFCLK + /* Set MFT operation frequence */ + mft_freq_changed(); + /* Set the active power mode. */ + CLEAR_BIT(NPCX_TCKC(mft_channels[mft_ch].module), + NPCX_TCKC_LOW_PWR); +#else + /* Set the low power mode. */ + SET_BIT(NPCX_TCKC(mft_channels[mft_ch].module), + NPCX_TCKC_LOW_PWR); +#endif + if (NPCX_MFT_MODULE_PORT_TB == mft_channels[mft_ch].port) { + /* Set the default count-down timer. */ + NPCX_TCNT2(mft_channels[mft_ch].module) = + mft_channels[mft_ch].default_count; + NPCX_TCRB(mft_channels[mft_ch].module) = + mft_channels[mft_ch].default_count; + /* Set the edge polarity to rising. */ + SET_BIT(NPCX_TMCTRL(mft_channels[mft_ch].module), + NPCX_TMCTRL_TBEDG); + /* Enable capture TCNT2 into TCRB and preset TCNT2. */ + SET_BIT(NPCX_TMCTRL(mft_channels[mft_ch].module), + NPCX_TMCTRL_TBEN); + /* Enable input debounce logic into TB. */ + SET_BIT(NPCX_TCFG(mft_channels[mft_ch].module), + NPCX_TCFG_TBDBEN); + /* Set the no clock to TCNT2. */ + NPCX_TCKC(mft_channels[mft_ch].module) = + (NPCX_TCKC(mft_channels[mft_ch].module) + & (~(((1<<3)-1)<<NPCX_TCKC_C2CSEL))) + | (TCKC_NOCLK<<NPCX_TCKC_C2CSEL); + /* Set timer wake-up enable */ + SET_BIT(NPCX_TWUEN(mft_channels[mft_ch].module), + NPCX_TWUEN_TBWEN); + SET_BIT(NPCX_TWUEN(mft_channels[mft_ch].module), + NPCX_TWUEN_TDWEN); + } else { + /* Set the default count-down timer. */ + NPCX_TCNT1(mft_channels[mft_ch].module) = + mft_channels[mft_ch].default_count; + NPCX_TCRA(mft_channels[mft_ch].module) = + mft_channels[mft_ch].default_count; + /* Set the edge polarity to rising. */ + SET_BIT(NPCX_TMCTRL(mft_channels[mft_ch].module), + NPCX_TMCTRL_TAEDG); + /* Enable capture TCNT1 into TCRA and preset TCNT1. */ + SET_BIT(NPCX_TMCTRL(mft_channels[mft_ch].module), + NPCX_TMCTRL_TAEN); + /* Enable input debounce logic into TA. */ + SET_BIT(NPCX_TCFG(mft_channels[mft_ch].module), + NPCX_TCFG_TADBEN); + /* Set the no clock to TCNT1. */ + NPCX_TCKC(mft_channels[mft_ch].module) = + (NPCX_TCKC(mft_channels[mft_ch].module) + & (~(((1<<3)-1)<<NPCX_TCKC_C1CSEL))) + | (TCKC_NOCLK<<NPCX_TCKC_C1CSEL); + /* Set timer wake-up enable */ + SET_BIT(NPCX_TWUEN(mft_channels[mft_ch].module), + NPCX_TWUEN_TAWEN); + SET_BIT(NPCX_TWUEN(mft_channels[mft_ch].module), + NPCX_TWUEN_TCWEN); + } + } + + /* Back to Idle mode*/ + tacho_status.cur_state = TACHO_IN_IDLE; +} + +/** + * Set fan enabled. + * + * @param ch operation channel + * @param enabled enabled flag + * @return none + */ +void fan_set_enabled(int ch, int enabled) +{ + int pwm_ch = fan_op_ch(ch, NPCX_FAN_OP_PWM); + + pwm_enable(pwm_ch, enabled); +} + +/** + * Check fan enabled. + * + * @param ch operation channel + * @return enabled or not + */ +int fan_get_enabled(int ch) +{ + int pwm_ch = fan_op_ch(ch, NPCX_FAN_OP_PWM); + + return pwm_get_enabled(pwm_ch); +} + +/** + * Set fan duty cycle. + * + * @param ch operation channel + * @param percent duty cycle percent + * @return none + */ +void fan_set_duty(int ch, int percent) +{ + int pwm_ch = fan_op_ch(ch, NPCX_FAN_OP_PWM); + + CPRINTS("set duty percent=%d", percent); + + /* Set the duty cycle */ + pwm_set_duty(pwm_ch, percent); +} + +/** + * Get fan duty cycle. + * + * @param ch operation channel + * @return duty cycle + */ +int fan_get_duty(int ch) +{ + int pwm_ch = fan_op_ch(ch, NPCX_FAN_OP_PWM); + + /* Return percent */ + return pwm_get_duty(pwm_ch); +} + +/** + * Check fan is rpm operation mode. + * + * @param ch operation channel + * @return rpm operation mode or not + * @notes not support in npcx chip + */ +int fan_get_rpm_mode(int ch) +{ + /* TODO: (Benson_TBD_4) not support rpm mode, return 0 always */ + return 0; +} + +/** + * Set fan to rpm operation mode. + * + * @param ch operation channel + * @param rpm_mode rpm operation mode flag + * @return none + * @notes not support in npcx chip + */ +void fan_set_rpm_mode(int ch, int rpm_mode) +{ + /* TODO: (Benson_TBD_4) not support rpm mode */ +} + +/** + * Get fan actual operation rpm. + * + * @param ch operation channel + * @return actual operation rpm value + */ +int fan_get_rpm_actual(int ch) +{ + int mft_ch = fan_op_ch(ch, NPCX_FAN_OP_MFT); + uint8_t capture_pnd = NPCX_TECTRL_TBPND, + underflow_pnd = NPCX_TECTRL_TDPND; + uint8_t capture_clr = NPCX_TECLR_TBCLR, + underflow_clr = NPCX_TECLR_TDCLR; + + /* Init pending/clear flag bit */ + if (NPCX_MFT_MODULE_PORT_TA == mft_channels[mft_ch].port) { + capture_pnd = NPCX_TECTRL_TAPND; + underflow_pnd = NPCX_TECTRL_TCPND; + capture_clr = NPCX_TECLR_TACLR; + underflow_clr = NPCX_TECLR_TCCLR; + } + /* Start measure and return previous value when fan is working*/ + if ((fan_get_enabled(ch)) && (fan_get_duty(ch))) { + if (tacho_status.cur_state == TACHO_IN_IDLE) { + CPRINTS("mft_startmeasure"); + if ((0 == rpm_actual) || (-1 == rpm_actual)) + rpm_actual = fans[ch].rpm_min; + /* Clear all pending flags */ + NPCX_TECLR(mft_channels[mft_ch].module) = + NPCX_TECTRL(mft_channels[mft_ch].module); + /* Start from first edge state */ + tacho_status.cur_state = TACHO_WAIT_FOR_1_EDGE; + /* Start measure */ + mft_startmeasure(ch); + } + /* Check whether MFT underflow flag is occurred */ + else if (IS_BIT_SET(NPCX_TECTRL(mft_channels[mft_ch].module), + underflow_pnd)) { + /* Measurement is active - stop the measurement */ + mft_stopmeasure(fan_init_ch); + /* Need to avoid underflow state happen */ + rpm_actual = fans[ch].rpm_max; + /* + * Flag TDPND means mft underflow happen then complete + * measurement immediately + */ + tacho_status.cur_state = TACHO_UNDERFLOW; + CPRINTS("TACHO_UNDERFLOW"); + + /* Clear pending flags */ + SET_BIT(NPCX_TECLR(mft_channels[mft_ch].module), + underflow_clr); + } + /* Check whether MFT signal detection flag is occurred */ + else if (IS_BIT_SET(NPCX_TECTRL(mft_channels[mft_ch].module), + capture_pnd)) { + /* Start of tacho cycle is detected */ + switch (tacho_status.cur_state) { + case TACHO_WAIT_FOR_1_EDGE: + CPRINTS("TACHO_WAIT_FOR_1_EDGE"); + /* + * Start of the first tacho cycle is detected + * and wait for the second tacho cycle + * (second edge) + */ + tacho_status.cur_state = TACHO_WAIT_FOR_2_EDGE; + /* Send previous rpm before complete measure */ + break; + case TACHO_WAIT_FOR_2_EDGE: + /* Complete measure tach and get actual tach */ + mft_finalmeasure(fan_init_ch); + /* Stop the measurement */ + mft_stopmeasure(ch); + /* Transfer actual tach to actual rpm */ + rpm_actual = (tacho_status.edge_interval > 0) ? + (TACH_TO_RPM(mft_ch, + tacho_status.edge_interval)) : 0; + /* Back to Idle mode*/ + tacho_status.cur_state = TACHO_IN_IDLE; + CPRINTS("TACHO_WAIT_FOR_2_EDGE"); + CPRINTS("edge_interval=%x", + tacho_status.edge_interval); + CPRINTS("rpm_actual=%d", rpm_actual); + break; + default: + break; + } + /* Clear pending flags */ + SET_BIT(NPCX_TECLR(mft_channels[mft_ch].module), + capture_clr); + } + } else { + CPRINTS("preset rpm"); + /* Send preset rpm before fan is working */ + if (fan_get_enabled(ch)) + rpm_actual = fans[ch].rpm_min; + else + rpm_actual = 0; + + tacho_status.cur_state = TACHO_IN_IDLE; + } + return rpm_actual; +} + +/** + * Get fan setting rpm. + * + * @param ch operation channel + * @return setting rpm value + */ +int fan_get_rpm_target(int ch) +{ + return rpm_target; +} + +/** + * Set fan setting rpm. + * + * @param ch operation channel + * @param rpm setting rpm value + * @return none + */ +void fan_set_rpm_target(int ch, int rpm) +{ + uint32_t percent = 0; + int pwm_ch = fan_op_ch(ch, NPCX_FAN_OP_PWM); + + rpm_target = rpm; + /* Transfer rpm to tach then calculate percentage */ + percent = (RPM_TO_TACH(pwm_ch, rpm_target)*100) + /(pwm_channels[pwm_ch].cycle_pulses); + + if (percent < 0) + percent = 0; + else if (percent > 100) + percent = 100; + + /* RPM is inverse ratio to tach and percentage */ + percent = 100 - percent; + + pwm_set_duty(pwm_ch, percent); +} + +/** + * Check fan operation status. + * + * @param ch operation channel + * @return fan_status fan operation status + */ +enum fan_status fan_get_status(int ch) +{ + int rpm_actual; + + rpm_actual = fan_get_rpm_actual(ch); + + if (((fan_get_duty(ch)) && (0 == rpm_actual)) + || ((!fan_get_duty(ch)) && (rpm_actual))) + return FAN_STATUS_FRUSTRATED; + else if ((rpm_actual == 0) && (!fan_get_duty(ch))) + return FAN_STATUS_STOPPED; + else + return FAN_STATUS_LOCKED; +} + +/** + * Check fan is stall condition. + * + * @param ch operation channel + * @return non-zero if fan is enabled but stalled + */ +int fan_is_stalled(int ch) +{ + int rpm_actual; + + rpm_actual = fan_get_rpm_actual(ch); + + /* Check for normal condition, others are stall condition */ + if ((!fan_get_enabled(ch)) || ((fan_get_duty(ch)) && (rpm_actual)) + || ((!fan_get_duty(ch)) && (!rpm_actual))) + return 0; + + return 1; +} + +/** + * Fan channel setup. + * + * @param ch operation channel + * @param flags input flags + * @return none + */ +void fan_channel_setup(int ch, unsigned int flags) +{ + fan_config(ch, (flags & FAN_USE_RPM_MODE)); +} + +/** + * Fan initial. + * + * @param none + * @return none + */ +static void fan_init(void) +{ +#ifdef CONFIG_PWM_DSLEEP + /* Enable the fan module and delay a few clocks */ + clock_enable_peripheral(CGC_OFFSET_FAN, CGC_FAN_MASK, CGC_MODE_ALL); +#else + /* Enable the fan module and delay a few clocks */ + clock_enable_peripheral(CGC_OFFSET_FAN, CGC_FAN_MASK, + CGC_MODE_RUN | CGC_MODE_SLEEP); +#endif +} +DECLARE_HOOK(HOOK_INIT, fan_init, HOOK_PRIO_INIT_PWM); diff --git a/chip/npcx/fan_chip.h b/chip/npcx/fan_chip.h new file mode 100644 index 0000000000..b6af2a7c2e --- /dev/null +++ b/chip/npcx/fan_chip.h @@ -0,0 +1,62 @@ +/* Copyright (c) 2014 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. + */ + +/* NPCX-specific MFT module for Chrome EC */ + +#ifndef __CROS_EC_NPCX_FAN_H +#define __CROS_EC_NPCX_FAN_H + +/* MFT module select */ +enum npcx_mft_module { + NPCX_MFT_MODULE_1 = 0, + NPCX_MFT_MODULE_2 = 0, + NPCX_MFT_MODULE_3 = 0, + /* Number of MFT modules */ + NPCX_MFT_MODULE_COUNT +}; + +/* MFT module port */ +enum npcx_mft_module_port { + NPCX_MFT_MODULE_PORT_TA, + NPCX_MFT_MODULE_PORT_TB, + /* Number of MFT module ports */ + NPCX_MFT_MODULE_PORT_COUNT +}; + +/* Data structure to define MFT channels. */ +struct mft_t { + /* MFT module ID */ + enum npcx_mft_module module; + /* MFT port */ + enum npcx_mft_module_port port; + /* MFT TCNT default count */ + uint32_t default_count; + /* MFT freq */ + uint32_t freq; +}; + +/* Tacho measurement state */ +enum tacho_measure_state { + /* Tacho init state */ + TACHO_IN_IDLE = 0, + /* Tacho first edge state */ + TACHO_WAIT_FOR_1_EDGE, + /* Tacho second edge state */ + TACHO_WAIT_FOR_2_EDGE, + /* Tacho underflow state */ + TACHO_UNDERFLOW +}; + +/* Tacho status data structure */ +struct tacho_status_t { + /* Current state of the measurement */ + enum tacho_measure_state cur_state; + /* Pulse counter value between edge1 and edge2 */ + uint32_t edge_interval; +}; + +extern const struct mft_t mft_channels[]; + +#endif /* __CROS_EC_NPCX_FAN_H */ diff --git a/chip/npcx/flash.c b/chip/npcx/flash.c new file mode 100644 index 0000000000..3114848c3f --- /dev/null +++ b/chip/npcx/flash.c @@ -0,0 +1,708 @@ +/* Copyright (c) 2014 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. + */ + +/* Flash memory module for Chrome EC */ + +#include "flash.h" +#include "registers.h" +#include "switch.h" +#include "system.h" +#include "timer.h" +#include "util.h" +#include "task.h" +#include "watchdog.h" +#include "console.h" +#include "hwtimer_chip.h" + +int all_protected; /* Has all-flash protection been requested? */ +int addr_prot_start; +int addr_prot_length; + +#define FLASH_ABORT_TIMEOUT 10000 + +#ifdef CONFIG_CODERAM_ARCH +#define TRISTATE_FLASH(x) +#else +#define TRISTATE_FLASH(x) flash_tristate(x) +#endif +/*****************************************************************************/ +/* flash internal functions */ +void flash_pinmux(int enable) +{ + /* Select pin-mux for FIU*/ + UPDATE_BIT(NPCX_DEVALT(0), NPCX_DEVALT0_NO_F_SPI, !enable); + + /* CS0/1 pinmux */ + if (enable) { +#if (FIU_CHIP_SELECT == 1) + SET_BIT(NPCX_DEVALT(0), NPCX_DEVALT0_F_SPI_CS1_1); +#elif (FIU_CHIP_SELECT == 2) + SET_BIT(NPCX_DEVALT(0), NPCX_DEVALT0_F_SPI_CS1_2); +#endif + } else { + CLEAR_BIT(NPCX_DEVALT(0), NPCX_DEVALT0_F_SPI_CS1_1); + CLEAR_BIT(NPCX_DEVALT(0), NPCX_DEVALT0_F_SPI_CS1_2); + } +} + +void flash_tristate(int enable) +{ + /* Enable/Disable FIU pins to tri-state */ + UPDATE_BIT(NPCX_DEVCNT, NPCX_DEVCNT_F_SPI_TRIS, enable); +} + +void flash_execute_cmd(uint8_t code, uint8_t cts) +{ + /* set UMA_CODE */ + NPCX_UMA_CODE = code; + /* execute UMA flash transaction */ + NPCX_UMA_CTS = cts; + while (IS_BIT_SET(NPCX_UMA_CTS, NPCX_UMA_CTS_EXEC_DONE)) + ; +} + +void flash_cs_level(int level) +{ + /* Set chip select to high/low level */ + UPDATE_BIT(NPCX_UMA_ECTS, NPCX_UMA_ECTS_SW_CS1, level); +} + +void flash_wait_ready(void) +{ + uint8_t mask = SPI_FLASH_SR1_BUSY; + uint16_t timeout = FLASH_ABORT_TIMEOUT; + + /* Chip Select down. */ + flash_cs_level(0); + /* Command for Read status register */ + flash_execute_cmd(CMD_READ_STATUS_REG, MASK_CMD_ONLY); + while (--timeout) { + /* Read status register */ + NPCX_UMA_CTS = MASK_RD_1BYTE; + while (IS_BIT_SET(NPCX_UMA_CTS, NPCX_UMA_CTS_EXEC_DONE)) + ; + /* Busy bit is clear */ + if ((NPCX_UMA_DB0 & mask) == 0) + break; + + /* check task scheduling has started to prevent infinite loop */ + if (task_start_called()) + msleep(1); + }; /* Wait for Busy clear */ + /* Chip Select high. */ + flash_cs_level(1); +} + +int flash_write_enable(void) +{ + uint8_t mask = SPI_FLASH_SR1_WEL; + /* Write enable command */ + flash_execute_cmd(CMD_WRITE_EN, MASK_CMD_ONLY); + /* Wait for flash is not busy */ + flash_wait_ready(); + + if (NPCX_UMA_DB0 & mask) + return 1; + else + return 0; +} + +void flash_set_address(uint32_t dest_addr) +{ + uint8_t *addr = (uint8_t *)&dest_addr; + /* Write address */ + NPCX_UMA_AB2 = addr[2]; + NPCX_UMA_AB1 = addr[1]; + NPCX_UMA_AB0 = addr[0]; +} + +uint8_t flash_get_status1(void) +{ + /* Disable tri-state */ + TRISTATE_FLASH(0); + /* Read status register1 */ + flash_execute_cmd(CMD_READ_STATUS_REG, MASK_CMD_RD_1BYTE); + /* Enable tri-state */ + TRISTATE_FLASH(1); + return NPCX_UMA_DB0; +} + +uint8_t flash_get_status2(void) +{ + /* Disable tri-state */ + TRISTATE_FLASH(0); + /* Read status register2 */ + flash_execute_cmd(CMD_READ_STATUS_REG2, MASK_CMD_RD_1BYTE); + /* Enable tri-state */ + TRISTATE_FLASH(1); + return NPCX_UMA_DB0; +} + +/*****************************************************************************/ +/* flash protection functions */ +/* Use a copy function of spi_flash.c in flash driver */ +/** + * Computes block write protection range from registers + * Returns start == len == 0 for no protection + * + * @param sr1 Status register 1 + * @param sr2 Status register 2 + * @param start Output pointer for protection start offset + * @param len Output pointer for protection length + * + * @return EC_SUCCESS, or non-zero if any error. + */ +static int reg_to_protect(uint8_t sr1, uint8_t sr2, unsigned int *start, + unsigned int *len) +{ + int blocks; + int size; + uint8_t cmp; + uint8_t sec; + uint8_t tb; + uint8_t bp; + + /* Determine flags */ + cmp = (sr2 & SPI_FLASH_SR2_CMP) ? 1 : 0; + sec = (sr1 & SPI_FLASH_SR1_SEC) ? 1 : 0; + tb = (sr1 & SPI_FLASH_SR1_TB) ? 1 : 0; + bp = (sr1 & (SPI_FLASH_SR1_BP2 | SPI_FLASH_SR1_BP1 | SPI_FLASH_SR1_BP0)) + >> 2; + + /* Bad pointers or invalid data */ + if (!start || !len || sr1 == -1 || sr2 == -1) + return EC_ERROR_INVAL; + + /* Not defined by datasheet */ + if (sec && bp == 6) + return EC_ERROR_INVAL; + + /* Determine granularity (4kb sector or 64kb block) */ + /* Computation using 2 * 1024 is correct */ + size = sec ? (2 * 1024) : (64 * 1024); + + /* Determine number of blocks */ + /* Equivalent to pow(2, bp) with pow(2, 0) = 0 */ + blocks = bp ? (1 << bp) : 0; + /* Datasheet specifies don't care for BP == 4, BP == 5 */ + if (sec && bp == 5) + blocks = (1 << 4); + + /* Determine number of bytes */ + *len = size * blocks; + + /* Determine bottom/top of memory to protect */ + *start = tb ? 0 : + (CONFIG_FLASH_SIZE - *len) % CONFIG_FLASH_SIZE; + + /* Reverse computations if complement set */ + if (cmp) { + *start = (*start + *len) % CONFIG_FLASH_SIZE; + *len = CONFIG_FLASH_SIZE - *len; + } + + return EC_SUCCESS; +} + +/** + * Computes block write protection registers from range + * + * @param start Desired protection start offset + * @param len Desired protection length + * @param sr1 Output pointer for status register 1 + * @param sr2 Output pointer for status register 2 + * + * @return EC_SUCCESS, or non-zero if any error. + */ +static int protect_to_reg(unsigned int start, unsigned int len, + uint8_t *sr1, uint8_t *sr2) +{ + char cmp = 0; + char sec = 0; + char tb = 0; + char bp = 0; + int blocks; + int size; + + /* Bad pointers */ + if (!sr1 || !sr2 || *sr1 == -1 || *sr2 == -1) + return EC_ERROR_INVAL; + + /* Invalid data */ + if ((start && !len) || start + len > CONFIG_FLASH_SIZE) + return EC_ERROR_INVAL; + + /* Set complement bit based on whether length is power of 2 */ + if ((len & (len - 1)) != 0) { + cmp = 1; + start = (start + len) % CONFIG_FLASH_SIZE; + len = CONFIG_FLASH_SIZE - len; + } + + /* Set bottom/top bit based on start address */ + /* Do not set if len == 0 or len == CONFIG_FLASH_SIZE */ + if (!start && (len % CONFIG_FLASH_SIZE)) + tb = 1; + + /* Set sector bit and determine block length based on protect length */ + if (len == 0 || len >= 128 * 1024) { + sec = 0; + size = 64 * 1024; + } else if (len >= 4 * 1024 && len <= 32 * 1024) { + sec = 1; + size = 2 * 1024; + } else + return EC_ERROR_INVAL; + + /* Determine number of blocks */ + if (len % size != 0) + return EC_ERROR_INVAL; + blocks = len / size; + + /* Determine bp = log2(blocks) with log2(0) = 0 */ + bp = blocks ? (31 - __builtin_clz(blocks)) : 0; + + /* Clear bits */ + *sr1 &= ~(SPI_FLASH_SR1_SEC | SPI_FLASH_SR1_TB + | SPI_FLASH_SR1_BP2 | SPI_FLASH_SR1_BP1 | SPI_FLASH_SR1_BP0); + *sr2 &= ~SPI_FLASH_SR2_CMP; + + /* Set bits */ + *sr1 |= (sec ? SPI_FLASH_SR1_SEC : 0) | (tb ? SPI_FLASH_SR1_TB : 0) + | (bp << 2); + *sr2 |= (cmp ? SPI_FLASH_SR2_CMP : 0); + + return EC_SUCCESS; +} + +int flash_set_status_for_prot(int reg1, int reg2) +{ + /* Disable tri-state */ + TRISTATE_FLASH(0); + /* Enable write */ + flash_write_enable(); + + NPCX_UMA_DB0 = reg1; + NPCX_UMA_DB1 = reg2; + + /* Write status register 1/2 */ + flash_execute_cmd(CMD_WRITE_STATUS_REG, MASK_CMD_WR_2BYTE); + /* Enable tri-state */ + TRISTATE_FLASH(1); + + reg_to_protect(reg1, reg2, &addr_prot_start, &addr_prot_length); + + return EC_SUCCESS; +} + +int flash_check_prot_range(unsigned int offset, unsigned int bytes) +{ + /* Invalid value */ + if (offset + bytes > CONFIG_FLASH_PHYSICAL_SIZE) + return EC_ERROR_INVAL; + /* Check if ranges overlap */ + if (MAX(addr_prot_start, offset) < MIN(addr_prot_start + + addr_prot_length, offset + bytes)) + return EC_ERROR_ACCESS_DENIED; + + return EC_SUCCESS; +} + +int flash_check_prot_reg(unsigned int offset, unsigned int bytes) +{ + unsigned int start; + unsigned int len; + uint8_t sr1 = 0, sr2 = 0; + int rv = EC_SUCCESS; + + sr1 = flash_get_status1(); + sr2 = flash_get_status2(); + + /* Invalid value */ + if (offset + bytes > CONFIG_FLASH_PHYSICAL_SIZE) + return EC_ERROR_INVAL; + + /* Compute current protect range */ + rv = reg_to_protect(sr1, sr2, &start, &len); + if (rv) + return rv; + + /* Check if ranges overlap */ + if (MAX(start, offset) < MIN(start + len, offset + bytes)) + return EC_ERROR_ACCESS_DENIED; + + return EC_SUCCESS; + +} + +int flash_write_prot_reg(unsigned int offset, unsigned int bytes) +{ + int rv; + uint8_t sr1 = flash_get_status1(); + uint8_t sr2 = flash_get_status2(); + + /* Invalid values */ + if (offset + bytes > CONFIG_FLASH_SIZE) + return EC_ERROR_INVAL; + + /* Compute desired protect range */ + rv = protect_to_reg(offset, bytes, &sr1, &sr2); + if (rv) + return rv; + + return flash_set_status_for_prot(sr1, sr2); +} + +void flash_burst_write(unsigned int dest_addr, unsigned int bytes, + const char *data) +{ + unsigned int i; + /* Chip Select down. */ + flash_cs_level(0); + /* Set erase address */ + flash_set_address(dest_addr); + /* Start write */ + flash_execute_cmd(CMD_FLASH_PROGRAM, MASK_CMD_WR_ADR); + for (i = 0; i < bytes; i++) { + flash_execute_cmd(*data, MASK_CMD_WR_ONLY); + data++; + } + /* Chip Select up */ + flash_cs_level(1); +} +/*****************************************************************************/ +/* Physical layer APIs */ +int flash_physical_read(int offset, int size, char *data) +{ + int dest_addr = offset; + uint32_t idx; + + /* Disable tri-state */ + TRISTATE_FLASH(0); + /* Chip Select down. */ + flash_cs_level(0); + + /* Set read address */ + flash_set_address(dest_addr); + /* Start fast read - 1110 1001 - EXEC, WR, CMD, ADDR */ + flash_execute_cmd(CMD_FAST_READ, MASK_CMD_ADR_WR); + + /* Burst read transaction */ + for (idx = 0; idx < size; idx++) { + /* 1101 0101 - EXEC, RD, NO CMD, NO ADDR, 4 bytes */ + NPCX_UMA_CTS = MASK_RD_1BYTE; + /* wait for UMA to complete */ + while (IS_BIT_SET(NPCX_UMA_CTS, EXEC_DONE)) + ; + /* Get read transaction results*/ + data[idx] = NPCX_UMA_DB0; + } + + /* Chip Select up */ + flash_cs_level(1); + /* Enable tri-state */ + TRISTATE_FLASH(1); + return EC_SUCCESS; +} + +int flash_physical_read_image_size(int offset, int size) +{ + int dest_addr = offset; + uint8_t temp; + uint32_t idx; + uint32_t image_size = 0; + + /* Disable tri-state */ + TRISTATE_FLASH(0); + /* Chip Select down. */ + flash_cs_level(0); + + /* Set read address */ + flash_set_address(dest_addr); + /* Start fast read - 1110 1001 - EXEC, WR, CMD, ADDR */ + flash_execute_cmd(CMD_FAST_READ, MASK_CMD_ADR_WR); + + /* Burst read transaction */ + for (idx = 0; idx < size; idx++) { + /* 1101 0101 - EXEC, RD, NO CMD, NO ADDR, 4 bytes */ + NPCX_UMA_CTS = MASK_RD_1BYTE; + /* wait for UMA to complete */ + while (IS_BIT_SET(NPCX_UMA_CTS, EXEC_DONE)) + ; + /* Find eof of image */ + temp = NPCX_UMA_DB0; + if (temp == 0xea) + image_size = idx; + } + + /* Chip Select up */ + flash_cs_level(1); + /* Enable tri-state */ + TRISTATE_FLASH(1); + return image_size; +} + +int flash_physical_is_erased(uint32_t offset, int size) +{ + int dest_addr = offset; + uint32_t idx; + uint8_t temp; + + /* Chip Select down. */ + flash_cs_level(0); + + /* Set read address */ + flash_set_address(dest_addr); + /* Start fast read -1110 1001 - EXEC, WR, CMD, ADDR */ + flash_execute_cmd(CMD_FAST_READ, MASK_CMD_ADR_WR); + + /* Burst read transaction */ + for (idx = 0; idx < size; idx++) { + /* 1101 0101 - EXEC, RD, NO CMD, NO ADDR, 4 bytes */ + NPCX_UMA_CTS = MASK_RD_1BYTE; + /* Wait for UMA to complete */ + while (IS_BIT_SET(NPCX_UMA_CTS, EXEC_DONE)) + ; + /* Get read transaction results */ + temp = NPCX_UMA_DB0; + if (temp != 0xFF) + break; + } + + /* Chip Select up */ + flash_cs_level(1); + + if (idx == size) + return 1; + else + return 0; +} + +int flash_physical_write(int offset, int size, const char *data) +{ + int dest_addr = offset; + const int sz_page = CONFIG_FLASH_WRITE_IDEAL_SIZE; + + /* Fail if offset, size, and data aren't at least word-aligned */ + if ((offset | size + | (uint32_t)(uintptr_t)data) & (CONFIG_FLASH_WRITE_SIZE - 1)) + return EC_ERROR_INVAL; + + /* check protection */ + if (all_protected) + return EC_ERROR_ACCESS_DENIED; + + /* Disable tri-state */ + TRISTATE_FLASH(0); + + /* Write the data per CONFIG_FLASH_WRITE_IDEAL_SIZE bytes */ + for (; size >= sz_page; size -= sz_page) { + + /* check protection */ + if (flash_check_prot_range(dest_addr, sz_page)) + return EC_ERROR_ACCESS_DENIED; + + /* Enable write */ + flash_write_enable(); + /* Burst UMA transaction */ + flash_burst_write(dest_addr, sz_page, data); + /* Wait write completed */ + flash_wait_ready(); + + data += sz_page; + dest_addr += sz_page; + } + + /* Handle final partial page, if any */ + if (size != 0) { + /* check protection */ + if (flash_check_prot_range(dest_addr, size)) + return EC_ERROR_ACCESS_DENIED; + + /* Enable write */ + flash_write_enable(); + /* Burst UMA transaction */ + flash_burst_write(dest_addr, size, data); + /* Wait write completed */ + flash_wait_ready(); + } + + /* Enable tri-state */ + TRISTATE_FLASH(1); + return EC_SUCCESS; +} + +int flash_physical_erase(int offset, int size) +{ + /* check protection */ + if (all_protected) + return EC_ERROR_ACCESS_DENIED; + + /* Disable tri-state */ + TRISTATE_FLASH(0); + + /* Alignment has been checked in upper layer */ + for (; size > 0; size -= CONFIG_FLASH_ERASE_SIZE, + offset += CONFIG_FLASH_ERASE_SIZE) { + + /* Do nothing if already erased */ + if (flash_is_erased(offset, CONFIG_FLASH_ERASE_SIZE)) + continue; + + /* check protection */ + if (flash_check_prot_range(offset, CONFIG_FLASH_ERASE_SIZE)) + return EC_ERROR_ACCESS_DENIED; + + + /* + * Reload the watchdog timer, so that erasing many flash pages + * doesn't cause a watchdog reset. May not need this now that + * we're using msleep() below. + */ + watchdog_reload(); + + /* Enable write */ + flash_write_enable(); + /* Set erase address */ + flash_set_address(offset); + /* Start erase */ + flash_execute_cmd(CMD_SECTOR_ERASE, MASK_CMD_ADR); + + /* Wait erase completed */ + flash_wait_ready(); + } + + /* Enable tri-state */ + TRISTATE_FLASH(1); + return EC_SUCCESS; +} + +int flash_physical_get_protect(int bank) +{ + uint32_t addr = bank * CONFIG_FLASH_BANK_SIZE; + return flash_check_prot_reg(addr, CONFIG_FLASH_BANK_SIZE); +} + +uint32_t flash_physical_get_protect_flags(void) +{ + uint32_t flags = 0; + + /* Read all-protected state from our shadow copy */ + if (all_protected) + flags |= EC_FLASH_PROTECT_ALL_NOW; + + return flags; +} + +int flash_physical_protect_now(int all) +{ + if (all) { + /* Protect the entire flash */ + all_protected = 1; + flash_write_prot_reg(0, CONFIG_FLASH_PHYSICAL_SIZE); + } else { + /* Protect the read-only section and persistent state */ +#ifdef CONFIG_PSTATE_AT_END + flash_write_prot_reg(RO_BANK_OFFSET*CONFIG_FLASH_BANK_SIZE, + RO_BANK_COUNT*CONFIG_FLASH_BANK_SIZE); + flash_write_prot_reg(PSTATE_BANK * CONFIG_FLASH_BANK_SIZE, + CONFIG_FLASH_BANK_SIZE); +#else + /* PSTATE immediately follows RO, in the first half of flash */ + flash_write_prot_reg(RO_BANK_OFFSET*CONFIG_FLASH_BANK_SIZE, + (RO_BANK_COUNT+1) * CONFIG_FLASH_BANK_SIZE); +#endif + } + + return EC_SUCCESS; +} + +uint32_t flash_physical_get_valid_flags(void) +{ + return EC_FLASH_PROTECT_RO_AT_BOOT | + EC_FLASH_PROTECT_RO_NOW | + EC_FLASH_PROTECT_ALL_NOW; +} + +uint32_t flash_physical_get_writable_flags(uint32_t cur_flags) +{ + uint32_t ret = 0; + + /* If RO protection isn't enabled, its at-boot state can be changed. */ + if (!(cur_flags & EC_FLASH_PROTECT_RO_NOW)) + ret |= EC_FLASH_PROTECT_RO_AT_BOOT; + + /* + * If entire flash isn't protected at this boot, it can be enabled if + * the WP GPIO is asserted. + */ + if (!(cur_flags & EC_FLASH_PROTECT_ALL_NOW) && + (cur_flags & EC_FLASH_PROTECT_GPIO_ASSERTED)) + ret |= EC_FLASH_PROTECT_ALL_NOW; + + return ret; +} + +/*****************************************************************************/ +/* High-level APIs */ + +int flash_pre_init(void) +{ + uint32_t reset_flags, prot_flags, unwanted_prot_flags; + + /* Enable FIU interface */ + flash_pinmux(1); + +#ifdef CONFIG_CODERAM_ARCH + /* Disable tristate all the time */ + CLEAR_BIT(NPCX_DEVCNT, NPCX_DEVCNT_F_SPI_TRIS); +#endif + + reset_flags = system_get_reset_flags(); + prot_flags = flash_get_protect(); + unwanted_prot_flags = EC_FLASH_PROTECT_ALL_NOW | + EC_FLASH_PROTECT_ERROR_INCONSISTENT; + + /* + * If we have already jumped between images, an earlier image could + * have applied write protection. Nothing additional needs to be done. + */ + if (reset_flags & RESET_FLAG_SYSJUMP) + return EC_SUCCESS; + + /* Handle flash write-protection */ + if (prot_flags & EC_FLASH_PROTECT_GPIO_ASSERTED) { + /* + * Write protect is asserted. If we want RO flash protected, + * protect it now. + */ + if ((prot_flags & EC_FLASH_PROTECT_RO_AT_BOOT) && + !(prot_flags & EC_FLASH_PROTECT_RO_NOW)) { + int rv = flash_set_protect(EC_FLASH_PROTECT_RO_NOW, + EC_FLASH_PROTECT_RO_NOW); + if (rv) + return rv; + + /* Re-read flags */ + prot_flags = flash_get_protect(); + } + + /* Update all-now flag if all flash is protected */ + if (prot_flags & EC_FLASH_PROTECT_ALL_NOW) + all_protected = 1; + } else { + /* Don't want RO flash protected */ + unwanted_prot_flags |= EC_FLASH_PROTECT_RO_NOW; + } + + /* If there are no unwanted flags, done */ + if (!(prot_flags & unwanted_prot_flags)) + return EC_SUCCESS; + + /* Otherwise, clear the flash protection bits of status registers */ + flash_set_status_for_prot(0, 0); + + return EC_SUCCESS; +} diff --git a/chip/npcx/gpio.c b/chip/npcx/gpio.c new file mode 100644 index 0000000000..bd046c0683 --- /dev/null +++ b/chip/npcx/gpio.c @@ -0,0 +1,648 @@ +/* Copyright (c) 2014 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 Chrome EC */ + +#include "clock.h" +#include "common.h" +#include "gpio.h" +#include "keyboard_config.h" +#include "hooks.h" +#include "registers.h" +#include "switch.h" +#include "task.h" +#include "timer.h" +#include "util.h" + +/* Marco functions for GPIO WUI/ALT table */ +#define NPCX_GPIO(grp, pin) \ + GPIO_PORT_##grp, MASK_PIN##pin +#define NPCX_GPIO_NONE \ + GPIO_PORT_COUNT, 0xFF +#define NPCX_WUI(tbl, grp, pin) \ + MIWU_TABLE_##tbl, MIWU_GROUP_##grp, MASK_PIN##pin + +#define ALT_MASK(pin) \ + CONCAT2(MASK_PIN, pin) +#define ALT_PIN(grp, pin) \ + ALT_MASK(NPCX_DEVALT##grp##_##pin) +#define NPCX_ALT(grp, pin) \ + ALT_GROUP_##grp, ALT_PIN(grp, pin) + +struct gpio_wui_map { + uint8_t gpio_port; + uint8_t gpio_mask; + uint8_t wui_table; + uint8_t wui_group; + uint8_t wui_mask; +}; + +struct gpio_wui_item { + struct gpio_wui_map wui_map[8]; + uint8_t irq; +}; + +const struct gpio_wui_item gpio_wui_table[] = { + /* MIWU0 Group A */ + { { { NPCX_GPIO(8, 0), NPCX_WUI(0, 1, 0) }, + { NPCX_GPIO(8, 1), NPCX_WUI(0, 1, 1) }, + { NPCX_GPIO(8, 2), NPCX_WUI(0, 1, 2) }, + { NPCX_GPIO(8, 3), NPCX_WUI(0, 1, 3) }, + { NPCX_GPIO(8, 4), NPCX_WUI(0, 1, 4) }, + { NPCX_GPIO(8, 5), NPCX_WUI(0, 1, 5) }, + { NPCX_GPIO(8, 6), NPCX_WUI(0, 1, 6) }, + { NPCX_GPIO(8, 7), NPCX_WUI(0, 1, 7) }, + }, NPCX_IRQ_MTC_WKINTAD_0 }, + /* MIWU0 Group B */ + { { { NPCX_GPIO(9, 0), NPCX_WUI(0, 2, 0) }, + { NPCX_GPIO(9, 1), NPCX_WUI(0, 2, 1) }, + { NPCX_GPIO(9, 2), NPCX_WUI(0, 2, 2) }, + { NPCX_GPIO(9, 3), NPCX_WUI(0, 2, 3) }, + { NPCX_GPIO(9, 4), NPCX_WUI(0, 2, 4) }, + { NPCX_GPIO(9, 5), NPCX_WUI(0, 2, 5) }, + { NPCX_GPIO_NONE, NPCX_WUI(0, 2, 6) }, /* MSWC Wake-Up */ + { NPCX_GPIO_NONE, NPCX_WUI(0, 2, 7) }, /* T0OUT Wake-Up */ + }, NPCX_IRQ_TWD_WKINTB_0 }, + /* MIWU0 Group C */ + { { { NPCX_GPIO(9, 6), NPCX_WUI(0, 3, 0) }, + { NPCX_GPIO(9, 7), NPCX_WUI(0, 3, 1) }, + { NPCX_GPIO(A, 0), NPCX_WUI(0, 3, 2) }, + { NPCX_GPIO(A, 1), NPCX_WUI(0, 3, 3) }, + { NPCX_GPIO(A, 2), NPCX_WUI(0, 3, 4) }, + { NPCX_GPIO(A, 3), NPCX_WUI(0, 3, 5) }, + { NPCX_GPIO(A, 4), NPCX_WUI(0, 3, 6) }, + { NPCX_GPIO(A, 5), NPCX_WUI(0, 3, 7) }, + }, NPCX_IRQ_WKINTC_0 }, + /* MIWU0 Group D */ + { { { NPCX_GPIO(A, 6), NPCX_WUI(0, 4, 0) }, + { NPCX_GPIO(A, 7), NPCX_WUI(0, 4, 1) }, + { NPCX_GPIO(B, 0), NPCX_WUI(0, 4, 2) }, + { NPCX_GPIO_NONE, NPCX_WUI(0, 4, 3) }, /* SMB0 Wake-Up */ + { NPCX_GPIO_NONE, NPCX_WUI(0, 4, 4) }, /* SMB1 Wake-Up */ + { NPCX_GPIO(B, 1), NPCX_WUI(0, 4, 5) }, + { NPCX_GPIO(B, 2), NPCX_WUI(0, 4, 6) }, + { NPCX_GPIO_NONE, NPCX_WUI(0, 4, 7) }, /* MTC Wake-Up */ + }, NPCX_IRQ_MTC_WKINTAD_0 }, + + /* MIWU0 Group E */ + { { { NPCX_GPIO(B, 3), NPCX_WUI(0, 5 , 0) }, + { NPCX_GPIO(B, 4), NPCX_WUI(0, 5 , 1) }, + { NPCX_GPIO(B, 5), NPCX_WUI(0, 5 , 2) }, + { NPCX_GPIO_NONE, NPCX_WUI(0, 5 , 3) }, + { NPCX_GPIO(B, 7), NPCX_WUI(0, 5 , 4) }, + { NPCX_GPIO_NONE, NPCX_WUI(0, 5 , 5) }, + { NPCX_GPIO_NONE, NPCX_WUI(0, 5 , 6) }, /* Host Wake-Up */ + { NPCX_GPIO_NONE, NPCX_WUI(0, 5 , 7) }, /* LRESET Wake-Up */ + }, NPCX_IRQ_WKINTEFGH_0 }, + + /* MIWU0 Group F */ + { { { NPCX_GPIO(C, 0), NPCX_WUI(0, 6, 0) }, + { NPCX_GPIO(C, 1), NPCX_WUI(0, 6, 1) }, + { NPCX_GPIO(C, 2), NPCX_WUI(0, 6, 2) }, + { NPCX_GPIO(C, 3), NPCX_WUI(0, 6, 3) }, + { NPCX_GPIO(C, 4), NPCX_WUI(0, 6, 4) }, + { NPCX_GPIO(C, 5), NPCX_WUI(0, 6, 5) }, + { NPCX_GPIO(C, 6), NPCX_WUI(0, 6, 6) }, + { NPCX_GPIO(C, 7), NPCX_WUI(0, 6, 7) }, + }, NPCX_IRQ_WKINTEFGH_0 }, + /* MIWU0 Group G */ + { { { NPCX_GPIO(D, 0), NPCX_WUI(0, 7, 0) }, + { NPCX_GPIO(D, 1), NPCX_WUI(0, 7, 1) }, + { NPCX_GPIO(D, 2), NPCX_WUI(0, 7, 2) }, + { NPCX_GPIO(D, 3), NPCX_WUI(0, 7, 3) }, + { NPCX_GPIO_NONE, NPCX_WUI(0, 7, 4) }, + { NPCX_GPIO_NONE, NPCX_WUI(0, 7, 5) }, + { NPCX_GPIO_NONE, NPCX_WUI(0, 7, 6) }, + { NPCX_GPIO_NONE, NPCX_WUI(0, 7, 7) }, + }, NPCX_IRQ_WKINTEFGH_0 }, + /* MIWU0 Group H */ + { { { NPCX_GPIO_NONE, NPCX_WUI(0, 8, 0) }, + { NPCX_GPIO_NONE, NPCX_WUI(0, 8, 1) }, + { NPCX_GPIO_NONE, NPCX_WUI(0, 8, 2) }, + { NPCX_GPIO_NONE, NPCX_WUI(0, 8, 3) }, + { NPCX_GPIO_NONE, NPCX_WUI(0, 8, 4) }, + { NPCX_GPIO_NONE, NPCX_WUI(0, 8, 5) }, + { NPCX_GPIO_NONE, NPCX_WUI(0, 8, 6) }, + { NPCX_GPIO(E, 7), NPCX_WUI(0, 8, 7) }, + }, NPCX_IRQ_WKINTEFGH_0 }, + + /* MIWU1 Group A */ + { { { NPCX_GPIO(0, 0), NPCX_WUI(1, 1, 0) }, + { NPCX_GPIO(0, 1), NPCX_WUI(1, 1, 1) }, + { NPCX_GPIO(0, 2), NPCX_WUI(1, 1, 2) }, + { NPCX_GPIO(0, 3), NPCX_WUI(1, 1, 3) }, + { NPCX_GPIO(0, 4), NPCX_WUI(1, 1, 4) }, + { NPCX_GPIO(0, 5), NPCX_WUI(1, 1, 5) }, + { NPCX_GPIO(0, 6), NPCX_WUI(1, 1, 6) }, + { NPCX_GPIO(0, 7), NPCX_WUI(1, 1, 7) }, + }, NPCX_IRQ_WKINTA_1 }, + /* MIWU1 Group B */ + { { { NPCX_GPIO(1, 0), NPCX_WUI(1, 2, 0) }, + { NPCX_GPIO(1, 1), NPCX_WUI(1, 2, 1) }, + { NPCX_GPIO_NONE, NPCX_WUI(1, 2, 2) }, + { NPCX_GPIO(1, 3), NPCX_WUI(1, 2, 3) }, + { NPCX_GPIO(1, 4), NPCX_WUI(1, 2, 4) }, + { NPCX_GPIO(1, 5), NPCX_WUI(1, 2, 5) }, + { NPCX_GPIO(1, 6), NPCX_WUI(1, 2, 6) }, + { NPCX_GPIO(1, 7), NPCX_WUI(1, 2, 7) }, + }, NPCX_IRQ_WKINTB_1 }, + /* MIWU1 Group C -- Skipping */ + /* MIWU1 Group D */ + { { { NPCX_GPIO(2, 0), NPCX_WUI(1, 4, 0) }, + { NPCX_GPIO(2, 1), NPCX_WUI(1, 4, 1) }, + { NPCX_GPIO_NONE, NPCX_WUI(1, 4, 2) }, + { NPCX_GPIO(3, 3), NPCX_WUI(1, 4, 3) }, + { NPCX_GPIO(3, 4), NPCX_WUI(1, 4, 4) }, + { NPCX_GPIO_NONE, NPCX_WUI(1, 4, 5) }, + { NPCX_GPIO(3, 6), NPCX_WUI(1, 4, 6) }, + { NPCX_GPIO(3, 7), NPCX_WUI(1, 4, 7) }, + }, NPCX_IRQ_WKINTD_1 }, + + /* MIWU1 Group E */ + { { { NPCX_GPIO(4, 0), NPCX_WUI(1, 5, 0) }, + { NPCX_GPIO(4, 1), NPCX_WUI(1, 5, 1) }, + { NPCX_GPIO(4, 2), NPCX_WUI(1, 5, 2) }, + { NPCX_GPIO(4, 3), NPCX_WUI(1, 5, 3) }, + { NPCX_GPIO(4, 4), NPCX_WUI(1, 5, 4) }, + { NPCX_GPIO(4, 5), NPCX_WUI(1, 5, 5) }, + { NPCX_GPIO(4, 6), NPCX_WUI(1, 5, 6) }, + { NPCX_GPIO(4, 7), NPCX_WUI(1, 5, 7) }, + }, NPCX_IRQ_WKINTE_1 }, + + /* MIWU1 Group F */ + { { { NPCX_GPIO(5, 0), NPCX_WUI(1, 6, 0) }, + { NPCX_GPIO(5, 1), NPCX_WUI(1, 6, 1) }, + { NPCX_GPIO(5, 2), NPCX_WUI(1, 6, 2) }, + { NPCX_GPIO(5, 3), NPCX_WUI(1, 6, 3) }, + { NPCX_GPIO(5, 4), NPCX_WUI(1, 6, 4) }, + { NPCX_GPIO(5, 5), NPCX_WUI(1, 6, 5) }, + { NPCX_GPIO(5, 6), NPCX_WUI(1, 6, 6) }, + { NPCX_GPIO(5, 7), NPCX_WUI(1, 6, 7) }, + }, NPCX_IRQ_WKINTF_1 }, + /* MIWU1 Group G */ + { { { NPCX_GPIO(6, 0), NPCX_WUI(1, 7, 0) }, + { NPCX_GPIO(6, 1), NPCX_WUI(1, 7, 1) }, + { NPCX_GPIO(6, 2), NPCX_WUI(1, 7, 2) }, + { NPCX_GPIO(6, 3), NPCX_WUI(1, 7, 3) }, + { NPCX_GPIO(6, 4), NPCX_WUI(1, 7, 4) }, + { NPCX_GPIO(6, 5), NPCX_WUI(1, 7, 5) }, + { NPCX_GPIO(6, 6), NPCX_WUI(1, 7, 6) }, + { NPCX_GPIO(7, 1), NPCX_WUI(1, 7, 7) }, + }, NPCX_IRQ_WKINTG_1 }, + /* MIWU1 Group H */ + { { { NPCX_GPIO(7, 0), NPCX_WUI(1, 8, 0) }, + { NPCX_GPIO(6, 7), NPCX_WUI(1, 8, 1) }, + { NPCX_GPIO(7, 2), NPCX_WUI(1, 8, 2) }, + { NPCX_GPIO(7, 3), NPCX_WUI(1, 8, 3) }, + { NPCX_GPIO(7, 4), NPCX_WUI(1, 8, 4) }, + { NPCX_GPIO(7, 5), NPCX_WUI(1, 8, 5) }, + { NPCX_GPIO(7, 6), NPCX_WUI(1, 8, 6) }, + { NPCX_GPIO_NONE, NPCX_WUI(1, 8, 7) }, + }, NPCX_IRQ_WKINTH_1 }, +}; + +struct gpio_alt_map { + uint8_t gpio_port; + uint8_t gpio_mask; + uint8_t alt_group; + uint8_t alt_mask; +}; + +const struct gpio_alt_map gpio_alt_table[] = { + /* I2C Module */ +#if I2C0_BUS0 + { NPCX_GPIO(B, 4), NPCX_ALT(2, I2C0_0_SL)}, /* SMB0SDA */ + { NPCX_GPIO(B, 5), NPCX_ALT(2, I2C0_0_SL)}, /* SMB0SCL */ +#else + { NPCX_GPIO(B, 2), NPCX_ALT(2, I2C0_1_SL)}, /* SMB0SDA */ + { NPCX_GPIO(B, 3), NPCX_ALT(2, I2C0_1_SL)}, /* SMB0SCL */ +#endif + { NPCX_GPIO(8, 7), NPCX_ALT(2, I2C1_0_SL)}, /* SMB1SDA */ + { NPCX_GPIO(9, 0), NPCX_ALT(2, I2C1_0_SL)}, /* SMB1SCL */ + { NPCX_GPIO(9, 1), NPCX_ALT(2, I2C2_0_SL)}, /* SMB2SDA */ + { NPCX_GPIO(9, 2), NPCX_ALT(2, I2C2_0_SL)}, /* SMB2SCL */ + { NPCX_GPIO(D, 0), NPCX_ALT(2, I2C3_0_SL)}, /* SMB3SDA */ + { NPCX_GPIO(D, 1), NPCX_ALT(2, I2C3_0_SL)}, /* SMB3SCL */ + /* ADC Module */ + { NPCX_GPIO(4, 5), NPCX_ALT(6, ADC0_SL)}, /* ADC0 */ + { NPCX_GPIO(4, 4), NPCX_ALT(6, ADC1_SL)}, /* ADC1 */ + { NPCX_GPIO(4, 3), NPCX_ALT(6, ADC2_SL)}, /* ADC2 */ + { NPCX_GPIO(4, 2), NPCX_ALT(6, ADC3_SL)}, /* ADC3 */ + { NPCX_GPIO(4, 1), NPCX_ALT(6, ADC4_SL)}, /* ADC4 */ + /* UART Module */ + { NPCX_GPIO(1, 0), NPCX_ALT(9, NO_KSO08_SL)}, /* CR_SIN/KSO09/GPIO10*/ + { NPCX_GPIO(1, 1), NPCX_ALT(9, NO_KSO09_SL)}, /* CR_SOUT/KSO10/GPIO11*/ + /* SPI Module */ + { NPCX_GPIO(9, 5), NPCX_ALT(0, SPIP_SL)}, /* SPIP_MISO */ + { NPCX_GPIO(A, 5), NPCX_ALT(0, SPIP_SL)}, /* SPIP_CS1 */ + { NPCX_GPIO(A, 3), NPCX_ALT(0, SPIP_SL)}, /* SPIP_MOSI */ + { NPCX_GPIO(A, 1), NPCX_ALT(0, SPIP_SL)}, /* SPIP_SCLK */ + /* PWM Module */ + { NPCX_GPIO(C, 3), NPCX_ALT(4, PWM0_SL)}, /* PWM0 */ + { NPCX_GPIO(C, 2), NPCX_ALT(4, PWM1_SL)}, /* PWM1 */ + { NPCX_GPIO(C, 4), NPCX_ALT(4, PWM2_SL)}, /* PWM2 */ + { NPCX_GPIO(8, 0), NPCX_ALT(4, PWM3_SL)}, /* PWM3 */ + { NPCX_GPIO(B, 6), NPCX_ALT(4, PWM4_SL)}, /* PWM4 */ + { NPCX_GPIO(B, 7), NPCX_ALT(4, PWM5_SL)}, /* PWM5 */ + { NPCX_GPIO(C, 0), NPCX_ALT(4, PWM6_SL)}, /* PWM6 */ + { NPCX_GPIO(6, 0), NPCX_ALT(4, PWM7_SL)}, /* PWM7 */ + /* MFT Module */ +#if TACH_SEL1 + { NPCX_GPIO(4, 0), NPCX_ALT(3, TA1_TACH1_SL1)},/* TA1_TACH1 */ + { NPCX_GPIO(A, 4), NPCX_ALT(3, TB1_TACH2_SL1)},/* TB1_TACH2 */ +#else + { NPCX_GPIO(9, 3), NPCX_ALT(C, TA1_TACH1_SL2)},/* TA1_TACH1 */ + { NPCX_GPIO(D, 3), NPCX_ALT(C, TB1_TACH2_SL2)},/* TB1_TACH2 */ +#endif + /* JTAG Module */ +#if !(JTAG1) + { NPCX_GPIO(2, 1), NPCX_ALT(5, NJEN0_EN) }, /* TCLK */ + { NPCX_GPIO(1, 7), NPCX_ALT(5, NJEN0_EN) }, /* TDI */ + { NPCX_GPIO(1, 6), NPCX_ALT(5, NJEN0_EN) }, /* TDO */ + { NPCX_GPIO(2, 0), NPCX_ALT(5, NJEN0_EN) }, /* TMS */ +#else + { NPCX_GPIO(D, 5), NPCX_ALT(5, NJEN1_EN) }, /* TCLK */ + { NPCX_GPIO(E, 2), NPCX_ALT(5, NJEN1_EN) }, /* TDI */ + { NPCX_GPIO(D, 4), NPCX_ALT(5, NJEN1_EN) }, /* TDO */ + { NPCX_GPIO(E, 5), NPCX_ALT(5, NJEN1_EN) }, /* TMS */ +#endif + /* 01 for PWRGD_OUT*/ +}; + +/*****************************************************************************/ +/* Internal functions */ +const struct gpio_wui_map *gpio_find_wui_from_io(uint8_t port, uint8_t mask) +{ + int i, j; + for (i = 0; i < ARRAY_SIZE(gpio_wui_table); i++) { + const struct gpio_wui_map *map = gpio_wui_table[i].wui_map; + for (j = 0; j < 8; j++, map++) { + if (map->gpio_port == port && map->gpio_mask == mask) + return map; + } + } + return NULL; +} + +int gpio_find_irq_from_io(uint8_t port, uint8_t mask) +{ + int i, j; + for (i = 0; i < ARRAY_SIZE(gpio_wui_table); i++) { + const struct gpio_wui_map *map = gpio_wui_table[i].wui_map; + for (j = 0; j < 8; j++, map++) { + if (map->gpio_port == port && map->gpio_mask == mask) + return gpio_wui_table[i].irq; + } + } + return -1; +} + +int gpio_alt_sel(uint8_t port, uint8_t mask, uint8_t func) +{ + int i; + const struct gpio_alt_map *map = gpio_alt_table; + for (i = 0; i < ARRAY_SIZE(gpio_alt_table); i++, map++) { + if (map->gpio_port == port && + (map->gpio_mask == mask)) { + /* Enable alternative function if func >=0 */ + if (func <= 0) /* GPIO functionality */ + NPCX_DEVALT(map->alt_group) &= ~(map->alt_mask); + else + NPCX_DEVALT(map->alt_group) |= (map->alt_mask); + return 1; + } + } + return -1; +} + +void gpio_execute_isr(uint8_t port, uint8_t mask) +{ + int i; + const struct gpio_info *g = gpio_list; + /* Find GPIOs and execute interrupt service routine */ + for (i = 0; i < GPIO_COUNT; i++, g++) { + if (port == g->port && mask == g->mask && g->irq_handler) { + g->irq_handler(i); + return; + } + } +} + +/* Set interrupt type for GPIO input */ +void gpio_interrupt_type_sel(uint8_t port, uint8_t mask, uint32_t flags) +{ + const struct gpio_wui_map *map = gpio_find_wui_from_io(port, mask); + uint8_t table, group, pmask; + + if (map == NULL) + return; + + table = map->wui_table; + group = map->wui_group; + pmask = map->wui_mask; + + /* Handle interrupt for level trigger */ + if ((flags & GPIO_INT_F_HIGH) || + (flags & GPIO_INT_F_LOW)) { + /* Set detection mode to level */ + NPCX_WKMOD(table, group) |= pmask; + /* Handle interrupting on level high */ + if (flags & GPIO_INT_F_HIGH) + NPCX_WKEDG(table, group) &= ~pmask; + /* Handle interrupting on level low */ + else if (flags & GPIO_INT_F_LOW) + NPCX_WKEDG(table, group) |= pmask; + + /* Enable wake-up input sources */ + NPCX_WKEN(table, group) |= pmask; + } + /* Handle interrupt for edge trigger */ + else if ((flags & GPIO_INT_F_RISING) || + (flags & GPIO_INT_F_FALLING)) { + /* Set detection mode to edge */ + NPCX_WKMOD(table, group) &= ~pmask; + /* Handle interrupting on both edges */ + if ((flags & GPIO_INT_F_RISING) && + (flags & GPIO_INT_F_FALLING)) { + /* Enable any edge */ + NPCX_WKAEDG(table, group) |= pmask; + } + /* Handle interrupting on rising edge */ + else if (flags & GPIO_INT_F_RISING) { + /* Disable any edge */ + NPCX_WKAEDG(table, group) &= ~pmask; + NPCX_WKEDG(table, group) &= ~pmask; + } + /* Handle interrupting on falling edge */ + else if (flags & GPIO_INT_F_FALLING) { + /* Disable any edge */ + NPCX_WKAEDG(table, group) &= ~pmask; + NPCX_WKEDG(table, group) |= pmask; + } + /* Enable wake-up input sources */ + NPCX_WKEN(table, group) |= pmask; + } else{ + /* Disable wake-up input sources */ + NPCX_WKEN(table, group) &= ~pmask; + } + + /* No support analog mode */ +} + +/*****************************************************************************/ +/* IC specific low-level driver */ + +void gpio_set_alternate_function(uint32_t port, uint32_t mask, int func) +{ + /* Enable alternative pins by func*/ + int pin; + uint8_t pmask; + /* check each bit from mask */ + for (pin = 0; pin < 8; pin++) { + pmask = (mask & (1 << pin)); + if (pmask) + gpio_alt_sel(port, pmask, func); + } +} + +test_mockable int gpio_get_level(enum gpio_signal signal) +{ + return (NPCX_PDIN(gpio_list[signal].port) & + gpio_list[signal].mask) ? 1 : 0; +} + +void gpio_set_level(enum gpio_signal signal, int value) +{ + if (value) + NPCX_PDOUT(gpio_list[signal].port) |= + gpio_list[signal].mask; + else + NPCX_PDOUT(gpio_list[signal].port) &= + ~gpio_list[signal].mask; +} + +void gpio_set_flags_by_mask(uint32_t port, uint32_t mask, uint32_t flags) +{ + /* + * Select open drain first, so that we don't glitch the signal + * when changing the line to an output. 0:push-pull 1:open-drain + */ + if (flags & GPIO_OPEN_DRAIN) + NPCX_PTYPE(port) |= mask; + else + NPCX_PTYPE(port) &= ~mask; + + /* Select direction of GPIO 0:input 1:output */ + if (flags & GPIO_OUTPUT) + NPCX_PDIR(port) |= mask; + else + NPCX_PDIR(port) &= ~mask; + + /* Select pull-up/down of GPIO 0:pull-up 1:pull-down */ + if (flags & GPIO_PULL_UP) { + NPCX_PPUD(port) &= ~mask; + NPCX_PPULL(port) |= mask; /* enable pull down/up */ + } else if (flags & GPIO_PULL_DOWN) { + NPCX_PPUD(port) |= mask; + NPCX_PPULL(port) |= mask; /* enable pull down/up */ + } else { + /* No pull up/down */ + NPCX_PPULL(port) &= ~mask; /* disable pull down/up */ + } + + /* Set up interrupt type */ + if (flags & GPIO_INPUT) + gpio_interrupt_type_sel(port, mask, flags); + + /* Set level 0:low 1:high*/ + if (flags & GPIO_HIGH) + NPCX_PDOUT(port) |= mask; + else if (flags & GPIO_LOW) + NPCX_PDOUT(port) &= ~mask; + +} + +int gpio_enable_interrupt(enum gpio_signal signal) +{ + const struct gpio_info *g = gpio_list + signal; + int irq = gpio_find_irq_from_io(g->port, g->mask); + + /* Fail if no interrupt handler */ + if (irq < 0) + return EC_ERROR_UNKNOWN; + + task_enable_irq(irq); + return EC_SUCCESS; +} + +int gpio_disable_interrupt(enum gpio_signal signal) +{ + const struct gpio_info *g = gpio_list + signal; + int irq = gpio_find_irq_from_io(g->port, g->mask); + + /* Fail if no interrupt handler */ + if (irq < 0) + return EC_ERROR_UNKNOWN; + + task_disable_irq(irq); + return EC_SUCCESS; +} + +int gpio_is_reboot_warm(void) +{ + /* Check for debugger or watch-dog Warm reset */ + if (IS_BIT_SET(NPCX_RSTCTL, NPCX_RSTCTL_DBGRST_STS) + /* TODO: 5M5G has cleared WDRST_STS bit in booter */ +#if !defined(CHIP_NPCX5M5G) + /* watch-dog warm reset */ + || (IS_BIT_SET(NPCX_T0CSR, NPCX_T0CSR_WDRST_STS) + && (IS_BIT_SET(NPCX_TWCFG, NPCX_TWCFG_WDRST_MODE)))) +#else + ) +#endif + return 1; + else + return 0; +} + +void gpio_pre_init(void) +{ + const struct gpio_info *g = gpio_list; + int flags; + int i, j; + int is_warm = gpio_is_reboot_warm(); + + uint32_t ksi_mask = (~((1<<KEYBOARD_ROWS)-1)) & KB_ROW_MASK; + uint32_t ks0_mask = (~((1<<KEYBOARD_COLS)-1)) & KB_COL_MASK; + + /* Set necessary pin mux first */ + /* Pin_Mux for KSO0-17 & KSI0-7 */ + NPCX_DEVALT(ALT_GROUP_7) = (uint8_t)(ksi_mask); + NPCX_DEVALT(ALT_GROUP_8) = (uint8_t)(ks0_mask); + NPCX_DEVALT(ALT_GROUP_9) = (uint8_t)(ks0_mask >> 8); + NPCX_DEVALT(ALT_GROUP_A) |= (uint8_t)(ks0_mask >> 16); + + /* Pin_Mux for FIU/SPI (set to GPIO) */ + SET_BIT(NPCX_DEVALT(0), NPCX_DEVALT0_GPIO_NO_SPIP); + SET_BIT(NPCX_DEVALT(0), NPCX_DEVALT0_NO_F_SPI); + + /* Clear all pending bits of GPIOS*/ + for (i = 0; i < 2; i++) + for (j = 0; j < 8; j++) + NPCX_WKPCL(i, j) = 0xFF; + + + /* No support enable clock for the GPIO port in run and sleep. */ + /* Set flag for each GPIO pin in 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); + + /* Set up GPIO based on flags */ + gpio_set_flags_by_mask(g->port, g->mask, flags); + } +} + +/* List of GPIO IRQs to enable. Don't automatically enable interrupts for + * the keyboard input GPIO bank - that's handled separately. Of course the + * bank is different for different systems. */ +static void gpio_init(void) +{ + int i; + /* Enable IRQs now that pins are set up */ + for (i = 0; i < ARRAY_SIZE(gpio_wui_table); i++) + task_enable_irq(gpio_wui_table[i].irq); + +} +DECLARE_HOOK(HOOK_INIT, gpio_init, HOOK_PRIO_DEFAULT); + +/*****************************************************************************/ +/* Interrupt handlers */ + +/** + * Handle a GPIO interrupt. + * + * @param int_no Interrupt number for GPIO + */ + +static void gpio_interrupt(int int_no) +{ +#if DEBUG_GPIO + static uint8_t i, pin, wui_mask; +#else + uint8_t i, pin, wui_mask; +#endif + for (i = 0; i < ARRAY_SIZE(gpio_wui_table); i++) { + /* If interrupt number is the same */ + if (gpio_wui_table[i].irq == int_no) { + /* Mapping relationship between WUI and GPIO */ + const struct gpio_wui_map *map = + gpio_wui_table[i].wui_map; + /* Get pending mask */ + wui_mask = NPCX_WKPND(map->wui_table , map->wui_group); + + /* If pending bits is not zero */ + if (wui_mask) { + /* Clear pending bits of WUI */ + NPCX_WKPCL(map->wui_table , map->wui_group) + = wui_mask; + + for (pin = 0; pin < 8; pin++, map++) { + /* If pending bit is high, execute ISR*/ + if (wui_mask & (1<<pin)) + gpio_execute_isr(map->gpio_port, + map->gpio_mask); + } + } + } + } +} + +/** + * Handlers for each GPIO port. These read and clear the interrupt bits for + * the port, then call the master handler above. + */ + +#define GPIO_IRQ_FUNC(_irq_func, int_no) \ +void _irq_func(void) \ +{ \ + gpio_interrupt(int_no); \ +} + +GPIO_IRQ_FUNC(__gpio_wk0ad_interrupt , NPCX_IRQ_MTC_WKINTAD_0); +GPIO_IRQ_FUNC(__gpio_wk0b_interrupt , NPCX_IRQ_TWD_WKINTB_0); +GPIO_IRQ_FUNC(__gpio_wk0c_interrupt , NPCX_IRQ_WKINTC_0); +GPIO_IRQ_FUNC(__gpio_wk0efgh_interrupt, NPCX_IRQ_WKINTEFGH_0); +GPIO_IRQ_FUNC(__gpio_wk1a_interrupt , NPCX_IRQ_WKINTA_1); +GPIO_IRQ_FUNC(__gpio_wk1b_interrupt , NPCX_IRQ_WKINTB_1); +GPIO_IRQ_FUNC(__gpio_wk1d_interrupt , NPCX_IRQ_WKINTD_1); +GPIO_IRQ_FUNC(__gpio_wk1e_interrupt , NPCX_IRQ_WKINTE_1); +GPIO_IRQ_FUNC(__gpio_wk1f_interrupt , NPCX_IRQ_WKINTF_1); +GPIO_IRQ_FUNC(__gpio_wk1g_interrupt , NPCX_IRQ_WKINTG_1); +GPIO_IRQ_FUNC(__gpio_wk1h_interrupt , NPCX_IRQ_WKINTH_1); + +DECLARE_IRQ(NPCX_IRQ_MTC_WKINTAD_0, __gpio_wk0ad_interrupt, 1); +DECLARE_IRQ(NPCX_IRQ_TWD_WKINTB_0, __gpio_wk0b_interrupt, 1); +DECLARE_IRQ(NPCX_IRQ_WKINTC_0, __gpio_wk0c_interrupt, 1); +DECLARE_IRQ(NPCX_IRQ_WKINTEFGH_0, __gpio_wk0efgh_interrupt, 1); +DECLARE_IRQ(NPCX_IRQ_WKINTA_1, __gpio_wk1a_interrupt, 1); +DECLARE_IRQ(NPCX_IRQ_WKINTB_1, __gpio_wk1b_interrupt, 1); +DECLARE_IRQ(NPCX_IRQ_WKINTD_1, __gpio_wk1d_interrupt, 1); +DECLARE_IRQ(NPCX_IRQ_WKINTE_1, __gpio_wk1e_interrupt, 1); +DECLARE_IRQ(NPCX_IRQ_WKINTF_1, __gpio_wk1f_interrupt, 1); +DECLARE_IRQ(NPCX_IRQ_WKINTG_1, __gpio_wk1g_interrupt, 1); +DECLARE_IRQ(NPCX_IRQ_WKINTH_1, __gpio_wk1h_interrupt, 1); + + +#undef GPIO_IRQ_FUNC diff --git a/chip/npcx/hwtimer.c b/chip/npcx/hwtimer.c new file mode 100644 index 0000000000..80bb903e67 --- /dev/null +++ b/chip/npcx/hwtimer.c @@ -0,0 +1,255 @@ +/* Copyright (c) 2014 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. + */ + +/* Hardware timers driver */ + +#include "clock.h" +#include "clock_chip.h" +#include "common.h" +#include "hooks.h" +#include "hwtimer.h" +#include "hwtimer_chip.h" +#include "registers.h" +#include "task.h" +#include "timer.h" + +/* (2^TICK_ITIM_DEPTH us) between 2 ticks of timer */ +#define TICK_ITIM_DEPTH 16 /* Depth of ITIM Unit: bits */ +#define TICK_INTERVAL (1 << TICK_ITIM_DEPTH) /* Unit: us */ +#define TICK_INTERVAL_MASK (TICK_INTERVAL - 1) /* Mask of interval */ +#define TICK_ITIM_MAX_CNT (TICK_INTERVAL - 1) /* Maximum counter value */ + +/* 32-bits counter value */ +static volatile uint32_t cur_cnt_us; +static volatile uint32_t pre_cnt_us; +/* Time when event will be expired unit:us */ +static volatile uint32_t evt_expired_us; +/* 32-bits event counter */ +static volatile uint32_t evt_cnt; +/* Debugger information */ +#if DEBUG_TMR +static volatile uint32_t evt_cnt_us_dbg; +static volatile uint32_t cur_cnt_us_dbg; +#endif + +/*****************************************************************************/ +/* Internal functions */ +void init_hw_timer(int itim_no, enum ITIM16_SOURCE_CLOCK_T source) +{ + /* Use internal 32K clock/APB2 for ITIM16 */ + UPDATE_BIT(NPCX_ITCTS(itim_no), NPCX_ITIM16_CKSEL, + source != ITIM16_SOURCE_CLOCK_APB2); + + /* Clear timeout status */ + SET_BIT(NPCX_ITCTS(itim_no), NPCX_ITIM16_TO_STS); + + /* ITIM timeout interrupt enable */ + SET_BIT(NPCX_ITCTS(itim_no), NPCX_ITIM16_TO_IE); + + /* ITIM timeout wake-up enable */ + SET_BIT(NPCX_ITCTS(itim_no), NPCX_ITIM16_TO_WUE); +} + +/*****************************************************************************/ +/* HWTimer event handlers */ +void __hw_clock_event_set(uint32_t deadline) +{ + float inv_evt_tick = INT_32K_CLOCK/(float)SECOND; + uint32_t evt_cnt_us; + /* Is deadline min value? */ + if (evt_expired_us != 0 && evt_expired_us < deadline) + return; + + /* mark min event value */ + evt_expired_us = deadline; + evt_cnt_us = deadline - __hw_clock_source_read(); +#if DEBUG_TMR + evt_cnt_us_dbg = deadline - __hw_clock_source_read(); +#endif + + /* Event module disable */ + CLEAR_BIT(NPCX_ITCTS(ITIM_EVENT_NO), NPCX_ITIM16_ITEN); + /* + * ITIM count down : event expired : Unit: 1/32768 sec + * It must exceed evt_expired_us for process_timers function + */ + evt_cnt = ((uint32_t)(evt_cnt_us*inv_evt_tick)+1)-1; + if (evt_cnt > TICK_ITIM_MAX_CNT) + evt_cnt = TICK_ITIM_MAX_CNT; + NPCX_ITCNT16(ITIM_EVENT_NO) = evt_cnt; + + /* Event module enable */ + SET_BIT(NPCX_ITCTS(ITIM_EVENT_NO), NPCX_ITIM16_ITEN); + + /* Enable interrupt of ITIM */ + task_enable_irq(ITIM16_INT(ITIM_EVENT_NO)); +} + +/* Returns the time-stamp of the next programmed event */ +uint32_t __hw_clock_event_get(void) +{ + return evt_expired_us; +} + +/* Returns time delay cause of deep idle */ +uint32_t __hw_clock_get_sleep_time(void) +{ + float evt_tick = SECOND/(float)INT_32K_CLOCK; + uint32_t sleep_time; + uint32_t cnt = NPCX_ITCNT16(ITIM_EVENT_NO); + + interrupt_disable(); + /* Event has been triggered but timer ISR dosen't handle it */ + if (IS_BIT_SET(NPCX_ITCTS(ITIM_EVENT_NO), NPCX_ITIM16_TO_STS)) + sleep_time = (uint32_t) (evt_cnt+1)*evt_tick; + /* Event hasn't been triggered */ + else + sleep_time = (uint32_t) (evt_cnt+1 - cnt)*evt_tick; + interrupt_enable(); + + return sleep_time; +} + +/* Cancel the next event programmed by __hw_clock_event_set */ +void __hw_clock_event_clear(void) +{ + /* ITIM event module disable */ + CLEAR_BIT(NPCX_ITCTS(ITIM_EVENT_NO), NPCX_ITIM16_ITEN); + + /* Disable interrupt of Event */ + task_disable_irq(ITIM16_INT(ITIM_EVENT_NO)); + + /* Clear event parameters */ + evt_expired_us = 0; + evt_cnt = 0; +} + +/* Irq for hwtimer event */ +void __hw_clock_event_irq(void) +{ + int delay; + /* Clear timeout status for event */ + SET_BIT(NPCX_ITCTS(ITIM_EVENT_NO), NPCX_ITIM16_TO_STS); + + /* ITIM event module disable */ + CLEAR_BIT(NPCX_ITCTS(ITIM_EVENT_NO), NPCX_ITIM16_ITEN); + + /* Disable interrupt of event */ + task_disable_irq(ITIM16_INT(ITIM_EVENT_NO)); + + /* Workaround for tick interrupt latency */ + delay = evt_expired_us - __hw_clock_source_read(); + if (delay > 0) + cur_cnt_us += delay; + + /* Clear event parameters */ + evt_expired_us = 0; + evt_cnt = 0; + + /* handle upper driver */ + process_timers(0); +} +DECLARE_IRQ(ITIM16_INT(ITIM_EVENT_NO) , __hw_clock_event_irq, 1); + + +/*****************************************************************************/ +/* HWTimer tick handlers */ + +/* Returns the value of the free-running counter used as clock. */ +uint32_t __hw_clock_source_read(void) +{ + uint32_t us; + uint32_t cnt = NPCX_ITCNT16(ITIM_TIME_NO); + /* Is timeout expired? - but timer ISR dosen't handle it */ + if (IS_BIT_SET(NPCX_ITCTS(ITIM_TIME_NO), NPCX_ITIM16_TO_STS)) + us = TICK_INTERVAL; + else + us = TICK_INTERVAL - cnt; + +#if DEBUG_TMR + cur_cnt_us_dbg = cur_cnt_us + us; +#endif + return cur_cnt_us + us; +} + +/* Override the current value of the hardware counter */ +void __hw_clock_source_set(uint32_t ts) +{ + /* Set current time */ + cur_cnt_us = ts; +} + +/* Irq for hwtimer tick */ +void __hw_clock_source_irq(void) +{ + /* Is timeout trigger trigger? */ + if (IS_BIT_SET(NPCX_ITCTS(ITIM_TIME_NO), NPCX_ITIM16_TO_STS)) { + /* Clear timeout status*/ + SET_BIT(NPCX_ITCTS(ITIM_TIME_NO), NPCX_ITIM16_TO_STS); + + /* Store previous time counter value */ + pre_cnt_us = cur_cnt_us; + /* Increase TICK_INTERVAL unit:us */ + cur_cnt_us += TICK_INTERVAL; + + /* Is 32-bits timer count overflow? */ + if (pre_cnt_us > cur_cnt_us) + process_timers(1); + + } else { /* Handle soft trigger */ + process_timers(0); + } +} +DECLARE_IRQ(NPCX_IRQ_ITIM16_1, __hw_clock_source_irq, 1); + +static void update_prescaler(void) +{ + /* + * prescaler to time tick + * Ttick_unit = (PRE_8+1) * Tapb2_clk + * PRE_8 = (Ttick_unit/Tapb2_clk) -1 + */ + NPCX_ITPRE(ITIM_TIME_NO) = (clock_get_apb2_freq() / SECOND) - 1; + /* Set event tick unit = 1/32768 sec */ + NPCX_ITPRE(ITIM_EVENT_NO) = 0; + +} +DECLARE_HOOK(HOOK_FREQ_CHANGE, update_prescaler, HOOK_PRIO_DEFAULT); + +int __hw_clock_source_init(uint32_t start_t) +{ + /* + * 1. Use ITIM16-1 as internal time reading + * 2. Use ITIM16-2 for event handling + */ + + /* Enable clock for ITIM peripheral */ + clock_enable_peripheral(CGC_OFFSET_TIMER, CGC_TIMER_MASK, + CGC_MODE_RUN | CGC_MODE_SLEEP); + + /* init tick & event timer first */ + init_hw_timer(ITIM_TIME_NO, ITIM16_SOURCE_CLOCK_APB2); + init_hw_timer(ITIM_EVENT_NO, ITIM16_SOURCE_CLOCK_32K); + + /* Set initial prescaler */ + update_prescaler(); + + /* ITIM count down : TICK_INTERVAL expired*/ + NPCX_ITCNT16(ITIM_TIME_NO) = TICK_ITIM_MAX_CNT; + + /* + * Override the count with the start value now that counting has + * started. + */ + __hw_clock_source_set(start_t); + + /* ITIM module enable */ + SET_BIT(NPCX_ITCTS(ITIM_TIME_NO), NPCX_ITIM16_ITEN); + + /* Enable interrupt of ITIM */ + task_enable_irq(ITIM16_INT(ITIM_TIME_NO)); + + return ITIM16_INT(ITIM_TIME_NO); +} diff --git a/chip/npcx/hwtimer_chip.h b/chip/npcx/hwtimer_chip.h new file mode 100644 index 0000000000..490d67b80d --- /dev/null +++ b/chip/npcx/hwtimer_chip.h @@ -0,0 +1,28 @@ +/* Copyright (c) 2014 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. + */ + +/* NPCX-specific hwtimer module for Chrome EC */ + +#ifndef HWTIMER_CHIP_H_ +#define HWTIMER_CHIP_H_ + +/* Channel definition for ITIM16 */ +#define ITIM_TIME_NO ITIM16_1 +#define ITIM_EVENT_NO ITIM16_2 +#define ITIM_WDG_NO ITIM16_5 + +/* Clock source for ITIM16 */ +enum ITIM16_SOURCE_CLOCK_T { + ITIM16_SOURCE_CLOCK_APB2 = 0, + ITIM16_SOURCE_CLOCK_32K = 1, +}; + +/* Initialize ITIM16 timer */ +void init_hw_timer(int itim_no, enum ITIM16_SOURCE_CLOCK_T source); + +/* Returns time delay cause of deep idle */ +uint32_t __hw_clock_get_sleep_time(void); + +#endif /* HWTIMER_CHIP_H_ */ diff --git a/chip/npcx/i2c.c b/chip/npcx/i2c.c new file mode 100644 index 0000000000..1e71ed6c7b --- /dev/null +++ b/chip/npcx/i2c.c @@ -0,0 +1,592 @@ +/* Copyright (c) 2014 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. + */ + +/* I2C port module for Chrome EC */ + +#include "clock.h" +#include "clock_chip.h" +#include "common.h" +#include "console.h" +#include "gpio.h" +#include "hooks.h" +#include "i2c.h" +#include "registers.h" +#include "task.h" +#include "timer.h" +#include "util.h" + +#if !(DEBUG_I2C) +#define CPUTS(...) +#define CPRINTS(...) +#else +#define CPUTS(outstr) cputs(CC_I2C, outstr) +#define CPRINTS(format, args...) cprints(CC_I2C, format, ## args) +#endif + +/* Data abort timeout unit:ms*/ +#define I2C_ABORT_TIMEOUT 10000 +/* Maximum time we allow for an I2C transfer */ +#define I2C_TIMEOUT_US (100*MSEC) +/* Marco functions of I2C */ +#define I2C_START(port) SET_BIT(NPCX_SMBCTL1(port), NPCX_SMBCTL1_START) +#define I2C_STOP(port) SET_BIT(NPCX_SMBCTL1(port), NPCX_SMBCTL1_STOP) +#define I2C_NACK(port) SET_BIT(NPCX_SMBCTL1(port), NPCX_SMBCTL1_ACK) +#define I2C_WRITE_BYTE(port, data) (NPCX_SMBSDA(port) = data) +#define I2C_READ_BYTE(port, data) (data = NPCX_SMBSDA(port)) + +/* Error values that functions can return */ +enum smb_error { + SMB_OK = 0, /* No error */ + SMB_CH_OCCUPIED, /* Channel is already occupied */ + SMB_MEM_POOL_INIT_ERROR, /* Memory pool initialization error */ + SMB_BUS_FREQ_ERROR, /* SMbus freq was not valid */ + SMB_INVLAID_REGVALUE, /* Invalid SMbus register value */ + SMB_UNEXIST_CH_ERROR, /* Channel does not exist */ + SMB_NO_SUPPORT_PTL, /* Not support SMBus Protocol */ + SMB_BUS_ERROR, /* Encounter bus error */ + SMB_MASTER_NO_ADDRESS_MATCH,/* No slave address match (Master Mode)*/ + SMB_READ_DATA_ERROR, /* Read data for SDA error */ + SMB_READ_OVERFLOW_ERROR, /* Read data over than we predict */ + SMB_TIMEOUT_ERROR, /* Timeout expired */ + SMB_MODULE_ISBUSY, /* Module is occupied by other device */ + SMB_BUS_BUSY, /* SMBus is occupied by other device */ +}; + +/* + * Internal SMBus Interface driver states values, which reflect events + * which occured on the bus + */ +enum smb_oper_state_t { + SMB_IDLE, + SMB_MASTER_START, + SMB_WRITE_OPER, + SMB_READ_OPER, + SMB_REPEAT_START, + SMB_WAIT_REPEAT_START, +}; + + +/* IRQ for each port */ +static const uint32_t i2c_irqs[I2C_PORT_COUNT] = { + NPCX_IRQ_SMB1, NPCX_IRQ_SMB2, NPCX_IRQ_SMB3, NPCX_IRQ_SMB4}; +BUILD_ASSERT(ARRAY_SIZE(i2c_irqs) == I2C_PORT_COUNT); + +/* I2C port state data */ +struct i2c_status { + int flags; /* Flags (I2C_XFER_*) */ + const uint8_t *tx_buf; /* Entry pointer of transmit buffer */ + uint8_t *rx_buf; /* Entry pointer of receive buffer */ + uint16_t sz_txbuf; /* Size of Tx buffer in bytes */ + uint16_t sz_rxbuf; /* Size of rx buffer in bytes */ + uint16_t idx_buf; /* Current index of Tx/Rx buffer */ + uint8_t slave_addr;/* target slave address */ + enum smb_oper_state_t oper_state;/* smbus operation state */ + enum smb_error err_code; /* Error code */ + int task_waiting; /* Task waiting on port */ +}; +/* I2C port state data array */ +static struct i2c_status i2c_stsobjs[I2C_PORT_COUNT]; + +int i2c_bus_busy(int port) +{ + return IS_BIT_SET(NPCX_SMBCST(port), NPCX_SMBCST_BB) ? 1 : 0; +} + +void i2c_abort_data(int port) +{ + uint16_t timeout = I2C_ABORT_TIMEOUT; + + /* Generate a STOP condition */ + I2C_STOP(port); + + /* Clear NEGACK, STASTR and BER bits */ + SET_BIT(NPCX_SMBST(port), NPCX_SMBST_BER); + SET_BIT(NPCX_SMBST(port), NPCX_SMBST_STASTR); + /* + * In Master mode, NEGACK should be cleared only + * after generating STOP + */ + SET_BIT(NPCX_SMBST(port), NPCX_SMBST_NEGACK); + + /* Wait till STOP condition is generated */ + while (--timeout) { + msleep(1); + if (!IS_BIT_SET(NPCX_SMBCTL1(port), NPCX_SMBCTL1_STOP)) + break; + } + + /* Clear BB (BUS BUSY) bit */ + SET_BIT(NPCX_SMBCST(port), NPCX_SMBCST_BB); +} + +void i2c_reset(int port) +{ + uint16_t timeout = I2C_ABORT_TIMEOUT; + /* Disable the SMB module */ + CLEAR_BIT(NPCX_SMBCTL2(port), NPCX_SMBCTL2_ENABLE); + + while (--timeout) { + msleep(1); + /* WAIT FOR SCL & SDA IS HIGH */ + if (IS_BIT_SET(NPCX_SMBCTL3(port), NPCX_SMBCTL3_SCL_LVL) && + IS_BIT_SET(NPCX_SMBCTL3(port), NPCX_SMBCTL3_SDA_LVL)) + break; + } + + /* Enable the SMB module */ + SET_BIT(NPCX_SMBCTL2(port), NPCX_SMBCTL2_ENABLE); +} + +void i2c_recovery(int port) +{ + /* Abort data, generating STOP condition */ + i2c_abort_data(port); + + /* Reset i2c port by re-enable i2c port*/ + i2c_reset(port); +} + +enum smb_error i2c_master_transaction(int port) +{ + /* Set i2c mode to object */ + int events = 0; + volatile struct i2c_status *p_status = i2c_stsobjs + port; + + if (p_status->oper_state == SMB_IDLE) { + p_status->oper_state = SMB_MASTER_START; + } else if (p_status->oper_state == SMB_WAIT_REPEAT_START) { + p_status->oper_state = SMB_REPEAT_START; + CPUTS("R"); + } + + /* Generate a START condition */ + I2C_START(port); + CPUTS("ST"); + + /* Wait for transfer complete or timeout */ + events = task_wait_event_mask(TASK_EVENT_I2C_IDLE, I2C_TIMEOUT_US); + /* Handle timeout */ + if ((events & TASK_EVENT_I2C_IDLE) == 0) { + /* Recovery I2C port */ + i2c_recovery(port); + p_status->err_code = SMB_TIMEOUT_ERROR; + } + + /* + * In slave write operation, NACK is OK, otherwise it is a problem + */ + else if (p_status->err_code == SMB_BUS_ERROR || + p_status->err_code == SMB_MASTER_NO_ADDRESS_MATCH){ + i2c_recovery(port); + } + + return p_status->err_code; +} + +inline void i2c_handle_sda_irq(int port) +{ + volatile struct i2c_status *p_status = i2c_stsobjs + port; + /* 1 Issue Start is successful ie. write address byte */ + if (p_status->oper_state == SMB_MASTER_START + || p_status->oper_state == SMB_REPEAT_START) { + uint8_t addr = p_status->slave_addr; + /* Prepare address byte */ + if (p_status->sz_txbuf == 0) {/* Receive mode */ + p_status->oper_state = SMB_READ_OPER; + /* + * Receiving one byte only - set nack just + * before writing address byte + */ + if (p_status->sz_rxbuf == 1) + I2C_NACK(port); + + /* Write the address to the bus R bit*/ + I2C_WRITE_BYTE(port, (addr | 0x1)); + CPUTS("-ARR"); + } else {/* Transmit mode */ + p_status->oper_state = SMB_WRITE_OPER; + /* Write the address to the bus W bit*/ + I2C_WRITE_BYTE(port, addr); + CPUTS("-ARW"); + } + /* Completed handling START condition */ + return; + } + /* 2 Handle master write operation */ + else if (p_status->oper_state == SMB_WRITE_OPER) { + /* all bytes have been written, in a pure write operation */ + if (p_status->idx_buf == p_status->sz_txbuf) { + /* no more message */ + if (p_status->sz_rxbuf == 0) { + /* need to STOP or not */ + if (p_status->flags & I2C_XFER_STOP) { + /* Issue a STOP condition on the bus */ + I2C_STOP(port); + CPUTS("-SP"); + } + /* Clear SDA Status bit by writing dummy byte */ + I2C_WRITE_BYTE(port, 0xFF); + /* Set error code */ + p_status->err_code = SMB_OK; + /* Notify upper layer */ + p_status->oper_state + = (p_status->flags & I2C_XFER_STOP) + ? SMB_IDLE : SMB_WAIT_REPEAT_START; + task_set_event(p_status->task_waiting, + TASK_EVENT_I2C_IDLE, 0); + CPUTS("-END"); + } + /* need to restart & send slave address immediately */ + else { + uint8_t addr_byte = p_status->slave_addr; + /* + * Prepare address byte + * and start to receive bytes + */ + p_status->oper_state = SMB_READ_OPER; + /* Reset index of buffer */ + p_status->idx_buf = 0; + + /* + * Generate (Repeated) Start + * upon next write to SDA + */ + I2C_START(port); + CPUTS("-RST"); + /* + * Receiving one byte only - set nack just + * before writing address byte + */ + if (p_status->sz_rxbuf == 1) { + I2C_NACK(port); + CPUTS("-GNA"); + } + /* Write the address to the bus R bit*/ + I2C_WRITE_BYTE(port, (addr_byte | 0x1)); + CPUTS("-ARR"); + } + } + /* write next byte (not last byte and not slave address */ + else { + I2C_WRITE_BYTE(port, + p_status->tx_buf[p_status->idx_buf++]); + CPRINTS("-W(%02x)", + p_status->tx_buf[p_status->idx_buf-1]); + } + } + /* 3 Handle master read operation (read or after a write operation) */ + else if (p_status->oper_state == SMB_READ_OPER) { + uint8_t data; + /* last byte is about to be read - end of transaction */ + if (p_status->idx_buf == (p_status->sz_rxbuf - 1)) { + /* need to STOP or not */ + if (p_status->flags & I2C_XFER_STOP) { + /* Stop should set before reading last byte */ + I2C_STOP(port); + CPUTS("-SP"); + } + } + /* Check if byte-before-last is about to be read */ + else if (p_status->idx_buf == (p_status->sz_rxbuf - 2)) { + /* + * Set nack before reading byte-before-last, + * so that nack will be generated after receive + * of last byte + */ + I2C_NACK(port); + CPUTS("-GNA"); + } + + /* Read data for SMBSDA */ + I2C_READ_BYTE(port, data); + CPRINTS("-R(%02x)", data); + /* Read to buffer */ + p_status->rx_buf[p_status->idx_buf++] = data; + + /* last byte is read - end of transaction */ + if (p_status->idx_buf == p_status->sz_rxbuf) { + /* Set error code */ + p_status->err_code = SMB_OK; + /* Notify upper layer of missing data */ + p_status->oper_state = (p_status->flags & I2C_XFER_STOP) + ? SMB_IDLE : SMB_WAIT_REPEAT_START; + task_set_event(p_status->task_waiting, + TASK_EVENT_I2C_IDLE, 0); + CPUTS("-END"); + } + } +} + +void i2c_master_int_handler (int port) +{ + volatile struct i2c_status *p_status = i2c_stsobjs + port; + /* Condition 1 : A Bus Error has been identified */ + if (IS_BIT_SET(NPCX_SMBST(port), NPCX_SMBST_BER)) { + /* Clear BER Bit */ + SET_BIT(NPCX_SMBST(port), NPCX_SMBST_BER); + /* Set error code */ + p_status->err_code = SMB_BUS_ERROR; + /* Notify upper layer */ + p_status->oper_state = SMB_IDLE; + task_set_event(p_status->task_waiting, TASK_EVENT_I2C_IDLE, 0); + CPUTS("-BER"); + } + + /* Condition 2: A negative acknowledge has occurred */ + if (IS_BIT_SET(NPCX_SMBST(port), NPCX_SMBST_NEGACK)) { + /* Clear NEGACK Bit */ + SET_BIT(NPCX_SMBST(port), NPCX_SMBST_NEGACK); + /* Set error code */ + p_status->err_code = SMB_MASTER_NO_ADDRESS_MATCH; + /* Notify upper layer */ + p_status->oper_state = SMB_IDLE; + task_set_event(p_status->task_waiting, TASK_EVENT_I2C_IDLE, 0); + CPUTS("-NA"); + } + + /* Condition 3: SDA status is set - transmit or receive */ + if (IS_BIT_SET(NPCX_SMBST(port), NPCX_SMBST_SDAST)) + i2c_handle_sda_irq(port); +} + +/** + * Handle an interrupt on the specified port. + * + * @param port I2C port generating interrupt + */ +void handle_interrupt(int port) +{ + i2c_master_int_handler(port); +} + +void i2c0_interrupt(void) { handle_interrupt(0); } +void i2c1_interrupt(void) { handle_interrupt(1); } +void i2c2_interrupt(void) { handle_interrupt(2); } +void i2c3_interrupt(void) { handle_interrupt(3); } + +DECLARE_IRQ(NPCX_IRQ_SMB1, i2c0_interrupt, 2); +DECLARE_IRQ(NPCX_IRQ_SMB2, i2c1_interrupt, 2); +DECLARE_IRQ(NPCX_IRQ_SMB3, i2c2_interrupt, 2); +DECLARE_IRQ(NPCX_IRQ_SMB4, i2c3_interrupt, 2); + +/*****************************************************************************/ +/* IC specific low-level driver */ + +int i2c_xfer(int port, int slave_addr, const uint8_t *out, int out_size, + uint8_t *in, int in_size, int flags) +{ + volatile struct i2c_status *p_status = i2c_stsobjs + port; + + if (port < 0 || port >= i2c_ports_used) + return EC_ERROR_INVAL; + + if (out_size == 0 && in_size == 0) + return EC_SUCCESS; + + /* Copy data to port struct */ + p_status->flags = flags; + p_status->tx_buf = out; + p_status->sz_txbuf = out_size; + p_status->rx_buf = in; + p_status->sz_rxbuf = in_size; +#if I2C_7BITS_ADDR + /* Set slave address from 7-bits to 8-bits */ + p_status->slave_addr = (slave_addr<<1); +#else + /* Set slave address (8-bits) */ + p_status->slave_addr = slave_addr; +#endif + /* Reset index & error */ + p_status->idx_buf = 0; + p_status->err_code = SMB_OK; + + + /* Make sure we're in a good state to start */ + if ((flags & I2C_XFER_START) && (i2c_bus_busy(port) + || (i2c_get_line_levels(port) != I2C_LINE_IDLE))) { + + /* Attempt to unwedge the port. */ + i2c_unwedge(port); + /* recovery i2c port */ + i2c_recovery(port); + } + + /* Enable SMB interrupt and New Address Match interrupt source */ + SET_BIT(NPCX_SMBCTL1(port), NPCX_SMBCTL1_NMINTE); + SET_BIT(NPCX_SMBCTL1(port), NPCX_SMBCTL1_INTEN); + + CPUTS("\n"); + + /* Assign current task ID */ + p_status->task_waiting = task_get_current(); + + /* Start master transaction */ + i2c_master_transaction(port); + + /* Reset task ID */ + p_status->task_waiting = TASK_ID_INVALID; + + /* Disable SMB interrupt and New Address Match interrupt source */ + CLEAR_BIT(NPCX_SMBCTL1(port), NPCX_SMBCTL1_NMINTE); + CLEAR_BIT(NPCX_SMBCTL1(port), NPCX_SMBCTL1_INTEN); + + CPRINTS("-Err:0x%02x\n", p_status->err_code); + + return (p_status->err_code == SMB_OK) ? EC_SUCCESS : EC_ERROR_UNKNOWN; +} + +/** + * Return raw I/O line levels (I2C_LINE_*) for a port when port is in alternate + * function mode. + * + * @param port Port to check + * @return State of SCL/SDA bit 0/1 + */ +int i2c_get_line_levels(int port) +{ + return (i2c_raw_get_sda(port) ? I2C_LINE_SDA_HIGH : 0) | + (i2c_raw_get_scl(port) ? I2C_LINE_SCL_HIGH : 0); +} + +int i2c_raw_get_scl(int port) +{ + enum gpio_signal g; + + /* Check do we support this port of i2c and return gpio number of scl */ + if (get_scl_from_i2c_port(port, &g) == EC_SUCCESS) +#if !(I2C_LEVEL_SUPPORT) + return gpio_get_level(g); +#else + return IS_BIT_SET(NPCX_SMBCTL3(port), NPCX_SMBCTL3_SCL_LVL); +#endif + + /* If no SCL pin defined for this port, then return 1 to appear idle */ + return 1; +} + +int i2c_raw_get_sda(int port) +{ + enum gpio_signal g; + /* Check do we support this port of i2c and return gpio number of scl */ + if (get_sda_from_i2c_port(port, &g) == EC_SUCCESS) +#if !(I2C_LEVEL_SUPPORT) + return gpio_get_level(g); +#else + return IS_BIT_SET(NPCX_SMBCTL3(port), NPCX_SMBCTL3_SDA_LVL); +#endif + + /* If no SDA pin defined for this port, then return 1 to appear idle */ + return 1; +} + +int i2c_read_string(int port, int slave_addr, int offset, uint8_t *data, + int len) +{ + int rv; + uint8_t reg, block_length; + + /* Check block protocol size */ + if ((len <= 0) || (len > 32)) + return EC_ERROR_INVAL; + + i2c_lock(port, 1); + reg = offset; + + /* + * Send device reg space offset, and read back block length. Keep this + * session open without a stop. + */ + rv = i2c_xfer(port, slave_addr, ®, 1, &block_length, 1, + I2C_XFER_START); + if (rv) + goto exit; + + if (len && block_length > (len - 1)) + block_length = len - 1; + + rv = i2c_xfer(port, slave_addr, 0, 0, data, block_length, + I2C_XFER_STOP); + data[block_length] = 0; + +exit: + i2c_lock(port, 0); + + return rv; +} + +/*****************************************************************************/ +/* Hooks */ +static void i2c_freq_changed(void) +{ + /* I2C is under APB2 */ + int freq; + int port; + + for (port = 0; port < i2c_ports_used; port++) { + int bus_freq = i2c_ports[port].kbps; + int scl_time; + + /* SMB0/1 use core clock & SMB2/3 use apb2 clock */ + if (port < 2) + freq = clock_get_freq(); + else + freq = clock_get_apb2_freq(); + + /* use Fast Mode */ + SET_BIT(NPCX_SMBCTL3(port) , NPCX_SMBCTL3_400K); + + /* + * Set SCLLT/SCLHT: + * tSCLL = 2 * SCLLT7-0 * tCLK + * tSCLH = 2 * SCLHT7-0 * tCLK + * (tSCLL+tSCLH) = 4 * SCLH(L)T * tCLK if tSCLL == tSCLH + * SCLH(L)T = T(SCL)/4/T(CLK) = FREQ(CLK)/4/FREQ(SCL) + */ + scl_time = (freq/1000) / (bus_freq * 4); /* bus_freq is KHz */ + + /* set SCL High/Low time */ + NPCX_SMBSCLLT(port) = scl_time; + NPCX_SMBSCLHT(port) = scl_time; + } +} +DECLARE_HOOK(HOOK_FREQ_CHANGE, i2c_freq_changed, HOOK_PRIO_DEFAULT); + +static void i2c_init(void) +{ + int port = 0; + /* Configure pins from GPIOs to I2Cs */ + gpio_config_module(MODULE_I2C, 1); + + /* Enable clock for I2C peripheral */ + clock_enable_peripheral(CGC_OFFSET_I2C, CGC_I2C_MASK, + CGC_MODE_RUN | CGC_MODE_SLEEP); + /* + * initialize smb status and register + */ + for (port = 0; port < i2c_ports_used; port++) { + volatile struct i2c_status *p_status = i2c_stsobjs + port; + /* Configure pull-up for SMB interface pins */ +#ifndef SMB_SUPPORT18V + /* Enable 3.3V pull-up */ + SET_BIT(NPCX_DEVPU0, port); +#else + /* Set GPIO Pin voltage judgment to 1.8V */ + SET_BIT(NPCX_LV_GPIO_CTL1, port+1); +#endif + + /* Enable module - before configuring CTL1 */ + SET_BIT(NPCX_SMBCTL2(port), NPCX_SMBCTL2_ENABLE); + + /* status init */ + p_status->oper_state = SMB_IDLE; + + /* Reset task ID */ + p_status->task_waiting = TASK_ID_INVALID; + + /* Enable event and error interrupts */ + task_enable_irq(i2c_irqs[port]); + } +} +DECLARE_HOOK(HOOK_INIT, i2c_init, HOOK_PRIO_DEFAULT); diff --git a/chip/npcx/jtag.c b/chip/npcx/jtag.c new file mode 100644 index 0000000000..05dda6fa78 --- /dev/null +++ b/chip/npcx/jtag.c @@ -0,0 +1,16 @@ +/* Copyright (c) 2014 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 "clock.h" +#include "gpio.h" +#include "jtag.h" +#include "registers.h" +#include "system.h" + +void jtag_pre_init(void) +{ + /* Enable automatic freeze mode */ + CLEAR_BIT(NPCX_DBGFRZEN3, NPCX_DBGFRZEN3_GLBL_FRZ_DIS); +} diff --git a/chip/npcx/keyboard_raw.c b/chip/npcx/keyboard_raw.c new file mode 100644 index 0000000000..e6599a35e2 --- /dev/null +++ b/chip/npcx/keyboard_raw.c @@ -0,0 +1,128 @@ +/* Copyright (c) 2014 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. + */ + +/* Functions needed by keyboard scanner module for Chrome EC */ + +#include "common.h" +#include "keyboard_raw.h" +#include "keyboard_scan.h" +#include "clock.h" +#include "registers.h" +#include "task.h" + +/** + * Initialize the raw keyboard interface. + */ +void keyboard_raw_init(void) +{ + /* Enable clock for KBS peripheral */ + clock_enable_peripheral(CGC_OFFSET_KBS, CGC_KBS_MASK, + CGC_MODE_RUN | CGC_MODE_SLEEP); + + /* Ensure top-level interrupt is disabled */ + keyboard_raw_enable_interrupt(0); + + /* pull-up KBSIN 0-7 internally */ + NPCX_KBSINPU = 0xFF; + + /* Disable automatic scan mode */ + CLEAR_BIT(NPCX_KBSCTL, NPCX_KBSMODE); + + /* Disable automatic interrupt enable */ + CLEAR_BIT(NPCX_KBSCTL, NPCX_KBSIEN); + + /* Disable increment enable */ + CLEAR_BIT(NPCX_KBSCTL, NPCX_KBSINC); + + /* Set KBSOUT to zero to detect key-press */ + NPCX_KBSOUT0 = 0x00; + NPCX_KBSOUT1 = 0x00; + + /* + * Enable interrupts for the inputs. The top-level interrupt is still + * masked off, so this won't trigger interrupts yet. + */ + + /* Clear pending input sources used by scanner */ + NPCX_WKPCL(MIWU_TABLE_WKKEY, MIWU_GROUP_WKKEY) = 0xFF; + + /* Enable Wake-up Button */ + NPCX_WKEN(MIWU_TABLE_WKKEY, MIWU_GROUP_WKKEY) = 0xFF; + + /* Select high to low transition (falling edge) */ + NPCX_WKEDG(MIWU_TABLE_WKKEY, MIWU_GROUP_WKKEY) = 0xFF; + + /* Enable interrupt of WK KBS */ + keyboard_raw_enable_interrupt(1); +} + +/** + * Finish initialization after task scheduling has started. + */ +void keyboard_raw_task_start(void) +{ + /* Enable MIWU to trigger KBS interrupt */ + task_enable_irq(NPCX_IRQ_KSI_WKINTC_1); +} + +/** + * Drive the specified column low. + */ +test_mockable void keyboard_raw_drive_column(int col) +{ + /* + * Nuvoton Keyboard Scan IP supports 18x8 Matrix + * It also support automatic scan functionality + */ + uint32_t mask; + + /* Drive all lines to high */ + if (col == KEYBOARD_COLUMN_NONE) + mask = KB_COL_MASK; + /* Set KBSOUT to zero to detect key-press */ + else if (col == KEYBOARD_COLUMN_ALL) + mask = 0; + /* Drive one line for detection */ + else + mask = ((~(1 << col)) & KB_COL_MASK); + + /* Set KBSOUT */ + NPCX_KBSOUT0 = (mask & 0xFFFF); + NPCX_KBSOUT1 = ((mask >> 16) & 0x03); +} + +/** + * Read raw row state. + * Bits are 1 if signal is present, 0 if not present. + */ +test_mockable int keyboard_raw_read_rows(void) +{ + /* Bits are active-low, so invert returned levels */ + return (~NPCX_KBSIN) & KB_ROW_MASK; +} + +/** + * Enable or disable keyboard interrupts. + */ +void keyboard_raw_enable_interrupt(int enable) +{ + if (enable) + task_enable_irq(NPCX_IRQ_KSI_WKINTC_1); + else + task_disable_irq(NPCX_IRQ_KSI_WKINTC_1); +} + +/* + * Interrupt handler for the entire GPIO bank of keyboard rows. + */ +void keyboard_raw_interrupt(void) +{ + /* Clear pending input sources used by scanner */ + NPCX_WKPCL(MIWU_TABLE_WKKEY, MIWU_GROUP_WKKEY) = 0xFF; + + /* Wake the scan task */ + task_wake(TASK_ID_KEYSCAN); +} +DECLARE_IRQ(NPCX_IRQ_KSI_WKINTC_1, keyboard_raw_interrupt, 3); diff --git a/chip/npcx/lfw/ec_lfw.c b/chip/npcx/lfw/ec_lfw.c new file mode 100644 index 0000000000..9875681559 --- /dev/null +++ b/chip/npcx/lfw/ec_lfw.c @@ -0,0 +1,158 @@ +/* Copyright (c) 2014 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. + * + * NPCX5M5G SoC little FW used by booter + */ + +#include <stdint.h> +#include "registers.h" +#include "config_chip.h" +#include "ec_lfw.h" + +/* size of little FW */ +#define LFW_SIZE 0x1000 +/* signature used by booter */ +#define SIG_GOOGLE_EC 0x55AA650E +/* little FW located on TOP of Flash - 4K */ +#define FW_ADDR (CONFIG_SPI_FLASH_SIZE - 0x1000) + +/* Header used by NPCX5M5G Booter */ +struct booter_header_t { + uint32_t signature; /* A constant used to verify FW pointer is valid */ + uint32_t pointer_fw;/* Holds the BootLoader location in the flash */ +}; + +__attribute__ ((section(".booter_pointer"))) +const struct booter_header_t booter_header = { + /* 00 */ SIG_GOOGLE_EC, + /* 04 */ FW_ADDR +}; + + +/* Original sp during sysjump */ +uint32_t org_sp; + +/*****************************************************************************/ +/* flash internal functions */ + +void __attribute__ ((section(".instrucion_ram"))) +flash_burst_copy_fw_to_mram(uint32_t addr_flash, uint32_t addr_mram, + uint32_t size) +{ + uint32_t bit32_idx; + uint32_t bit32_size; + uint32_t *bit32_ptr_mram; + + bit32_ptr_mram = (uint32_t *)addr_mram; + + /* Round it up and get it in 4 bytes */ + bit32_size = (size+3) / 4; + + /* Set chip select to low */ + CLEAR_BIT(NPCX_UMA_ECTS, NPCX_UMA_ECTS_SW_CS1); + + /* Write flash address */ + NPCX_UMA_AB2 = (uint8_t)((addr_flash & 0xFF0000)>>16); + NPCX_UMA_AB1 = (uint8_t)((addr_flash & 0xFF00)>>8); + NPCX_UMA_AB0 = (uint8_t)((addr_flash & 0xFF)); + + NPCX_UMA_CODE = CMD_FAST_READ; + NPCX_UMA_CTS = MASK_CMD_ADR_WR; + /* wait for UMA to complete */ + while (IS_BIT_SET(NPCX_UMA_CTS, EXEC_DONE)) + ; + + /* Start to burst read and copy data to Code RAM */ + for (bit32_idx = 0; bit32_idx < bit32_size; bit32_idx++) { + /* 1101 0100 - EXEC, RD, NO CMD, NO ADDR, 4 bytes */ + NPCX_UMA_CTS = MASK_RD_4BYTE; + while (IS_BIT_SET(NPCX_UMA_CTS, EXEC_DONE)) + ; + /* copy data to Code RAM */ + bit32_ptr_mram[bit32_idx] = NPCX_UMA_DB0_3; + } + + /* Set chip select to high */ + SET_BIT(NPCX_UMA_ECTS, NPCX_UMA_ECTS_SW_CS1); +} + +void __attribute__ ((section(".instrucion_ram"))) +bin2ram(void) +{ + /* copy image from RO base */ + if (IS_BIT_SET(NPCX_FWCTRL, NPCX_FWCTRL_RO_REGION)) + flash_burst_copy_fw_to_mram(CONFIG_FW_RO_OFF, CONFIG_CDRAM_BASE, + CONFIG_FW_RO_SIZE - LFW_SIZE); + /* copy image from RW base */ + else + flash_burst_copy_fw_to_mram(CONFIG_FW_RW_OFF, CONFIG_CDRAM_BASE, + CONFIG_FW_RW_SIZE - LFW_SIZE); + + /* Disable FIU pins to tri-state */ + CLEAR_BIT(NPCX_DEVCNT, NPCX_DEVCNT_F_SPI_TRIS); + + /* TODO: (ML) Booter has cleared watchdog flag */ +#ifndef CHIP_NPCX5M5G + static uint32_t reset_flag; + /* Check for VCC1 reset */ + if (IS_BIT_SET(NPCX_RSTCTL, NPCX_RSTCTL_VCC1_RST_STS)) { + /* Clear flag bit */ + SET_BIT(NPCX_RSTCTL, NPCX_RSTCTL_VCC1_RST_STS); + reset_flag = 1; + } + /* Software debugger reset */ + else if (IS_BIT_SET(NPCX_RSTCTL, NPCX_RSTCTL_DBGRST_STS)) + reset_flag = 1; + /* Watchdog Reset */ + else if (IS_BIT_SET(NPCX_T0CSR, NPCX_T0CSR_WDRST_STS)) { + reset_flag = 1; + } else { + reset_flag = 0; + } + + if (reset_flag) { +#else + /* Workaround method to distinguish reboot or sysjump */ + if (org_sp < 0x200C0000) { +#endif + /* restore sp from begin of RO image */ + asm volatile("ldr r0, =0x10088000\n" + "ldr r1, [r0]\n" + "mov sp, r1\n"); + } else { + /* restore sp from sysjump */ + asm volatile("mov sp, %0" : : "r"(org_sp)); + } + + /* Jump to reset ISR */ + asm volatile( + "ldr r0, =0x10088004\n" + "ldr r1, [r0]\n" + "mov pc, r1\n"); +} + +/* Entry function of little FW */ +void __attribute__ ((section(".startup_text"), noreturn)) +entry_lfw(void) +{ + uint32_t i; + + /* Backup sp value */ + asm volatile("mov %0, sp" : "=r"(org_sp)); + /* initialize sp with Data RAM */ + asm volatile( + "ldr r0, =0x100A8000\n" + "mov sp, r0\n"); + + /* Copy the bin2ram code to RAM */ + for (i = 0; i < &__iram_fw_end - &__iram_fw_start; i++) + *(&__iram_fw_start + i) = *(&__flash_fw_start + i); + + /* Run code in RAM */ + bin2ram(); + + /* Should never reach this */ + for (;;) + ; +} diff --git a/chip/npcx/lfw/ec_lfw.h b/chip/npcx/lfw/ec_lfw.h new file mode 100644 index 0000000000..857135d6d0 --- /dev/null +++ b/chip/npcx/lfw/ec_lfw.h @@ -0,0 +1,18 @@ +/* Copyright (c) 2014 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. + * + * NPCX5M5G SoC little FW used by booter + */ + +#ifndef __CROS_EC_LFW_H_ +#define __CROS_EC_LFW_H_ + +/* Begin address for the .iram section; defined in linker script */ +extern unsigned int __iram_fw_start; +/* End address for the .iram section; defined in linker script */ +extern unsigned int __iram_fw_end; +/* Begin address for the iram codes; defined in linker script */ +extern unsigned int __flash_fw_start; + +#endif /* __CROS_EC_LFW_H_ */ diff --git a/chip/npcx/lfw/ec_lfw.ld b/chip/npcx/lfw/ec_lfw.ld new file mode 100644 index 0000000000..dd93bba89e --- /dev/null +++ b/chip/npcx/lfw/ec_lfw.ld @@ -0,0 +1,122 @@ +/* Copyright (c) 2014 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. + * + * NPCX5M5G SoC little FW for booting + */ + +/* Memory Spaces Definitions */ +MEMORY +{ + FLASH (rx) : ORIGIN = 0x647FF000, LENGTH = 4K - 256 + POINTER(r) : ORIGIN = 0x647FFF00, LENGTH = 256 + CODERAM(rx): ORIGIN = 0x100A7C00, LENGTH = 1K - 256 + RAM (xrw) : ORIGIN = 0x100A7F00, LENGTH = 256 +} + +/* + * The entry point is informative, for debuggers and simulators, + * since the Cortex-M vector points to it anyway. + */ +ENTRY(entry_lfw) + +/* Sections Definitions */ + +SECTIONS +{ + + /* + * The beginning of the startup code + */ + .startup_text : + { + . = ALIGN(4); + *(.startup_text ) /* Startup code */ + . = ALIGN(4); + } >FLASH + + + /* + * The program code is stored in the .text section, + * which goes to FLASH. + */ + .text : + { + . = ALIGN(4); + *(.text .text.*) /* all remaining code */ + *(.rodata .rodata.*) /* read-only data (constants) */ + } >FLASH + + . = ALIGN(4); + + __flash_fw_start = .; + .instrucion_ram : AT(__flash_fw_start) + { + . = ALIGN(4); + __iram_fw_start = .; + *(.instrucion_ram .instrucion_ram.*) /* CODERAM in 0x200C0000 */ + __iram_fw_end = .; + } > CODERAM + + /* + * The POINTER section used for booter + */ + .booter_pointer : + { + . = ALIGN(4); + KEEP(*(.booter_pointer)) /* Booter pointer in 0xFFFF00 */ + } > POINTER + + . = ALIGN(4); + _etext = .; + + /* + * This address is used by the startup code to + * initialise the .data section. + */ + _sidata = _etext; + + /* + * The initialised data section. + * The program executes knowing that the data is in the RAM + * but the loader puts the initial values in the FLASH (inidata). + * It is one task of the startup to copy the initial values from + * FLASH to RAM. + */ + .data : AT ( _sidata ) + { + . = ALIGN(4); + + /* This is used by the startup code to initialise the .data section */ + __data_start__ = . ; + *(.data_begin .data_begin.*) + + *(.data .data.*) + + *(.data_end .data_end.*) + . = ALIGN(4); + + /* This is used by the startup code to initialise the .data section */ + __data_end__ = . ; + + } > RAM + + + /* + * The uninitialised data section. NOLOAD is used to avoid + * the "section `.bss' type changed to PROGBITS" warning + */ + .bss (NOLOAD) : + { + . = ALIGN(4); + __bss_start__ = .; /* standard newlib definition */ + *(.bss_begin .bss_begin.*) + + *(.bss .bss.*) + *(COMMON) + + *(.bss_end .bss_end.*) + . = ALIGN(4); + __bss_end__ = .; /* standard newlib definition */ + } >RAM +}
\ No newline at end of file diff --git a/chip/npcx/lpc.c b/chip/npcx/lpc.c new file mode 100644 index 0000000000..55d76f46d0 --- /dev/null +++ b/chip/npcx/lpc.c @@ -0,0 +1,672 @@ +/* Copyright (c) 2014 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. + */ + +/* LPC module for Chrome EC */ + +#include "acpi.h" +#include "clock.h" +#include "common.h" +#include "console.h" +#include "gpio.h" +#include "hooks.h" +#include "host_command.h" +#include "keyboard_protocol.h" +#include "lpc.h" +#include "port80.h" +#include "pwm.h" +#include "registers.h" +#include "system.h" +#include "task.h" +#include "timer.h" +#include "uart.h" +#include "util.h" +#include "system_chip.h" + +/* Console output macros */ +#define CPUTS(outstr) cputs(CC_LPC, outstr) +#define CPRINTS(format, args...) cprints(CC_LPC, format, ## args) + +#define LPC_SYSJUMP_TAG 0x4c50 /* "LP" */ + +static uint32_t host_events; /* Currently pending SCI/SMI events */ +static uint32_t event_mask[3]; /* Event masks for each type */ +static struct host_packet lpc_packet; +static struct host_cmd_handler_args host_cmd_args; +static uint8_t host_cmd_flags; /* Flags from host command */ +static uint8_t shm_mem_host_cmd[256] __aligned(8); +static uint8_t shm_memmap[256] __aligned(8); +/* Params must be 32-bit aligned */ +static uint8_t params_copy[EC_LPC_HOST_PACKET_SIZE] __aligned(4); +static int init_done; + +static uint8_t * const cmd_params = (uint8_t *)shm_mem_host_cmd + + EC_LPC_ADDR_HOST_PARAM - EC_LPC_ADDR_HOST_ARGS; +static struct ec_lpc_host_args * const lpc_host_args = + (struct ec_lpc_host_args *)shm_mem_host_cmd; + + +#ifdef CONFIG_KEYBOARD_IRQ_GPIO +static void keyboard_irq_assert(void) +{ + /* + * Enforce signal-high for long enough for the signal to be pulled high + * by the external pullup resistor. This ensures the host will see the + * following falling edge, regardless of the line state before this + * function call. + */ + gpio_set_level(CONFIG_KEYBOARD_IRQ_GPIO, 1); + udelay(4); + /* Generate a falling edge */ + gpio_set_level(CONFIG_KEYBOARD_IRQ_GPIO, 0); + udelay(4); + /* Set signal high, now that we've generated the edge */ + gpio_set_level(CONFIG_KEYBOARD_IRQ_GPIO, 1); +} +#else +static inline void keyboard_irq_assert(void) +{ + /* Use serirq method. */ + /* Using manual IRQ for KBC */ + SET_BIT(NPCX_HIIRQC, 0); /* set IRQ1B to high */ + CLEAR_BIT(NPCX_HICTRL, 0); /* set IRQ1 control by IRQB1 */ +} +#endif + +static void lpc_task_enable_irq(void){ + + task_enable_irq(NPCX_IRQ_SHM); + task_enable_irq(NPCX_IRQ_KBC_IBF); + task_enable_irq(NPCX_IRQ_PM_CHAN_IBF); + task_enable_irq(NPCX_IRQ_PORT80); +} +static void lpc_task_disable_irq(void){ + + task_disable_irq(NPCX_IRQ_SHM); + task_disable_irq(NPCX_IRQ_KBC_IBF); + task_disable_irq(NPCX_IRQ_PM_CHAN_IBF); + task_disable_irq(NPCX_IRQ_PORT80); +} +/** + * Generate SMI pulse to the host chipset via GPIO. + * + * If the x86 is in S0, SMI# is sampled at 33MHz, so minimum pulse length is + * 60ns. If the x86 is in S3, SMI# is sampled at 32.768KHz, so we need pulse + * length >61us. Both are short enough and events are infrequent, so just + * delay for 65us. + */ +static void lpc_generate_smi(void) +{ +#ifdef CONFIG_SCI_GPIO + /* Enforce signal-high for long enough to debounce high */ + gpio_set_level(GPIO_PCH_SMI_L, 1); + udelay(65); + /* Generate a falling edge */ + gpio_set_level(GPIO_PCH_SMI_L, 0); + udelay(65); + /* Set signal high, now that we've generated the edge */ + gpio_set_level(GPIO_PCH_SMI_L, 1); +#else + NPCX_HIPMIE(PM_CHAN_1) |= NPCX_HIPMIE_SMIE; +#endif + if (host_events & event_mask[LPC_HOST_EVENT_SMI]) + CPRINTS("smi 0x%08x", + host_events & event_mask[LPC_HOST_EVENT_SMI]); +} + +/** + * Generate SCI pulse to the host chipset via LPC0SCI. + */ +static void lpc_generate_sci(void) +{ +#ifdef CONFIG_SCI_GPIO + /* Enforce signal-high for long enough to debounce high */ + gpio_set_level(CONFIG_SCI_GPIO, 1); + udelay(65); + /* Generate a falling edge */ + gpio_set_level(CONFIG_SCI_GPIO, 0); + udelay(65); + /* Set signal high, now that we've generated the edge */ + gpio_set_level(CONFIG_SCI_GPIO, 1); +#else + SET_BIT(NPCX_HIPMIE(PM_CHAN_1), NPCX_HIPMIE_SCIE); +#endif + + if (host_events & event_mask[LPC_HOST_EVENT_SCI]) + CPRINTS("sci 0x%08x", + host_events & event_mask[LPC_HOST_EVENT_SCI]); +} + +/** + * Update the level-sensitive wake signal to the AP. + * + * @param wake_events Currently asserted wake events + */ +static void lpc_update_wake(uint32_t wake_events) +{ + /* + * Mask off power button event, since the AP gets that through a + * separate dedicated GPIO. + */ + wake_events &= ~EC_HOST_EVENT_MASK(EC_HOST_EVENT_POWER_BUTTON); + + /* Signal is asserted low when wake events is non-zero */ + gpio_set_level(GPIO_PCH_WAKE_L, !wake_events); +} + +uint8_t *lpc_get_memmap_range(void) +{ + return (uint8_t *)shm_memmap; +} + +static void lpc_send_response(struct host_cmd_handler_args *args) +{ + uint8_t *out; + int size = args->response_size; + int csum; + int i; + + /* Ignore in-progress on LPC since interface is synchronous anyway */ + if (args->result == EC_RES_IN_PROGRESS) + return; + + /* Handle negative size */ + if (size < 0) { + args->result = EC_RES_INVALID_RESPONSE; + size = 0; + } + + /* New-style response */ + lpc_host_args->flags = + (host_cmd_flags & ~EC_HOST_ARGS_FLAG_FROM_HOST) | + EC_HOST_ARGS_FLAG_TO_HOST; + + lpc_host_args->data_size = size; + + csum = args->command + lpc_host_args->flags + + lpc_host_args->command_version + + lpc_host_args->data_size; + + for (i = 0, out = (uint8_t *)args->response; i < size; i++, out++) + csum += *out; + + lpc_host_args->checksum = (uint8_t)csum; + + /* Fail if response doesn't fit in the param buffer */ + if (size > EC_PROTO2_MAX_PARAM_SIZE) + args->result = EC_RES_INVALID_RESPONSE; + + /* Write result to the data byte. This sets the TOH status bit. */ + NPCX_HIPMDO(PM_CHAN_2) = args->result; + /* Clear processing flag */ + CLEAR_BIT(NPCX_HIPMST(PM_CHAN_2), 2); +} + +static void lpc_send_response_packet(struct host_packet *pkt) +{ + /* Ignore in-progress on LPC since interface is synchronous anyway */ + if (pkt->driver_result == EC_RES_IN_PROGRESS) + return; + + /* Write result to the data byte. This sets the TOH status bit. */ + NPCX_HIPMDO(PM_CHAN_2) = pkt->driver_result; + /* Clear processing flag */ + CLEAR_BIT(NPCX_HIPMST(PM_CHAN_2), 2); +} + +int lpc_keyboard_has_char(void) +{ + /* if OBF '1', that mean still have a data in the FIFO */ + return (NPCX_HIKMST&0x01) ? 1 : 0; +} + +/* Return true if the FRMH is set */ +int lpc_keyboard_input_pending(void) +{ + return (NPCX_HIKMST&0x02) ? 1 : 0; +} + +/* Put a char to host buffer and send IRQ if specified. */ +void lpc_keyboard_put_char(uint8_t chr, int send_irq) +{ + UPDATE_BIT(NPCX_HICTRL, NPCX_HICTRL_OBFKIE, send_irq); + NPCX_HIKDO = chr; + task_enable_irq(NPCX_IRQ_KBC_OBF); +} + +void lpc_keyboard_clear_buffer(void) +{ + /* Make sure the previous TOH and IRQ has been sent out. */ + udelay(4); + /*FW_OBF write 1*/ + NPCX_HICTRL |= 0x80; + /* Ensure there is no TOH set in this period. */ + udelay(4); +} + +void lpc_keyboard_resume_irq(void) +{ + if (lpc_keyboard_has_char()) + keyboard_irq_assert(); +} + +/** + * Update the host event status. + * + * Sends a pulse if masked event status becomes non-zero: + * - SMI pulse via EC_SMI_L GPIO + * - SCI pulse via LPC0SCI + */ +static void update_host_event_status(void) +{ + int need_sci = 0; + int need_smi = 0; + + if (!init_done) + return; + + /* Disable LPC interrupt while updating status register */ + lpc_task_disable_irq(); + if (host_events & event_mask[LPC_HOST_EVENT_SMI]) { + /* Only generate SMI for first event */ + if (!(NPCX_HIPMIE(PM_CHAN_1) & NPCX_HIPMIE_SMIE)) + need_smi = 1; + SET_BIT(NPCX_HIPMIE(PM_CHAN_1), NPCX_HIPMIE_SMIE); + } else + CLEAR_BIT(NPCX_HIPMIE(PM_CHAN_1), NPCX_HIPMIE_SMIE); + + if (host_events & event_mask[LPC_HOST_EVENT_SCI]) { + /* Generate SCI for every event */ + need_sci = 1; + SET_BIT(NPCX_HIPMIE(PM_CHAN_1), NPCX_HIPMIE_SCIE); + } else + CLEAR_BIT(NPCX_HIPMIE(PM_CHAN_1), NPCX_HIPMIE_SCIE); + + /* Copy host events to mapped memory */ + *(uint32_t *)host_get_memmap(EC_MEMMAP_HOST_EVENTS) = host_events; + + lpc_task_enable_irq(); + + /* Process the wake events. */ + lpc_update_wake(host_events & event_mask[LPC_HOST_EVENT_WAKE]); + + /* Send pulse on SMI signal if needed */ + if (need_smi) + lpc_generate_smi(); + + /* ACPI 5.0-12.6.1: Generate SCI for SCI_EVT=1. */ + if (need_sci) + lpc_generate_sci(); +} + +void lpc_set_host_event_state(uint32_t mask) +{ + if (mask != host_events) { + host_events = mask; + update_host_event_status(); + } +} + +int lpc_query_host_event_state(void) +{ + const uint32_t any_mask = event_mask[0] | event_mask[1] | event_mask[2]; + int evt_index = 0; + int i; + + for (i = 0; i < 32; i++) { + const uint32_t e = (1 << i); + + if (host_events & e) { + host_clear_events(e); + + /* + * If host hasn't unmasked this event, drop it. We do + * this at query time rather than event generation time + * so that the host has a chance to unmask events + * before they're dropped by a query. + */ + if (!(e & any_mask)) + continue; + + evt_index = i + 1; /* Events are 1-based */ + break; + } + } + + return evt_index; +} + +void lpc_set_host_event_mask(enum lpc_host_event_type type, uint32_t mask) +{ + event_mask[type] = mask; + update_host_event_status(); +} + +uint32_t lpc_get_host_event_mask(enum lpc_host_event_type type) +{ + return event_mask[type]; +} + +int lpc_get_pltrst_asserted(void) +{ + /* TODO: (Simon) need to define GPIO_PLTRST */ + return 0; +} + +/** + * Handle write to ACPI I/O port + * + * @param is_cmd Is write command (is_cmd=1) or data (is_cmd=0) + */ +static void handle_acpi_write(int is_cmd) +{ + uint8_t value, result; + + /* Read command/data; this clears the FRMH status bit. */ + value = NPCX_HIPMDI(PM_CHAN_1); + + /* Handle whatever this was. */ + if (acpi_ap_to_ec(is_cmd, value, &result)) + NPCX_HIPMDO(PM_CHAN_1) = result; + + /* + * ACPI 5.0-12.6.1: Generate SCI for Input Buffer Empty / Output Buffer + * Full condition on the kernel channel. + */ + lpc_generate_sci(); +} + +/** + * Handle write to host command I/O ports. + * + * @param is_cmd Is write command (1) or data (0)? + */ +static void handle_host_write(int is_cmd) +{ + /* + * Read the command byte. This clears the FRMH bit in + * the status byte. + */ + host_cmd_args.command = NPCX_HIPMDI(PM_CHAN_2); + + host_cmd_args.result = EC_RES_SUCCESS; + host_cmd_args.send_response = lpc_send_response; + host_cmd_flags = lpc_host_args->flags; + + /* See if we have an old or new style command */ + if (host_cmd_args.command == EC_COMMAND_PROTOCOL_3) { + lpc_packet.send_response = lpc_send_response_packet; + + lpc_packet.request = (const void *)shm_mem_host_cmd; + lpc_packet.request_temp = params_copy; + lpc_packet.request_max = sizeof(params_copy); + /* Don't know the request size so pass in the entire buffer */ + lpc_packet.request_size = EC_LPC_HOST_PACKET_SIZE; + + lpc_packet.response = (void *)shm_mem_host_cmd; + lpc_packet.response_max = EC_LPC_HOST_PACKET_SIZE; + lpc_packet.response_size = 0; + + lpc_packet.driver_result = EC_RES_SUCCESS; + /* Set processing flag */ + SET_BIT(NPCX_HIPMST(PM_CHAN_2), 2); + host_packet_receive(&lpc_packet); + return; + + } else if (host_cmd_flags & EC_HOST_ARGS_FLAG_FROM_HOST) { + /* Version 2 (link) style command */ + int size = lpc_host_args->data_size; + int csum, i; + + host_cmd_args.version = lpc_host_args->command_version; + host_cmd_args.params = params_copy; + host_cmd_args.params_size = size; + host_cmd_args.response = cmd_params; + host_cmd_args.response_max = EC_PROTO2_MAX_PARAM_SIZE; + host_cmd_args.response_size = 0; + + /* Verify params size */ + if (size > EC_PROTO2_MAX_PARAM_SIZE) { + host_cmd_args.result = EC_RES_INVALID_PARAM; + } else { + const uint8_t *src = cmd_params; + uint8_t *copy = params_copy; + + /* + * Verify checksum and copy params out of LPC space. + * This ensures the data acted on by the host command + * handler can't be changed by host writes after the + * checksum is verified. + */ + csum = host_cmd_args.command + + host_cmd_flags + + host_cmd_args.version + + host_cmd_args.params_size; + + for (i = 0; i < size; i++) { + csum += *src; + *(copy++) = *(src++); + } + + if ((uint8_t)csum != lpc_host_args->checksum) + host_cmd_args.result = EC_RES_INVALID_CHECKSUM; + } + } else { + /* Old style command, now unsupported */ + host_cmd_args.result = EC_RES_INVALID_COMMAND; + } + + /* Hand off to host command handler */ + host_command_received(&host_cmd_args); +} + + +void lpc_shm_interrupt(void){ +} +DECLARE_IRQ(NPCX_IRQ_SHM, lpc_shm_interrupt, 2); + +void lpc_kbc_ibf_interrupt(void) +{ +#ifdef CONFIG_KEYBOARD_PROTOCOL_8042 + /* If "command" input 0, else 1*/ + keyboard_host_write(NPCX_HIKMDI, (NPCX_HIKMST & 0x08) ? 1 : 0); +#endif +} +DECLARE_IRQ(NPCX_IRQ_KBC_IBF, lpc_kbc_ibf_interrupt, 2); + +void lpc_kbc_obf_interrupt(void){ + /* reserve for future handle */ + if (!IS_BIT_SET(NPCX_HICTRL, 0)) { + SET_BIT(NPCX_HICTRL, 0); /* back to H/W control of IRQ1 */ + CLEAR_BIT(NPCX_HIIRQC, 0); /* back to default of IRQB1 */ + } + task_disable_irq(NPCX_IRQ_KBC_OBF); +} +DECLARE_IRQ(NPCX_IRQ_KBC_OBF, lpc_kbc_obf_interrupt, 2); + +void lpc_pmc_ibf_interrupt(void){ + /* Channel-1 for ACPI usage*/ + /* Channel-2 for Host Command usage , so the argument data had been + * put on the share memory firstly*/ + if (NPCX_HIPMST(PM_CHAN_1) & 0x02) + handle_acpi_write((NPCX_HIPMST(PM_CHAN_1)&0x08) ? 1 : 0); + else if (NPCX_HIPMST(PM_CHAN_2)&0x02) + handle_host_write((NPCX_HIPMST(PM_CHAN_2)&0x08) ? 1 : 0); +} +DECLARE_IRQ(NPCX_IRQ_PM_CHAN_IBF, lpc_pmc_ibf_interrupt, 2); + +void lpc_pmc_obf_interrupt(void){ +} +DECLARE_IRQ(NPCX_IRQ_PM_CHAN_OBF, lpc_pmc_obf_interrupt, 2); + +void lpc_port80_interrupt(void){ + port_80_write((NPCX_GLUE_SDPD0<<0) | (NPCX_GLUE_SDPD1<<8)); + /* No matter what , just clear error status bit */ + SET_BIT(NPCX_DP80STS, 7); + SET_BIT(NPCX_DP80STS, 5); +} +DECLARE_IRQ(NPCX_IRQ_PORT80, lpc_port80_interrupt, 2); + +/** + * Preserve event masks across a sysjump. + */ +static void lpc_sysjump(void) +{ + system_add_jump_tag(LPC_SYSJUMP_TAG, 1, + sizeof(event_mask), event_mask); +} +DECLARE_HOOK(HOOK_SYSJUMP, lpc_sysjump, HOOK_PRIO_DEFAULT); + +/** + * Restore event masks after a sysjump. + */ +static void lpc_post_sysjump(void) +{ + const uint32_t *prev_mask; + int size, version; + + prev_mask = (const uint32_t *)system_get_jump_tag(LPC_SYSJUMP_TAG, + &version, &size); + if (!prev_mask || version != 1 || size != sizeof(event_mask)) + return; + + memcpy(event_mask, prev_mask, sizeof(event_mask)); +} + +static void lpc_init(void) +{ + /* Enable clock for LPC peripheral */ + clock_enable_peripheral(CGC_OFFSET_LPC, CGC_LPC_MASK, + CGC_MODE_RUN | CGC_MODE_SLEEP); + /* Switching to LPC interface */ + NPCX_DEVCNT |= 0x04; + /* Enable 4E/4F */ + if (!IS_BIT_SET(NPCX_MSWCTL1, 3)) { + NPCX_HCBAL = 0x4E; + NPCX_HCBAH = 0x0; + } + /* Clear Host Access Hold state */ + NPCX_SMC_CTL = 0xC0; + + /* Initialize Hardware for UART Host */ +#if CONFIG_UART_HOST + /* Init COMx LPC UART */ + /* FMCLK have to using 50MHz */ + NPCX_DEVALT(0xB) = 0xFF; + /* Make sure Host Access unlock */ + CLEAR_BIT(NPCX_LKSIOHA, 2); + /* Clear Host Access Lock Violation */ + SET_BIT(NPCX_SIOLV, 2); +#endif + + /* Don't stall SHM transactions */ + NPCX_SHM_CTL = NPCX_SHM_CTL & ~0x40; + /* Semaphore and Indirect access disable */ + NPCX_SHCFG = 0xE0; + /* Disable Protect Win1&2*/ + NPCX_WIN_WR_PROT(0) = 0; + NPCX_WIN_WR_PROT(1) = 0; + NPCX_WIN_RD_PROT(0) = 0; + NPCX_WIN_RD_PROT(1) = 0; + /* Open Win1 256 byte for Host CMD, Win2 256 for MEMMAP*/ + NPCX_WIN_SIZE = 0x88; + NPCX_WIN_BASE(0) = (uint32_t)shm_mem_host_cmd; + NPCX_WIN_BASE(1) = (uint32_t)shm_memmap; + + /* Turn on PMC2 for Host Command usage */ + SET_BIT(NPCX_HIPMCTL(PM_CHAN_2), 0); + SET_BIT(NPCX_HIPMCTL(PM_CHAN_2), 1); + /* enable PMC2 IRQ */ + SET_BIT(NPCX_HIPMIE(PM_CHAN_2), 0); + /* IRQ control from HW */ + SET_BIT(NPCX_HIPMIE(PM_CHAN_2), 3); + /* + * Set required control value (avoid setting HOSTWAIT bit at this stage) + */ + NPCX_SMC_CTL = NPCX_SMC_CTL&~0x7F; + /* Clear status */ + NPCX_SMC_STS = NPCX_SMC_STS; + /* Create mailbox */ + + /* + * Init KBC + * Clear OBF status, PM1 IBF/OBF INT enable, IRQ11 enable, + * IBF(K&M) INT enable, OBF(K&M) empty INT enable , + * OBF Mouse Full INT enable and OBF KB Full INT enable + */ + NPCX_HICTRL = 0xFF; + /* Normally Polarity IRQ1,12,11 type (level + high) setting */ + NPCX_HIIRQC = 0x00; /* Make sure to default */ + + /* + * Init PORT80 + * Enable Port80, Enable Port80 function & Interrupt & Read auto + */ + NPCX_DP80CTL = 0x29; + SET_BIT(NPCX_GLUE_SDP_CTS, 3); + SET_BIT(NPCX_GLUE_SDP_CTS, 0); + /* Just turn on IRQE */ + NPCX_HIPMIE(PM_CHAN_1) = 0x01; + lpc_task_enable_irq(); + + /* Initialize host args and memory map to all zero */ + memset(lpc_host_args, 0, sizeof(*lpc_host_args)); + memset(lpc_get_memmap_range(), 0, EC_MEMMAP_SIZE); + + /* We support LPC args and version 3 protocol */ + *(lpc_get_memmap_range() + EC_MEMMAP_HOST_CMD_FLAGS) = + EC_HOST_CMD_FLAG_LPC_ARGS_SUPPORTED | + EC_HOST_CMD_FLAG_VERSION_3; + + + + /* Restore event masks if needed */ + lpc_post_sysjump(); + + /* Sufficiently initialized */ + init_done = 1; + + /* Update host events now that we can copy them to memmap */ + + update_host_event_status(); + + /* initial IO port address via SIB-write modules */ + system_lpc_host_register_init(); +} +/* + * Set prio to higher than default; this way LPC memory mapped data is ready + * before other inits try to initialize their memmap data. + */ +DECLARE_HOOK(HOOK_INIT, lpc_init, HOOK_PRIO_INIT_LPC); + +static void lpc_resume(void) +{ + /* Mask all host events until the host unmasks them itself. */ + lpc_set_host_event_mask(LPC_HOST_EVENT_SMI, 0); + lpc_set_host_event_mask(LPC_HOST_EVENT_SCI, 0); + lpc_set_host_event_mask(LPC_HOST_EVENT_WAKE, 0); + + /* Store port 80 event so we know where resume happened */ + port_80_write(PORT_80_EVENT_RESUME); +} +DECLARE_HOOK(HOOK_CHIPSET_RESUME, lpc_resume, HOOK_PRIO_DEFAULT); + +/* Get protocol information */ +static int lpc_get_protocol_info(struct host_cmd_handler_args *args) +{ + struct ec_response_get_protocol_info *r = args->response; + + memset(r, 0, sizeof(*r)); + r->protocol_versions = (1 << 2) | (1 << 3); + r->max_request_packet_size = EC_LPC_HOST_PACKET_SIZE; + r->max_response_packet_size = EC_LPC_HOST_PACKET_SIZE; + r->flags = 0; + + args->response_size = sizeof(*r); + + return EC_SUCCESS; +} +DECLARE_HOST_COMMAND(EC_CMD_GET_PROTOCOL_INFO, + lpc_get_protocol_info, + EC_VER_MASK(0)); diff --git a/chip/npcx/openocd/jlink.cfg b/chip/npcx/openocd/jlink.cfg new file mode 100644 index 0000000000..5294b733fd --- /dev/null +++ b/chip/npcx/openocd/jlink.cfg @@ -0,0 +1,4 @@ +interface jlink + +source [find npcx.cfg] +source [find npcx_cmds.tcl] diff --git a/chip/npcx/openocd/npcx.cfg b/chip/npcx/openocd/npcx.cfg new file mode 100644 index 0000000000..ccc53437bb --- /dev/null +++ b/chip/npcx/openocd/npcx.cfg @@ -0,0 +1,63 @@ +# Copyright (c) 2014 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. +# +# nuvoton-m4 devices support both JTAG and SWD transports. +# + +source [find target/swj-dp.tcl] + +if { [info exists CHIPNAME] } { + set _CHIPNAME $CHIPNAME +} else { + set _CHIPNAME npcx5m5g +} + +if { [info exists ENDIAN] } { + set _ENDIAN $ENDIAN +} else { + set _ENDIAN little +} + +# Work-area is a space in RAM used for flash programming +# By default use 16kB +if { [info exists WORKAREASIZE] } { + set _WORKAREASIZE $WORKAREASIZE +} else { + set _WORKAREASIZE 0x8000 +} + +#jtag scan chain +if { [info exists CPUTAPID ] } { + set _CPUTAPID $CPUTAPID +} else { + set _CPUTAPID 0x4BA00477 +} + +#jtag newtap $_CHIPNAME cpu -irlen 4 -expected-id $_CPUTAPID +swj_newdap $_CHIPNAME cpu -irlen 4 -expected-id $_CPUTAPID + +set _TARGETNAME $_CHIPNAME.cpu +target create $_TARGETNAME cortex_m -endian $_ENDIAN -chain-position \ + $_CHIPNAME.cpu -work-area-phys 0x200C0000 \ + -work-area-size $_WORKAREASIZE + +# JTAG speed +adapter_khz 1000 + +adapter_nsrst_delay 100 +if {$using_jtag} { + jtag_ntrst_delay 100 +} + +# use srst to perform a system reset +cortex_m reset_config srst + +#reset configuration +reset_config trst_and_srst + +$_TARGETNAME configure -event reset-start { + echo "NPCX5M5G Reset..." + adapter_khz 1000 + halt +} diff --git a/chip/npcx/openocd/npcx_cmds.tcl b/chip/npcx/openocd/npcx_cmds.tcl new file mode 100644 index 0000000000..2d5a6c930e --- /dev/null +++ b/chip/npcx/openocd/npcx_cmds.tcl @@ -0,0 +1,104 @@ +# Copyright (c) 2014 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. +# +# Command automation for NPCX5M5G chip + +# Program spi flash +source [find mem_helper.tcl] + +proc flash_npcx {image_path image_offset image_size spifw_image} { + set UPLOAD_FLAG 0x200C4000; + + # Upload program spi image FW to lower 16KB Data RAM + fast_load_image $spifw_image 0x200C0000 + fast_load + # Clear whole 128KB Code RAM + mwb 0x10088000 0xFF 0x20000 + # Upload binary image to Code RAM + fast_load_image $image_path 0x10088000 + fast_load + # Set sp to upper 16KB Data RAM + reg sp 0x200C8000 + # Set spi offset address of uploaded image + reg r0 $image_offset + # Set spi program size of uploaded image + reg r1 $image_size + # Clear upload flag + mww $UPLOAD_FLAG 0x0 + # Start to program spi flash + resume 0x200C0001 + echo "*** Program ... ***" + sleep 1 + # Wait for any pending flash operations to complete + while {[expr [mrw $UPLOAD_FLAG] & 0x01] == 0} { sleep 1 } + + # Halt CPU + halt + + if {[expr [mrw $UPLOAD_FLAG] & 0x02] == 0} { + echo "*** Program Fail ***" + } else { + echo "*** Program Done ***" + } + +} + +proc flash_npcx_ro {image_offset} { + # 128 KB for RO& RW regions + set fw_size 0x20000 + # images path + set outdir ../../../build/npcx_evb + set ro_image_path $outdir/ec.RO.flat + set spifw_image $outdir/chip/npcx/spiflashfw/ec_npcxflash.bin + + # Halt CPU first + halt + echo "*** Start to program RO region ***" + # Write to lower 128kB from offset + flash_npcx $ro_image_path $image_offset $fw_size $spifw_image + echo "*** Finish program RO region ***" + + # Reset CPU + reset +} + +proc flash_npcx_evb {image_offset} { + # 128 KB for RO& RW regions + set fw_size 0x20000 + # 4K little FW + set lfw_size 0x1000 + # 8M spi-flash + set flash_size 0x800000 + + # images path + set outdir ../../../build/npcx_evb + set ro_image_path $outdir/ec.RO.flat + set rw_image_path $outdir/ec.RW.bin + set lfw_image_path $outdir/chip/npcx/lfw/ec_lfw.bin + set spifw_image $outdir/chip/npcx/spiflashfw/ec_npcxflash.bin + + # images offset + set rw_image_offset [expr ($image_offset + $fw_size)] + set lfw_image_offset [expr ($flash_size - $lfw_size)] + + # Halt CPU first + halt + echo "*** Start to program RO region ***" + # Write to lower 128kB from offset + flash_npcx $ro_image_path $image_offset $fw_size $spifw_image + echo "*** Finish program RO region ***" + + echo "*** Start to program RW region ***" + # Write to upper 128kB from offset + flash_npcx $rw_image_path $rw_image_offset $fw_size $spifw_image + echo "*** Finish program RW region ***" + + echo "*** Start to program LFW region ***" + # Write to top of flash minus 4KB + flash_npcx $lfw_image_path $lfw_image_offset $lfw_size $spifw_image + echo "*** Finish program LFW region ***" + + # Reset CPU + reset +} diff --git a/chip/npcx/openocd/servo_v2.cfg b/chip/npcx/openocd/servo_v2.cfg new file mode 100644 index 0000000000..578e3f9c43 --- /dev/null +++ b/chip/npcx/openocd/servo_v2.cfg @@ -0,0 +1,13 @@ +telnet_port 4444 +gdb_port 3333 +gdb_memory_map enable +gdb_flash_program enable + +interface ftdi +ftdi_vid_pid 0x18d1 0x5002 +ftdi_layout_init 0x0c08 0x0f1b +ftdi_layout_signal nTRST -data 0x0100 -noe 0x0400 +ftdi_layout_signal nSRST -data 0x0200 -noe 0x0800 + +source [find npcx.cfg] +source [find npcx_cmds.tcl] diff --git a/chip/npcx/peci.c b/chip/npcx/peci.c new file mode 100644 index 0000000000..d5de9c1e40 --- /dev/null +++ b/chip/npcx/peci.c @@ -0,0 +1,295 @@ +/* Copyright (c) 2014 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. + */ + +/* PECI interface for Chrome EC */ + +#include "chipset.h" +#include "clock.h" +#include "clock_chip.h" +#include "common.h" +#include "console.h" +#include "gpio.h" +#include "hooks.h" +#include "peci.h" +#include "registers.h" +#include "task.h" +#include "timer.h" +#include "temp_sensor.h" +#include "util.h" + + +/* Initial PECI baud rate */ +#define PECI_BAUD_RATE 750000 + +#define TEMP_AVG_LENGTH 4 /* Should be power of 2 */ + + +/* PECI Time-out */ +#define PECI_DONE_TIMEOUT_US (100*MSEC) +/* Task Event for PECI */ +#define TASK_EVENT_PECI_DONE TASK_EVENT_CUSTOM(1<<26) + +#define NULL_PENDING_TASK_ID 0xFFFFFFFF +#define PECI_MAX_FIFO_SIZE 16 +#define PROC_SOCKET 0x30 +/* PECI Command Code */ +enum peci_command_t { + PECI_COMMAND_PING = 0x00, + PECI_COMMAND_GET_DIB = 0xF7, + PECI_COMMAND_GET_TEMP = 0x01, + PECI_COMMAND_RD_PKG_CFG = 0xA1, + PECI_COMMAND_WR_PKG_CFG = 0xA5, + PECI_COMMAND_RD_IAMSR = 0xB1, + PECI_COMMAND_RD_PCI_CFG = 0x61, + PECI_COMMAND_RD_PCI_CFG_LOCAL = 0xE1, + PECI_COMMAND_WR_PCI_CFG_LOCAL = 0xE5, + PECI_COMMAND_NONE = 0xFF +}; + +#define PECI_COMMAND_GET_TEMP_WR_LENS 0x00 +#define PECI_COMMAND_GET_TEMP_RD_LENS 0x02 + +/* PECI Domain Number */ +static int temp_vals[TEMP_AVG_LENGTH]; +static int temp_idx; +static uint8_t peci_sts; +/* For PECI Done interrupt usage */ +static int peci_pending_task_id; + +/*****************************************************************************/ +/* Internal functions */ + +/** + * This routine initiates the parameters of a PECI transaction + * + * @param wr_length How many byte of *wr_data went to be send + * @param rd_length How many byte went to received (not include FCS) + * @param cmd_code Command code + * @param *wr_data Buffer pointer of write data + * @return TASK_EVENT_PECI_DONE that mean slave had a response + */ +static uint32_t peci_trans( + uint8_t wr_length, + uint8_t rd_length, + enum peci_command_t cmd_code, + uint8_t *wr_data +) +{ + uint32_t events; + /* Ensure no PECI transaction is in progress */ + if (IS_BIT_SET(NPCX_PECI_CTL_STS, NPCX_PECI_CTL_STS_START_BUSY)) { + /* + * PECI transaction is in progress - + * can not initiate a new one + */ + return 0; + } + /* Set basic transaction parameters */ + NPCX_PECI_ADDR = PROC_SOCKET; + NPCX_PECI_CMD = cmd_code; + /* Aviod over space */ + if (rd_length > PECI_MAX_FIFO_SIZE) + rd_length = PECI_MAX_FIFO_SIZE; + /* Read-Length */ + NPCX_PECI_RD_LENGTH = rd_length; + if (wr_length > PECI_MAX_FIFO_SIZE) + wr_length = PECI_MAX_FIFO_SIZE; + /* copy of data */ + for (events = 0; events < wr_length; events++) + NPCX_PECI_DATA_OUT(events) = wr_data[events]; + /* Write-Length */ + if (cmd_code != PECI_COMMAND_PING) { + if ((cmd_code == PECI_COMMAND_WR_PKG_CFG) || + (cmd_code == PECI_COMMAND_WR_PCI_CFG_LOCAL)) { + /*CMD+AWFCS*/ + NPCX_PECI_WR_LENGTH = wr_length + 2; + /* Enable AWFCS */ + SET_BIT(NPCX_PECI_CTL_STS, NPCX_PECI_CTL_STS_AWFCS_EN); + } else { + /*CMD*/ + NPCX_PECI_WR_LENGTH = wr_length + 1; + /* Enable AWFCS */ + CLEAR_BIT(NPCX_PECI_CTL_STS, + NPCX_PECI_CTL_STS_AWFCS_EN); + } + } + + /* Start the PECI transaction */ + SET_BIT(NPCX_PECI_CTL_STS, NPCX_PECI_CTL_STS_START_BUSY); + + /* It should be using a interrupt , don't waste cpu computing power */ + peci_pending_task_id = task_get_current(); + return task_wait_event_mask(TASK_EVENT_PECI_DONE, + PECI_DONE_TIMEOUT_US); + +} + +/** + * PECI transaction error status. + * + * @return Bit3 - CRC error Bit4 - ABRT error + */ +static uint8_t peci_check_error_state(void) +{ + return peci_sts; +} + +/*****************************************************************************/ +/* PECI drivers */ +int peci_get_cpu_temp(void) +{ + uint32_t events; + int16_t cpu_temp = -1; + + /* Start PECI trans */ + events = peci_trans(PECI_COMMAND_GET_TEMP_WR_LENS, + PECI_COMMAND_GET_TEMP_RD_LENS, + PECI_COMMAND_GET_TEMP, NULL); + /* if return DONE , that mean slave had a PECI response */ + if ((events & TASK_EVENT_PECI_DONE) == TASK_EVENT_PECI_DONE) { + /* check CRC & ABRT */ + events = peci_check_error_state(); + if (events) { + ; + } else { + uint16_t *ptr; + ptr = (uint16_t *)&cpu_temp; + ptr[0] = (NPCX_PECI_DATA_IN(1) << 8) | + (NPCX_PECI_DATA_IN(0) << 0); + } + } + return (int)cpu_temp; +} + +int peci_temp_sensor_get_val(int idx, int *temp_ptr) +{ + int sum = 0; + int success_cnt = 0; + int i; + + if (!chipset_in_state(CHIPSET_STATE_ON)) + return EC_ERROR_NOT_POWERED; + + for (i = 0; i < TEMP_AVG_LENGTH; ++i) { + if (temp_vals[i] >= 0) { + success_cnt++; + sum += temp_vals[i]; + } + } + + /* + * Require at least two valid samples. When the AP transitions into S0, + * it is possible, depending on the timing of the PECI sample, to read + * an invalid temperature. This is very rare, but when it does happen + * the temperature returned is CONFIG_PECI_TJMAX. Requiring two valid + * samples here assures us that one bad maximum temperature reading + * when entering S0 won't cause us to trigger an over temperature. + */ + if (success_cnt < 2) + return EC_ERROR_UNKNOWN; + + *temp_ptr = sum / success_cnt; + return EC_SUCCESS; +} + +static void peci_temp_sensor_poll(void) +{ + int val; + + val = peci_get_cpu_temp(); + if (val != -1) { + temp_vals[temp_idx] = val; + temp_idx = (temp_idx + 1) & (TEMP_AVG_LENGTH - 1); + } +} +DECLARE_HOOK(HOOK_TICK, peci_temp_sensor_poll, HOOK_PRIO_TEMP_SENSOR); + +static void peci_freq_changed(void) +{ + /* PECI is under APB2 */ + int freq = clock_get_freq(); + int baud = 0xF; + + /* Disable polling while reconfiguring */ + NPCX_PECI_CTL_STS = 0; + + /* + * Set the maximum bit rate used by the PECI module during both + * Address Timing Negotiation and Data Timing Negotiation. + * The resulting maximum bit rate MAX_BIT_RATE in decimal is + * according to the following formula: + * + * MAX_BIT_RATE [d] = (freq / (4 * baudrate)) - 1 + * Maximum bit rate should not extend the field's boundaries. + */ + if (freq != 0) { + baud = (uint8_t)(freq / (4 * PECI_BAUD_RATE)) - 1; + /* Set maximum PECI baud rate (bit0 - bit4) */ + if (baud > 0x1F) + baud = 0x1F; + } + /* Enhanced High-Speed */ + if (baud >= 7) { + CLEAR_BIT(NPCX_PECI_RATE, 6); + CLEAR_BIT(NPCX_PECI_CFG, 3); + } else { + SET_BIT(NPCX_PECI_RATE, 6); + SET_BIT(NPCX_PECI_CFG, 3); + } + /* Setting Rate */ + NPCX_PECI_RATE = baud; +} +DECLARE_HOOK(HOOK_FREQ_CHANGE, peci_freq_changed, HOOK_PRIO_DEFAULT); + +static void peci_init(void) +{ + int i; + + /* make sure PECI_DATA function pin enable */ + CLEAR_BIT(NPCX_DEVALT(0x0A), 6); + /* Set initial clock frequency */ + peci_freq_changed(); + /* Initialize temperature reading buffer to a sane value. */ + for (i = 0; i < TEMP_AVG_LENGTH; ++i) + temp_vals[i] = 300; /* 27 C */ + + /* init Pending task id */ + peci_pending_task_id = NULL_PENDING_TASK_ID; + /* Enable PECI Done interrupt */ + SET_BIT(NPCX_PECI_CTL_STS, NPCX_PECI_CTL_STS_DONE_EN); + + task_enable_irq(NPCX_IRQ_PECI); +} +DECLARE_HOOK(HOOK_INIT, peci_init, HOOK_PRIO_DEFAULT); + +/* If received a PECI DONE interrupt, post the event to PECI task */ +void peci_done_interrupt(void){ + if (peci_pending_task_id != NULL_PENDING_TASK_ID) + task_set_event(peci_pending_task_id, TASK_EVENT_PECI_DONE, 0); + peci_sts = NPCX_PECI_CTL_STS & 0x18; + /* no matter what, clear status bit again */ + SET_BIT(NPCX_PECI_CTL_STS, NPCX_PECI_CTL_STS_DONE); + SET_BIT(NPCX_PECI_CTL_STS, NPCX_PECI_CTL_STS_CRC_ERR); + SET_BIT(NPCX_PECI_CTL_STS, NPCX_PECI_CTL_STS_ABRT_ERR); +} +DECLARE_IRQ(NPCX_IRQ_PECI, peci_done_interrupt, 2); + +/*****************************************************************************/ +/* Console commands */ + +static int command_peci_temp(int argc, char **argv) +{ + int t = peci_get_cpu_temp(); + if (t == -1) { + ccprintf("PECI response timeout\n"); + return EC_ERROR_UNKNOWN; + } + ccprintf("CPU temp = %d K = %d\n", t, K_TO_C(t)); + return EC_SUCCESS; +} +DECLARE_CONSOLE_COMMAND(pecitemp, command_peci_temp, + NULL, + "Print CPU temperature", + NULL); diff --git a/chip/npcx/pwm.c b/chip/npcx/pwm.c new file mode 100644 index 0000000000..bfbf2090d2 --- /dev/null +++ b/chip/npcx/pwm.c @@ -0,0 +1,269 @@ +/* Copyright (c) 2014 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. + */ + +/* PWM control module for NPCX. + * + * On this chip, the PWM logic is implemented by the hardware FAN modules. + */ + +#include "clock.h" +#include "clock_chip.h" +#include "fan.h" +#include "gpio.h" +#include "hooks.h" +#include "pwm.h" +#include "pwm_chip.h" +#include "registers.h" +#include "util.h" +#include "console.h" + +#if !(DEBUG_PWM) +#define CPRINTS(...) +#else +#define CPRINTS(format, args...) cprints(CC_PWM, format, ## args) +#endif + +/* PWM clock source */ +enum npcx_pwm_source_clock { + NPCX_PWM_CLOCK_APB2_LFCLK = 0, + NPCX_PWM_CLOCK_FX = 1, + NPCX_PWM_CLOCK_FR = 2, + NPCX_PWM_CLOCK_RESERVED = 0x3, + NPCX_PWM_CLOCK_UNDEF = 0xFF +}; + +/* PWM heartbeat mode */ +enum npcx_pwm_heartbeat_mode { + NPCX_PWM_HBM_NORMAL = 0, + NPCX_PWM_HBM_25 = 1, + NPCX_PWM_HBM_50 = 2, + NPCX_PWM_HBM_100 = 3, + NPCX_PWM_HBM_UNDEF = 0xFF +}; + +/* Global variables */ +static int pwm_init_ch; + +/** + * Preset PWM operation clock. + * + * @param none + * @return none + * @notes changed when initial or HOOK_FREQ_CHANGE command + */ +void pwm_freq_changed(void) +{ + uint32_t prescaler_divider = 0; + + if (pwm_init_ch == PWM_CH_FAN) { + /* + * Using PWM Frequency and Resolution we calculate + * prescaler for input clock + */ + /* (Benson_TBD_9) pwm_clock/freq/resolution not confirm */ +#ifdef CONFIG_PWM_INPUT_LFCLK + prescaler_divider = (uint32_t)(32768 / + pwm_channels[pwm_init_ch].freq); +#else + prescaler_divider = (uint32_t)(clock_get_apb2_freq() + / pwm_channels[pwm_init_ch].freq); +#endif + } else { + prescaler_divider = (uint32_t)(clock_get_apb2_freq() + / pwm_channels[pwm_init_ch].freq); + } + /* Set clock prescalre divider to ADC module*/ + if (prescaler_divider >= 1) + prescaler_divider = prescaler_divider - 1; + if (prescaler_divider > 0xFFFF) + prescaler_divider = 0xFFFF; + + /* Configure computed prescaler and resolution */ + NPCX_PRSC(pwm_channels[pwm_init_ch].channel) = + (uint16_t)prescaler_divider; +} +DECLARE_HOOK(HOOK_FREQ_CHANGE, pwm_freq_changed, HOOK_PRIO_DEFAULT); + +/** + * Set PWM enabled. + * + * @param ch operation channel + * @param enabled enabled flag + * @return none + */ +void pwm_enable(enum pwm_channel ch, int enabled) +{ + /* Start or close PWM module */ + if (enabled) + SET_BIT(NPCX_PWMCTL(pwm_channels[ch].channel), NPCX_PWMCTL_PWR); + else + CLEAR_BIT(NPCX_PWMCTL(pwm_channels[ch].channel), + NPCX_PWMCTL_PWR); +} + +/** + * Check PWM enabled. + * + * @param ch operation channel + * @return enabled or not + */ +int pwm_get_enabled(enum pwm_channel ch) +{ + return IS_BIT_SET(NPCX_PWMCTL(pwm_channels[ch].channel), + NPCX_PWMCTL_PWR); +} + +/** + * Set PWM duty cycle. + * + * @param ch operation channel + * @param percent duty cycle percent + * @return none + */ +void pwm_set_duty(enum pwm_channel ch, int percent) +{ + uint32_t resolution = 0; + uint16_t duty_cycle = 0; + + CPRINTS("pwm0=%d", percent); + /* Assume the fan control is active high and invert it ourselves */ + if (pwm_channels[ch].flags & PWM_CONFIG_ACTIVE_LOW) + SET_BIT(NPCX_PWMCTL(pwm_channels[ch].channel), + NPCX_PWMCTL_INVP); + else + CLEAR_BIT(NPCX_PWMCTL(pwm_channels[ch].channel), + NPCX_PWMCTL_INVP); + + if (percent < 0) + percent = 0; + /* (Benson_TBD_14) if 100% make mft cannot get TCRB, + * it will need to change to 99% */ + else if (percent > 100) + percent = 100; + CPRINTS("pwm1duty=%d", percent); + + resolution = NPCX_CTR(pwm_channels[ch].channel) + 1; + duty_cycle = percent*resolution/100; + CPRINTS("freq=0x%x", pwm_channels[ch].freq); + CPRINTS("resolution=%d", resolution); + CPRINTS("duty_cycle=%d", duty_cycle); + + /* Set the duty cycle */ + /* (Benson_TBD_14) Always enable the fan channel or not */ + if (percent) { + NPCX_DCR(pwm_channels[ch].channel) = (duty_cycle - 1); + pwm_enable(ch, 1); + } else { + NPCX_DCR(pwm_channels[ch].channel) = 0; + pwm_enable(ch, 0); + } +} + +/** + * Get PWM duty cycle. + * + * @param ch operation channel + * @return duty cycle percent + */ +int pwm_get_duty(enum pwm_channel ch) +{ + /* Return percent */ + if (0 == pwm_get_enabled(ch)) + return 0; + else + return (((NPCX_DCR(pwm_channels[ch].channel) + 1) * 100) + / (NPCX_CTR(pwm_channels[ch].channel) + 1)); +} + +/** + * PWM configuration. + * + * @param ch operation channel + * @return none + */ +void pwm_config(enum pwm_channel ch) +{ + pwm_init_ch = ch; + + /* Configure pins from GPIOs to PWM */ + if (ch == PWM_CH_FAN) + gpio_config_module(MODULE_PWM_FAN, 1); + else + gpio_config_module(MODULE_PWM_KBLIGHT, 1); + + /* Disable PWM for module configuration */ + pwm_enable(ch, 0); + + /* Set PWM heartbeat mode is no heartbeat*/ + NPCX_PWMCTL(pwm_channels[ch].channel) = + (NPCX_PWMCTL(pwm_channels[ch].channel) + &(~(((1<<2)-1)<<NPCX_PWMCTL_HB_DC_CTL))) + |(NPCX_PWM_HBM_NORMAL<<NPCX_PWMCTL_HB_DC_CTL); + + /* Set PWM operation frequence */ + pwm_freq_changed(); + + /* Set PWM cycle time */ + NPCX_CTR(pwm_channels[ch].channel) = + (pwm_channels[ch].cycle_pulses - 1); + + /* Set the duty cycle */ + NPCX_DCR(pwm_channels[ch].channel) = 0; + + /* Set PWM polarity is normal*/ + CLEAR_BIT(NPCX_PWMCTL(pwm_channels[ch].channel), NPCX_PWMCTL_INVP); + + /* Set PWM open drain output is push-pull type*/ + CLEAR_BIT(NPCX_PWMCTL(pwm_channels[ch].channel), NPCX_PWMCTLEX_OD_OUT); + + /* Select default CLK or LFCLK clock input to PWM module */ + NPCX_PWMCTLEX(pwm_channels[ch].channel) = + (NPCX_PWMCTLEX(pwm_channels[ch].channel) + & (~(((1<<2)-1)<<NPCX_PWMCTLEX_FCK_SEL))) + | (NPCX_PWM_CLOCK_APB2_LFCLK<<NPCX_PWMCTLEX_FCK_SEL); + + if (ch == PWM_CH_FAN) { +#ifdef CONFIG_PWM_INPUT_LFCLK + /* Select default LFCLK clock input to PWM module */ + SET_BIT(NPCX_PWMCTL(pwm_channels[ch].channel), + NPCX_PWMCTL_CKSEL); +#else + /* Select default core clock input to PWM module */ + CLEAR_BIT(NPCX_PWMCTL(pwm_channels[ch].channel), + NPCX_PWMCTL_CKSEL); +#endif + } else { + /* Select default core clock input to PWM module */ + CLEAR_BIT(NPCX_PWMCTL(pwm_channels[ch].channel), + NPCX_PWMCTL_CKSEL); + } +} + +/** + * PWM initial. + * + * @param none + * @return none + */ +static void pwm_init(void) +{ + int i; + +#ifdef CONFIG_PWM_DSLEEP + + /* Enable the PWM module and delay a few clocks */ + clock_enable_peripheral(CGC_OFFSET_PWM, CGC_PWM_MASK, CGC_MODE_ALL); +#else + /* Enable the PWM module and delay a few clocks */ + clock_enable_peripheral(CGC_OFFSET_PWM, CGC_PWM_MASK, + CGC_MODE_RUN | CGC_MODE_SLEEP); +#endif + + for (i = 0; i < PWM_CH_COUNT; i++) + pwm_config(i); +} + +/* The chip-specific fan module initializes before this. */ +DECLARE_HOOK(HOOK_INIT, pwm_init, HOOK_PRIO_DEFAULT); diff --git a/chip/npcx/pwm_chip.h b/chip/npcx/pwm_chip.h new file mode 100644 index 0000000000..77d6991c7b --- /dev/null +++ b/chip/npcx/pwm_chip.h @@ -0,0 +1,26 @@ +/* Copyright (c) 2014 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. + */ + +/* NPCX-specific PWM module for Chrome EC */ + +#ifndef __CROS_EC_NPCX_PWM_H +#define __CROS_EC_NPCX_PWM_H + +/* Data structure to define PWM channels. */ +struct pwm_t { + /* PWM channel ID */ + int channel; + /* PWM channel flags. See include/pwm.h */ + uint32_t flags; + /* PWM freq. */ + uint32_t freq; + /* PWM pulses each cycle. */ + uint32_t cycle_pulses; +}; + +extern const struct pwm_t pwm_channels[]; +void pwm_config(enum pwm_channel ch); + +#endif /* __CROS_EC_NPCX_PWM_H */ diff --git a/chip/npcx/registers.h b/chip/npcx/registers.h new file mode 100644 index 0000000000..1d11770ad1 --- /dev/null +++ b/chip/npcx/registers.h @@ -0,0 +1,1189 @@ +/* Copyright (c) 2014 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 NPCX processor + */ + +#ifndef __CROS_EC_REGISTERS_H +#define __CROS_EC_REGISTERS_H + +#include "common.h" +/******************************************************************************/ +/* + * Macro Functions + */ +#define SET_BIT(reg, bit) ((reg) |= (0x1 << (bit))) +#define CLEAR_BIT(reg, bit) ((reg) &= (~(0x1 << (bit)))) +#define IS_BIT_SET(reg, bit) ((reg >> bit) & (0x1)) +#define UPDATE_BIT(reg, bit, cond) { if (cond) \ + SET_BIT(reg, bit); \ + else \ + CLEAR_BIT(reg, bit); } + +/******************************************************************************/ +/* + * NPCX (Nuvoton M4 EC) Register Definitions + */ + +/* Global Definition */ +#define CHIP_NPCX5M5G +#define SUPPORT_JTAG +#define I2C0_BUS0 1 /* Use I2C0_SDA0/1 I2C0_SCL0/1 */ +#define TACH_SEL1 1 /* Use TACH_SEL1 or TACH_SEL2 */ +#define JTAG1 0 /* Use JTAG0/1 JTAG1 only support + 132-Pins package*/ +#define I2C_7BITS_ADDR 0 +#define I2C_LEVEL_SUPPORT 1 +/* Switcher of features */ +#define SUPPORT_LCT 1 +#define SUPPORT_WDG 0 +#define SUPPORT_HIB 1 +/* Switcher of debugging */ +#define DEBUG_I2C 0 +#define DEBUG_TMR 1 +#define DEBUG_WDG 0 +#define DEBUG_GPIO 1 +#define DEBUG_FAN 0 +#define DEBUG_PWM 0 +#define DEBUG_SPI 0 +#define DEBUG_FLH 0 +#define DEBUG_PECI 0 +#define DEBUG_CLK 1 + +/* Modules Map */ +#define NPCX_MDC_BASE_ADDR 0x4000C000 +#define NPCX_SIB_BASE_ADDR 0x4000E000 +#define NPCX_PMC_BASE_ADDR 0x4000D000 +#define NPCX_SHM_BASE_ADDR 0x40010000 +#define NPCX_FIU_BASE_ADDR 0x40020000 +#define NPCX_KBSCAN_REGS_BASE 0x400A3000 +#define NPCX_GLUE_REGS_BASE 0x400A5000 +#define NPCX_BBRAM_BASE_ADDR 0x400AF000 +#define NPCX_HFCG_BASE_ADDR 0x400B5000 +#define NPCX_MTC_BASE_ADDR 0x400B7000 +#define NPCX_MSWC_BASE_ADDR 0x400C1000 +#define NPCX_SCFG_BASE_ADDR 0x400C3000 +#define NPCX_CR_UART_BASE_ADDR 0x400C4000 +#define NPCX_KBC_BASE_ADDR 0x400C7000 +#define NPCX_ADC_BASE_ADDR 0x400D1000 +#define NPCX_SPI_BASE_ADDR 0x400D2000 +#define NPCX_PECI_BASE_ADDR 0x400D4000 +#define NPCX_TWD_BASE_ADDR 0x400D8000 + +/* Multi-Modules Map */ +#define NPCX_PWM_BASE_ADDR(mdl) (0x40080000 + ((mdl) * 0x2000L)) +#define NPCX_GPIO_BASE_ADDR(mdl) (0x40081000 + ((mdl) * 0x2000L)) +#define NPCX_ITIM16_BASE_ADDR(mdl) (0x400B0000 + ((mdl) * 0x2000L)) +#define NPCX_MIWU_BASE_ADDR(mdl) (0x400BB000 + ((mdl) * 0x2000L)) +#define NPCX_MFT_BASE_ADDR(mdl) (0x400E1000 + ((mdl) * 0x2000L)) +#define NPCX_PM_CH_BASE_ADDR(mdl) (0x400C9000 + ((mdl) * 0x2000L)) +#define NPCX_SMB_BASE_ADDR(mdl) ((mdl < 2) ? (0x40009000 + \ + ((mdl) * 0x2000L)) : \ + (0x400C0000 + ((mdl) * 0x2000L))) + +/* + * NPCX-IRQ numbers + */ +#define NPCX_IRQ_0 0 +#define NPCX_IRQ_1 1 +#define NPCX_IRQ_2 2 +#define NPCX_IRQ_3 3 +#define NPCX_IRQ_4 4 +#define NPCX_IRQ_5 5 +#define NPCX_IRQ_6 6 +#define NPCX_IRQ_7 7 +#define NPCX_IRQ_8 8 +#define NPCX_IRQ_9 9 +#define NPCX_IRQ_10 10 +#define NPCX_IRQ_11 11 +#define NPCX_IRQ_12 12 +#define NPCX_IRQ_13 13 +#define NPCX_IRQ_14 14 +#define NPCX_IRQ_15 15 +#define NPCX_IRQ_16 16 +#define NPCX_IRQ_17 17 +#define NPCX_IRQ_18 18 +#define NPCX_IRQ_19 19 +#define NPCX_IRQ_20 20 +#define NPCX_IRQ_21 21 +#define NPCX_IRQ_22 22 +#define NPCX_IRQ_23 23 +#define NPCX_IRQ_24 24 +#define NPCX_IRQ_25 25 +#define NPCX_IRQ_26 26 +#define NPCX_IRQ_27 27 +#define NPCX_IRQ_28 28 +#define NPCX_IRQ_29 29 +#define NPCX_IRQ_30 30 +#define NPCX_IRQ_31 31 +#define NPCX_IRQ_32 32 +#define NPCX_IRQ_33 33 +#define NPCX_IRQ_34 34 +#define NPCX_IRQ_35 35 +#define NPCX_IRQ_36 36 +#define NPCX_IRQ_37 37 +#define NPCX_IRQ_38 38 +#define NPCX_IRQ_39 39 +#define NPCX_IRQ_40 40 +#define NPCX_IRQ_41 41 +#define NPCX_IRQ_42 42 +#define NPCX_IRQ_43 43 +#define NPCX_IRQ_44 44 +#define NPCX_IRQ_45 45 +#define NPCX_IRQ_46 46 +#define NPCX_IRQ_47 47 +#define NPCX_IRQ_48 48 +#define NPCX_IRQ_49 49 +#define NPCX_IRQ_50 50 +#define NPCX_IRQ_51 51 +#define NPCX_IRQ_52 52 +#define NPCX_IRQ_53 53 +#define NPCX_IRQ_54 54 +#define NPCX_IRQ_55 55 +#define NPCX_IRQ_56 56 +#define NPCX_IRQ_57 57 +#define NPCX_IRQ_58 58 +#define NPCX_IRQ_59 59 +#define NPCX_IRQ_60 60 +#define NPCX_IRQ_61 61 +#define NPCX_IRQ_62 62 +#define NPCX_IRQ_63 63 + +#define NPCX_IRQ0_NOUSED NPCX_IRQ_0 +#define NPCX_IRQ1_NOUSED NPCX_IRQ_1 +#define NPCX_IRQ_KBSCAN NPCX_IRQ_2 +#define NPCX_IRQ_PM_CHAN_OBF NPCX_IRQ_3 +#define NPCX_IRQ_PECI NPCX_IRQ_4 +#define NPCX_IRQ5_NOUSED NPCX_IRQ_5 +#define NPCX_IRQ_PORT80 NPCX_IRQ_6 +#define NPCX_IRQ_MTC_WKINTAD_0 NPCX_IRQ_7 +#define NPCX_IRQ8_NOUSED NPCX_IRQ_8 +#define NPCX_IRQ_MFT_1 NPCX_IRQ_9 +#define NPCX_IRQ_ADC NPCX_IRQ_10 +#define NPCX_IRQ_WKINTEFGH_0 NPCX_IRQ_11 +#define NPCX_IRQ_CDMA NPCX_IRQ_12 +#define NPCX_IRQ_SMB1 NPCX_IRQ_13 +#define NPCX_IRQ_SMB2 NPCX_IRQ_14 +#define NPCX_IRQ_WKINTC_0 NPCX_IRQ_15 +#define NPCX_IRQ16_NOUSED NPCX_IRQ_16 +#define NPCX_IRQ_ITIM16_3 NPCX_IRQ_17 +#define NPCX_IRQ_ESPI NPCX_IRQ_18 +#define NPCX_IRQ19_NOUSED NPCX_IRQ_19 +#define NPCX_IRQ20_NOUSED NPCX_IRQ_20 +#define NPCX_IRQ_PS2 NPCX_IRQ_21 +#define NPCX_IRQ22_NOUSED NPCX_IRQ_22 +#define NPCX_IRQ_MFT_2 NPCX_IRQ_23 +#define NPCX_IRQ_SHM NPCX_IRQ_24 +#define NPCX_IRQ_KBC_IBF NPCX_IRQ_25 +#define NPCX_IRQ_PM_CHAN_IBF NPCX_IRQ_26 +#define NPCX_IRQ_ITIM16_2 NPCX_IRQ_27 +#define NPCX_IRQ_ITIM16_1 NPCX_IRQ_28 +#define NPCX_IRQ29_NOUSED NPCX_IRQ_29 +#define NPCX_IRQ30_NOUSED NPCX_IRQ_30 +#define NPCX_IRQ_TWD_WKINTB_0 NPCX_IRQ_31 +#define NPCX_IRQ32_NOUSED NPCX_IRQ_32 +#define NPCX_IRQ_UART NPCX_IRQ_33 +#define NPCX_IRQ34_NOUSED NPCX_IRQ_34 +#define NPCX_IRQ35_NOUSED NPCX_IRQ_35 +#define NPCX_IRQ_SMB3 NPCX_IRQ_36 +#define NPCX_IRQ_SMB4 NPCX_IRQ_37 +#define NPCX_IRQ38_NOUSED NPCX_IRQ_38 +#define NPCX_IRQ39_NOUSED NPCX_IRQ_39 +#define NPCX_IRQ40_NOUSED NPCX_IRQ_40 +#define NPCX_IRQ_MFT_3 NPCX_IRQ_41 +#define NPCX_IRQ42_NOUSED NPCX_IRQ_42 +#define NPCX_IRQ_ITIM16_4 NPCX_IRQ_43 +#define NPCX_IRQ_ITIM16_5 NPCX_IRQ_44 +#define NPCX_IRQ_ITIM16_6 NPCX_IRQ_45 +#define NPCX_IRQ46_NOUSED NPCX_IRQ_46 +#define NPCX_IRQ_WKINTA_1 NPCX_IRQ_47 +#define NPCX_IRQ_WKINTB_1 NPCX_IRQ_48 +#define NPCX_IRQ_KSI_WKINTC_1 NPCX_IRQ_49 +#define NPCX_IRQ_WKINTD_1 NPCX_IRQ_50 +#define NPCX_IRQ_WKINTE_1 NPCX_IRQ_51 +#define NPCX_IRQ_WKINTF_1 NPCX_IRQ_52 +#define NPCX_IRQ_WKINTG_1 NPCX_IRQ_53 +#define NPCX_IRQ_WKINTH_1 NPCX_IRQ_54 +#define NPCX_IRQ55_NOUSED NPCX_IRQ_55 +#define NPCX_IRQ_KBC_OBF NPCX_IRQ_56 +#define NPCX_IRQ_SPI NPCX_IRQ_57 +#define NPCX_IRQ58_NOUSED NPCX_IRQ_58 +#define NPCX_IRQ59_NOUSED NPCX_IRQ_59 +#define NPCX_IRQ_WKINTA_2 NPCX_IRQ_60 +#define NPCX_IRQ_WKINTB_2 NPCX_IRQ_61 +#define NPCX_IRQ_WKINTC_2 NPCX_IRQ_62 +#define NPCX_IRQ_WKINTD_2 PCX_IRQ_63 + +#define NPCX_IRQ_COUNT 64 + +/******************************************************************************/ +/* Miscellaneous Device Control (MDC) registers */ +#define NPCX_FWCTRL REG8(NPCX_MDC_BASE_ADDR + 0x007) + +/* MDC register fields */ +#define NPCX_FWCTRL_RO_REGION 0 + +/******************************************************************************/ +/* High Frequency Clock Generator (HFCG) registers */ +#define NPCX_HFCGCTRL REG8(NPCX_HFCG_BASE_ADDR + 0x000) +#define NPCX_HFCGML REG8(NPCX_HFCG_BASE_ADDR + 0x002) +#define NPCX_HFCGMH REG8(NPCX_HFCG_BASE_ADDR + 0x004) +#define NPCX_HFCGN REG8(NPCX_HFCG_BASE_ADDR + 0x006) +#define NPCX_HFCGP REG8(NPCX_HFCG_BASE_ADDR + 0x008) +#define NPCX_HFCBCD REG8(NPCX_HFCG_BASE_ADDR + 0x010) + +/* HFCG register fields */ +#define NPCX_HFCGCTRL_LOAD 0 +#define NPCX_HFCGCTRL_LOCK 2 +#define NPCX_HFCGCTRL_CLK_CHNG 7 + +/******************************************************************************/ +/*CR UART Register */ +#define NPCX_UTBUF REG8(NPCX_CR_UART_BASE_ADDR + 0x000) +#define NPCX_URBUF REG8(NPCX_CR_UART_BASE_ADDR + 0x002) +#define NPCX_UICTRL REG8(NPCX_CR_UART_BASE_ADDR + 0x004) +#define NPCX_USTAT REG8(NPCX_CR_UART_BASE_ADDR + 0x006) +#define NPCX_UFRS REG8(NPCX_CR_UART_BASE_ADDR + 0x008) +#define NPCX_UMDSL REG8(NPCX_CR_UART_BASE_ADDR + 0x00A) +#define NPCX_UBAUD REG8(NPCX_CR_UART_BASE_ADDR + 0x00C) +#define NPCX_UPSR REG8(NPCX_CR_UART_BASE_ADDR + 0x00E) + +/******************************************************************************/ +/* KBSCAN registers */ +#define NPCX_KBSIN REG8(NPCX_KBSCAN_REGS_BASE + 0x04) +#define NPCX_KBSINPU REG8(NPCX_KBSCAN_REGS_BASE + 0x05) +#define NPCX_KBSOUT0 REG16(NPCX_KBSCAN_REGS_BASE + 0x06) +#define NPCX_KBSOUT1 REG16(NPCX_KBSCAN_REGS_BASE + 0x08) +#define NPCX_KBS_BUF_INDX REG8(NPCX_KBSCAN_REGS_BASE + 0x0A) +#define NPCX_KBS_BUF_DATA REG8(NPCX_KBSCAN_REGS_BASE + 0x0B) +#define NPCX_KBSEVT REG8(NPCX_KBSCAN_REGS_BASE + 0x0C) +#define NPCX_KBSCTL REG8(NPCX_KBSCAN_REGS_BASE + 0x0D) +#define NPCX_KBS_CFG_INDX REG8(NPCX_KBSCAN_REGS_BASE + 0x0E) +#define NPCX_KBS_CFG_DATA REG8(NPCX_KBSCAN_REGS_BASE + 0x0F) + +/* KBSCAN register fields */ +#define NPCX_KBSBUFINDX 0 +#define NPCX_KBSDONE 0 +#define NPCX_KBSERR 1 +#define NPCX_KBSSTART 0 +#define NPCX_KBSMODE 1 +#define NPCX_KBSIEN 2 +#define NPCX_KBSINC 3 +#define NPCX_KBSCFGINDX 0 + +/* KBSCAN definitions */ +#define KB_ROW_NUM 8 /* Rows numbers of keyboard matrix */ +#define KB_COL_NUM 18 /* Columns numbers of keyboard matrix */ +#define KB_ROW_MASK ((1<<KB_ROW_NUM) - 1) /* Mask of rows of keyboard matrix */ +#define KB_COL_MASK ((1<<KB_COL_NUM) - 1) /* Mask of cols of keyboard matrix */ + +/******************************************************************************/ +/* GLUE registers */ +#define NPCX_GLUE_SDPD0 REG8(NPCX_GLUE_REGS_BASE + 0x010) +#define NPCX_GLUE_SDPD1 REG8(NPCX_GLUE_REGS_BASE + 0x012) +#define NPCX_GLUE_SDP_CTS REG8(NPCX_GLUE_REGS_BASE + 0x014) + +/******************************************************************************/ +/* MIWU registers */ +#define NPCX_WKEDG_ADDR(port, n) (NPCX_MIWU_BASE_ADDR(port) + 0x00 + \ + ((n) * 2L) + ((n) < 5 ? 0 : 0x1E)) +#define NPCX_WKAEDG_ADDR(port, n) (NPCX_MIWU_BASE_ADDR(port) + 0x01 + \ + ((n) * 2L) + ((n) < 5 ? 0 : 0x1E)) +#define NPCX_WKPND_ADDR(port, n) (NPCX_MIWU_BASE_ADDR(port) + 0x0A + \ + ((n) * 4L) + ((n) < 5 ? 0 : 0x10)) +#define NPCX_WKPCL_ADDR(port, n) (NPCX_MIWU_BASE_ADDR(port) + 0x0C + \ + ((n) * 4L) + ((n) < 5 ? 0 : 0x10)) +#define NPCX_WKEN_ADDR(port, n) (NPCX_MIWU_BASE_ADDR(port) + 0x1E + \ + ((n) * 2L) + ((n) < 5 ? 0 : 0x12)) +#define NPCX_WKMOD_ADDR(port, n) (NPCX_MIWU_BASE_ADDR(port) + 0x70 + n) + +#define NPCX_WKEDG(port, n) REG8(NPCX_WKEDG_ADDR(port, n)) +#define NPCX_WKAEDG(port, n) REG8(NPCX_WKAEDG_ADDR(port, n)) +#define NPCX_WKPND(port, n) REG8(NPCX_WKPND_ADDR(port, n)) +#define NPCX_WKPCL(port, n) REG8(NPCX_WKPCL_ADDR(port, n)) +#define NPCX_WKEN(port, n) REG8(NPCX_WKEN_ADDR(port, n)) +#define NPCX_WKMOD(port, n) REG8(NPCX_WKMOD_ADDR(port, n)) + +/* MIWU enumeration */ +enum { + MIWU_TABLE_0, + MIWU_TABLE_1, + MIWU_TABLE_2, + MIWU_TABLE_COUNT +}; + +enum { + MIWU_GROUP_1, + MIWU_GROUP_2, + MIWU_GROUP_3, + MIWU_GROUP_4, + MIWU_GROUP_5, + MIWU_GROUP_6, + MIWU_GROUP_7, + MIWU_GROUP_8, + MIWU_GROUP_COUNT +}; + +enum { + MIWU_EDGE_RISING, + MIWU_EDGE_FALLING, +}; + +/* MIWU utilities */ +#define MIWU_TABLE_WKKEY MIWU_TABLE_1 +#define MIWU_GROUP_WKKEY MIWU_GROUP_3 + +/******************************************************************************/ +/* GPIO registers */ +#define NPCX_PDOUT(n) REG8(NPCX_GPIO_BASE_ADDR(n) + 0x000) +#define NPCX_PDIN(n) REG8(NPCX_GPIO_BASE_ADDR(n) + 0x001) +#define NPCX_PDIR(n) REG8(NPCX_GPIO_BASE_ADDR(n) + 0x002) +#define NPCX_PPULL(n) REG8(NPCX_GPIO_BASE_ADDR(n) + 0x003) +#define NPCX_PPUD(n) REG8(NPCX_GPIO_BASE_ADDR(n) + 0x004) +#define NPCX_PENVDD(n) REG8(NPCX_GPIO_BASE_ADDR(n) + 0x005) +#define NPCX_PTYPE(n) REG8(NPCX_GPIO_BASE_ADDR(n) + 0x006) + +/* GPIO enumeration */ +enum { + GPIO_PORT_0, + GPIO_PORT_1, + GPIO_PORT_2, + GPIO_PORT_3, + GPIO_PORT_4, + GPIO_PORT_5, + GPIO_PORT_6, + GPIO_PORT_7, + GPIO_PORT_8, + GPIO_PORT_9, + GPIO_PORT_A, + GPIO_PORT_B, + GPIO_PORT_C, + GPIO_PORT_D, + GPIO_PORT_E, + GPIO_PORT_F, + GPIO_PORT_COUNT +}; + +enum { + MASK_PIN0 = (1<<0), + MASK_PIN1 = (1<<1), + MASK_PIN2 = (1<<2), + MASK_PIN3 = (1<<3), + MASK_PIN4 = (1<<4), + MASK_PIN5 = (1<<5), + MASK_PIN6 = (1<<6), + MASK_PIN7 = (1<<7), +}; + +/* Chip-independent aliases for port base group */ +#define GPIO_0 GPIO_PORT_0 +#define GPIO_1 GPIO_PORT_1 +#define GPIO_2 GPIO_PORT_2 +#define GPIO_3 GPIO_PORT_3 +#define GPIO_4 GPIO_PORT_4 +#define GPIO_5 GPIO_PORT_5 +#define GPIO_6 GPIO_PORT_6 +#define GPIO_7 GPIO_PORT_7 +#define GPIO_8 GPIO_PORT_8 +#define GPIO_9 GPIO_PORT_9 +#define GPIO_A GPIO_PORT_A +#define GPIO_B GPIO_PORT_B +#define GPIO_C GPIO_PORT_C +#define GPIO_D GPIO_PORT_D +#define GPIO_E GPIO_PORT_E +#define GPIO_F GPIO_PORT_F + +/******************************************************************************/ +/* MSWC Registers */ +#define NPCX_MSWCTL1 REG8(NPCX_MSWC_BASE_ADDR + 0x000) +#define NPCX_HCBAL REG8(NPCX_MSWC_BASE_ADDR + 0x008) +#define NPCX_HCBAH REG8(NPCX_MSWC_BASE_ADDR + 0x00A) + +/******************************************************************************/ +/* System Configuration (SCFG) Registers */ +#define NPCX_DEVCNT REG8(NPCX_SCFG_BASE_ADDR + 0x000) +#define NPCX_STRPST REG8(NPCX_SCFG_BASE_ADDR + 0x001) +#define NPCX_RSTCTL REG8(NPCX_SCFG_BASE_ADDR + 0x002) +#define NPCX_DEV_CTL4 REG8(NPCX_SCFG_BASE_ADDR + 0x006) +#define NPCX_DEVALT(n) REG8(NPCX_SCFG_BASE_ADDR + 0x010 + n) +#define NPCX_DEVPU0 REG8(NPCX_SCFG_BASE_ADDR + 0x028) +#define NPCX_DEVPU1 REG8(NPCX_SCFG_BASE_ADDR + 0x029) +#define NPCX_LV_GPIO_CTL0 REG8(NPCX_SCFG_BASE_ADDR + 0x02A) +#define NPCX_LV_GPIO_CTL1 REG8(NPCX_SCFG_BASE_ADDR + 0x02B) +#define NPCX_LV_GPIO_CTL2 REG8(NPCX_SCFG_BASE_ADDR + 0x02C) +#define NPCX_LV_GPIO_CTL3 REG8(NPCX_SCFG_BASE_ADDR + 0x02D) +#define NPCX_SCFG_VER REG8(NPCX_SCFG_BASE_ADDR + 0x02F) + +#define TEST_BKSL REG8(NPCX_SCFG_BASE_ADDR + 0x037) +#define TEST0 REG8(NPCX_SCFG_BASE_ADDR + 0x038) +#define BLKSEL 0 + +/* SCFG enumeration */ +enum { + ALT_GROUP_0, + ALT_GROUP_1, + ALT_GROUP_2, + ALT_GROUP_3, + ALT_GROUP_4, + ALT_GROUP_5, + ALT_GROUP_6, + ALT_GROUP_7, + ALT_GROUP_8, + ALT_GROUP_9, + ALT_GROUP_A, + ALT_GROUP_B, + ALT_GROUP_C, + ALT_GROUP_D, + ALT_GROUP_E, + ALT_GROUP_F, + ALT_GROUP_COUNT +}; + +/* SCFG register fields */ +#define NPCX_DEVCNT_F_SPI_TRIS 6 +#define NPCX_DEVCNT_JEN1_HEN 5 +#define NPCX_DEVCNT_JEN0_HEN 4 +#define NPCX_STRPST_TRIST 1 +#define NPCX_STRPST_TEST 2 +#define NPCX_STRPST_JEN1 4 +#define NPCX_STRPST_JEN0 5 +#define NPCX_STRPST_SPI_COMP 7 +#define NPCX_RSTCTL_VCC1_RST_STS 0 +#define NPCX_RSTCTL_DBGRST_STS 1 +#define NPCX_RSTCTL_LRESET_PLTRST_MODE 5 +#define NPCX_RSTCTL_HIPRST_MODE 6 +#define NPCX_DEV_CTL4_SPI_SP_SEL 4 +#define NPCX_DEVPU0_I2C0_0_PUE 0 +#define NPCX_DEVPU0_I2C0_1_PUE 1 +#define NPCX_DEVPU0_I2C1_0_PUE 2 +#define NPCX_DEVPU0_I2C2_0_PUE 4 +#define NPCX_DEVPU0_I2C3_0_PUE 6 +#define NPCX_DEVPU1_F_SPI_PUD_EN 7 + +/* DEVALT */ +#define NPCX_DEVALT0_SPIP_SL 0 +#define NPCX_DEVALT0_GPIO_NO_SPIP 3 +#define NPCX_DEVALT0_F_SPI_CS1_2 4 +#define NPCX_DEVALT0_F_SPI_CS1_1 5 +#define NPCX_DEVALT0_F_SPI_QUAD 6 +#define NPCX_DEVALT0_NO_F_SPI 7 + +#define NPCX_DEVALT1_KBRST_SL 0 +#define NPCX_DEVALT1_A20M_SL 1 +#define NPCX_DEVALT1_SMI_SL 2 +#define NPCX_DEVALT1_EC_SCI_SL 3 +#define NPCX_DEVALT1_NO_PWRGD 4 +#define NPCX_DEVALT1_RST_OUT_SL 5 +#define NPCX_DEVALT1_CLKRN_SL 6 +#define NPCX_DEVALT1_NO_LPC_ESPI 7 + +#define NPCX_DEVALT2_I2C0_0_SL 0 +#define NPCX_DEVALT2_I2C0_1_SL 1 +#define NPCX_DEVALT2_I2C1_0_SL 2 +#define NPCX_DEVALT2_I2C2_0_SL 4 +#define NPCX_DEVALT2_I2C3_0_SL 6 + +#define NPCX_DEVALT3_PS2_0_SL 0 +#define NPCX_DEVALT3_PS2_1_SL 1 +#define NPCX_DEVALT3_PS2_2_SL 2 +#define NPCX_DEVALT3_PS2_3_SL 3 +#define NPCX_DEVALT3_TA1_TACH1_SL1 4 +#define NPCX_DEVALT3_TB1_TACH2_SL1 5 +#define NPCX_DEVALT3_TA2_SL1 6 +#define NPCX_DEVALT3_TB2_SL1 7 + +#define NPCX_DEVALT4_PWM0_SL 0 +#define NPCX_DEVALT4_PWM1_SL 1 +#define NPCX_DEVALT4_PWM2_SL 2 +#define NPCX_DEVALT4_PWM3_SL 3 +#define NPCX_DEVALT4_PWM4_SL 4 +#define NPCX_DEVALT4_PWM5_SL 5 +#define NPCX_DEVALT4_PWM6_SL 6 +#define NPCX_DEVALT4_PWM7_SL 7 + +#define NPCX_DEVALT5_TRACE_EN 0 +#define NPCX_DEVALT5_NJEN1_EN 1 +#define NPCX_DEVALT5_NJEN0_EN 2 + +#define NPCX_DEVALT6_ADC0_SL 0 +#define NPCX_DEVALT6_ADC1_SL 1 +#define NPCX_DEVALT6_ADC2_SL 2 +#define NPCX_DEVALT6_ADC3_SL 3 +#define NPCX_DEVALT6_ADC4_SL 4 + +#define NPCX_DEVALT7_NO_KSI0_SL 0 +#define NPCX_DEVALT7_NO_KSI1_SL 1 +#define NPCX_DEVALT7_NO_KSI2_SL 2 +#define NPCX_DEVALT7_NO_KSI3_SL 3 +#define NPCX_DEVALT7_NO_KSI4_SL 4 +#define NPCX_DEVALT7_NO_KSI5_SL 5 +#define NPCX_DEVALT7_NO_KSI6_SL 6 +#define NPCX_DEVALT7_NO_KSI7_SL 7 + +#define NPCX_DEVALT8_NO_KSO00_SL 0 +#define NPCX_DEVALT8_NO_KSO01_SL 1 +#define NPCX_DEVALT8_NO_KSO02_SL 2 +#define NPCX_DEVALT8_NO_KSO03_SL 3 +#define NPCX_DEVALT8_NO_KSO04_SL 4 +#define NPCX_DEVALT8_NO_KSO05_SL 5 +#define NPCX_DEVALT8_NO_KSO06_SL 6 +#define NPCX_DEVALT8_NO_KSO07_SL 7 + +#define NPCX_DEVALT9_NO_KSO08_SL 0 +#define NPCX_DEVALT9_NO_KSO09_SL 1 +#define NPCX_DEVALT9_NO_KSO10_SL 2 +#define NPCX_DEVALT9_NO_KSO11_SL 3 +#define NPCX_DEVALT9_NO_KSO12_SL 4 +#define NPCX_DEVALT9_NO_KSO13_SL 5 +#define NPCX_DEVALT9_NO_KSO14_SL 6 +#define NPCX_DEVALT9_NO_KSO15_SL 7 + +#define NPCX_DEVALTA_NO_KSO16_SL 0 +#define NPCX_DEVALTA_NO_KSO17_SL 1 +#define NPCX_DEVALTA_32K_OUT_SL 2 +#define NPCX_DEVALTA_32KCLKIN_SL 3 +#define NPCX_DEVALTA_NO_VCC1_RST 4 +#define NPCX_DEVALTA_NO_PECI_EN 6 +#define NPCX_DEVALTA_UART_SL 7 + +#define NPCX_DEVALTB_RXD_SL 0 +#define NPCX_DEVALTB_TXD_SL 1 + +#define NPCX_DEVALTC_PS2_3_SL2 3 +#define NPCX_DEVALTC_TA1_TACH1_SL2 4 +#define NPCX_DEVALTC_TB1_TACH2_SL2 5 +#define NPCX_DEVALTC_TA2_SL2 6 +#define NPCX_DEVALTC_TB2_SL2 7 + +/******************************************************************************/ +/* Development and Debug Support (DBG) Registers */ +#define NPCX_DBGCTRL REG8(NPCX_SCFG_BASE_ADDR + 0x074) +#define NPCX_DBGFRZEN1 REG8(NPCX_SCFG_BASE_ADDR + 0x076) +#define NPCX_DBGFRZEN2 REG8(NPCX_SCFG_BASE_ADDR + 0x077) +#define NPCX_DBGFRZEN3 REG8(NPCX_SCFG_BASE_ADDR + 0x078) +/* DBG register fields */ +#define NPCX_DBGFRZEN3_GLBL_FRZ_DIS 7 + + +/******************************************************************************/ +/* SMBus Registers */ +#define NPCX_SMBSDA(n) REG8(NPCX_SMB_BASE_ADDR(n) + 0x000) +#define NPCX_SMBST(n) REG8(NPCX_SMB_BASE_ADDR(n) + 0x002) +#define NPCX_SMBCST(n) REG8(NPCX_SMB_BASE_ADDR(n) + 0x004) +#define NPCX_SMBCTL1(n) REG8(NPCX_SMB_BASE_ADDR(n) + 0x006) +#define NPCX_SMBADDR1(n) REG8(NPCX_SMB_BASE_ADDR(n) + 0x008) +#define NPCX_SMBTMR_ST(n) REG8(NPCX_SMB_BASE_ADDR(n) + 0x009) +#define NPCX_SMBCTL2(n) REG8(NPCX_SMB_BASE_ADDR(n) + 0x00A) +#define NPCX_SMBTMR_EN(n) REG8(NPCX_SMB_BASE_ADDR(n) + 0x00B) +#define NPCX_SMBADDR2(n) REG8(NPCX_SMB_BASE_ADDR(n) + 0x00C) +#define NPCX_SMBCTL3(n) REG8(NPCX_SMB_BASE_ADDR(n) + 0x00E) +#define NPCX_SMBADDR3(n) REG8(NPCX_SMB_BASE_ADDR(n) + 0x010) +#define NPCX_SMBADDR7(n) REG8(NPCX_SMB_BASE_ADDR(n) + 0x011) +#define NPCX_SMBADDR4(n) REG8(NPCX_SMB_BASE_ADDR(n) + 0x012) +#define NPCX_SMBADDR8(n) REG8(NPCX_SMB_BASE_ADDR(n) + 0x013) +#define NPCX_SMBADDR5(n) REG8(NPCX_SMB_BASE_ADDR(n) + 0x014) +#define NPCX_SMBADDR6(n) REG8(NPCX_SMB_BASE_ADDR(n) + 0x016) +#define NPCX_SMBCST2(n) REG8(NPCX_SMB_BASE_ADDR(n) + 0x018) +#define NPCX_SMBCST3(n) REG8(NPCX_SMB_BASE_ADDR(n) + 0x019) +#define NPCX_SMBCTL4(n) REG8(NPCX_SMB_BASE_ADDR(n) + 0x01A) +#define NPCX_SMBSCLLT(n) REG8(NPCX_SMB_BASE_ADDR(n) + 0x01C) +#define NPCX_SMBSCLHT(n) REG8(NPCX_SMB_BASE_ADDR(n) + 0x01E) + +/* SMBus register fields */ +#define NPCX_SMBST_XMIT 0 +#define NPCX_SMBST_MASTER 1 +#define NPCX_SMBST_NMATCH 2 +#define NPCX_SMBST_STASTR 3 +#define NPCX_SMBST_NEGACK 4 +#define NPCX_SMBST_BER 5 +#define NPCX_SMBST_SDAST 6 +#define NPCX_SMBST_SLVSTP 7 +#define NPCX_SMBCST_BUSY 0 +#define NPCX_SMBCST_BB 1 +#define NPCX_SMBCST_MATCH 2 +#define NPCX_SMBCST_GCMATCH 3 +#define NPCX_SMBCST_TSDA 4 +#define NPCX_SMBCST_TGSCL 5 +#define NPCX_SMBCST_MATCHAF 6 +#define NPCX_SMBCST_ARPMATCH 7 +#define NPCX_SMBCST2_MATCHA1F 0 +#define NPCX_SMBCST2_MATCHA2F 1 +#define NPCX_SMBCST2_MATCHA3F 2 +#define NPCX_SMBCST2_MATCHA4F 3 +#define NPCX_SMBCST2_MATCHA5F 4 +#define NPCX_SMBCST2_MATCHA6F 5 +#define NPCX_SMBCST2_MATCHA7F 6 +#define NPCX_SMBCST2_INTSTS 7 +#define NPCX_SMBCST3_MATCHA8F 0 +#define NPCX_SMBCST3_MATCHA9F 1 +#define NPCX_SMBCST3_MATCHA10F 2 +#define NPCX_SMBCTL1_START 0 +#define NPCX_SMBCTL1_STOP 1 +#define NPCX_SMBCTL1_INTEN 2 +#define NPCX_SMBCTL1_ACK 4 +#define NPCX_SMBCTL1_GCMEN 5 +#define NPCX_SMBCTL1_NMINTE 6 +#define NPCX_SMBCTL1_STASTRE 7 +#define NPCX_SMBCTL2_ENABLE 0 +#define NPCX_SMBCTL3_ARPMEN 2 +#define NPCX_SMBCTL3_IDL_START 3 +#define NPCX_SMBCTL3_400K 4 +#define NPCX_SMBCTL3_SDA_LVL 6 +#define NPCX_SMBCTL3_SCL_LVL 7 +#define NPCX_SMBADDR1_SAEN 7 +#define NPCX_SMBADDR2_SAEN 7 +#define NPCX_SMBADDR3_SAEN 7 +#define NPCX_SMBADDR4_SAEN 7 +#define NPCX_SMBADDR5_SAEN 7 +#define NPCX_SMBADDR6_SAEN 7 +#define NPCX_SMBADDR7_SAEN 7 +#define NPCX_SMBADDR8_SAEN 7 + +/******************************************************************************/ +/* Power Management Controller (PMC) Registers */ +#define NPCX_PMCSR REG8(NPCX_PMC_BASE_ADDR + 0x000) +#define NPCX_ENIDL_CTL REG8(NPCX_PMC_BASE_ADDR + 0x003) +#define NPCX_DISIDL_CTL REG8(NPCX_PMC_BASE_ADDR + 0x004) +#define NPCX_DISIDL_CTL1 REG8(NPCX_PMC_BASE_ADDR + 0x005) +#define NPCX_PWDWN_CTL(offset) REG8(NPCX_PMC_BASE_ADDR + 0x008 + offset) +#define NPCX_PWDWN_CTL_COUNT 6 + +/* PMC register fields */ +#define NPCX_PMCSR_DI_INSTW 0 +#define NPCX_PMCSR_DHF 1 +#define NPCX_PMCSR_IDLE 2 +#define NPCX_PMCSR_NWBI 3 +#define NPCX_PMCSR_OHFC 6 +#define NPCX_PMCSR_OLFC 7 +#define NPCX_DISIDL_CTL_RAM_DID 5 +#define NPCX_ENIDL_CTL_ADC_LFSL 7 +#define NPCX_ENIDL_CTL_LP_WK_CTL 6 +#define NPCX_ENIDL_CTL_PECI_ENI 2 +#define NPCX_ENIDL_CTL_ADC_ACC_DIS 1 +#define NPCX_PWDWN_CTL1_KBS_PD 0 +#define NPCX_PWDWN_CTL1_SDP_PD 1 +#define NPCX_PWDWN_CTL1_FIU_PD 2 +#define NPCX_PWDWN_CTL1_PS2_PD 3 +#define NPCX_PWDWN_CTL1_UART_PD 4 +#define NPCX_PWDWN_CTL1_MFT1_PD 5 +#define NPCX_PWDWN_CTL1_MFT2_PD 6 +#define NPCX_PWDWN_CTL1_MFT3_PD 7 +#define NPCX_PWDWN_CTL2_PWM0_PD 0 +#define NPCX_PWDWN_CTL2_PWM1_PD 1 +#define NPCX_PWDWN_CTL2_PWM2_PD 2 +#define NPCX_PWDWN_CTL2_PWM3_PD 3 +#define NPCX_PWDWN_CTL2_PWM4_PD 4 +#define NPCX_PWDWN_CTL2_PWM5_PD 5 +#define NPCX_PWDWN_CTL2_PWM6_PD 6 +#define NPCX_PWDWN_CTL2_PWM7_PD 7 +#define NPCX_PWDWN_CTL3_SMB0_PD 0 +#define NPCX_PWDWN_CTL3_SMB1_PD 1 +#define NPCX_PWDWN_CTL3_SMB2_PD 2 +#define NPCX_PWDWN_CTL3_SMB3_PD 3 +#define NPCX_PWDWN_CTL3_GMDA_PD 7 +#define NPCX_PWDWN_CTL4_ITIM1_PD 0 +#define NPCX_PWDWN_CTL4_ITIM2_PD 1 +#define NPCX_PWDWN_CTL4_ITIM3_PD 2 +#define NPCX_PWDWN_CTL4_ADC_PD 4 +#define NPCX_PWDWN_CTL4_PECI_PD 5 +#define NPCX_PWDWN_CTL4_PWM6_PD 6 +#define NPCX_PWDWN_CTL4_SPIP_PD 7 +#define NPCX_PWDWN_CTL5_MRFSH_DIS 2 +#define NPCX_PWDWN_CTL5_C2HACC_PD 3 +#define NPCX_PWDWN_CTL5_SHM_REG_PD 4 +#define NPCX_PWDWN_CTL5_SHM_PD 5 +#define NPCX_PWDWN_CTL5_DP80_PD 6 +#define NPCX_PWDWN_CTL5_MSWC_PD 7 +#define NPCX_PWDWN_CTL6_ITIM4_PD 0 +#define NPCX_PWDWN_CTL6_ITIM5_PD 1 +#define NPCX_PWDWN_CTL6_ITIM6_PD 2 +#define NPCX_PWDWN_CTL6_ESPI_PD 7 + +/* + * PMC enumeration + * Offsets from CGC_BASE registers for each peripheral. + */ +enum { + CGC_OFFSET_KBS = 0, + CGC_OFFSET_UART = 0, + CGC_OFFSET_FAN = 0, + CGC_OFFSET_FIU = 0, + CGC_OFFSET_PWM = 1, + CGC_OFFSET_I2C = 2, + CGC_OFFSET_ADC = 3, + CGC_OFFSET_TIMER = 3, + CGC_OFFSET_LPC = 4, + CGC_OFFSET_ESPI = 5, +}; + +#define CGC_KBS_MASK (1 << NPCX_PWDWN_CTL1_KBS_PD) +#define CGC_UART_MASK (1 << NPCX_PWDWN_CTL1_UART_PD) +#define CGC_FAN_MASK (1 << NPCX_PWDWN_CTL1_MFT1_PD) +#define CGC_FIU_MASK (1 << NPCX_PWDWN_CTL1_FIU_PD) +#define CGC_PWM_MASK (1 << NPCX_PWDWN_CTL2_PWM2_PD) +#define CGC_I2C_MASK ((1 << NPCX_PWDWN_CTL3_SMB0_PD) | \ + (1 << NPCX_PWDWN_CTL3_SMB1_PD) | \ + (1 << NPCX_PWDWN_CTL3_SMB2_PD)) +#define CGC_ADC_MASK (1 << NPCX_PWDWN_CTL4_ADC_PD) +#define CGC_TIMER_MASK ((1 << NPCX_PWDWN_CTL4_ITIM1_PD) | \ + (1 << NPCX_PWDWN_CTL4_ITIM2_PD) | \ + (1 << NPCX_PWDWN_CTL4_ITIM3_PD)) +#define CGC_LPC_MASK ((1 << NPCX_PWDWN_CTL5_C2HACC_PD) | \ + (1 << NPCX_PWDWN_CTL5_SHM_REG_PD) | \ + (1 << NPCX_PWDWN_CTL5_SHM_PD) | \ + (1 << NPCX_PWDWN_CTL5_DP80_PD) | \ + (1 << NPCX_PWDWN_CTL5_MSWC_PD)) +#define CGC_ESPI_MASK (1 << NPCX_PWDWN_CTL6_ESPI_PD) + +/******************************************************************************/ +/* Flash Interface Unit (FIU) Registers */ +#define NPCX_FIU_CFG REG8(NPCX_FIU_BASE_ADDR + 0x000) +#define NPCX_BURST_CFG REG8(NPCX_FIU_BASE_ADDR + 0x001) +#define NPCX_RESP_CFG REG8(NPCX_FIU_BASE_ADDR + 0x002) +#define NPCX_SPI_FL_CFG REG8(NPCX_FIU_BASE_ADDR + 0x014) +#define NPCX_UMA_CODE REG8(NPCX_FIU_BASE_ADDR + 0x016) +#define NPCX_UMA_AB0 REG8(NPCX_FIU_BASE_ADDR + 0x017) +#define NPCX_UMA_AB1 REG8(NPCX_FIU_BASE_ADDR + 0x018) +#define NPCX_UMA_AB2 REG8(NPCX_FIU_BASE_ADDR + 0x019) +#define NPCX_UMA_DB0 REG8(NPCX_FIU_BASE_ADDR + 0x01A) +#define NPCX_UMA_DB1 REG8(NPCX_FIU_BASE_ADDR + 0x01B) +#define NPCX_UMA_DB2 REG8(NPCX_FIU_BASE_ADDR + 0x01C) +#define NPCX_UMA_DB3 REG8(NPCX_FIU_BASE_ADDR + 0x01D) +#define NPCX_UMA_CTS REG8(NPCX_FIU_BASE_ADDR + 0x01E) +#define NPCX_UMA_ECTS REG8(NPCX_FIU_BASE_ADDR + 0x01F) +#define NPCX_UMA_DB0_3 REG32(NPCX_FIU_BASE_ADDR + 0x020) +#define NPCX_FIU_RD_CMD REG8(NPCX_FIU_BASE_ADDR + 0x030) +#define NPCX_FIU_DMM_CYC REG8(NPCX_FIU_BASE_ADDR + 0x032) +#define NPCX_FIU_EXT_CFG REG8(NPCX_FIU_BASE_ADDR + 0x033) +#define NPCX_FIU_UMA_AB0_3 REG32(NPCX_FIU_BASE_ADDR + 0x034) + +/* FIU register fields */ +#define NPCX_RESP_CFG_IAD_EN 0 +#define NPCX_RESP_CFG_DEV_SIZE_EX 2 +#define NPCX_UMA_CTS_A_SIZE 3 +#define NPCX_UMA_CTS_C_SIZE 4 +#define NPCX_UMA_CTS_RD_WR 5 +#define NPCX_UMA_CTS_DEV_NUM 6 +#define NPCX_UMA_CTS_EXEC_DONE 7 +#define NPCX_UMA_ECTS_SW_CS0 0 +#define NPCX_UMA_ECTS_SW_CS1 1 +#define NPCX_UMA_ECTS_SEC_CS 2 + +/******************************************************************************/ +/* Shared Memory (SHM) Registers */ +#define NPCX_SMC_STS REG8(NPCX_SHM_BASE_ADDR + 0x000) +#define NPCX_SMC_CTL REG8(NPCX_SHM_BASE_ADDR + 0x001) +#define NPCX_SHM_CTL REG8(NPCX_SHM_BASE_ADDR + 0x002) +#define NPCX_IMA_WIN_SIZE REG8(NPCX_SHM_BASE_ADDR + 0x005) +#define NPCX_WIN_SIZE REG8(NPCX_SHM_BASE_ADDR + 0x007) +#define NPCX_SHAW_SEM(win) REG8(NPCX_SHM_BASE_ADDR + 0x008 + (win)) +#define NPCX_IMA_SEM REG8(NPCX_SHM_BASE_ADDR + 0x00B) +#define NPCX_SHCFG REG8(NPCX_SHM_BASE_ADDR + 0x00E) +#define NPCX_WIN_WR_PROT(win) REG8(NPCX_SHM_BASE_ADDR + 0x010 + (win*2L)) +#define NPCX_WIN_RD_PROT(win) REG8(NPCX_SHM_BASE_ADDR + 0x011 + (win*2L)) +#define NPCX_IMA_WR_PROT REG8(NPCX_SHM_BASE_ADDR + 0x016) +#define NPCX_IMA_RD_PROT REG8(NPCX_SHM_BASE_ADDR + 0x017) +#define NPCX_WIN_BASE(win) REG32(NPCX_SHM_BASE_ADDR + 0x020 + (win*4L)) + +#define NPCX_PWIN_BASEI(win) REG16(NPCX_SHM_BASE_ADDR + 0x020 + (win*4L)) +#define NPCX_PWIN_SIZEI(win) REG16(NPCX_SHM_BASE_ADDR + 0x022 + (win*4L)) + +#define NPCX_IMA_BASE REG32(NPCX_SHM_BASE_ADDR + 0x02C) +#define NPCX_RST_CFG REG8(NPCX_SHM_BASE_ADDR + 0x03A) +#define NPCX_DP80BUF REG16(NPCX_SHM_BASE_ADDR + 0x040) +#define NPCX_DP80STS REG8(NPCX_SHM_BASE_ADDR + 0x042) +#define NPCX_DP80CTL REG8(NPCX_SHM_BASE_ADDR + 0x044) +#define NPCX_HOFS_STS REG8(NPCX_SHM_BASE_ADDR + 0x048) +#define NPCX_HOFS_CTL REG8(NPCX_SHM_BASE_ADDR + 0x049) +#define NPCX_COFS2 REG16(NPCX_SHM_BASE_ADDR + 0x04A) +#define NPCX_COFS1 REG16(NPCX_SHM_BASE_ADDR + 0x04C) +#define NPCX_IHOFS2 REG16(NPCX_SHM_BASE_ADDR + 0x050) +#define NPCX_IHOFS1 REG16(NPCX_SHM_BASE_ADDR + 0x052) +#define NPCX_SHM_VER REG8(NPCX_SHM_BASE_ADDR + 0x07F) + + +/* SHM register fields */ +#define NPCX_SMC_STS_HRERR 0 +#define NPCX_SMC_STS_HWERR 1 +#define NPCX_SMC_STS_HSEM1W 4 +#define NPCX_SMC_STS_HSEM2W 5 +#define NPCX_SMC_STS_SHM_ACC 6 +#define NPCX_SMC_CTL_HERR_IE 2 +#define NPCX_SMC_CTL_HSEM1_IE 3 +#define NPCX_SMC_CTL_HSEM2_IE 4 +#define NPCX_SMC_CTL_ACC_IE 5 +#define NPCX_SMC_CTL_PREF_EN 6 +#define NPCX_SMC_CTL_HOSTWAIT 7 +#define NPCX_FLASH_SIZE_STALL_HOST 6 +#define NPCX_FLASH_SIZE_RD_BURST 7 +#define NPCX_WIN_PROT_RW1L_RP 0 +#define NPCX_WIN_PROT_RW1L_WP 1 +#define NPCX_WIN_PROT_RW1H_RP 2 +#define NPCX_WIN_PROT_RW1H_WP 3 +#define NPCX_WIN_PROT_RW2L_RP 4 +#define NPCX_WIN_PROT_RW2L_WP 5 +#define NPCX_WIN_PROT_RW2H_RP 6 +#define NPCX_WIN_PROT_RW2H_WP 7 +#define NPCX_PWIN_SIZEI_RPROT 13 +#define NPCX_PWIN_SIZEI_WPROT 14 +#define NPCX_CSEM2 6 +#define NPCX_CSEM3 7 +#define NPCX_DP80STS_FWR 5 +#define NPCX_DP80STS_FNE 6 +#define NPCX_DP80STS_FOR 7 +#define NPCX_DP80CTL_DP80EN 0 +#define NPCX_DP80CTL_SYNCEN 1 +#define NPCX_DP80CTL_RFIFO 4 +#define NPCX_DP80CTL_CIEN 5 + +/******************************************************************************/ +/* KBC Registers */ +#define NPCX_HICTRL REG8(NPCX_KBC_BASE_ADDR + 0x000) +#define NPCX_HIIRQC REG8(NPCX_KBC_BASE_ADDR + 0x002) +#define NPCX_HIKMST REG8(NPCX_KBC_BASE_ADDR + 0x004) +#define NPCX_HIKDO REG8(NPCX_KBC_BASE_ADDR + 0x006) +#define NPCX_HIMDO REG8(NPCX_KBC_BASE_ADDR + 0x008) +#define NPCX_KBCVER REG8(NPCX_KBC_BASE_ADDR + 0x009) +#define NPCX_HIKMDI REG8(NPCX_KBC_BASE_ADDR + 0x00A) +#define NPCX_SHIKMDI REG8(NPCX_KBC_BASE_ADDR + 0x00B) + +/* KBC register field */ +#define NPCX_HICTRL_OBFKIE 0 +#define NPCX_HICTRL_OBFMIE 1 + +/******************************************************************************/ +/* PM Channel Registers */ +#define NPCX_HIPMST(n) REG8(NPCX_PM_CH_BASE_ADDR(n) + 0x000) +#define NPCX_HIPMDO(n) REG8(NPCX_PM_CH_BASE_ADDR(n) + 0x002) +#define NPCX_HIPMDI(n) REG8(NPCX_PM_CH_BASE_ADDR(n) + 0x004) +#define NPCX_SHIPMDI(n) REG8(NPCX_PM_CH_BASE_ADDR(n) + 0x005) +#define NPCX_HIPMDOC(n) REG8(NPCX_PM_CH_BASE_ADDR(n) + 0x006) +#define NPCX_HIPMDOM(n) REG8(NPCX_PM_CH_BASE_ADDR(n) + 0x008) +#define NPCX_HIPMDIC(n) REG8(NPCX_PM_CH_BASE_ADDR(n) + 0x00A) +#define NPCX_HIPMCTL(n) REG8(NPCX_PM_CH_BASE_ADDR(n) + 0x00C) +#define NPCX_HIPMCTL2(n) REG8(NPCX_PM_CH_BASE_ADDR(n) + 0x00D) +#define NPCX_HIPMIC(n) REG8(NPCX_PM_CH_BASE_ADDR(n) + 0x00E) +#define NPCX_HIPMIE(n) REG8(NPCX_PM_CH_BASE_ADDR(n) + 0x010) + +/* PM Channel register field */ +#define NPCX_HIPMIE_SCIE 1 +#define NPCX_HIPMIE_SMIE 2 + +/* + * PM Channel enumeration + */ +enum PM_CHANNEL_T { + PM_CHAN_1, + PM_CHAN_2, + PM_CHAN_3, + PM_CHAN_4 +}; + +/******************************************************************************/ +/* SuperI/O Internal Bus (SIB) Registers */ +#define NPCX_IHIOA REG16(NPCX_SIB_BASE_ADDR + 0x000) +#define NPCX_IHD REG8(NPCX_SIB_BASE_ADDR + 0x002) +#define NPCX_LKSIOHA REG16(NPCX_SIB_BASE_ADDR + 0x004) +#define NPCX_SIOLV REG16(NPCX_SIB_BASE_ADDR + 0x006) +#define NPCX_CRSMAE REG16(NPCX_SIB_BASE_ADDR + 0x008) +#define NPCX_SIBCTRL REG8(NPCX_SIB_BASE_ADDR + 0x00A) +#define NPCX_C2H_VER REG8(NPCX_SIB_BASE_ADDR + 0x00E) +/* SIB register fields */ +#define NPCX_SIBCTRL_CSAE 0 +#define NPCX_SIBCTRL_CSRD 1 +#define NPCX_SIBCTRL_CSWR 2 +#define NPCX_LKSIOHA_LKCFG 0 +#define NPCX_CRSMAE_CFGAE 0 + +/******************************************************************************/ +/* Battery-Backed RAM (BBRAM) Registers */ +#define NPCX_BKUP_STS REG8(NPCX_BBRAM_BASE_ADDR + 0x000) +#define NPCX_BBRAM(offset) REG8(NPCX_BBRAM_BASE_ADDR + 0x001 + offset) + +/* BBRAM register fields */ +#define NPCX_BKUP_STS_IBBR 7 +#define NPCX_BBRAM_SIZE 63 /* Size of BBRAM */ + +/******************************************************************************/ +/* Timer Watch Dog (TWD) Registers */ +#define NPCX_TWCFG REG8(NPCX_TWD_BASE_ADDR + 0x000) +#define NPCX_TWCP REG8(NPCX_TWD_BASE_ADDR + 0x002) +#define NPCX_TWDT0 REG16(NPCX_TWD_BASE_ADDR + 0x004) +#define NPCX_T0CSR REG8(NPCX_TWD_BASE_ADDR + 0x006) +#define NPCX_WDCNT REG8(NPCX_TWD_BASE_ADDR + 0x008) +#define NPCX_WDSDM REG8(NPCX_TWD_BASE_ADDR + 0x00A) +#define NPCX_TWMT0 REG16(NPCX_TWD_BASE_ADDR + 0x00C) +#define NPCX_TWMWD REG8(NPCX_TWD_BASE_ADDR + 0x00E) +#define NPCX_WDCP REG8(NPCX_TWD_BASE_ADDR + 0x010) + +/* TWD register fields */ +#define NPCX_TWCFG_LTWCFG 0 +#define NPCX_TWCFG_LTWCP 1 +#define NPCX_TWCFG_LTWDT0 2 +#define NPCX_TWCFG_LWDCNT 3 +#define NPCX_TWCFG_WDCT0I 4 +#define NPCX_TWCFG_WDSDME 5 +#define NPCX_TWCFG_WDRST_MODE 6 +#define NPCX_TWCFG_WDC2POR 7 +#define NPCX_T0CSR_RST 0 +#define NPCX_T0CSR_TC 1 +#define NPCX_T0CSR_WDLTD 3 +#define NPCX_T0CSR_WDRST_STS 4 +#define NPCX_T0CSR_WD_RUN 5 +#define NPCX_T0CSR_TESDIS 7 + +/******************************************************************************/ +/* ADC Registers */ +#define NPCX_ADCSTS REG16(NPCX_ADC_BASE_ADDR + 0x000) +#define NPCX_ADCCNF REG16(NPCX_ADC_BASE_ADDR + 0x002) +#define NPCX_ATCTL REG16(NPCX_ADC_BASE_ADDR + 0x004) +#define NPCX_ASCADD REG16(NPCX_ADC_BASE_ADDR + 0x006) +#define NPCX_ADCCS REG16(NPCX_ADC_BASE_ADDR + 0x008) +#define NPCX_CHNDAT(n) REG16(NPCX_ADC_BASE_ADDR + 0x040 + (2L*(n))) +#define NPCX_ADCCNF2 REG16(NPCX_ADC_BASE_ADDR + 0x020) +#define NPCX_GENDLY REG16(NPCX_ADC_BASE_ADDR + 0x022) +#define NPCX_MEAST REG16(NPCX_ADC_BASE_ADDR + 0x026) + +/* ADC register fields */ +#define NPCX_ATCTL_SCLKDIV 0 +#define NPCX_ATCTL_DLY 8 +#define NPCX_ASCADD_SADDR 0 +#define NPCX_ADCSTS_EOCEV 0 +#define NPCX_ADCCNF_ADCMD 1 +#define NPCX_ADCCNF_ADCRPTC 3 +#define NPCX_ADCCNF_INTECEN 6 +#define NPCX_ADCCNF_START 4 +#define NPCX_ADCCNF_ADCEN 0 +#define NPCX_ADCCNF_STOP 11 +#define NPCX_CHNDAT_CHDAT 0 +#define NPCX_CHNDAT_NEW 15 +/******************************************************************************/ +/* SPI Register */ +#define NPCX_SPI_DATA REG16(NPCX_SPI_BASE_ADDR + 0x00) +#define NPCX_SPI_CTL1 REG16(NPCX_SPI_BASE_ADDR + 0x02) +#define NPCX_SPI_STAT REG8(NPCX_SPI_BASE_ADDR + 0x04) + +/* SPI register fields */ +#define NPCX_SPI_CTL1_SPIEN 0 +#define NPCX_SPI_CTL1_SNM 1 +#define NPCX_SPI_CTL1_MOD 2 +#define NPCX_SPI_CTL1_EIR 5 +#define NPCX_SPI_CTL1_EIW 6 +#define NPCX_SPI_CTL1_SCM 7 +#define NPCX_SPI_CTL1_SCIDL 8 +#define NPCX_SPI_CTL1_SCDV 9 +#define NPCX_SPI_STAT_BSY 0 +#define NPCX_SPI_STAT_RBF 1 + +/******************************************************************************/ +/* PECI Registers */ + +#define NPCX_PECI_CTL_STS REG8(NPCX_PECI_BASE_ADDR + 0x000) +#define NPCX_PECI_RD_LENGTH REG8(NPCX_PECI_BASE_ADDR + 0x001) +#define NPCX_PECI_ADDR REG8(NPCX_PECI_BASE_ADDR + 0x002) +#define NPCX_PECI_CMD REG8(NPCX_PECI_BASE_ADDR + 0x003) +#define NPCX_PECI_CTL2 REG8(NPCX_PECI_BASE_ADDR + 0x004) +#define NPCX_PECI_INDEX REG8(NPCX_PECI_BASE_ADDR + 0x005) +#define NPCX_PECI_IDATA REG8(NPCX_PECI_BASE_ADDR + 0x006) +#define NPCX_PECI_WR_LENGTH REG8(NPCX_PECI_BASE_ADDR + 0x007) +#define NPCX_PECI_CFG REG8(NPCX_PECI_BASE_ADDR + 0x009) +#define NPCX_PECI_RATE REG8(NPCX_PECI_BASE_ADDR + 0x00F) +#define NPCX_PECI_DATA_IN(i) REG8(NPCX_PECI_BASE_ADDR + 0x010 + (i)) +#define NPCX_PECI_DATA_OUT(i) REG8(NPCX_PECI_BASE_ADDR + 0x010 + (i)) + +/* PECI register fields */ +#define NPCX_PECI_CTL_STS_START_BUSY 0 +#define NPCX_PECI_CTL_STS_DONE 1 +#define NPCX_PECI_CTL_STS_AVL_ERR 2 +#define NPCX_PECI_CTL_STS_CRC_ERR 3 +#define NPCX_PECI_CTL_STS_ABRT_ERR 4 +#define NPCX_PECI_CTL_STS_AWFCS_EN 5 +#define NPCX_PECI_CTL_STS_DONE_EN 6 +#define NPCX_ESTRPST_PECIST 0 +#define SFT_STRP_CFG_CK50 5 + +/******************************************************************************/ +/* PWM Registers */ +#define NPCX_PRSC(n) REG16(NPCX_PWM_BASE_ADDR(n) + 0x000) +#define NPCX_CTR(n) REG16(NPCX_PWM_BASE_ADDR(n) + 0x002) +#define NPCX_PWMCTL(n) REG8(NPCX_PWM_BASE_ADDR(n) + 0x004) +#define NPCX_DCR(n) REG16(NPCX_PWM_BASE_ADDR(n) + 0x006) +#define NPCX_PWMCTLEX(n) REG8(NPCX_PWM_BASE_ADDR(n) + 0x00C) + +/* PWM register fields */ +#define NPCX_PWMCTL_INVP 0 +#define NPCX_PWMCTL_CKSEL 1 +#define NPCX_PWMCTL_HB_DC_CTL 2 +#define NPCX_PWMCTL_PWR 7 +#define NPCX_PWMCTLEX_FCK_SEL 4 +#define NPCX_PWMCTLEX_OD_OUT 7 +/******************************************************************************/ +/* MFT Registers */ +#define NPCX_TCNT1(n) REG16(NPCX_MFT_BASE_ADDR(n) + 0x000) +#define NPCX_TCRA(n) REG16(NPCX_MFT_BASE_ADDR(n) + 0x002) +#define NPCX_TCRB(n) REG16(NPCX_MFT_BASE_ADDR(n) + 0x004) +#define NPCX_TCNT2(n) REG16(NPCX_MFT_BASE_ADDR(n) + 0x006) +#define NPCX_TPRSC(n) REG8(NPCX_MFT_BASE_ADDR(n) + 0x008) +#define NPCX_TCKC(n) REG8(NPCX_MFT_BASE_ADDR(n) + 0x00A) +#define NPCX_TMCTRL(n) REG8(NPCX_MFT_BASE_ADDR(n) + 0x00C) +#define NPCX_TECTRL(n) REG8(NPCX_MFT_BASE_ADDR(n) + 0x00E) +#define NPCX_TECLR(n) REG8(NPCX_MFT_BASE_ADDR(n) + 0x010) +#define NPCX_TIEN(n) REG8(NPCX_MFT_BASE_ADDR(n) + 0x012) +#define NPCX_TWUEN(n) REG8(NPCX_MFT_BASE_ADDR(n) + 0x01A) +#define NPCX_TCFG(n) REG8(NPCX_MFT_BASE_ADDR(n) + 0x01C) + +/* MFT register fields */ +#define NPCX_TMCTRL_MDSEL 0 +#define NPCX_TCKC_LOW_PWR 7 +#define NPCX_TCKC_PLS_ACC_CLK 6 +#define NPCX_TCKC_C1CSEL 0 +#define NPCX_TCKC_C2CSEL 3 +#define NPCX_TMCTRL_TAEN 5 +#define NPCX_TMCTRL_TBEN 6 +#define NPCX_TMCTRL_TAEDG 3 +#define NPCX_TMCTRL_TBEDG 4 +#define NPCX_TCFG_TADBEN 6 +#define NPCX_TCFG_TBDBEN 7 +#define NPCX_TECTRL_TAPND 0 +#define NPCX_TECTRL_TBPND 1 +#define NPCX_TECTRL_TCPND 2 +#define NPCX_TECTRL_TDPND 3 +#define NPCX_TECLR_TACLR 0 +#define NPCX_TECLR_TBCLR 1 +#define NPCX_TECLR_TCCLR 2 +#define NPCX_TECLR_TDCLR 3 +#define NPCX_TIEN_TAIEN 0 +#define NPCX_TIEN_TBIEN 1 +#define NPCX_TIEN_TCIEN 2 +#define NPCX_TIEN_TDIEN 3 +#define NPCX_TWUEN_TAWEN 0 +#define NPCX_TWUEN_TBWEN 1 +#define NPCX_TWUEN_TCWEN 2 +#define NPCX_TWUEN_TDWEN 3 +/******************************************************************************/ +/*ITIM16 Define*/ +#define ITIM16_INT(module) CONCAT2(NPCX_IRQ_, module) + +/* ITIM16 registers */ +#define NPCX_ITCNT(n) REG8(NPCX_ITIM16_BASE_ADDR(n) + 0x000) +#define NPCX_ITPRE(n) REG8(NPCX_ITIM16_BASE_ADDR(n) + 0x001) +#define NPCX_ITCNT16(n) REG16(NPCX_ITIM16_BASE_ADDR(n) + 0x002) +#define NPCX_ITCTS(n) REG8(NPCX_ITIM16_BASE_ADDR(n) + 0x004) + +/* ITIM16 register fields */ +#define NPCX_ITIM16_TO_STS 0 +#define NPCX_ITIM16_TO_IE 2 +#define NPCX_ITIM16_TO_WUE 3 +#define NPCX_ITIM16_CKSEL 4 +#define NPCX_ITIM16_ITEN 7 + +/* ITIM16 enumeration*/ +enum ITIM16_MODULE_T { + ITIM16_1, + ITIM16_2, + ITIM16_3, + ITIM16_4, + ITIM16_5, + ITIM16_6, + ITIM16_MODULE_COUNT, +}; + +/******************************************************************************/ +/* Monotonic Counter (MTC) Registers */ +#define NPCX_TTC REG32(NPCX_MTC_BASE_ADDR + 0x000) +#define NPCX_WTC REG32(NPCX_MTC_BASE_ADDR + 0x004) +#define NPCX_MTCTST REG8(NPCX_MTC_BASE_ADDR + 0x008) +#define NPCX_MTCVER REG8(NPCX_MTC_BASE_ADDR + 0x00C) + +/* MTC register fields */ +#define NPCX_WTC_PTO 30 +#define NPCX_WTC_WIE 31 + +/******************************************************************************/ +/* Low Power RAM definitions */ +#define NPCX_LPRAM_CTRL REG32(0x40001044) + +/******************************************************************************/ +/* Optional M4 Registers */ +#define CPU_DHCSR REG32(0xE000EDF0) +#define CPU_MPU_CTRL REG32(0xE000ED94) +#define CPU_MPU_RNR REG32(0xE000ED98) +#define CPU_MPU_RBAR REG32(0xE000ED9C) +#define CPU_MPU_RASR REG32(0xE000EDA0) + + +/******************************************************************************/ +/* Flash Utiltiy definition */ +/* + * Flash commands for the W25Q16CV SPI flash + */ +#define CMD_READ_ID 0x9F +#define CMD_WRITE_EN 0x06 +#define CMD_WRITE_STATUS 0x50 +#define CMD_READ_STATUS_REG 0x05 +#define CMD_READ_STATUS_REG2 0x35 +#define CMD_WRITE_STATUS_REG 0x01 +#define CMD_FLASH_PROGRAM 0x02 +#define CMD_SECTOR_ERASE 0x20 +#define CMD_PROGRAM_UINT_SIZE 0x08 +#define CMD_PAGE_SIZE 0x00 +#define CMD_READ_ID_TYPE 0x47 +#define CMD_FAST_READ 0x0B + +/* + * Status registers for the W25Q16CV SPI flash + */ +#define SPI_FLASH_SR2_SUS (1 << 7) +#define SPI_FLASH_SR2_CMP (1 << 6) +#define SPI_FLASH_SR2_LB3 (1 << 5) +#define SPI_FLASH_SR2_LB2 (1 << 4) +#define SPI_FLASH_SR2_LB1 (1 << 3) +#define SPI_FLASH_SR2_QE (1 << 1) +#define SPI_FLASH_SR2_SRP1 (1 << 0) +#define SPI_FLASH_SR1_SRP0 (1 << 7) +#define SPI_FLASH_SR1_SEC (1 << 6) +#define SPI_FLASH_SR1_TB (1 << 5) +#define SPI_FLASH_SR1_BP2 (1 << 4) +#define SPI_FLASH_SR1_BP1 (1 << 3) +#define SPI_FLASH_SR1_BP0 (1 << 2) +#define SPI_FLASH_SR1_WEL (1 << 1) +#define SPI_FLASH_SR1_BUSY (1 << 0) + + +/* 0: F_CS0 1: F_CS1_1(GPIO86) 2:F_CS1_2(GPIOA6) */ +#define FIU_CHIP_SELECT 0 +/* Create UMA control mask */ +#define MASK(bit) (0x1 << (bit)) +#define A_SIZE 0x03 /* 0: No ADR field 1: 3-bytes ADR field */ +#define C_SIZE 0x04 /* 0: 1-Byte CMD field 1:No CMD field */ +#define RD_WR 0x05 /* 0: Read 1: Write */ +#define DEV_NUM 0x06 /* 0: PVT is used 1: SHD is used */ +#define EXEC_DONE 0x07 +#define D_SIZE_1 0x01 +#define D_SIZE_2 0x02 +#define D_SIZE_3 0x03 +#define D_SIZE_4 0x04 +#define FLASH_SEL MASK(DEV_NUM) + +#define MASK_CMD_ONLY (MASK(EXEC_DONE) | FLASH_SEL) +#define MASK_CMD_ADR (MASK(EXEC_DONE) | FLASH_SEL | MASK(A_SIZE)) +#define MASK_CMD_ADR_WR (MASK(EXEC_DONE) | FLASH_SEL | MASK(RD_WR) \ + |MASK(A_SIZE) | D_SIZE_1) +#define MASK_RD_1BYTE (MASK(EXEC_DONE) | FLASH_SEL | MASK(C_SIZE) | D_SIZE_1) +#define MASK_RD_2BYTE (MASK(EXEC_DONE) | FLASH_SEL | MASK(C_SIZE) | D_SIZE_2) +#define MASK_RD_3BYTE (MASK(EXEC_DONE) | FLASH_SEL | MASK(C_SIZE) | D_SIZE_3) +#define MASK_RD_4BYTE (MASK(EXEC_DONE) | FLASH_SEL | MASK(C_SIZE) | D_SIZE_4) +#define MASK_CMD_RD_1BYTE (MASK(EXEC_DONE) | FLASH_SEL | D_SIZE_1) +#define MASK_CMD_RD_2BYTE (MASK(EXEC_DONE) | FLASH_SEL | D_SIZE_2) +#define MASK_CMD_RD_3BYTE (MASK(EXEC_DONE) | FLASH_SEL | D_SIZE_3) +#define MASK_CMD_RD_4BYTE (MASK(EXEC_DONE) | FLASH_SEL | D_SIZE_4) +#define MASK_CMD_WR_ONLY (MASK(EXEC_DONE) | FLASH_SEL | MASK(RD_WR)) +#define MASK_CMD_WR_1BYTE (MASK(EXEC_DONE) | FLASH_SEL | MASK(RD_WR) \ + | MASK(C_SIZE) | D_SIZE_1) +#define MASK_CMD_WR_2BYTE (MASK(EXEC_DONE) | FLASH_SEL | MASK(RD_WR) \ + | MASK(C_SIZE) | D_SIZE_2) +#define MASK_CMD_WR_ADR (MASK(EXEC_DONE) | FLASH_SEL | MASK(RD_WR) \ + | MASK(A_SIZE)) + +#endif /* __CROS_EC_REGISTERS_H */ diff --git a/chip/npcx/spi.c b/chip/npcx/spi.c new file mode 100644 index 0000000000..c2802fea4f --- /dev/null +++ b/chip/npcx/spi.c @@ -0,0 +1,242 @@ +/* Copyright (c) 2014 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. + */ + +/* SPI module for Chrome EC */ + +#include "console.h" +#include "gpio.h" +#include "hooks.h" +#include "clock.h" +#include "clock_chip.h" +#include "registers.h" +#include "spi.h" +#include "task.h" +#include "timer.h" +#include "util.h" + +/* Console output macros */ +#if !DEBUG_SPI +#define CPUTS(...) +#define CPRINTS(...) +#else +#define CPUTS(outstr) cputs(CC_SPI, outstr) +#define CPRINTS(format, args...) cprints(CC_SPI, format, ## args) +#endif + +/* SPI IP as SPI master */ +#define SPI_CLK 8000000 +/** + * Clear SPI data buffer. + * + * @param none + * @return none. + */ +static void clear_databuf(void) +{ + volatile uint8_t dummy __attribute__((unused)); + while (IS_BIT_SET(NPCX_SPI_STAT, NPCX_SPI_STAT_RBF)) + dummy = NPCX_SPI_DATA; +} + +/** + * Preset SPI operation clock. + * + * @param none + * @return none + * @notes changed when initial or HOOK_FREQ_CHANGE command + */ +void spi_freq_changed(void) +{ + uint8_t prescaler_divider = 0; + + /* Set clock prescaler divider to SPI module*/ + prescaler_divider = (uint8_t)((uint32_t)clock_get_apb2_freq() + / 2 / SPI_CLK); + if (prescaler_divider >= 1) + prescaler_divider = prescaler_divider - 1; + if (prescaler_divider > 0x7F) + prescaler_divider = 0x7F; + + /* Set core clock division factor in order to obtain the SPI clock */ + NPCX_SPI_CTL1 = (NPCX_SPI_CTL1&(~(((1<<7)-1)<<NPCX_SPI_CTL1_SCDV))) + |(prescaler_divider<<NPCX_SPI_CTL1_SCDV); +} +DECLARE_HOOK(HOOK_FREQ_CHANGE, spi_freq_changed, HOOK_PRIO_DEFAULT); + +/** + * Set SPI enabled. + * + * @param enable enabled flag + * @return success + */ +int spi_enable(int enable) +{ + if (enable) { + /* Enabling spi module for gpio configuration */ + gpio_config_module(MODULE_SPI, 1); + /* GPIO No SPI Select */ + CLEAR_BIT(NPCX_DEVALT(0), NPCX_DEVALT0_GPIO_NO_SPIP); + + /* Make sure CS# is a GPIO output mode. */ + gpio_set_flags(GPIO_SPI_CS_L, GPIO_OUTPUT); + /* Make sure CS# is deselected */ + gpio_set_level(GPIO_SPI_CS_L, 1); + /* Enabling spi module */ + SET_BIT(NPCX_SPI_CTL1, NPCX_SPI_CTL1_SPIEN); + } else { + /* Disabling spi module */ + CLEAR_BIT(NPCX_SPI_CTL1, NPCX_SPI_CTL1_SPIEN); + /* Make sure CS# is deselected */ + gpio_set_level(GPIO_SPI_CS_L, 1); + gpio_set_flags(GPIO_SPI_CS_L, GPIO_ODR_HIGH); + /* Disabling spi module for gpio configuration */ + gpio_config_module(MODULE_SPI, 0); + /* GPIO No SPI Select */ + SET_BIT(NPCX_DEVALT(0), NPCX_DEVALT0_GPIO_NO_SPIP); + } + return EC_SUCCESS; +} + + +/** + * Flush an SPI transaction and receive data from slave. + * + * @param txdata transfer data + * @param txlen transfer length + * @param rxdata receive data + * @param rxlen receive length + * @return success + * @notes set master transaction mode in npcx chip + */ +int spi_transaction(const uint8_t *txdata, int txlen, + uint8_t *rxdata, int rxlen) +{ + int i = 0; + + /* Make sure CS# is a GPIO output mode. */ + gpio_set_flags(GPIO_SPI_CS_L, GPIO_OUTPUT); + /* Make sure CS# is deselected */ + gpio_set_level(GPIO_SPI_CS_L, 1); + /* Cleaning junk data in the buffer */ + clear_databuf(); + /* Assert CS# to start transaction */ + gpio_set_level(GPIO_SPI_CS_L, 0); + CPRINTS("NPCX_SPI_DATA=%x", NPCX_SPI_DATA); + CPRINTS("NPCX_SPI_CTL1=%x", NPCX_SPI_CTL1); + CPRINTS("NPCX_SPI_STAT=%x", NPCX_SPI_STAT); + /* Writing the data */ + for (i = 0; i < txlen; ++i) { + /* Making sure we can write */ + while (IS_BIT_SET(NPCX_SPI_STAT, NPCX_SPI_STAT_BSY)) + ; + /* Write the data */ + NPCX_SPI_DATA = txdata[i]; + CPRINTS("txdata[i]=%x", txdata[i]); + /* Waiting till reading is finished */ + while (!IS_BIT_SET(NPCX_SPI_STAT, NPCX_SPI_STAT_RBF)) + ; + /* Reading the (dummy) data */ + clear_databuf(); + } + CPRINTS("write end"); + /* Reading the data */ + for (i = 0; i < rxlen; ++i) { + /* Making sure we can write */ + while (IS_BIT_SET(NPCX_SPI_STAT, NPCX_SPI_STAT_BSY)) + ; + /* Write the (dummy) data */ + NPCX_SPI_DATA = 0; + /* Wait till reading is finished */ + while (!IS_BIT_SET(NPCX_SPI_STAT, NPCX_SPI_STAT_RBF)) + ; + /* Reading the data */ + rxdata[i] = (uint8_t)NPCX_SPI_DATA; + CPRINTS("rxdata[i]=%x", rxdata[i]); + } + /* Deassert CS# (high) to end transaction */ + gpio_set_level(GPIO_SPI_CS_L, 1); + + return EC_SUCCESS; +} + +/** + * SPI initial. + * + * @param none + * @return none + */ +static void spi_init(void) +{ + /* Disabling spi module */ + spi_enable(0); + + /* Disabling spi irq */ + CLEAR_BIT(NPCX_SPI_CTL1, NPCX_SPI_CTL1_EIR); + CLEAR_BIT(NPCX_SPI_CTL1, NPCX_SPI_CTL1_EIW); + + /* Setting clocking mode to normal mode */ + CLEAR_BIT(NPCX_SPI_CTL1, NPCX_SPI_CTL1_SCM); + /* Setting 8bit mode transfer */ + CLEAR_BIT(NPCX_SPI_CTL1, NPCX_SPI_CTL1_MOD); + /* Set core clock division factor in order to obtain the spi clock */ + spi_freq_changed(); + + /* We emit zeros in idle (default behaivor) */ + CLEAR_BIT(NPCX_SPI_CTL1, NPCX_SPI_CTL1_SCIDL); + + CPRINTS("nSPI_COMP=%x", IS_BIT_SET(NPCX_STRPST, NPCX_STRPST_SPI_COMP)); + CPRINTS("SPI_SP_SEL=%x", IS_BIT_SET(NPCX_DEV_CTL4, + NPCX_DEV_CTL4_SPI_SP_SEL)); + /* Cleaning junk data in the buffer */ + clear_databuf(); +} +DECLARE_HOOK(HOOK_INIT, spi_init, HOOK_PRIO_DEFAULT); + +/*****************************************************************************/ +/* Console commands */ + +static int printrx(const char *desc, const uint8_t *txdata, int txlen, + int rxlen) +{ + uint8_t rxdata[32]; + int rv; + int i; + + rv = spi_transaction(txdata, txlen, rxdata, rxlen); + if (rv) + return rv; + + CPRINTS("%-12s:", desc); + for (i = 0; i < rxlen; i++) + CPRINTS(" 0x%02x", rxdata[i]); + CPUTS("\n"); + return EC_SUCCESS; +} + + +static int command_spirom(int argc, char **argv) +{ + uint8_t txmandev[] = {0x90, 0x00, 0x00, 0x00}; + uint8_t txjedec[] = {0x9f}; + uint8_t txunique[] = {0x4b, 0x00, 0x00, 0x00, 0x00}; + uint8_t txsr1[] = {0x05}; + uint8_t txsr2[] = {0x35}; + + spi_enable(1); + + printrx("Man/Dev ID", txmandev, sizeof(txmandev), 2); + printrx("JEDEC ID", txjedec, sizeof(txjedec), 3); + printrx("Unique ID", txunique, sizeof(txunique), 8); + printrx("Status reg 1", txsr1, sizeof(txsr1), 1); + printrx("Status reg 2", txsr2, sizeof(txsr2), 1); + + spi_enable(0); + + return EC_SUCCESS; +} +DECLARE_CONSOLE_COMMAND(spirom, command_spirom, + NULL, + "Test reading SPI EEPROM", + NULL); diff --git a/chip/npcx/spiflashfw/ec_npcxflash.c b/chip/npcx/spiflashfw/ec_npcxflash.c new file mode 100644 index 0000000000..0a7dc2b2e2 --- /dev/null +++ b/chip/npcx/spiflashfw/ec_npcxflash.c @@ -0,0 +1,295 @@ +/* Copyright (c) 2014 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. + * + * NPCX5M5G SoC spi flash update tool + */ + +#include <stdint.h> +#include "registers.h" +#include "config_chip.h" + +/*****************************************************************************/ +/* spi flash internal functions */ +void sspi_flash_pinmux(int enable) +{ + if (enable) + CLEAR_BIT(NPCX_DEVALT(0), NPCX_DEVALT0_NO_F_SPI); + else + SET_BIT(NPCX_DEVALT(0), NPCX_DEVALT0_NO_F_SPI); + + /* CS0/1 pinmux */ + if (enable) { +#if (FIU_CHIP_SELECT == 1) + SET_BIT(NPCX_DEVALT(0), NPCX_DEVALT0_F_SPI_CS1_1); +#elif (FIU_CHIP_SELECT == 2) + SET_BIT(NPCX_DEVALT(0), NPCX_DEVALT0_F_SPI_CS1_2); +#endif + } else { + CLEAR_BIT(NPCX_DEVALT(0), NPCX_DEVALT0_F_SPI_CS1_1); + CLEAR_BIT(NPCX_DEVALT(0), NPCX_DEVALT0_F_SPI_CS1_2); + } +} + +void sspi_flash_tristate(int enable) +{ + if (enable) { + /* Enable FIU pins to tri-state */ + SET_BIT(NPCX_DEVCNT, NPCX_DEVCNT_F_SPI_TRIS); + } else { + /* Disable FIU pins to tri-state */ + CLEAR_BIT(NPCX_DEVCNT, NPCX_DEVCNT_F_SPI_TRIS); + } +} + +void sspi_flash_execute_cmd(uint8_t code, uint8_t cts) +{ + /* set UMA_CODE */ + NPCX_UMA_CODE = code; + /* execute UMA flash transaction */ + NPCX_UMA_CTS = cts; + while (IS_BIT_SET(NPCX_UMA_CTS, NPCX_UMA_CTS_EXEC_DONE)) + ; +} + +void sspi_flash_cs_level(int level) +{ + /* level is high */ + if (level) { + /* Set chip select to high */ + SET_BIT(NPCX_UMA_ECTS, NPCX_UMA_ECTS_SW_CS1); + } else { /* level is low */ + /* Set chip select to low */ + CLEAR_BIT(NPCX_UMA_ECTS, NPCX_UMA_ECTS_SW_CS1); + } +} + +void sspi_flash_wait_ready(void) +{ + uint8_t mask = SPI_FLASH_SR1_BUSY; + + /* Chip Select down. */ + sspi_flash_cs_level(0); + /* Command for Read status register */ + sspi_flash_execute_cmd(CMD_READ_STATUS_REG, MASK_CMD_ONLY); + do { + /* Read status register */ + NPCX_UMA_CTS = MASK_RD_1BYTE; + while (IS_BIT_SET(NPCX_UMA_CTS, NPCX_UMA_CTS_EXEC_DONE)) + ; + } while (NPCX_UMA_DB0 & mask); /* Wait for Busy clear */ + /* Chip Select high. */ + sspi_flash_cs_level(1); +} + +int sspi_flash_write_enable(void) +{ + uint8_t mask = SPI_FLASH_SR1_WEL; + /* Write enable command */ + sspi_flash_execute_cmd(CMD_WRITE_EN, MASK_CMD_ONLY); + /* Wait for flash is not busy */ + sspi_flash_wait_ready(); + + if (NPCX_UMA_DB0 & mask) + return 1; + else + return 0; +} + +void sspi_flash_set_address(uint32_t dest_addr) +{ + uint8_t *addr = (uint8_t *)&dest_addr; + /* Write address */ + NPCX_UMA_AB2 = addr[2]; + NPCX_UMA_AB1 = addr[1]; + NPCX_UMA_AB0 = addr[0]; +} + +void sspi_flash_burst_write(unsigned int dest_addr, unsigned int bytes, + const char *data) +{ + unsigned int i; + /* Chip Select down. */ + sspi_flash_cs_level(0); + /* Set erase address */ + sspi_flash_set_address(dest_addr); + /* Start write */ + sspi_flash_execute_cmd(CMD_FLASH_PROGRAM, MASK_CMD_WR_ADR); + for (i = 0; i < bytes; i++) { + sspi_flash_execute_cmd(*data, MASK_CMD_WR_ONLY); + data++; + } + /* Chip Select up */ + sspi_flash_cs_level(1); +} + +void sspi_flash_physical_clear_stsreg(void) +{ + /* Disable tri-state */ + sspi_flash_tristate(0); + /* Enable write */ + sspi_flash_write_enable(); + + NPCX_UMA_DB0 = 0x0; + NPCX_UMA_DB1 = 0x0; + + /* Write status register 1/2 */ + sspi_flash_execute_cmd(CMD_WRITE_STATUS_REG, MASK_CMD_WR_2BYTE); + + /* Wait writing completed */ + sspi_flash_wait_ready(); + + /* Read status register 1/2 */ + sspi_flash_execute_cmd(CMD_READ_STATUS_REG, MASK_CMD_RD_1BYTE); + sspi_flash_execute_cmd(CMD_READ_STATUS_REG2, MASK_CMD_RD_1BYTE); + /* Enable tri-state */ + sspi_flash_tristate(1); +} + +void sspi_flash_physical_write(int offset, int size, const char *data) +{ + int dest_addr = offset; + const int sz_page = CONFIG_FLASH_WRITE_IDEAL_SIZE; + + /* Disable tri-state */ + sspi_flash_tristate(0); + + /* Write the data per CONFIG_FLASH_WRITE_IDEAL_SIZE bytes */ + for (; size >= sz_page; size -= sz_page) { + /* Enable write */ + sspi_flash_write_enable(); + /* Burst UMA transaction */ + sspi_flash_burst_write(dest_addr, sz_page, data); + /* Wait write completed */ + sspi_flash_wait_ready(); + + data += sz_page; + dest_addr += sz_page; + } + + /* Handle final partial page, if any */ + if (size != 0) { + /* Enable write */ + sspi_flash_write_enable(); + /* Burst UMA transaction */ + sspi_flash_burst_write(dest_addr, size, data); + + /* Wait write completed */ + sspi_flash_wait_ready(); + } + + /* Enable tri-state */ + sspi_flash_tristate(1); +} + +void sspi_flash_physical_erase(int offset, int size) +{ + /* Disable tri-state */ + sspi_flash_tristate(0); + + /* Alignment has been checked in upper layer */ + for (; size > 0; size -= CONFIG_FLASH_ERASE_SIZE, + offset += CONFIG_FLASH_ERASE_SIZE) { + /* Enable write */ + sspi_flash_write_enable(); + /* Set erase address */ + sspi_flash_set_address(offset); + /* Start erase */ + sspi_flash_execute_cmd(CMD_SECTOR_ERASE, MASK_CMD_ADR); + + /* Wait erase completed */ + sspi_flash_wait_ready(); + } + + /* Enable tri-state */ + sspi_flash_tristate(1); +} + +/* Start to write */ +int sspi_flash_verify(int offset, int size, const char *data) +{ + int i, result; + uint8_t *ptr_flash; + uint8_t *ptr_mram; + + ptr_flash = (uint8_t *)(0x64000000+offset); + ptr_mram = (uint8_t *)data; + result = 1; + + /* Disable tri-state */ + sspi_flash_tristate(0); + + /* Start to verify */ + for (i = 0; i < size; i++) { + if (ptr_flash[i] != ptr_mram[i]) { + result = 0; + break; + } + } + + /* Enable tri-state */ + sspi_flash_tristate(1); + return result; +} + +int sspi_flash_get_image_used(const char *fw_base) +{ + const uint8_t *image; + int size = 0x20000; /* maximum size is 128KB */ + + image = (const uint8_t *)fw_base; + /* + * Scan backwards looking for 0xea byte, which is by definition the + * last byte of the image. See ec.lds.S for how this is inserted at + * the end of the image. + */ + for (size--; size > 0 && image[size] != 0xea; size--) + ; + + return size ? size + 1 : 0; /* 0xea byte IS part of the image */ + +} + +volatile __attribute__((section(".up_flag"))) unsigned int flag_upload; + +/* Entry function of spi upload function */ +void __attribute__ ((section(".startup_text"), noreturn)) +sspi_flash_upload(int spi_offset, int spi_size) +{ + /* + * Flash image has been uploaded to Code RAM + */ + const char *image_base = (char *)0x10088000; + uint32_t sz_image = spi_size; + + /* Set pinmux first */ + sspi_flash_pinmux(1); + + /* Get size of image automatically */ + if (sz_image == 0) + sz_image = sspi_flash_get_image_used(image_base); + + /* Clear status reg of spi flash for protection */ + sspi_flash_physical_clear_stsreg(); + + /* Start to erase */ + sspi_flash_physical_erase(spi_offset, sz_image); + + /* Start to write */ + sspi_flash_physical_write(spi_offset, sz_image, image_base); + + /* Verify data */ + if (sspi_flash_verify(spi_offset, sz_image, image_base)) + flag_upload |= 0x02; + + /* Disable pinmux */ + sspi_flash_pinmux(0); + + /* Mark we have finished upload work */ + flag_upload |= 0x01; + + /* Should never reach this*/ + for (;;) + ; +} + diff --git a/chip/npcx/spiflashfw/ec_npcxflash.ld b/chip/npcx/spiflashfw/ec_npcxflash.ld new file mode 100644 index 0000000000..3ab642b074 --- /dev/null +++ b/chip/npcx/spiflashfw/ec_npcxflash.ld @@ -0,0 +1,104 @@ +/* Copyright (c) 2014 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. + * + * NPCX5M5G SoC spi flash update tool + */ + +/* Memory Spaces Definitions */ +MEMORY +{ + CODERAM (rx) : ORIGIN = 0x200C0000, LENGTH = 16K + RAM (xrw) : ORIGIN = 0x200C4000, LENGTH = 16K +} + +/* + * The entry point is informative, for debuggers and simulators, + * since the Cortex-M vector points to it anyway. + */ +ENTRY(sspi_flash_upload) + + +/* Sections Definitions */ + +SECTIONS +{ + .startup_text : + { + . = ALIGN(4); + *(.startup_text ) /* Startup code */ + . = ALIGN(4); + } >CODERAM + + /* + * The program code is stored in the .text section, + * which goes to CODERAM. + */ + .text : + { + . = ALIGN(4); + *(.text .text.*) /* all remaining code */ + *(.rodata .rodata.*) /* read-only data (constants) */ + } >CODERAM + + . = ALIGN(4); + _etext = .; + + /* + * This address is used by the startup code to + * initialise the .data section. + */ + _sidata = _etext; + + /* + * Used for validation only, do not allocate anything here! + * + * This is just to check that there is enough RAM left for the Main + * stack. It should generate an error if it's full. + */ + .up_flag : + { + . = ALIGN(4); + *(.up_flag ) /* Startup code */ + . = ALIGN(4); + } >RAM + + /* + * The initialised data section. + */ + .data : AT ( _sidata ) + { + . = ALIGN(4); + + /* This is used by the startup code to initialise the .data section */ + __data_start__ = . ; + *(.data_begin .data_begin.*) + + *(.data .data.*) + + *(.data_end .data_end.*) + . = ALIGN(4); + + /* This is used by the startup code to initialise the .data section */ + __data_end__ = . ; + + } >RAM + + /* + * The uninitialised data section. NOLOAD is used to avoid + * the "section `.bss' type changed to PROGBITS" warning + */ + .bss (NOLOAD) : + { + . = ALIGN(4); + __bss_start__ = .; /* standard newlib definition */ + *(.bss_begin .bss_begin.*) + + *(.bss .bss.*) + *(COMMON) + + *(.bss_end .bss_end.*) + . = ALIGN(4); + __bss_end__ = .; /* standard newlib definition */ + } >RAM +} diff --git a/chip/npcx/system.c b/chip/npcx/system.c new file mode 100644 index 0000000000..ac6a4ba4d9 --- /dev/null +++ b/chip/npcx/system.c @@ -0,0 +1,780 @@ +/* Copyright (c) 2014 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 : NPCX hardware specific implementation */ + +#include "clock.h" +#include "common.h" +#include "console.h" +#include "cpu.h" +#include "host_command.h" +#include "registers.h" +#include "system.h" +#include "hooks.h" +#include "task.h" +#include "timer.h" +#include "util.h" +#include "hwtimer_chip.h" +#include "system_chip.h" + +#ifdef CONFIG_CODERAM_ARCH +/* base address for jumping */ +uint32_t base_addr; +#endif + +/* Indices for battery-backed ram (BBRAM) data position */ +enum bbram_data_index { + BBRM_DATA_INDEX_SCRATCHPAD = 0, /* General-purpose scratchpad */ + BBRM_DATA_INDEX_SAVED_RESET_FLAGS = 4, /* Saved reset flags */ + BBRM_DATA_INDEX_WAKE = 8, /* Wake reasons for hibernate */ +}; + +/* Flags for BBRM_DATA_INDEX_WAKE */ +#define PSLDATA_WAKE_MTC (1 << 0) /* LCT alarm */ +#define PSLDATA_WAKE_PIN (1 << 1) /* Wake pin */ + +/* Super-IO index and register definitions */ +#define SIO_OFFSET 0x4E +#define INDEX_SID 0x20 +#define INDEX_CHPREV 0x24 +#define INDEX_SRID 0x27 + +/* equivalent to 250us according to 48MHz core clock */ +#define MTC_TTC_LOAD_DELAY 1500 +#define MTC_ALARM_MASK ((1 << 25) - 1) +#define MTC_WUI_GROUP MIWU_GROUP_4 +#define MTC_WUI_MASK MASK_PIN7 + +uint32_t flag_hibernate; + +/* Begin address for the .lpram section; defined in linker script */ +uintptr_t __lpram_fw_start = CONFIG_LPRAM_BASE; + +/*****************************************************************************/ +/* Internal functions */ + +/* Super-IO read/write function */ +void system_sib_write_reg(uint8_t io_offset, uint8_t index_value, + uint8_t io_data) +{ + /* Disable interrupts */ + interrupt_disable(); + + /* Lock host CFG module */ + SET_BIT(NPCX_LKSIOHA, NPCX_LKSIOHA_LKCFG); + /* Enable Core-to-Host Modules Access */ + SET_BIT(NPCX_SIBCTRL, NPCX_SIBCTRL_CSAE); + /* Enable Core access to CFG module */ + SET_BIT(NPCX_CRSMAE, NPCX_CRSMAE_CFGAE); + /* Verify Core read/write to host modules is not in progress */ + while (IS_BIT_SET(NPCX_SIBCTRL, NPCX_SIBCTRL_CSRD)) + ; + while (IS_BIT_SET(NPCX_SIBCTRL, NPCX_SIBCTRL_CSWR)) + ; + + /* Specify the io_offset A0 = 0. the index register is accessed */ + NPCX_IHIOA = io_offset; + /* Write the data. This starts the write access to the host module */ + NPCX_IHD = index_value; + /* Wait while Core write operation is in progress */ + while (IS_BIT_SET(NPCX_SIBCTRL, NPCX_SIBCTRL_CSWR)) + ; + + /* Specify the io_offset A0 = 1. the data register is accessed */ + NPCX_IHIOA = io_offset+1; + /* Write the data. This starts the write access to the host module */ + NPCX_IHD = io_data; + /* Wait while Core write operation is in progress */ + while (IS_BIT_SET(NPCX_SIBCTRL, NPCX_SIBCTRL_CSWR)) + ; + + /* Disable Core access to CFG module */ + CLEAR_BIT(NPCX_CRSMAE, NPCX_CRSMAE_CFGAE); + /* Disable Core-to-Host Modules Access */ + CLEAR_BIT(NPCX_SIBCTRL, NPCX_SIBCTRL_CSAE); + /* unlock host CFG module */ + CLEAR_BIT(NPCX_LKSIOHA, NPCX_LKSIOHA_LKCFG); + + /* Enable interrupts */ + interrupt_enable(); +} + +uint8_t system_sib_read_reg(uint8_t io_offset, uint8_t index_value) +{ + uint8_t data_value; + + /* Disable interrupts */ + interrupt_disable(); + + /* Lock host CFG module */ + SET_BIT(NPCX_LKSIOHA, NPCX_LKSIOHA_LKCFG); + /* Enable Core-to-Host Modules Access */ + SET_BIT(NPCX_SIBCTRL, NPCX_SIBCTRL_CSAE); + /* Enable Core access to CFG module */ + SET_BIT(NPCX_CRSMAE, NPCX_CRSMAE_CFGAE); + /* Verify Core read/write to host modules is not in progress */ + while (IS_BIT_SET(NPCX_SIBCTRL, NPCX_SIBCTRL_CSRD)) + ; + while (IS_BIT_SET(NPCX_SIBCTRL, NPCX_SIBCTRL_CSWR)) + ; + + + /* Specify the io_offset A0 = 0. the index register is accessed */ + NPCX_IHIOA = io_offset; + /* Write the data. This starts the write access to the host module */ + NPCX_IHD = index_value; + /* Wait while Core write operation is in progress */ + while (IS_BIT_SET(NPCX_SIBCTRL, NPCX_SIBCTRL_CSWR)) + ; + + /* Specify the io_offset A0 = 1. the data register is accessed */ + NPCX_IHIOA = io_offset+1; + /* Start a Core read from host module */ + SET_BIT(NPCX_SIBCTRL, NPCX_SIBCTRL_CSRD); + /* Wait while Core read operation is in progress */ + while (IS_BIT_SET(NPCX_SIBCTRL, NPCX_SIBCTRL_CSRD)) + ; + /* Read the data */ + data_value = NPCX_IHD; + + /* Disable Core access to CFG module */ + CLEAR_BIT(NPCX_CRSMAE, NPCX_CRSMAE_CFGAE); + /* Disable Core-to-Host Modules Access */ + CLEAR_BIT(NPCX_SIBCTRL, NPCX_SIBCTRL_CSAE); + /* unlock host CFG module */ + CLEAR_BIT(NPCX_LKSIOHA, NPCX_LKSIOHA_LKCFG); + + /* Enable interrupts */ + interrupt_enable(); + + return data_value; +} + +void system_watchdog_reset(void) +{ + /* Unlock & stop watchdog registers */ + NPCX_WDSDM = 0x87; + NPCX_WDSDM = 0x61; + NPCX_WDSDM = 0x63; + + /* Reset TWCFG */ + NPCX_TWCFG = 0; + /* Select T0IN clock as watchdog prescaler clock */ + SET_BIT(NPCX_TWCFG, NPCX_TWCFG_WDCT0I); + + /* Clear watchdog reset status initially*/ + SET_BIT(NPCX_T0CSR, NPCX_T0CSR_WDRST_STS); + + /* Keep prescaler ratio timer0 clock to 1:1 */ + NPCX_TWCP = 0x00; + + /* Set internal counter and prescaler */ + NPCX_TWDT0 = 0x00; + NPCX_WDCNT = 0x01; + + /* Disable interrupt */ + interrupt_disable(); + /* Reload and restart Timer 0*/ + SET_BIT(NPCX_T0CSR, NPCX_T0CSR_RST); + /* Wait for timer is loaded and restart */ + while (IS_BIT_SET(NPCX_T0CSR, NPCX_T0CSR_RST)) + ; + /* Enable interrupt */ + interrupt_enable(); +} + +/** + * Read battery-backed ram (BBRAM) at specified index. + * + * @return The value of the register or 0 if invalid index. + */ +static uint32_t bbram_data_read(enum bbram_data_index index) +{ + uint32_t value = 0; + /* Check index */ + if (index < 0 || index >= NPCX_BBRAM_SIZE) + return 0; + + /* BBRAM is valid */ + if (IS_BIT_SET(NPCX_BKUP_STS, NPCX_BKUP_STS_IBBR)) + return 0; + + /* Read BBRAM */ + value += NPCX_BBRAM(index + 3); + value = value << 8; + value += NPCX_BBRAM(index + 2); + value = value << 8; + value += NPCX_BBRAM(index + 1); + value = value << 8; + value += NPCX_BBRAM(index); + + return value; +} + +/** + * Write battery-backed ram (BBRAM) at specified index. + * + * @return nonzero if error. + */ +static int bbram_data_write(enum bbram_data_index index, uint32_t value) +{ + /* Check index */ + if (index < 0 || index >= NPCX_BBRAM_SIZE) + return EC_ERROR_INVAL; + + /* BBRAM is valid */ + if (IS_BIT_SET(NPCX_BKUP_STS, NPCX_BKUP_STS_IBBR)) + return EC_ERROR_INVAL; + + /* Write BBRAM */ + NPCX_BBRAM(index) = value & 0xFF; + NPCX_BBRAM(index + 1) = (value >> 8) & 0xFF; + NPCX_BBRAM(index + 2) = (value >> 16) & 0xFF; + NPCX_BBRAM(index + 3) = (value >> 24) & 0xFF; + + /* Wait for write-complete */ + return EC_SUCCESS; +} + +/* MTC functions */ +uint32_t system_get_rtc_sec(void) +{ + /* Get MTC counter unit:seconds */ + uint32_t sec = NPCX_TTC; + return sec; +} + +void system_set_rtc(uint32_t seconds) +{ + volatile uint16_t __i; + + /* Set MTC counter unit:seconds */ + NPCX_TTC = seconds; + + /* Wait till clock is readable */ + for (__i = 0; __i < MTC_TTC_LOAD_DELAY; ++__i) + ; +} + +/* Check reset cause */ +static void check_reset_cause(void) +{ + uint32_t hib_wake_flags = bbram_data_read(BBRM_DATA_INDEX_WAKE); + uint32_t flags = 0; + + /* Check for VCC1 reset */ + if (IS_BIT_SET(NPCX_RSTCTL, NPCX_RSTCTL_VCC1_RST_STS)) + flags |= RESET_FLAG_POWER_ON; + + /* Software debugger reset */ + if (IS_BIT_SET(NPCX_RSTCTL, NPCX_RSTCTL_DBGRST_STS)) + flags |= RESET_FLAG_SOFT; + + /* Watchdog Reset */ + if (IS_BIT_SET(NPCX_T0CSR, NPCX_T0CSR_WDRST_STS)) { + flags |= RESET_FLAG_WATCHDOG; + /* Clear watchdog reset status initially*/ + SET_BIT(NPCX_T0CSR, NPCX_T0CSR_WDRST_STS); + } + + if ((hib_wake_flags & PSLDATA_WAKE_PIN)) + flags |= RESET_FLAG_WAKE_PIN; + + /* Restore then clear saved reset flags */ + flags |= bbram_data_read(BBRM_DATA_INDEX_SAVED_RESET_FLAGS); + bbram_data_write(BBRM_DATA_INDEX_SAVED_RESET_FLAGS, 0); + + system_set_reset_flags(flags); +} + +/** + * Configure address 0x40001600 in the the MPU + * (Memory Protection Unit) as a "regular" memory + */ +void system_mpu_config(void) +{ + /* Enable MPU */ + CPU_MPU_CTRL = 0x7; + + /* Create a new MPU Region for low-power ram */ + CPU_MPU_RNR = 0; /* Select region number 0 */ + CPU_MPU_RASR = CPU_MPU_RASR & 0xFFFFFFFE; /* Disable region */ + CPU_MPU_RBAR = CONFIG_LPRAM_BASE; /* Set region base address */ + /* + * Set region size & attribute and enable region + * [31:29] - Reserved. + * [28] - XN (Execute Never) = 0 + * [27] - Reserved. + * [26:24] - AP = 011 (Full access) + * [23:22] - Reserved. + * [21:19,18,17,16] - TEX,S,C,B = 001000 (Normal memory) + * [15:8] - SRD = 0 (Subregions enabled) + * [7:6] - Reserved. + * [5:1] - SIZE = 01001 (1K) + * [0] - ENABLE = 1 (enabled) + */ + CPU_MPU_RASR = 0x03080013; +} + +void __attribute__ ((section(".lowpower_ram"))) +__enter_hibernate_in_lpram(void) +{ + + /* Disable Code RAM first */ + SET_BIT(NPCX_PWDWN_CTL(5), NPCX_PWDWN_CTL5_MRFSH_DIS); + SET_BIT(NPCX_DISIDL_CTL, NPCX_DISIDL_CTL_RAM_DID); + + while (1) { + /* Set deep idle - instant wake-up mode*/ + NPCX_PMCSR = 0x7; + /* Enter deep idle, wake-up by GPIOxx or RTC */ + asm("wfi"); + + /*TODO: Using POWER_BUTTON_L GPIO02 to wake-up? */ + if (IS_BIT_SET(NPCX_WKPND(MIWU_TABLE_1 , MIWU_GROUP_1), 2)) + break; + /* RTC wake-up */ + else if (IS_BIT_SET(NPCX_WTC, NPCX_WTC_PTO)) { + /* Clear WUI pending bit of MTC */ + NPCX_WKPCL(MIWU_TABLE_0, MTC_WUI_GROUP) = MTC_WUI_MASK; + /* Clear interrupt & Disable alarm interrupt */ + CLEAR_BIT(NPCX_WTC, NPCX_WTC_WIE); + SET_BIT(NPCX_WTC, NPCX_WTC_PTO); + break; + } + } + + /* Start a watchdog reset */ + NPCX_WDCNT = 0x01; + /* Reload and restart Timer 0*/ + SET_BIT(NPCX_T0CSR, NPCX_T0CSR_RST); + /* Wait for timer is loaded and restart */ + while (IS_BIT_SET(NPCX_T0CSR, NPCX_T0CSR_RST)) + ; + + /* Spin and wait for reboot; should never return */ + while (1) + ; +} +/** + * Internal hibernate function. + * + * @param seconds Number of seconds to sleep before LCT alarm + * @param microseconds Number of microseconds to sleep before LCT alarm + */ +void __enter_hibernate(uint32_t seconds, uint32_t microseconds) +{ + int i; + void (*__hibernate_in_lpram)(void) = + (void(*)(void))(__lpram_fw_start | 0x01); + + /* Set instant wake up mode */ + SET_BIT(NPCX_ENIDL_CTL, NPCX_ENIDL_CTL_LP_WK_CTL); + interrupt_disable(); + + /* ITIM event module disable */ + CLEAR_BIT(NPCX_ITCTS(ITIM_EVENT_NO), NPCX_ITIM16_ITEN); + /* ITIM time module disable */ + CLEAR_BIT(NPCX_ITCTS(ITIM_TIME_NO), NPCX_ITIM16_ITEN); + /* ITIM watchdog warn module disable */ + CLEAR_BIT(NPCX_ITCTS(ITIM_WDG_NO), NPCX_ITIM16_ITEN); + + /* + * Set RTC interrupt in time to wake up before + * next event. + */ + if (seconds || microseconds) + system_set_rtc_alarm(seconds, microseconds); + + /* Unlock & stop watchdog registers */ + NPCX_WDSDM = 0x87; + NPCX_WDSDM = 0x61; + NPCX_WDSDM = 0x63; + + /* Configure address LPRAM in the MPU as a regular memory */ + system_mpu_config(); + + /* Enable Low Power RAM */ + NPCX_LPRAM_CTRL = 1; + + /* Initialize watchdog */ + NPCX_TWCFG = 0; /* Select T0IN clock as watchdog prescaler clock */ + SET_BIT(NPCX_TWCFG, NPCX_TWCFG_WDCT0I); + NPCX_TWCP = 0x00; /* Keep prescaler ratio timer0 clock to 1:1 */ + NPCX_TWDT0 = 0x00; /* Set internal counter and prescaler */ + + /* Copy the __enter_hibernate_in_lpram instructions to LPRAM */ + for (i = 0; i < &__flash_lpfw_end - &__flash_lpfw_start; i++) + *((uint32_t *)__lpram_fw_start + i) = + *(&__flash_lpfw_start + i); + + /* Disable interrupt */ + interrupt_disable(); + + /* execute hibernate func in LPRAM */ + __hibernate_in_lpram(); + +} + +/*****************************************************************************/ +/* IC specific low-level driver */ + +/* Microsecond will be ignore for hardware limitation */ +void system_set_rtc_alarm(uint32_t seconds, uint32_t microseconds) +{ + uint32_t cur_secs, alarm_secs; + + if (seconds == 0) + return; + + /* Get current clock */ + cur_secs = NPCX_TTC; + + /* If alarm clock is not sequential or not in range */ + alarm_secs = cur_secs + seconds; + alarm_secs = alarm_secs & MTC_ALARM_MASK; + + /* Reset alarm first */ + system_reset_rtc_alarm(); + + /* Set alarm, use first 25 bits of clock value */ + NPCX_WTC = alarm_secs; + + /* Enable interrupt mode alarm */ + SET_BIT(NPCX_WTC, NPCX_WTC_WIE); + + /* Enable MTC interrupt */ + task_enable_irq(NPCX_IRQ_MTC_WKINTAD_0); + + /* Enable wake-up input sources */ + NPCX_WKEN(MIWU_TABLE_0, MTC_WUI_GROUP) |= MTC_WUI_MASK; +} + +void system_reset_rtc_alarm(void) +{ + /* + * Clear interrupt & Disable alarm interrupt + * Update alarm value to zero + */ + CLEAR_BIT(NPCX_WTC, NPCX_WTC_WIE); + SET_BIT(NPCX_WTC, NPCX_WTC_PTO); + + /* Disable MTC interrupt */ + task_disable_irq(NPCX_IRQ_MTC_WKINTAD_0); +} + +/** + * Enable hibernate interrupt + */ +void system_enable_hib_interrupt(void) +{ + task_enable_irq(NPCX_IRQ_MTC_WKINTAD_0); +} + +void system_hibernate(uint32_t seconds, uint32_t microseconds) +{ + /* Flush console before hibernating */ + cflush(); + +#if SUPPORT_HIB + /* Add additional hibernate operations here */ + __enter_hibernate(seconds, microseconds); +#endif +} + +void system_pre_init(void) +{ + /* + * Add additional initialization here + * EC should be initialized in Booter + */ + +#ifndef CHIP_NPCX5M5G + /* Power-down the modules we don't need */ + NPCX_PWDWN_CTL(0) = 0xFD; /* Skip SDP_PD */ + NPCX_PWDWN_CTL(1) = 0xFF; + NPCX_PWDWN_CTL(2) = 0xFF; + NPCX_PWDWN_CTL(3) = 0xF0; /*Skip ITIM3/2/1_PD */ + NPCX_PWDWN_CTL(4) = 0xF8; + NPCX_PWDWN_CTL(5) = 0x87; +#endif + /* Check reset cause */ + check_reset_cause(); +} + +void system_reset(int flags) +{ + uint32_t save_flags = 0; + + /* Disable interrupts to avoid task swaps during reboot */ + interrupt_disable(); + + /* Save current reset reasons if necessary */ + if (flags & SYSTEM_RESET_PRESERVE_FLAGS) + save_flags = system_get_reset_flags() | RESET_FLAG_PRESERVED; + + /* Add in AP off flag into saved flags. */ + if (flags & SYSTEM_RESET_LEAVE_AP_OFF) + save_flags |= RESET_FLAG_AP_OFF; + + /* Save reset flag */ + if (flags & SYSTEM_RESET_HARD) + save_flags |= RESET_FLAG_HARD; + else { + save_flags |= RESET_FLAG_SOFT; + /* Use SYSRESETREQ to trigger a soft reboot */ + CPU_NVIC_APINT = 0x05fa0004; + } + /* Store flags to battery backed RAM. */ + bbram_data_write(BBRM_DATA_INDEX_SAVED_RESET_FLAGS, save_flags); + + /* Ask the watchdog to trigger a hard reboot */ + system_watchdog_reset(); + + /* Spin and wait for reboot; should never return */ + while (1) + ; +} + +/** + * Return the chip vendor/name/revision string. + */ +const char *system_get_chip_vendor(void) +{ + uint8_t fam_id = system_sib_read_reg(SIO_OFFSET, INDEX_SID); + switch (fam_id) { + case 0xFC: + return "NUC"; + default: + return "Unknown"; + } +} + +const char *system_get_chip_name(void) +{ + uint8_t chip_id = system_sib_read_reg(SIO_OFFSET, INDEX_SRID); + switch (chip_id) { + case 0x05: + return "NPCX5m5G"; + default: + return "Unknown"; + } +} + +const char *system_get_chip_revision(void) +{ + static char rev[1]; + uint8_t rev_num = system_sib_read_reg(SIO_OFFSET, INDEX_CHPREV); + + /* set revision from character '0' */ + rev[0] = '0' + rev_num; + + return rev; +} + +int system_set_console_force_enabled(int val) +{ + /* TODO(crosbug.com/p/23575): IMPLEMENT ME ! */ + return 0; +} + +int system_get_console_force_enabled(void) +{ + /* TODO(crosbug.com/p/23575): IMPLEMENT ME ! */ + return 0; +} + +/** + * Get/Set VbNvContext in non-volatile storage. The block should be 16 bytes + * long, which is the current size of VbNvContext block. + * + * @param block Pointer to a buffer holding VbNvContext. + * @return 0 on success, !0 on error. + */ +int system_get_vbnvcontext(uint8_t *block) +{ + return EC_ERROR_UNIMPLEMENTED; +} + +int system_set_vbnvcontext(const uint8_t *block) +{ + return EC_ERROR_UNIMPLEMENTED; +} + +/** + * Set a scratchpad register to the specified value. + * + * The scratchpad register must maintain its contents across a + * software-requested warm reset. + * + * @param value Value to store. + * @return EC_SUCCESS, or non-zero if error. + */ +int system_set_scratchpad(uint32_t value) +{ + return bbram_data_write(BBRM_DATA_INDEX_SCRATCHPAD, value); +} + +uint32_t system_get_scratchpad(void) +{ + return bbram_data_read(BBRM_DATA_INDEX_SCRATCHPAD); +} + +/*****************************************************************************/ +/* Console commands */ + +static int command_system_rtc(int argc, char **argv) +{ + uint32_t sec; + if (argc == 3 && !strcasecmp(argv[1], "set")) { + char *e; + uint32_t t = strtoi(argv[2], &e, 0); + if (*e) + return EC_ERROR_PARAM2; + + system_set_rtc(t); + } else if (argc > 1) { + return EC_ERROR_INVAL; + } + + sec = system_get_rtc_sec(); + ccprintf("RTC: 0x%08x (%d.00 s)\n", sec, sec); + + return EC_SUCCESS; +} +DECLARE_CONSOLE_COMMAND(rtc, command_system_rtc, + "[set <seconds>]", + "Get/set real-time clock", + NULL); + +#ifdef CONFIG_CMD_RTC_ALARM +/** + * Test the RTC alarm by setting an interrupt on RTC match. + */ +static int command_rtc_alarm_test(int argc, char **argv) +{ + int s = 1, us = 0; + char *e; + + ccprintf("Setting RTC alarm\n"); + system_enable_hib_interrupt(); + + if (argc > 1) { + s = strtoi(argv[1], &e, 10); + if (*e) + return EC_ERROR_PARAM1; + + } + if (argc > 2) { + us = strtoi(argv[2], &e, 10); + if (*e) + return EC_ERROR_PARAM2; + + } + + system_set_rtc_alarm(s, us); + + return EC_SUCCESS; +} +DECLARE_CONSOLE_COMMAND(rtc_alarm, command_rtc_alarm_test, + "[seconds [microseconds]]", + "Test alarm", + NULL); +#endif /* CONFIG_CMD_RTC_ALARM */ + +/*****************************************************************************/ +/* Host commands */ + +static int system_rtc_get_value(struct host_cmd_handler_args *args) +{ + struct ec_response_rtc *r = args->response; + + r->time = system_get_rtc_sec(); + args->response_size = sizeof(*r); + + return EC_RES_SUCCESS; +} +DECLARE_HOST_COMMAND(EC_CMD_RTC_GET_VALUE, + system_rtc_get_value, + EC_VER_MASK(0)); + +static int system_rtc_set_value(struct host_cmd_handler_args *args) +{ + const struct ec_params_rtc *p = args->params; + + system_set_rtc(p->time); + return EC_RES_SUCCESS; +} +DECLARE_HOST_COMMAND(EC_CMD_RTC_SET_VALUE, + system_rtc_set_value, + EC_VER_MASK(0)); + +/* For LPC host register initial via SIB module */ +void system_lpc_host_register_init(void){ + /* Setting PMC2 */ + /* LDN register = 0x12(PMC2) */ + system_sib_write_reg(SIO_OFFSET, 0x07, 0x12); + /* CMD port is 0x200 */ + system_sib_write_reg(SIO_OFFSET, 0x60, 0x02); + system_sib_write_reg(SIO_OFFSET, 0x61, 0x00); + /* Data port is 0x204 */ + system_sib_write_reg(SIO_OFFSET, 0x62, 0x02); + system_sib_write_reg(SIO_OFFSET, 0x63, 0x04); + /* enable PMC2 */ + system_sib_write_reg(SIO_OFFSET, 0x30, 0x01); + + /* Setting SHM */ + /* LDN register = 0x0F(SHM) */ + system_sib_write_reg(SIO_OFFSET, 0x07, 0x0F); + /* WIN1&2 mapping to IO */ + system_sib_write_reg(SIO_OFFSET, 0xF1, + system_sib_read_reg(SIO_OFFSET, 0xF1) | 0x30); + /* Host Command on the IO:0x0800 */ + system_sib_write_reg(SIO_OFFSET, 0xF7, 0x00); + system_sib_write_reg(SIO_OFFSET, 0xF6, 0x00); + system_sib_write_reg(SIO_OFFSET, 0xF5, 0x08); + system_sib_write_reg(SIO_OFFSET, 0xF4, 0x00); + /* WIN1 as Host Command on the IO:0x0800 */ + system_sib_write_reg(SIO_OFFSET, 0xFB, 0x00); + system_sib_write_reg(SIO_OFFSET, 0xFA, 0x00); + /* WIN2 as MEMMAP on the IO:0x900 */ + system_sib_write_reg(SIO_OFFSET, 0xF9, 0x09); + system_sib_write_reg(SIO_OFFSET, 0xF8, 0x00); + /* enable SHM */ + system_sib_write_reg(SIO_OFFSET, 0x30, 0x01); +} +#ifdef CONFIG_CODERAM_ARCH +uint32_t system_get_lfw_address(uint32_t flash_addr) +{ + /* Little FW located on top of flash - 4K */ + uint32_t jump_addr = (CONFIG_FLASH_BASE + CONFIG_SPI_FLASH_SIZE + - CONFIG_LFW_OFFSET + 1); + /* restore base address for jumping*/ + base_addr = flash_addr; + return jump_addr; +} + +enum system_image_copy_t system_get_shrspi_image_copy(void) +{ + /* RO region FW */ + if (IS_BIT_SET(NPCX_FWCTRL, NPCX_FWCTRL_RO_REGION)) + return SYSTEM_IMAGE_RO; + else/* RW region FW */ + return SYSTEM_IMAGE_RW; +} + +/** + * Set flag for jumping across a sysjump. + */ +static void system_sysjump(void) +{ + /* Jump to RO region -- set flag */ + if (base_addr == CONFIG_FLASH_BASE + CONFIG_FW_RO_OFF) + SET_BIT(NPCX_FWCTRL, NPCX_FWCTRL_RO_REGION); + else /* Jump to RW region -- clear flag */ + CLEAR_BIT(NPCX_FWCTRL, NPCX_FWCTRL_RO_REGION); +} +DECLARE_HOOK(HOOK_SYSJUMP, system_sysjump, HOOK_PRIO_DEFAULT); +#endif diff --git a/chip/npcx/system_chip.h b/chip/npcx/system_chip.h new file mode 100644 index 0000000000..280246c414 --- /dev/null +++ b/chip/npcx/system_chip.h @@ -0,0 +1,20 @@ +/* Copyright (c) 2014 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. + */ + +/* NPCX-specific SIB module for Chrome EC */ + +#ifndef __CROS_EC_NPCX_LPC_H +#define __CROS_EC_NPCX_LPC_H + +void system_lpc_host_register_init(void); + +/* End address for the .lpram section; defined in linker script */ +extern unsigned int __lpram_fw_end; +/* Begin flash address for the lpram codes; defined in linker script */ +extern unsigned int __flash_lpfw_start; +/* End flash address for the lpram codes; defined in linker script */ +extern unsigned int __flash_lpfw_end; + +#endif /* __CROS_EC_NPCX_LPC_H */ diff --git a/chip/npcx/uart.c b/chip/npcx/uart.c new file mode 100644 index 0000000000..893b02aa84 --- /dev/null +++ b/chip/npcx/uart.c @@ -0,0 +1,210 @@ +/* Copyright (c) 2014 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. + */ + +/* UART module for Chrome EC */ + +#include "clock.h" +#include "common.h" +#include "console.h" +#include "gpio.h" +#include "lpc.h" +#include "registers.h" +#include "clock_chip.h" +#include "system.h" +#include "task.h" +#include "uart.h" +#include "util.h" + +static int init_done; + +int uart_init_done(void) +{ + return init_done; +} + +void uart_tx_start(void) +{ + if (IS_BIT_SET(NPCX_WKEN(1, 1), 0)) { + /* disable MIWU*/ + CLEAR_BIT(NPCX_WKEN(1, 1), 0); + /* go back to original setting */ + task_enable_irq(NPCX_IRQ_WKINTB_1); + /* Go back CR_SIN*/ + SET_BIT(NPCX_DEVALT(0x0A), NPCX_DEVALTA_UART_SL); + /* enable uart again from MIWU mode */ + task_enable_irq(NPCX_IRQ_UART); + } + + /* If interrupt is already enabled, nothing to do */ + if (NPCX_UICTRL & 0x20) + return; + + /* Do not allow deep sleep while transmit in progress */ + disable_sleep(SLEEP_MASK_UART); + + /* + * Re-enable the transmit interrupt, then forcibly trigger the + * interrupt. This works around a hardware problem with the + * UART where the FIFO only triggers the interrupt when its + * threshold is _crossed_, not just met. + */ + NPCX_UICTRL |= 0x20; + + task_trigger_irq(NPCX_IRQ_UART); +} + +void uart_tx_stop(void) /* Disable TX interrupt */ +{ + NPCX_UICTRL &= ~0x20; + + /* Re-allow deep sleep */ + enable_sleep(SLEEP_MASK_UART); +} + +void uart_tx_flush(void) +{ + /* Wait for transmit FIFO empty */ + while (!(NPCX_UICTRL & 0x01)) + ; +} + +int uart_tx_ready(void) +{ + return NPCX_UICTRL & 0x01; /*if TX FIFO is empty return 1*/ +} + +int uart_tx_in_progress(void) +{ + /* Transmit is in progress if the TX busy bit is set. */ + return NPCX_USTAT & 0x40; /*BUSY bit , if busy return 1*/ +} + +int uart_rx_available(void) +{ + uint8_t ctrl = NPCX_UICTRL; +#ifdef CONFIG_LOW_POWER_IDLE + /* + * Activity seen on UART RX pin while UART was disabled for deep sleep. + * The console won't see that character because the UART is disabled, + * so we need to inform the clock module of UART activity ourselves. + */ + if (ctrl & 0x02) + clock_refresh_console_in_use(); +#endif + return ctrl & 0x02; /* If RX FIFO is empty return '0'*/ +} + +void uart_write_char(char c) +{ + /* Wait for space in transmit FIFO. */ + while (!uart_tx_ready()) + ; + + NPCX_UTBUF = c; +} + +int uart_read_char(void) +{ + return NPCX_URBUF; +} + +static void uart_clear_rx_fifo(int channel) +{ + int scratch __attribute__ ((unused)); + if (channel == 0) { /* suppose '0' is EC UART*/ + /*if '1' that mean have a RX data on the FIFO register*/ + while ((NPCX_UICTRL & 0x02)) + scratch = NPCX_URBUF; + } +} + +void uart_disable_interrupt(void) +{ + task_disable_irq(NPCX_IRQ_UART); +} + +void uart_enable_interrupt(void) +{ + task_enable_irq(NPCX_IRQ_UART); +} + +/** + * Interrupt handler for UART0 + */ +void uart_ec_interrupt(void) +{ + /* Read input FIFO until empty, then fill output FIFO */ + uart_process_input(); + uart_process_output(); +} +DECLARE_IRQ(NPCX_IRQ_UART, uart_ec_interrupt, 1); + + +static void uart_config(void) +{ + uint32_t div, opt_dev, min_deviation, clk, calc_baudrate, deviation; + uint8_t prescalar, opt_prescalar, i; + /* Enable the port */ + /* Configure pins from GPIOs to CR_UART */ + gpio_config_module(MODULE_UART, 1); + + /* Calculated UART baudrate , clock source from APB2 */ + opt_prescalar = opt_dev = 0; + prescalar = 10; + min_deviation = 0xFFFFFFFF; + clk = clock_get_apb2_freq(); + for (i = 1; i < 31; i++) { + div = (clk * 10) / (16 * CONFIG_UART_BAUD_RATE * prescalar); + if (div != 0) { + calc_baudrate = (clk * 10) / (16 * div * prescalar); + deviation = (calc_baudrate > CONFIG_UART_BAUD_RATE) ? + (calc_baudrate - CONFIG_UART_BAUD_RATE) : + (CONFIG_UART_BAUD_RATE - calc_baudrate); + if (deviation < min_deviation) { + min_deviation = deviation; + opt_prescalar = i; + opt_dev = div; + } + } + prescalar += 5; + } + opt_dev--; + NPCX_UPSR = ((opt_prescalar<<3) & 0xF8) | ((opt_dev >> 8) & 0x7); + NPCX_UBAUD = (uint8_t)opt_dev; + /* + * 8-N-1, FIFO enabled. Must be done after setting + * the divisor for the new divisor to take effect. + */ + NPCX_UFRS = 0x00; + NPCX_UICTRL = 0x40; /* receive int enable only */ +} + +void uart_init(void) +{ + uint32_t mask = 0; + + /* + * Enable UART0 in run, sleep, and deep sleep modes. Enable the Host + * UART in run and sleep modes. + */ + mask = 0x10; /* bit 4 */ + clock_enable_peripheral(CGC_OFFSET_UART, mask, CGC_MODE_ALL); + + /* Set pin-mask for UART */ + SET_BIT(NPCX_DEVALT(0x0A), NPCX_DEVALTA_UART_SL); + gpio_config_module(MODULE_UART, 1); + + /* Configure UARTs (identically) */ + uart_config(); + + /* + * Enable interrupts for UART0 only. Host UART will have to wait + * until the LPC bus is initialized. + */ + uart_clear_rx_fifo(0); + task_enable_irq(NPCX_IRQ_UART); + + init_done = 1; +} diff --git a/chip/npcx/watchdog.c b/chip/npcx/watchdog.c new file mode 100644 index 0000000000..411488877a --- /dev/null +++ b/chip/npcx/watchdog.c @@ -0,0 +1,142 @@ +/* Copyright (c) 2014 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. + */ + +/* Watchdog driver */ + +#include "clock.h" +#include "common.h" +#include "console.h" +#include "registers.h" +#include "hwtimer_chip.h" +#include "gpio.h" +#include "hooks.h" +#include "timer.h" +#include "task.h" +#include "util.h" +#include "watchdog.h" + +/* WDCNT value for watchdog period */ +#define WDCNT_VALUE ((CONFIG_WATCHDOG_PERIOD_MS*INT_32K_CLOCK) / (1024*1000)) +/* Delay counter time for print watchdog info through UART */ +#define WDCNT_DELAY 0x10 + + +void watchdog_init_warning_timer(void) +{ + /* init watchdog timer first */ + init_hw_timer(ITIM_WDG_NO, ITIM16_SOURCE_CLOCK_32K); + + /* + * prescaler to TIMER_TICK + * Ttick_unit = (PRE_8+1) * T32k + * PRE_8 = (Ttick_unit/T32K) - 1 + * Unit: 1 msec + */ + NPCX_ITPRE(ITIM_WDG_NO) = DIV_ROUND_NEAREST(1000*INT_32K_CLOCK, + SECOND) - 1; + + /* ITIM count down : event expired*/ + NPCX_ITCNT16(ITIM_WDG_NO) = CONFIG_WATCHDOG_PERIOD_MS-1; + /* Event module enable */ + SET_BIT(NPCX_ITCTS(ITIM_WDG_NO), NPCX_ITIM16_ITEN); + /* Enable interrupt of ITIM */ + task_enable_irq(ITIM16_INT(ITIM_WDG_NO)); +} + + +void watchdog_check(uint32_t excep_lr, uint32_t excep_sp) +{ + int wd_cnt; + /* Clear timeout status for event */ + SET_BIT(NPCX_ITCTS(ITIM_WDG_NO), NPCX_ITIM16_TO_STS); + + /* Read watchdog counter from TWMWD */ + wd_cnt = NPCX_TWMWD; +#if DEBUG_WDG + ccprintf("WD (%d)\r\n", wd_cnt); +#endif + if (wd_cnt <= WDCNT_DELAY) + watchdog_trace(excep_lr, excep_sp); +} + +/* ISR for watchdog warning naked will keep SP & LR */ +void IRQ_HANDLER(ITIM16_INT(ITIM_WDG_NO))(void) __attribute__((naked)); +void IRQ_HANDLER(ITIM16_INT(ITIM_WDG_NO))(void) + { + /* Naked call so we can extract raw LR and SP */ + asm volatile("mov r0, lr\n" + "mov r1, sp\n" + /* Must push registers in pairs to keep 64-bit aligned + * stack for ARM EABI. This also conveninently saves + * R0=LR so we can pass it to task_resched_if_needed. */ + "push {r0, lr}\n" + "bl watchdog_check\n" + "pop {r0, lr}\n" + "b task_resched_if_needed\n"); + } +const struct irq_priority IRQ_PRIORITY(ITIM16_INT(ITIM_WDG_NO)) +__attribute__((section(".rodata.irqprio"))) += {ITIM16_INT(ITIM_WDG_NO), 0}; +/* put the watchdog at the highest priority */ + +void watchdog_reload(void) +{ + /* Disable watchdog interrupt */ + task_disable_irq(ITIM16_INT(ITIM_WDG_NO)); + +#if 1 /* mark this for testing watchdog */ + /* Touch watchdog & reset software counter */ + NPCX_WDSDM = 0x5C; +#endif + + /* Enable watchdog interrupt */ + task_enable_irq(ITIM16_INT(ITIM_WDG_NO)); +} +DECLARE_HOOK(HOOK_TICK, watchdog_reload, HOOK_PRIO_DEFAULT); + +int watchdog_init(void) +{ +#if SUPPORT_WDG + /* Keep prescaler ratio timer0 clock to 1:1024 */ + NPCX_TWCP = 0x0A; + /* Keep prescaler ratio watchdog clock to 1:1 */ + NPCX_WDCP = 0; + + /* Clear watchdog reset status initially*/ + SET_BIT(NPCX_T0CSR, NPCX_T0CSR_WDRST_STS); + + /* Reset TWCFG */ + NPCX_TWCFG = 0; + /* Watchdog touch by writing 5Ch to WDSDM */ + SET_BIT(NPCX_TWCFG, NPCX_TWCFG_WDSDME); + /* Select T0IN clock as watchdog prescaler clock */ + SET_BIT(NPCX_TWCFG, NPCX_TWCFG_WDCT0I); + /* Disable early touch functionality */ + SET_BIT(NPCX_T0CSR, NPCX_T0CSR_TESDIS); + + /* + * Set WDCNT initial reload value and T0OUT timeout period + * 1. Watchdog clock source is 32768/1024 Hz and disable T0OUT. + * 2. ITIM16 will be issued to check WDCNT is under WDCNT_DELAY or not + * 3. Set RST to upload TWDT0 & WDCNT + */ + /* Set WDCNT --> WDCNT=0 will generate watchdog reset */ + NPCX_WDCNT = WDCNT_VALUE + WDCNT_DELAY; + + /* Disable interrupt */ + interrupt_disable(); + /* Reload TWDT0/WDCNT */ + SET_BIT(NPCX_T0CSR, NPCX_T0CSR_RST); + /* Wait for timer is loaded and restart */ + while (IS_BIT_SET(NPCX_T0CSR, NPCX_T0CSR_RST)) + ; + /* Enable interrupt */ + interrupt_enable(); + + /* Init watchdog warning timer */ + watchdog_init_warning_timer(); +#endif + return EC_SUCCESS; +} diff --git a/common/keyboard_8042.c b/common/keyboard_8042.c index cf2b5bec94..d21b4ceae5 100644 --- a/common/keyboard_8042.c +++ b/common/keyboard_8042.c @@ -453,7 +453,7 @@ static void clear_typematic_key(void) void keyboard_state_changed(int row, int col, int is_pressed) { uint8_t scan_code[MAX_SCAN_CODE_LEN]; - int32_t len; + int32_t len = 0; enum ec_error_list ret; CPRINTS5("KB (%d,%d)=%d", row, col, is_pressed); diff --git a/common/system.c b/common/system.c index de3000ccd1..cdb940c77b 100644 --- a/common/system.c +++ b/common/system.c @@ -310,6 +310,10 @@ void system_disable_jump(void) test_mockable enum system_image_copy_t system_get_image_copy(void) { + /* TODO: (ML) return which region is used in Code RAM */ +#ifdef CONFIG_CODERAM_ARCH + return system_get_shrspi_image_copy(); +#else uintptr_t my_addr = (uintptr_t)system_get_image_copy - CONFIG_FLASH_BASE; @@ -322,6 +326,7 @@ test_mockable enum system_image_copy_t system_get_image_copy(void) return SYSTEM_IMAGE_RW; return SYSTEM_IMAGE_UNKNOWN; +#endif } int system_get_image_used(enum system_image_copy_t copy) @@ -329,7 +334,7 @@ int system_get_image_used(enum system_image_copy_t copy) const uint8_t *image; int size = 0; - image = (const uint8_t *)(get_base(copy)); + image = (const uint8_t *)get_base(copy); size = get_size(copy); if (size <= 0) @@ -342,7 +347,6 @@ int system_get_image_used(enum system_image_copy_t copy) */ for (size--; size > 0 && image[size] != 0xea; size--) ; - return size ? size + 1 : 0; /* 0xea byte IS part of the image */ } @@ -473,12 +477,17 @@ int system_run_image_copy(enum system_image_copy_t copy) if (base == 0xffffffff) return EC_ERROR_INVAL; + /* TODO: (ML) jump to little FW for code ram architecture */ +#ifdef CONFIG_CODERAM_ARCH + init_addr = system_get_lfw_address(base); +#else /* Make sure the reset vector is inside the destination image */ init_addr = *(uintptr_t *)(base + 4); #ifndef EMU_BUILD if (init_addr < base || init_addr >= base + get_size(copy)) return EC_ERROR_UNKNOWN; #endif +#endif CPRINTS("Jumping to image %s", system_image_copy_t_to_string(copy)); @@ -503,7 +512,11 @@ const char *system_get_version(enum system_image_copy_t copy) /* The version string is always located after the reset vectors, so * it's the same as in the current image. */ +#ifdef CONFIG_CODERAM_ARCH /* TODO: (ML) we run FW in Code RAM */ + addr += ((uintptr_t)&version_data - CONFIG_CDRAM_BASE); +#else addr += ((uintptr_t)&version_data - get_base(system_get_image_copy())); +#endif /* Make sure the version struct cookies match before returning the * version string. */ diff --git a/core/cortex-m/ec.lds.S b/core/cortex-m/ec.lds.S index bd813a8b22..5d70f366e8 100644 --- a/core/cortex-m/ec.lds.S +++ b/core/cortex-m/ec.lds.S @@ -19,6 +19,9 @@ MEMORY { FLASH (rx) : ORIGIN = FW_OFF(SECTION), LENGTH = FW_SIZE(SECTION) IRAM (rw) : ORIGIN = CONFIG_RAM_BASE, LENGTH = CONFIG_RAM_SIZE +#ifdef CONFIG_CODERAM_ARCH + CDRAM (rx) : ORIGIN = CONFIG_CDRAM_BASE, LENGTH = CONFIG_CDRAM_SIZE +#endif #ifdef RSA_PUBLIC_KEY_SIZE PSTATE(r) : ORIGIN = FW_OFF(SECTION) + FW_SIZE(SECTION), \ LENGTH = CONFIG_FW_PSTATE_SIZE @@ -43,12 +46,21 @@ SECTIONS #endif OUTDIR/core/CORE/init.o (.text) *(.text*) +#ifdef CONFIG_CODERAM_ARCH + __flash_lpfw_start = .; + /* Entering deep idle FW for better power consumption */ + KEEP(*(.lowpower_ram)) + __flash_lpfw_end = .; + } > CDRAM AT > FLASH +#else #ifdef COMPILE_FOR_RAM } > IRAM #else } > FLASH #endif +#endif . = ALIGN(4); + .rodata : { /* Symbols defined here are declared in link_defs.h */ __irqprio = .; @@ -151,11 +163,15 @@ SECTIONS KEEP(*(.google)) #endif . = ALIGN(4); +#ifdef CONFIG_CODERAM_ARCH + } > CDRAM AT > FLASH +#else #ifdef COMPILE_FOR_RAM } > IRAM #else } > FLASH #endif +#endif __ro_end = . ; __deferred_funcs_count = @@ -184,8 +200,13 @@ SECTIONS #ifdef COMPILE_FOR_RAM .data : { #else +#ifdef CONFIG_CODERAM_ARCH + __data_start = . ; + .data : AT(LOADADDR(.rodata) + SIZEOF(.rodata)) { +#else .data : AT(ADDR(.rodata) + SIZEOF(.rodata)) { #endif +#endif . = ALIGN(4); __data_start = .; *(.data.tasks) diff --git a/include/system.h b/include/system.h index 71cad098f9..259e34fc92 100644 --- a/include/system.h +++ b/include/system.h @@ -369,4 +369,22 @@ void system_set_rtc_alarm(uint32_t seconds, uint32_t microseconds); */ void system_reset_rtc_alarm(void); +#ifdef CONFIG_CODERAM_ARCH +/** + * Determine which address should be jumped and return address of littel FW + * + * Note: This feature is used for code ram arch + * + * @param flash_addr jump address of spi flash for RO or RW region + */ +uint32_t system_get_lfw_address(uint32_t flash_addr); + +/** + * Return whcih region is used in Code RAM + * + * Note: This feature is used for code ram arch + * + */ +enum system_image_copy_t system_get_shrspi_image_copy(void); +#endif #endif /* __CROS_EC_SYSTEM_H */ diff --git a/util/flash_ec b/util/flash_ec index 0d840a9ae4..9cfc19baa1 100755 --- a/util/flash_ec +++ b/util/flash_ec @@ -64,7 +64,7 @@ BOARDS_STM32=( discovery firefly fruitpie - jerry + jerry kitty mighty minimuffin @@ -92,6 +92,10 @@ BOARDS_STM32_DFU=( twinkie ) +BOARDS_NPCX=( + npcx_evb +) + # Flags DEFINE_string board "${DEFAULT_BOARD}" \ "The board to run debugger on." @@ -405,6 +409,38 @@ function flash_lm4() { die "Failed to program ${IMG}" } +function flash_npcx() { + [[ -n "${EC_DIR}" ]] || die "Cannot locate openocd script" + + if [ "${FLAGS_usb}" = ${FLAGS_TRUE} ] ; then + OCD_CFG="${BOARD}.cfg" + else + OCD_CFG="servo_v2.cfg" + fi + + OCD_PATH="${EC_DIR}/chip/npcx/openocd" + if [ "${FLAGS_unprotect}" = ${FLAGS_TRUE} ] ; then + # Unprotect exists, but isn't needed because erasing pstate is + # implicit in writing the entire image + die "--unprotect not supported for this board." + fi + + dut_control jtag_buf_on_flex_en:on + dut_control jtag_buf_en:on + + if [ "${FLAGS_ro}" = ${FLAGS_TRUE} ] ; then + # Program RO region only + OCD_CMDS="init; flash_npcx_ro ${FLAGS_offset}; shutdown;" + sudo openocd -s "${OCD_PATH}" -f "${OCD_CFG}" -c "${OCD_CMDS}" || \ + die "Failed to program ${IMG}" + else + # Program all EC regions + OCD_CMDS="init; flash_npcx_evb ${FLAGS_offset}; shutdown;" + sudo openocd -s "${OCD_PATH}" -f "${OCD_CFG}" -c "${OCD_CMDS}" || \ + die "Failed to program ${IMG}" + fi +} + if dut_control boot_mode 2>/dev/null ; then if [[ "${MCU}" != "ec" ]] ; then die "Toad cable can't support non-ec UARTs" @@ -434,6 +470,8 @@ elif $(in_array "${BOARDS_STM32_DFU[@]}" "${BOARD}"); then flash_stm32_dfu elif [ "${BOARD}" == "link" ]; then flash_link +elif $(in_array "${BOARDS_NPCX[@]}" "${BOARD}"); then + flash_npcx else die "board ${BOARD} not supported" fi |