diff options
-rw-r--r-- | chip/ish/build.mk | 31 | ||||
-rw-r--r-- | chip/ish/clock.c | 20 | ||||
-rw-r--r-- | chip/ish/config_chip.h | 79 | ||||
-rw-r--r-- | chip/ish/config_flash_layout.h | 59 | ||||
-rw-r--r-- | chip/ish/flash.c | 18 | ||||
-rw-r--r-- | chip/ish/gpio.c | 34 | ||||
-rw-r--r-- | chip/ish/hpet.h | 65 | ||||
-rw-r--r-- | chip/ish/hwtimer.c | 129 | ||||
-rw-r--r-- | chip/ish/i2c.c | 422 | ||||
-rw-r--r-- | chip/ish/ipc.c | 398 | ||||
-rw-r--r-- | chip/ish/ipc.h | 96 | ||||
-rw-r--r-- | chip/ish/ish_i2c.h | 191 | ||||
-rw-r--r-- | chip/ish/jtag.c | 13 | ||||
-rw-r--r-- | chip/ish/registers.h | 105 | ||||
-rw-r--r-- | chip/ish/system.c | 120 | ||||
-rw-r--r-- | chip/ish/uart.c | 243 | ||||
-rw-r--r-- | chip/ish/uart_defs.h | 222 |
17 files changed, 2245 insertions, 0 deletions
diff --git a/chip/ish/build.mk b/chip/ish/build.mk new file mode 100644 index 0000000000..2206426757 --- /dev/null +++ b/chip/ish/build.mk @@ -0,0 +1,31 @@ +# -*- makefile -*- +# Copyright (c) 2016 The Chromium OS Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. +# +# ISH chip specific files build +# + +# ISH SoC has a Minute-IA core +CORE:=minute-ia +# Allow the i486 instruction set +CFLAGS_CPU+=-march=pentium -mtune=i486 -m32 + +ifeq ($(CONFIG_LTO),y) +# Re-include the core's build.mk file so we can remove the lto flag. +include core/$(CORE)/build.mk +endif + +# Required chip modules +chip-y+=clock.o gpio.o jtag.o system.o hwtimer.o uart.o flash.o +chip-$(CONFIG_I2C)+=i2c.o +chip-$(CONFIG_LPC)+=ipc.o +chip-$(CONFIG_WATCHDOG)+=watchdog.o + +# location of the scripts and keys used to pack the SPI flash image +SCRIPTDIR:=./chip/${CHIP}/util + +# Allow SPI size to be overridden by board specific size, default to 256KB. +CHIP_SPI_SIZE_KB?=256 + +$(out)/$(PROJECT).bin: diff --git a/chip/ish/clock.c b/chip/ish/clock.c new file mode 100644 index 0000000000..118061d997 --- /dev/null +++ b/chip/ish/clock.c @@ -0,0 +1,20 @@ +/* Copyright (c) 2016 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +/* Clocks and power management settings */ + +#include "clock.h" +#include "common.h" +#include "util.h" + +/* Console output macros */ +#define CPUTS(outstr) cputs(CC_CLOCK, outstr) +#define CPRINTS(format, args...) cprints(CC_CLOCK, format, ## args) + + +void clock_init(void) +{ + /* No initialization for ISH clock since D0ix is not enabled yet */ +} diff --git a/chip/ish/config_chip.h b/chip/ish/config_chip.h new file mode 100644 index 0000000000..ee31ccca05 --- /dev/null +++ b/chip/ish/config_chip.h @@ -0,0 +1,79 @@ +/* Copyright (c) 2016 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/minute-ia/config_core.h" + +/* Number of IRQ vectors on the ISH */ +#define CONFIG_IRQ_COUNT 15 + +/* Use a bigger console output buffer */ +#undef CONFIG_UART_TX_BUF_SIZE +#define CONFIG_UART_TX_BUF_SIZE 2048 + +/* Interval between HOOK_TICK notifications */ +#define HOOK_TICK_INTERVAL_MS 250 +#define HOOK_TICK_INTERVAL (HOOK_TICK_INTERVAL_MS * MSEC) + +/* Maximum number of deferrable functions */ +#define DEFERRABLE_MAX_COUNT 8 + + +/****************************************************************************/ +/* Memory mapping */ +/****************************************************************************/ + +/* Define our SRAM layout. */ +#define CONFIG_ISH_SRAM_BASE_START 0xFF000000 +#define CONFIG_ISH_SRAM_BASE_END 0xFF0A0000 +#define CONFIG_ISH_SRAM_SIZE (CONFIG_ISH_SRAM_BASE_END - \ + CONFIG_ISH_SRAM_BASE_START) + +/* Required for panic_output */ +#define CONFIG_RAM_SIZE CONFIG_ISH_SRAM_SIZE +#define CONFIG_RAM_BASE CONFIG_ISH_SRAM_BASE_START + +/* System stack size */ +#define CONFIG_STACK_SIZE 1024 + +/* non-standard task stack sizes */ +#define IDLE_TASK_STACK_SIZE 640 +#define LARGER_TASK_STACK_SIZE 1024 +#define HUGE_TASK_STACK_SIZE 2048 +/* Default task stack size */ +#define TASK_STACK_SIZE 640 + +/****************************************************************************/ +/* Define our flash layout. */ +/* Note: The 4 macros below are unnecesasry for the ISH chip. However they are + * referenced in common files and hence retained to avoid build errors. + */ + +/* Protect bank size 4K bytes */ +#define CONFIG_FLASH_BANK_SIZE 0x00001000 +/* Sector erase size 4K bytes */ +#define CONFIG_FLASH_ERASE_SIZE 0x00000000 +/* Minimum write size */ +#define CONFIG_FLASH_WRITE_SIZE 0x00000000 +/* Program memory base address */ +#define CONFIG_PROGRAM_MEMORY_BASE 0x00100000 + +#include "config_flash_layout.h" + +/****************************************************************************/ +/* Customize the build */ +/* Optional features present on this chip */ + +/* Note: ISH does not use the LPC bus but the protocol. */ +#define CONFIG_LPC + +/* GPIO - to be implemented */ +#define GPIO_PIN(index) (index) +#define GPIO_PIN_MASK(pin, mask) ((pin), (mask)) + +#endif /* __CROS_EC_CONFIG_CHIP_H */ diff --git a/chip/ish/config_flash_layout.h b/chip/ish/config_flash_layout.h new file mode 100644 index 0000000000..b470702972 --- /dev/null +++ b/chip/ish/config_flash_layout.h @@ -0,0 +1,59 @@ +/* Copyright 2015 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_FLASH_LAYOUT_H +#define __CROS_EC_CONFIG_FLASH_LAYOUT_H + +/* Mem-mapped, No external SPI for ISH */ +#undef CONFIG_EXTERNAL_STORAGE +#define CONFIG_MAPPED_STORAGE +#undef CONFIG_FLASH_PSTATE +#undef CONFIG_SPI_FLASH + +#define CONFIG_ISH_BOOT_START 0xFF000000 + +/*****************************************************************************/ +/* The following macros are not applicable for ISH, however the build fails if + * they are not defined. Ideally, there should be an option in EC build to + * turn off SPI and flash, making these unnecessary. + */ + +#define CONFIG_MAPPED_STORAGE_BASE 0x0 + +#define CONFIG_EC_PROTECTED_STORAGE_OFF (CONFIG_FLASH_SIZE - 0x20000) +#define CONFIG_EC_PROTECTED_STORAGE_SIZE 0x20000 +#define CONFIG_EC_WRITABLE_STORAGE_OFF (CONFIG_FLASH_SIZE - 0x40000) +#define CONFIG_EC_WRITABLE_STORAGE_SIZE 0x20000 + +/* Unused for ISH - loader is external to ISH FW */ +#define CONFIG_LOADER_MEM_OFF 0 +#define CONFIG_LOADER_SIZE 0xC00 + + +/* RO/RW images - not relevant for ISH + */ +#define CONFIG_RO_MEM_OFF (CONFIG_LOADER_MEM_OFF + \ + CONFIG_LOADER_SIZE) +#define CONFIG_RO_SIZE (97 * 1024) +#define CONFIG_RW_MEM_OFF CONFIG_RO_MEM_OFF +#define CONFIG_RW_SIZE CONFIG_RO_SIZE + +/*****************************************************************************/ + +/* Not relevant for ISH */ +#define CONFIG_BOOT_HEADER_STORAGE_OFF 0 +#define CONFIG_BOOT_HEADER_STORAGE_SIZE 0x240 + +#define CONFIG_LOADER_STORAGE_OFF (CONFIG_BOOT_HEADER_STORAGE_OFF + \ + CONFIG_BOOT_HEADER_STORAGE_SIZE) + +/* RO image immediately follows the loader image */ +#define CONFIG_RO_STORAGE_OFF (CONFIG_LOADER_STORAGE_OFF + \ + CONFIG_LOADER_SIZE) + +/* RW image starts at the beginning of SPI */ +#define CONFIG_RW_STORAGE_OFF 0 + +#endif /* __CROS_EC_CONFIG_FLASH_LAYOUT_H */ diff --git a/chip/ish/flash.c b/chip/ish/flash.c new file mode 100644 index 0000000000..8ef4d1a73c --- /dev/null +++ b/chip/ish/flash.c @@ -0,0 +1,18 @@ +/* Copyright 2015 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 "common.h" +#include "flash.h" + + +/** + * Initialize the module. + * + * Applies at-boot protection settings if necessary. + */ +int flash_pre_init(void) +{ + return EC_SUCCESS; +} diff --git a/chip/ish/gpio.c b/chip/ish/gpio.c new file mode 100644 index 0000000000..92304fc9b7 --- /dev/null +++ b/chip/ish/gpio.c @@ -0,0 +1,34 @@ +/* Copyright (c) 2016 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 ISH */ + +#include "common.h" +#include "gpio.h" +#include "hooks.h" +#include "registers.h" +#include "system.h" +#include "task.h" +#include "timer.h" +#include "util.h" + +test_mockable int gpio_get_level(enum gpio_signal signal) +{ + return 0; +} + +void gpio_set_level(enum gpio_signal signal, int value) +{ +} + +void gpio_pre_init(void) +{ +} + +static void gpio_init(void) +{ + /* TBD */ +} +DECLARE_HOOK(HOOK_INIT, gpio_init, HOOK_PRIO_DEFAULT); diff --git a/chip/ish/hpet.h b/chip/ish/hpet.h new file mode 100644 index 0000000000..c14df86ffb --- /dev/null +++ b/chip/ish/hpet.h @@ -0,0 +1,65 @@ +/* Copyright (c) 2016 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_HPET_H +#define __CROS_EC_HPET_H + +#include "common.h" + +/* ISH HPET config and timer registers */ +#define GENERAL_CAPS_ID_REG 0x00 +#define GENERAL_CONFIG_REG 0x10 +#define GENERAL_INT_STAT_REG 0x20 +#define MAIN_COUNTER_REG 0xF0 + +#define TIMER0_CONF_CAP_REG 0x100 +#define TIMER0_COMP_VAL_REG 0x108 +#define TIMER0_FSB_IR_REG 0x110 + +#define TIMER1_CONF_CAP_REG 0x120 +#define TIMER1_COMP_VAL_REG 0x128 +#define TIMER1_FSB_IR_REG 0x130 + +#define TIMER2_CONF_CAP_REG 0x140 +#define TIMER2_COMP_VAL_REG 0x148 +#define TIMER2_FSB_IR_REG 0x150 + +#define CONTROL_AND_STATUS_REG 0x160 + +#define HPET_ENABLE_CNF (1<<0) +#define HPET_LEGACY_RT_CNF (1<<1) +#define HPET_Tn_INT_TYPE_CNF (1<<1) +#define HPET_Tn_INT_ENB_CNF (1<<2) +#define HPET_Tn_TYPE_CNF (1<<3) +#define HPET_Tn_32MODE_CNF (1<<8) +#define HPET_Tn_INT_ROUTE_CNF_SHIFT 0x9 +#define HPET_Tn_INT_ROUTE_CNF_MASK (0x1f << 9) +#define HPET_GEN_CONF_STATUS_BIT 0x8 +#define HPET_T0_CONF_CAP_BIT 0x4 + +#define HPET_GENERAL_CONFIG REG32(ISH_HPET_BASE + GENERAL_CONFIG_REG) +#define HPET_MAIN_COUNTER REG32(ISH_HPET_BASE + MAIN_COUNTER_REG) +#define HPET_INTR_CLEAR REG32(ISH_HPET_BASE + GENERAL_INT_STAT_REG) +#define HPET_CTRL_STATUS REG32(ISH_HPET_BASE + CONTROL_AND_STATUS_REG) + +#define HPET_TIMER_CONF_CAP(x) \ + REG32(ISH_HPET_BASE + TIMER0_CONF_CAP_REG + (x * 0x20)) +#define HPET_TIMER_COMP(x) \ + REG32(ISH_HPET_BASE + TIMER0_COMP_VAL_REG + (x * 0x20)) + +#if defined CONFIG_ISH_20 +#define ISH_HPET_CLK_FREQ 1000000 /* 1 MHz clock */ + +#elif defined CONFIG_ISH_30 +#define ISH_HPET_CLK_FREQ 12000000 /* 12 MHz clock */ + +#elif defined CONFIG_ISH_40 +#define ISH_HPET_CLK_FREQ 32768 /* 32.768 KHz clock */ +#endif + +/* HPET timer 0 period of 10ms (100 ticks per second) */ +#define ISH_TICKS_PER_SEC 100 + +#endif /* __CROS_EC_HPET_H */ diff --git a/chip/ish/hwtimer.c b/chip/ish/hwtimer.c new file mode 100644 index 0000000000..13c07decd2 --- /dev/null +++ b/chip/ish/hwtimer.c @@ -0,0 +1,129 @@ +/* Copyright (c) 2016 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 - HPET */ + +#include "hpet.h" +#include "hwtimer.h" +#include "registers.h" +#include "task.h" + +#define CPUTS(outstr) cputs(CC_CLOCK, outstr) +#define CPRINTS(format, args...) cprints(CC_CLOCK, format, ## args) +#define CPRINTF(format, args...) cprintf(CC_CLOCK, format, ## args) + +void __hw_clock_event_set(uint32_t deadline) +{ + HPET_TIMER_COMP(1) = HPET_MAIN_COUNTER + deadline; + HPET_TIMER_CONF_CAP(1) |= HPET_Tn_INT_ENB_CNF; +} + +uint32_t __hw_clock_event_get(void) +{ + return 0; +} + +void __hw_clock_event_clear(void) +{ + HPET_TIMER_CONF_CAP(1) &= ~HPET_Tn_INT_ENB_CNF; +} + +uint32_t __hw_clock_source_read(void) +{ + return HPET_MAIN_COUNTER; +} + +void __hw_clock_source_set(uint32_t ts) +{ + HPET_GENERAL_CONFIG &= ~HPET_ENABLE_CNF; + HPET_MAIN_COUNTER = 0x00; + + while (HPET_CTRL_STATUS & HPET_GEN_CONF_STATUS_BIT) + ; + + HPET_GENERAL_CONFIG |= HPET_ENABLE_CNF; +} + +static void __hw_clock_source_irq(int timer_id) +{ + /* Clear interrupt */ + HPET_INTR_CLEAR = (1 << timer_id); + + /* If IRQ is from timer 0, 32-bit timer overflowed */ + process_timers(timer_id == 0); +} + +void __hw_clock_source_irq_0(void) +{ + __hw_clock_source_irq(0); +} +DECLARE_IRQ(ISH_HPET_TIMER0_IRQ, __hw_clock_source_irq_0); + +void __hw_clock_source_irq_1(void) +{ + __hw_clock_source_irq(1); +} +DECLARE_IRQ(ISH_HPET_TIMER1_IRQ, __hw_clock_source_irq_1); + +int __hw_clock_source_init(uint32_t start_t) +{ + + /* + * The timer can only fire interrupt when its value reaches zero. + * Therefore we need two timers: + * - Timer 0 as free running timer + * - Timer 1 as event timer + */ + + /* Disable HPET */ + HPET_GENERAL_CONFIG &= ~HPET_ENABLE_CNF; + HPET_MAIN_COUNTER = 0x00; + + /* Set comparator value */ + HPET_TIMER_COMP(0) = ISH_HPET_CLK_FREQ / ISH_TICKS_PER_SEC; + + /* Wait for timer to settle */ + while (HPET_CTRL_STATUS & HPET_GEN_CONF_STATUS_BIT) + ; + + /* Timer 0 - enable periodic mode */ + HPET_TIMER_CONF_CAP(0) |= HPET_Tn_TYPE_CNF; + HPET_TIMER_CONF_CAP(0) |= HPET_Tn_32MODE_CNF; + + while (HPET_CTRL_STATUS & HPET_T0_CONF_CAP_BIT) + ; + + /* Set IRQ routing */ +#if ISH_HPET_TIMER0_IRQ < 32 + HPET_TIMER_CONF_CAP(0) &= ~HPET_Tn_INT_ROUTE_CNF_MASK; + HPET_TIMER_CONF_CAP(0) |= (ISH_HPET_TIMER0_IRQ << + HPET_Tn_INT_ROUTE_CNF_SHIFT); +#else + HPET_TIMER_CONF_CAP(0) &= ~HPET_Tn_INT_ROUTE_CNF_MASK; +#endif + + while (HPET_CTRL_STATUS & HPET_T0_CONF_CAP_BIT) + ; + + /* Level interrupt */ + HPET_TIMER_CONF_CAP(0) |= HPET_Tn_INT_TYPE_CNF; + HPET_TIMER_CONF_CAP(1) |= HPET_Tn_INT_TYPE_CNF; + + /* Unask HPET IRQ in IOAPIC */ + task_enable_irq(ISH_HPET_TIMER0_IRQ); + task_enable_irq(ISH_HPET_TIMER1_IRQ); + + /* Enable interrupt */ + HPET_TIMER_CONF_CAP(0) |= HPET_Tn_INT_ENB_CNF; + HPET_TIMER_CONF_CAP(1) |= HPET_Tn_INT_ENB_CNF; + + while (HPET_CTRL_STATUS & HPET_T0_CONF_CAP_BIT) + ; + + /* Enable HPET main counter */ + HPET_GENERAL_CONFIG |= HPET_ENABLE_CNF; + + return ISH_HPET_TIMER1_IRQ; /* One shot */ +} diff --git a/chip/ish/i2c.c b/chip/ish/i2c.c new file mode 100644 index 0000000000..22bf3d81f1 --- /dev/null +++ b/chip/ish/i2c.c @@ -0,0 +1,422 @@ +/* Copyright (c) 2016 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 ISH */ + +#include "common.h" +#include "console.h" +#include "gpio.h" +#include "hooks.h" +#include "i2c.h" +#include "registers.h" +#include "ish_i2c.h" +#include "task.h" +#include "timer.h" +#include "hwtimer.h" +#include "util.h" + +#define CPUTS(outstr) cputs(CC_I2C, outstr) +#define CPRINTS(format, args...) cprints(CC_I2C, format, ## args) +#define CPRINTF(format, args...) cprintf(CC_I2C, format, ## args) + +#define I2C_FLAG_REPEATED_START_DISABLED 0 +#define EVENT_FLAG_I2C_TIMEOUT TASK_EVENT_CUSTOM(1 << 1) + +static uint16_t default_hcnt_scl_100[] = { + 4000, 4420, 4920, 4400, 4000, 4000, 4300 +}; + +static uint16_t default_lcnt_scl_100[] = { + 4720, 5180, 4990, 5333, 4700, 5200, 4950 +}; + +static uint16_t default_hcnt_scl_400[] = { + 600, 820, 1120, 1066, 600, 600, 450 +}; + +static uint16_t default_lcnt_scl_400[] = { + 1320, 1380, 1300, 1300, 1300, 1200, 1250 +}; + +static uint16_t default_hcnt_scl_hs[] = { 160, 300, 160, 166, 175, 150, 162 }; +static uint16_t default_lcnt_scl_hs[] = { 320, 340, 320, 325, 325, 300, 297 }; + +static uint8_t speed_val_arr[] = { + STD_SPEED_VAL, FAST_SPEED_VAL, HIGH_SPEED_VAL }; + +static uint8_t bus_freq[ISH_I2C_PORT_COUNT] = { + I2C_FREQ_120, I2C_FREQ_120, I2C_FREQ_120 +}; + +static struct i2c_context i2c_ctxs[ISH_I2C_PORT_COUNT] = { + { + .bus = 0, + .base = (uint32_t *) ISH_I2C0_BASE, + .speed = I2C_SPEED_FAST, + }, + { + .bus = 1, + .base = (uint32_t *) ISH_I2C1_BASE, + .speed = I2C_SPEED_FAST, + }, + { + .bus = 2, + .base = (uint32_t *) ISH_I2C2_BASE, + .speed = I2C_SPEED_FAST, + }, +}; + +static struct i2c_bus_info board_config[ISH_I2C_PORT_COUNT] = { + { + .bus_id = 0, + .std_speed.sda_hold = DEFAULT_SDA_HOLD, + .fast_speed.sda_hold = DEFAULT_SDA_HOLD, + .high_speed.sda_hold = DEFAULT_SDA_HOLD, + }, + { + .bus_id = 1, + .std_speed.sda_hold = DEFAULT_SDA_HOLD, + .fast_speed.sda_hold = DEFAULT_SDA_HOLD, + .high_speed.sda_hold = DEFAULT_SDA_HOLD, + }, + { + .bus_id = 2, + .std_speed.sda_hold = DEFAULT_SDA_HOLD, + .fast_speed.sda_hold = DEFAULT_SDA_HOLD, + .high_speed.sda_hold = DEFAULT_SDA_HOLD, + }, +}; + +static inline void i2c_mmio_write(uint32_t *base, uint8_t offset, + uint32_t data) +{ + REG32((uint32_t) ((uint8_t *)base + offset)) = data; +} + +static inline uint32_t i2c_mmio_read(uint32_t *base, uint8_t offset) +{ + return REG32((uint32_t) ((uint8_t *)base + offset)); +} + +static inline uint8_t i2c_read_byte(uint32_t *addr, uint8_t reg, + uint8_t offset) +{ + uint32_t ret = i2c_mmio_read(addr, reg) >> offset; + + return ret & 0xff; +} + +static void i2c_intr_switch(uint32_t *base, int mode) +{ + switch (mode) { + + case ENABLE_WRITE_INT: + i2c_mmio_write(base, IC_INTR_MASK, IC_INTR_WRITE_MASK_VAL); + break; + + case ENABLE_READ_INT: + i2c_mmio_write(base, IC_INTR_MASK, IC_INTR_READ_MASK_VAL); + break; + + case DISABLE_INT: + i2c_mmio_write(base, IC_INTR_MASK, 0); + /* clear interrupts: TX_ABORT + * Because the DW_apb_i2c's TX FIFO is forced into a + * flushed/reset state whenever a TX_ABRT event occurs, it + * is necessary for software to release the DW_apb_i2c from + * this state by reading the IC_CLR_TX_ABRT register before + * attempting to write into the TX FIFO + */ + i2c_mmio_read(base, IC_CLR_TX_ABRT); + /* STOP_DET */ + i2c_mmio_read(base, IC_CLR_STOP_DET); + break; + + default: + break; + } +} + +static void i2c_init_transaction(struct i2c_context *ctx, + uint8_t slave_addr, uint8_t flags) +{ + uint32_t con_value; + uint32_t *base = ctx->base; + struct i2c_bus_info *bus_info = &board_config[ctx->bus]; + uint32_t clk_in_val = clk_in[bus_freq[ctx->bus]]; + + /* disable interrupts */ + i2c_intr_switch(base, DISABLE_INT); + + i2c_mmio_write(base, IC_ENABLE, IC_ENABLE_DISABLE); + i2c_mmio_write(base, IC_TAR, (slave_addr << IC_TAR_OFFSET) | + TAR_SPECIAL_VAL | IC_10BITADDR_MASTER_VAL); + + /* set Clock SCL Count */ + switch (ctx->speed) { + + case I2C_SPEED_STD: + i2c_mmio_write(base, IC_SS_SCL_HCNT, + NS_2_COUNTERS(bus_info->std_speed.hcnt, + clk_in_val)); + i2c_mmio_write(base, IC_SS_SCL_LCNT, + NS_2_COUNTERS(bus_info->std_speed.lcnt, + clk_in_val)); + i2c_mmio_write(base, IC_SDA_HOLD, + NS_2_COUNTERS(bus_info->std_speed.sda_hold, + clk_in_val)); + break; + + case I2C_SPEED_FAST: + i2c_mmio_write(base, IC_FS_SCL_HCNT, + NS_2_COUNTERS(bus_info->fast_speed.hcnt, + clk_in_val)); + i2c_mmio_write(base, IC_FS_SCL_LCNT, + NS_2_COUNTERS(bus_info->fast_speed.lcnt, + clk_in_val)); + i2c_mmio_write(base, IC_SDA_HOLD, + NS_2_COUNTERS(bus_info->fast_speed.sda_hold, + clk_in_val)); + break; + + case I2C_SPEED_HIGH: + i2c_mmio_write(base, IC_HS_SCL_HCNT, + NS_2_COUNTERS(bus_info->high_speed.hcnt, + clk_in_val)); + i2c_mmio_write(base, IC_HS_SCL_LCNT, + NS_2_COUNTERS(bus_info->high_speed.lcnt, + clk_in_val)); + i2c_mmio_write(base, IC_SDA_HOLD, + NS_2_COUNTERS(bus_info->high_speed.sda_hold, + clk_in_val)); + + i2c_mmio_write(base, IC_FS_SCL_HCNT, + NS_2_COUNTERS(bus_info->fast_speed.hcnt, + clk_in_val)); + i2c_mmio_write(base, IC_FS_SCL_LCNT, + NS_2_COUNTERS(bus_info->fast_speed.lcnt, + clk_in_val)); + break; + + default: + break; + } + + /* in SPT HW we need to sync between I2C clock and data signals */ + con_value = i2c_mmio_read(base, IC_CON); + + if (flags & I2C_FLAG_REPEATED_START_DISABLED) + con_value &= ~IC_RESTART_EN_VAL; + else + con_value |= IC_RESTART_EN_VAL; + + i2c_mmio_write(base, IC_CON, con_value); + i2c_mmio_write(base, IC_FS_SPKLEN, spkln[bus_freq[ctx->bus]]); + i2c_mmio_write(base, IC_HS_SPKLEN, spkln[bus_freq[ctx->bus]]); + i2c_mmio_write(base, IC_ENABLE, IC_ENABLE_ENABLE); +} + +static void i2c_write_buffer(uint32_t *base, uint8_t len, + const uint8_t *buffer, ssize_t *cur_index, + ssize_t total_len) +{ + int i; + uint16_t out; + + for (i = 0; i < len; i++) { + + ++(*cur_index); + out = (buffer[i] << DATA_CMD_DAT_OFFSET) | DATA_CMD_WRITE_VAL; + + if (*cur_index == total_len) + out |= DATA_CMD_STOP_VAL; + + i2c_mmio_write(base, IC_DATA_CMD, out); + } +} + +static void i2c_write_read_commands(uint32_t *base, uint8_t len) +{ + int i; + + for (i = 0; i < len - 1; i++) + i2c_mmio_write(base, IC_DATA_CMD, DATA_CMD_READ_VAL); + + i2c_mmio_write(base, IC_DATA_CMD, + DATA_CMD_READ_VAL | DATA_CMD_STOP_VAL); +} + +int chip_i2c_xfer(int port, int slave_addr, const uint8_t *out, int out_size, + uint8_t *in, int in_size, int flags) +{ + int i, is_read = 0; + ssize_t total_len; + uint64_t expire_ts; + struct i2c_context *ctx; + ssize_t curr_index = 0; + + if (out_size == 0 && in_size == 0) + return EC_SUCCESS; + + if (in_size > 0) + is_read = 1; + + /* function interface specifies an 8-bit slave addr: convert it to + * a 7-bit addr to meet the expectations of the driver code. + */ + slave_addr >>= 1; + + ctx = &i2c_ctxs[port]; + ctx->error_flag = 0; + + total_len = 1 + (is_read ? 1 : in_size); + + i2c_init_transaction(ctx, slave_addr, flags); + + /* Write device id */ + i2c_write_buffer(ctx->base, 1, out, &curr_index, total_len); + + /* Write W data */ + i2c_write_buffer(ctx->base, (is_read ? 0 : out_size - 1), + (is_read ? NULL : out + 1), + &curr_index, total_len); + + if (is_read) { + /* Write R commands */ + i2c_write_read_commands(ctx->base, in_size); + + /* Set rx_theshold */ + i2c_mmio_write(ctx->base, IC_RX_TL, in_size - 1); + } + + /* Enable interrupts */ + i2c_intr_switch(ctx->base, + is_read ? ENABLE_READ_INT : ENABLE_WRITE_INT); + + /* Wait for interrupt */ + ctx->wait_task_id = task_get_current(); + task_wait_event_mask(EVENT_FLAG_I2C_TIMEOUT, -1); + + if ((ctx->interrupts & M_TX_ABRT) == 0) { + if (is_read) { + /* read data */ + for (i = 0; i < in_size; i++) + in[i] = i2c_read_byte(ctx->base, + IC_DATA_CMD, 0); + } + + } else { + ctx->error_flag = 1; + } + + ctx->reason = 0; + ctx->interrupts = 0; + + /* do not disable device before master is idle */ + expire_ts = __hw_clock_source_read() + I2C_TSC_TIMEOUT; + + while (i2c_mmio_read(ctx->base, IC_STATUS) & + (1 << IC_STATUS_MASTER_ACTIVITY)) { + + if (__hw_clock_source_read() >= expire_ts) { + ctx->error_flag = 1; + break; + } + } + + i2c_mmio_write(ctx->base, IC_ENABLE, IC_ENABLE_DISABLE); + +#ifdef ISH_DEBUG + if (req.operation == I2C_READ) { + CPRINTF("I2C read len: %d [", req.r_len); + for (i = 0; i < req.r_len; i++) + CPRINTF("0x%0x ", req.r_data[i]); + CPUTS("]\n"); + } +#endif + + return EC_SUCCESS; +} + +static void i2c_interrupt_handler(struct i2c_context *ctx) +{ + /* check interrupts */ + ctx->interrupts = i2c_mmio_read(ctx->base, IC_INTR_STAT); + ctx->reason = (uint16_t) i2c_mmio_read(ctx->base, IC_TX_ABRT_SOURCE); + + /* disable interrupts */ + i2c_intr_switch(ctx->base, DISABLE_INT); + task_set_event(ctx->wait_task_id, EVENT_FLAG_I2C_TIMEOUT, 0); +} + +static void i2c_isr_bus0(void) +{ + i2c_interrupt_handler(&i2c_ctxs[0]); +} +DECLARE_IRQ(ISH_I2C0_IRQ, i2c_isr_bus0); + +static void i2c_isr_bus1(void) +{ + i2c_interrupt_handler(&i2c_ctxs[1]); +} +DECLARE_IRQ(ISH_I2C1_IRQ, i2c_isr_bus1); + +static void i2c_isr_bus2(void) +{ + i2c_interrupt_handler(&i2c_ctxs[2]); +} +DECLARE_IRQ(ISH_I2C2_IRQ, i2c_isr_bus2); + +static void i2c_init_hardware(struct i2c_context *ctx) +{ + uint32_t *base = ctx->base; + + /* disable interrupts */ + i2c_intr_switch(base, DISABLE_INT); + i2c_mmio_write(base, IC_ENABLE, IC_ENABLE_DISABLE); + i2c_mmio_write(base, IC_CON, (MASTER_MODE_VAL + | speed_val_arr[ctx->speed] + | IC_RESTART_EN_VAL + | IC_SLAVE_DISABLE_VAL)); + + i2c_mmio_write(base, IC_FS_SPKLEN, spkln[bus_freq[ctx->bus]]); + i2c_mmio_write(base, IC_HS_SPKLEN, spkln[bus_freq[ctx->bus]]); + + /* get RX_FIFO and TX_FIFO depth */ + ctx->max_rx_depth = i2c_read_byte(base, IC_COMP_PARAM_1, + RX_BUFFER_DEPTH_OFFSET) + 1; + ctx->max_tx_depth = i2c_read_byte(base, IC_COMP_PARAM_1, + TX_BUFFER_DEPTH_OFFSET) + 1; +} + +static void i2c_initial_board_config(struct i2c_context *ctx) +{ + uint8_t freq = bus_freq[ctx->bus]; + struct i2c_bus_info *bus_info = &board_config[ctx->bus]; + + bus_info->std_speed.hcnt = default_hcnt_scl_100[freq]; + bus_info->std_speed.lcnt = default_lcnt_scl_100[freq]; + bus_info->fast_speed.hcnt = default_hcnt_scl_400[freq]; + bus_info->fast_speed.lcnt = default_lcnt_scl_400[freq]; + bus_info->high_speed.hcnt = default_hcnt_scl_hs[freq]; + bus_info->high_speed.lcnt = default_lcnt_scl_hs[freq]; +} + +static void i2c_init(void) +{ + int i; + + for (i = 0; i < ISH_I2C_PORT_COUNT; i++) { + i2c_initial_board_config(&i2c_ctxs[i]); + i2c_init_hardware(&i2c_ctxs[i]); + } + + task_enable_irq(ISH_I2C0_IRQ); + task_enable_irq(ISH_I2C1_IRQ); + task_enable_irq(ISH_I2C2_IRQ); + + CPRINTS("Done i2c_init"); +} +DECLARE_HOOK(HOOK_INIT, i2c_init, HOOK_PRIO_INIT_I2C); diff --git a/chip/ish/ipc.c b/chip/ish/ipc.c new file mode 100644 index 0000000000..958a26d440 --- /dev/null +++ b/chip/ish/ipc.c @@ -0,0 +1,398 @@ +/* Copyright (c) 2016 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. + */ + +/* IPC module for ISH */ + +/** + * IPC - Inter Processor Communication + * ----------------------------------- + * + * IPC is a bi-directional doorbell based message passing interface sans + * session and transport layers, between hardware blocks. ISH uses IPC to + * communicate with the Host, PMC (Power Management Controller), CSME + * (Converged Security and Manageability Engine), Audio, Graphics and ISP. + * + * Both the initiator and target ends each have a 32-bit doorbell register and + * 128-byte message regions. In addition, the following register pairs help in + * synchronizing IPC. + * + * - Peripheral Interrupt Status Register (PISR) + * - Peripheral Interrupt Mask Register (PIMR) + * - Doorbell Clear Status Register (DB CSR) + */ + +#include "registers.h" +#include "console.h" +#include "hooks.h" +#include "host_command.h" +#include "lpc.h" +#include "task.h" +#include "timer.h" +#include "util.h" +#include "ipc.h" + +#define CPUTS(outstr) cputs(CC_LPC, outstr) +#define CPRINTS(format, args...) cprints(CC_LPC, format, ## args) +#define CPRINTF(format, args...) cprintf(CC_LPC, format, ## args) + +static struct host_packet ipc_packet; /* For host command processing */ +static struct host_cmd_handler_args host_cmd_args; +static uint8_t host_cmd_flags; /* Flags from host command */ +static uint8_t params_copy[EC_LPC_HOST_PACKET_SIZE] __aligned(4); +static uint8_t mem_mapped[0x200] __attribute__ ((section(".bss.big_align"))); +static struct ec_lpc_host_args *const ipc_host_args = + (struct ec_lpc_host_args *)mem_mapped; + +/* Array of peer contexts */ +struct ipc_if_ctx ipc_peer_ctxs[IPC_PEERS_COUNT] = { + [IPC_PEER_HOST_ID] = { + .in_msg_reg = IPC_HOST2ISH_MSG_REGS, + .out_msg_reg = IPC_ISH2HOST_MSG_REGS, + .in_drbl_reg = IPC_HOST2ISH_DOORBELL, + .out_drbl_reg = IPC_ISH2HOST_DOORBELL, + .clr_bit = IPC_INT_ISH2HOST_CLR_BIT, + .irq_in = ISH_IPC_HOST2ISH_IRQ, + .irq_clr = ISH_IPC_ISH2HOST_CLR_IRQ, + }, + /* Other peers (PMC, CSME, etc) to be added when required */ +}; + +/* Peripheral Interrupt Mask Register bits */ +static uint8_t pimr_bit_array[IPC_PEERS_COUNT][3] = { + { + IPC_PIMR_HOST2ISH_OFFS, + IPC_PIMR_HOST2ISH_OFFS, + IPC_PIMR_ISH2HOST_CLR_OFFS + }, +}; + +/* Get protocol information */ +static int ipc_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 << 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, ipc_get_protocol_info, + EC_VER_MASK(0)); + +/* Set/un-set PIMR bits */ +static void ipc_set_pimr(uint8_t peer_id, int set, + enum pimr_signal_type signal_type) +{ + uint32_t new_pimr_val; + + new_pimr_val = (1 << (pimr_bit_array[peer_id][signal_type])); + + interrupt_disable(); + + if (set) + REG32(IPC_PIMR) |= new_pimr_val; + else + REG32(IPC_PIMR) &= ~new_pimr_val; + + interrupt_enable(); +} + +/** + * ipc_read: Host -> ISH communication + * + * 1. Host SW checks HOST2ISH doorbell bit[31] to ensure it is cleared. + * 2. Host SW writes data to HOST2ISH message registers (upto 128 bytes). + * 3. Host SW writes to HOST2ISH doorbell register, setting bit [31]. + * 4. ISH FW recieves interrupt, checks PISR[0] to realize the event. + * 5. After reading data, ISH FW clears HOST2ISH DB bit[31]. + * 6. Host SW recieves interrupt, reads Host PISR bit[8] to realize + * the message was consumed by ISH FW. + */ +static int ipc_read(uint8_t peer_id, void *out_buff, uint32_t buff_size) +{ +#ifdef ISH_DEBUG + int i; +#endif + struct ipc_if_ctx *ctx; + int retval = EC_SUCCESS; + + ctx = &ipc_peer_ctxs[peer_id]; + + if (buff_size > IPC_MSG_MAX_SIZE) + retval = IPC_FAILURE; + + if (retval >= 0) { + /* Copy message to out buffer. */ + memcpy(out_buff, (const uint32_t *)ctx->in_msg_reg, buff_size); + retval = buff_size; + +#ifdef ISH_DEBUG + CPRINTF("ipc_read, len=0x%0x [", buff_size); + for (i = 0; i < buff_size; i++) + CPRINTF("0x%0x ", (uint8_t) ((char *)out_buff)[i]); + CPUTS("]\n"); +#endif + } + + REG32(ctx->in_drbl_reg) = 0; + ipc_set_pimr(peer_id, SET_PIMR, PIMR_SIGNAL_IN); + + return retval; +} + +static int ipc_wait_until_msg_consumed(struct ipc_if_ctx *ctx, int timeout) +{ + int wait_sts = 0; + uint32_t drbl; + + drbl = REG32(ctx->out_drbl_reg); + if (!(drbl & IPC_DRBL_BUSY_BIT)) { + /* doorbell is already cleared. we can continue */ + return 0; + } + + while (1) { + wait_sts = task_wait_event_mask(EVENT_FLAG_BIT_WRITE_IPC, + timeout); + drbl = REG32(ctx->out_drbl_reg); + + if (!(drbl & IPC_DRBL_BUSY_BIT)) { + return 0; + } else if (wait_sts != 0) { + /* timeout */ + return wait_sts; + } + } +} + +/** + * ipc_write: ISH -> Host Communication + * + * 1. ISH FW ensures ISH2HOST doorbell busy bit [31] is cleared. + * 2. ISH FW writes data (upto 128 bytes) to ISH2HOST message registers. + * 3. ISH FW writes to ISH2HOST doorbell, busy bit (31) is set. + * 4. Host SW receives interrupt, reads host PISR[0] to realize event. + * 5. Upon reading data, Host driver clears ISH2HOST doorbell busy bit. This + * de-asserts the interrupt. + * 6. ISH FW also receieves an interrupt for the clear event. + */ +static int ipc_write(uint8_t peer_id, void *buff, uint32_t buff_size) +{ + struct ipc_if_ctx *ctx; + uint32_t drbl_val = 0; +#ifdef ISH_DEBUG + int i; +#endif + + ctx = &ipc_peer_ctxs[peer_id]; + + if (ipc_wait_until_msg_consumed(ctx, IPC_TIMEOUT)) { + /* timeout */ + return IPC_FAILURE; + } +#ifdef ISH_DEBUG + CPRINTF("ipc_write, len=0x%0x [", buff_size); + for (i = 0; i < buff_size; i++) + CPRINTF("0x%0x ", (uint8_t) ((char *)buff)[i]); + CPUTS("]\n"); +#endif + + /* write message */ + if (buff_size <= IPC_MSG_MAX_SIZE) { + /* write to message register */ + memcpy((uint32_t *) ctx->out_msg_reg, buff, buff_size); + drbl_val = IPC_BUILD_HEADER(buff_size, IPC_PROTOCOL_ECP, + SET_BUSY); + } else { + return IPC_FAILURE; + } + + /* write doorbell */ + REG32(ctx->out_drbl_reg) = drbl_val; + + return EC_SUCCESS; +} + +uint8_t *lpc_get_memmap_range(void) +{ + return mem_mapped + 0x100; +} + +static uint8_t *ipc_get_hostcmd_data_range(void) +{ + return mem_mapped; +} + +static void ipc_send_response_packet(struct host_packet *pkt) +{ + ipc_write(IPC_PEER_HOST_ID, pkt->response, pkt->response_size); +} + +void lpc_set_host_event_state(uint32_t mask) +{ +} + +void lpc_set_host_event_mask(enum lpc_host_event_type type, uint32_t mask) +{ +} + +uint32_t lpc_get_host_event_mask(enum lpc_host_event_type type) +{ + return 0; +} + +void lpc_clear_acpi_status_mask(uint8_t mask) +{ +} + +/** + * IPC interrupts are recieved by the FW when a) Host SW rings doorbell and + * b) when Host SW clears doorbell busy bit [31]. + * + * Doorbell Register (DB) bits + * ----+-------+--------+-----------+--------+------------+-------------------- + * 31 | 30 29 | 28-20 |19 18 17 16| 15 14 | 13 12 11 10| 9 8 7 6 5 4 3 2 1 0 + * ----+-------+--------+-----------+--------+------------+-------------------- + * Busy|Options|Reserved| Command |Reserved| Protocol | Message Length + * ----+-------+--------+-----------+--------+------------+-------------------- + * + * ISH Peripheral Interrupt Status Register: + * Bit 0 - If set, indicates interrupt was caused by setting Host2ISH DB + * + * ISH Peripheral Interrupt Mask Register + * Bit 0 - If set, mask interrupt caused by Host2ISH DB + * + * ISH Peripheral DB Clear Status Register + * Bit 0 - If set, indicates interrupt was caused by clearing Host2ISH DB + */ +static void ipc_interrupt_handler(void) +{ + uint32_t pisr = REG32(IPC_PISR); + uint32_t pimr = REG32(IPC_PIMR); + uint32_t busy_clear = REG32(IPC_BUSY_CLEAR); + uint32_t drbl = REG32(IPC_ISH2HOST_MSG_REGS); + uint8_t proto, cmd; + + if ((pisr & IPC_PISR_HOST2ISH_BIT) + && (pimr & IPC_PIMR_HOST2ISH_BIT)) { + + /* New message arrived */ + ipc_set_pimr(IPC_PEER_HOST_ID, UNSET_PIMR, PIMR_SIGNAL_IN); + task_set_event(TASK_ID_IPC_COMM, EVENT_FLAG_BIT_READ_IPC, 0); + proto = IPC_HEADER_GET_PROTOCOL(drbl); + cmd = IPC_HEADER_GET_MNG_CMD(drbl); + + if ((proto == IPC_PROTOCOL_MNG) && (cmd == MNG_TIME_UPDATE)) + /* Ignoring time update from host */ + ; + } + + if ((busy_clear & IPC_INT_ISH2HOST_CLR_BIT) + && (pimr & IPC_PIMR_ISH2HOST_CLR_MASK_BIT)) { + /* Written message cleared */ + REG32(IPC_BUSY_CLEAR) = IPC_ISH_FWSTS; + task_set_event(TASK_ID_IPC_COMM, EVENT_FLAG_BIT_WRITE_IPC, 0); + } +} +DECLARE_IRQ(ISH_IPC_HOST2ISH_IRQ, ipc_interrupt_handler); + +/* Task that listens for incomming IPC messages from Host and initiate host + * command processing. + */ +void ipc_comm_task(void) +{ + int ret = 0; + uint32_t out_drbl, pkt_len; + + for (;;) { + + ret = task_wait_event_mask(EVENT_FLAG_BIT_READ_IPC + | EVENT_FLAG_BIT_WRITE_IPC, -1); + + if ((ret & EVENT_FLAG_BIT_WRITE_IPC)) + continue; + else if (!(ret & EVENT_FLAG_BIT_READ_IPC)) + continue; + + /* Read the command byte. This clears the FRMH bit in + * the status byte. + */ + out_drbl = REG32(IPC_HOST2ISH_DOORBELL); + pkt_len = out_drbl & IPC_HEADER_LENGTH_MASK; + + ret = ipc_read(IPC_PEER_HOST_ID, ipc_host_args, pkt_len); + host_cmd_args.command = EC_COMMAND_PROTOCOL_3; + + host_cmd_args.result = EC_RES_SUCCESS; + host_cmd_flags = ipc_host_args->flags; + + /* We only support new style command (v3) now */ + if (host_cmd_args.command == EC_COMMAND_PROTOCOL_3) { + ipc_packet.send_response = ipc_send_response_packet; + + ipc_packet.request = + (const void *)ipc_get_hostcmd_data_range(); + ipc_packet.request_temp = params_copy; + ipc_packet.request_max = sizeof(params_copy); + /* Don't know the request size so pass in + * the entire buffer + */ + ipc_packet.request_size = EC_LPC_HOST_PACKET_SIZE; + + ipc_packet.response = + (void *)ipc_get_hostcmd_data_range(); + ipc_packet.response_max = EC_LPC_HOST_PACKET_SIZE; + ipc_packet.response_size = 0; + + ipc_packet.driver_result = EC_RES_SUCCESS; + host_packet_receive(&ipc_packet); + usleep(10); /* To force yield */ + + continue; + } else { + /* Old style command unsupported */ + host_cmd_args.result = EC_RES_INVALID_COMMAND; + } + + /* Hand off to host command handler */ + host_command_received(&host_cmd_args); + } +} + +static void setup_ipc(void) +{ + + task_enable_irq(ISH_IPC_HOST2ISH_IRQ); + task_enable_irq(ISH_IPC_ISH2HOST_CLR_IRQ); + + ipc_set_pimr(IPC_PEER_HOST_ID, SET_PIMR, PIMR_SIGNAL_IN); + ipc_set_pimr(IPC_PEER_HOST_ID, SET_PIMR, PIMR_SIGNAL_CLR); +} +DECLARE_HOOK(HOOK_CHIPSET_STARTUP, setup_ipc, HOOK_PRIO_FIRST); + +static void ipc_init(void) +{ + CPRINTS("ipc_init"); + + /* Initialize host args and memory map to all zero */ + memset(ipc_host_args, 0, sizeof(*ipc_host_args)); + memset(lpc_get_memmap_range(), 0, EC_MEMMAP_SIZE); + + setup_ipc(); +} +DECLARE_HOOK(HOOK_INIT, ipc_init, HOOK_PRIO_INIT_LPC); + +/* On boards without a host, this command is used to set up IPC */ +static int ipc_command_init(int argc, char **argv) +{ + ipc_init(); + return EC_SUCCESS; +} +DECLARE_CONSOLE_COMMAND(ipcinit, ipc_command_init, NULL, NULL); diff --git a/chip/ish/ipc.h b/chip/ish/ipc.h new file mode 100644 index 0000000000..72968e94c4 --- /dev/null +++ b/chip/ish/ipc.h @@ -0,0 +1,96 @@ +/* Copyright (c) 2016 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. + */ + +/* IPC module for ISH */ + +#ifndef __CROS_EC_IPC_H +#define __CROS_ECIPC_H + +#define IPC_FAILURE -1 +#define IPC_TIMEOUT -1 +#define UNSET_PIMR 0 +#define SET_PIMR 1 +#define SET_BUSY 1 + +#define IPC_PROTOCOL_MNG 3 /* Management protocol */ +#define IPC_PROTOCOL_ECP 4 /* EC protocol */ +#define MNG_TIME_UPDATE 5 + +#define EVENT_FLAG_BIT_READ_IPC (1<<0) +#define EVENT_FLAG_BIT_WRITE_IPC (1<<2) + +#define IPC_PIMR_HOST2ISH_OFFS (0) +#define IPC_PIMR_HOST2ISH_OFFS (0) +#define IPC_PIMR_ISH2HOST_CLR_OFFS (11) +#define IPC_INT_ISH2HOST_CLR_OFFS (0) +#define IPC_PISR_HOST2ISH_OFFS IPC_PIMR_HOST2ISH_OFFS +#define IPC_MSG_MAX_SIZE 0x80 +#define IPC_DRBL_BUSY_OFFS (31) +#define IPC_HEADER_PROTOCOL_OFFSET 10 +#define IPC_HEADER_PROTOCOL_MASK (0x0F) +#define IPC_HEADER_MNG_CMD_MASK (0x0F) +#define IPC_HEADER_LENGTH_MASK (0x03FF) +#define IPC_HEADER_MNG_CMD_OFFSET 16 +#define IPC_HEADER_LENGTH_OFFSET 0 +#define IPC_OOB_MSG_OFFS (30) + +#define IPC_PIMR_HOST2ISH_BIT (1 << IPC_PIMR_HOST2ISH_OFFS) +#define IPC_PIMR_ISH2HOST_CLR_MASK_BIT (1 << IPC_PIMR_ISH2HOST_CLR_OFFS) +#define IPC_PIMR_HOST2ISH_BIT (1 << IPC_PIMR_HOST2ISH_OFFS) +#define IPC_INT_ISH2HOST_CLR_BIT (1 << IPC_INT_ISH2HOST_CLR_OFFS) +#define IPC_PISR_HOST2ISH_BIT (1 << IPC_PISR_HOST2ISH_OFFS) +#define IPC_OOB_MSG_BIT (1 << IPC_OOB_MSG_OFFS) +#define IPC_DRBL_BUSY_BIT (1 << IPC_DRBL_BUSY_OFFS) + +#define IPC_IS_BUSY(drbl_reg) \ + ((drbl_reg & IPC_DRBL_BUSY_BIT) == ((uint32_t) IPC_DRBL_BUSY_BIT)) + +#define IPC_HEADER_GET_PROTOCOL(drbl_reg) \ + ((drbl_reg >> IPC_HEADER_PROTOCOL_OFFSET) & IPC_HEADER_PROTOCOL_MASK) + +#define IPC_HEADER_GET_MNG_CMD(drbl_reg) \ + ((drbl_reg >> IPC_HEADER_MNG_CMD_OFFSET) & IPC_HEADER_MNG_CMD_MASK) + +#define IPC_HEADER_GET_LENGTH(drbl_reg) \ + ((drbl_reg >> IPC_HEADER_LENGTH_OFFSET) & IPC_HEADER_LENGTH_MASK) + +#define IPC_BUILD_HEADER(length, protocol, busy) \ + ((busy << IPC_DRBL_BUSY_OFFS) \ + | (protocol << IPC_HEADER_PROTOCOL_OFFSET) \ + | (length << IPC_HEADER_LENGTH_OFFSET)) + +#define IPC_BUILD_MNG_MSG(cmd, length) \ + ((1 << IPC_DRBL_BUSY_OFFS)\ + | (IPC_PROTOCOL_MNG << IPC_HEADER_PROTOCOL_OFFSET) \ + | (cmd << IPC_HEADER_MNG_CMD_OFFSET)\ + | (length << IPC_HEADER_LENGTH_OFFSET)) + +struct ipc_if_ctx { + uint32_t in_msg_reg; + uint32_t out_msg_reg; + uint32_t in_drbl_reg; + uint32_t out_drbl_reg; + uint32_t clr_bit; + uint8_t irq_in; + uint8_t irq_clr; +}; + +struct ipc_oob_msg { + uint32_t address; + uint32_t length; +}; + +enum pimr_signal_type { + PIMR_SIGNAL_IN = 0, + PIMR_SIGNAL_OUT = 1, + PIMR_SIGNAL_CLR = 2, +}; + +enum { + IPC_PEER_HOST_ID = 0, + IPC_PEERS_COUNT, +}; + +#endif /* __CROS_ECIPC_H */ diff --git a/chip/ish/ish_i2c.h b/chip/ish/ish_i2c.h new file mode 100644 index 0000000000..0a894593ea --- /dev/null +++ b/chip/ish/ish_i2c.h @@ -0,0 +1,191 @@ +/* Copyright (c) 2016 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef __CROS_EC_ISH_I2C_H +#define __CROS_EC_ISH_I2C_H + +#include <stdint.h> +#include "task.h" + +#define I2C_TSC_TIMEOUT 2000000 +#define I2C_CALIB_ADDRESS 0x3 +#define I2C_INTERRUPT_TIMEOUT (TICKFREQ / 20) +#define NS_IN_SEC 1000 +#define DEFAULT_SDA_HOLD 133 +#define NS_2_COUNTERS(ns, clk) ((ns * clk)/NS_IN_SEC) +#define COUNTERS_2_NS(counters, clk) (counters * (NANOSECONDS_IN_SEC / \ + (clk * HZ_IN_MEGAHZ))) + +enum { + /* speed mode values */ + I2C_SPEED_STD = 0, + I2C_SPEED_FAST = 1, + I2C_SPEED_HIGH = 2, + /* freq mode values */ + I2C_FREQ_25 = 0, + I2C_FREQ_50 = 1, + I2C_FREQ_100 = 2, + I2C_FREQ_120 = 3, + I2C_FREQ_40 = 4, + I2C_FREQ_20 = 5, + I2C_FREQ_37 = 6 +}; + +const unsigned int clk_in[] = { + [I2C_FREQ_25] = 25, + [I2C_FREQ_50] = 50, + [I2C_FREQ_100] = 100, + [I2C_FREQ_120] = 120, + [I2C_FREQ_40] = 40, + [I2C_FREQ_20] = 20, + [I2C_FREQ_37] = 37, +}; + +const uint8_t spkln[] = { + [I2C_FREQ_25] = 2, + [I2C_FREQ_50] = 3, + [I2C_FREQ_100] = 5, + [I2C_FREQ_120] = 6, + [I2C_FREQ_40] = 2, + [I2C_FREQ_20] = 1, + [I2C_FREQ_37] = 2, +}; + +enum { + I2C_READ, + I2C_WRITE +}; + +enum { + /* REGISTERS */ + IC_ENABLE = 0x6c, + IC_STATUS = 0x70, + IC_ENABLE_STATUS = 0x9c, + IC_CON = 0x00, + IC_TAR = 0x04, + IC_DATA_CMD = 0x10, + IC_RX_TL = 0x38, + IC_TX_TL = 0x3c, + IC_COMP_PARAM_1 = 0xf4, + IC_INTR_MASK = 0x30, + IC_RAW_INTR_STAT = 0x34, + IC_INTR_STAT = 0x2c, + IC_CLR_TX_ABRT = 0x54, + IC_TX_ABRT_SOURCE = 0x80, + IC_SS_SCL_HCNT = 0x14, + IC_SS_SCL_LCNT = 0x18, + IC_FS_SCL_HCNT = 0x1c, + IC_FS_SCL_LCNT = 0x20, + IC_HS_SCL_HCNT = 0x24, + IC_HS_SCL_LCNT = 0x28, + IC_CLR_STOP_DET = 0x60, + IC_CLR_START_DET = 0x64, + IC_TXFLR = 0x74, + IC_SDA_HOLD = 0x7c, + IC_FS_SPKLEN = 0xA0, + IC_HS_SPKLEN = 0xA4, + /* IC_ENABLE VALUES */ + IC_ENABLE_ENABLE = 1, + IC_ENABLE_DISABLE = 0, + /* IC_STATUS OFFSETS */ + IC_STATUS_MASTER_ACTIVITY = 5, + /* IC_CON OFFSETS */ + MASTER_MODE_OFFSET = 0, + SPEED_OFFSET = 1, + IC_RESTART_EN_OFFSET = 5, + IC_SLAVE_DISABLE_OFFSET = 6, + /* IC_CON VALUES */ + MASTER_MODE = 1, + STD_SPEED = 1, + FAST_SPEED = 2, + HIGH_SPEED = 3, + IC_RESTART_EN = 1, + IC_SLAVE_DISABLE = 1, + /* IC_CON WRITE VALUES */ + MASTER_MODE_VAL = (MASTER_MODE << MASTER_MODE_OFFSET), + STD_SPEED_VAL = (STD_SPEED << SPEED_OFFSET), + FAST_SPEED_VAL = (FAST_SPEED << SPEED_OFFSET), + HIGH_SPEED_VAL = (HIGH_SPEED << SPEED_OFFSET), + SPEED_MASK = (0x3 << SPEED_OFFSET), + IC_RESTART_EN_VAL = (IC_RESTART_EN << IC_RESTART_EN_OFFSET), + IC_SLAVE_DISABLE_VAL = (IC_SLAVE_DISABLE << IC_SLAVE_DISABLE_OFFSET), + /* IC_TAR OFFSETS */ + IC_TAR_OFFSET = 0, + SPECIAL_OFFSET = 11, + IC_10BITADDR_MASTER_OFFSET = 12, + /* IC_TAR VALUES */ + TAR_SPECIAL = 0, + IC_10BITADDR_MASTER = 0, + /* IC_TAR WRITE VALUES */ + IC_10BITADDR_MASTER_VAL = + (IC_10BITADDR_MASTER << IC_10BITADDR_MASTER_OFFSET), + TAR_SPECIAL_VAL = (TAR_SPECIAL << SPECIAL_OFFSET), + /* IC_DATA_CMD OFFSETS */ + DATA_CMD_DAT_OFFSET = 0, + DATA_CMD_CMD_OFFSET = 8, + DATA_CMD_STOP_OFFSET = 9, + DATA_CMD_RESTART_OFFSET = 10, + /* IC_DATA_CMD VALUES */ + DATA_CMD_READ = 1, + DATA_CMD_WRITE = 0, + DATA_CMD_STOP = 1, + DATA_CMD_RESTART = 1, + /* IC_DATA_CMD WRITE VALUES */ + DATA_CMD_WRITE_VAL = (DATA_CMD_WRITE << DATA_CMD_CMD_OFFSET), + DATA_CMD_READ_VAL = (DATA_CMD_READ << DATA_CMD_CMD_OFFSET), + DATA_CMD_STOP_VAL = (DATA_CMD_STOP << DATA_CMD_STOP_OFFSET), + DATA_CMD_RESTART_VAL = (DATA_CMD_RESTART << DATA_CMD_RESTART_OFFSET), + /* IC_TX_TL */ + IC_TX_TL_VAL = 0, + /* IC_COM_PARAM_OFFSETS */ + TX_BUFFER_DEPTH_OFFSET = 16, + RX_BUFFER_DEPTH_OFFSET = 8, + /* IC_INTR_MASK VALUES */ + M_RX_FULL = (1 << 2), + M_TX_EMPTY = (1 << 4), + M_TX_ABRT = (1 << 6), + M_STOP_DET = (1 << 9), + M_START_DET = (1 << 10), + IC_INTR_WRITE_MASK_VAL = (M_STOP_DET | M_TX_ABRT), + IC_INTR_READ_MASK_VAL = (M_RX_FULL | M_TX_ABRT), + DISABLE_INT = 0, + ENABLE_WRITE_INT = 1, + ENABLE_READ_INT = 2, + /* IC_ENABLE_STATUS_OFFSETS */ + IC_EN_OFFSET = 0, + /* IC_ENABLE_STATUS_VALUES */ + IC_EN_DISABLED_VAL = 0, + IC_EN_DISABLED = (IC_EN_DISABLED_VAL << IC_EN_OFFSET), + IC_EN_MASK = (1 << IC_EN_OFFSET), + /* IC_TX_ABRT_SOURCE bits */ + ABRT_7B_ADDR_NOACK = 1, +}; + +struct i2c_bus_data { + uint16_t hcnt; + uint16_t lcnt; + uint16_t sda_hold; +}; + +struct i2c_bus_info { + uint8_t bus_id; + struct i2c_bus_data std_speed; + struct i2c_bus_data fast_speed; + struct i2c_bus_data high_speed; +} __attribute__ ((__packed__)); + +struct i2c_context { + uint32_t *base; + uint8_t max_rx_depth; + uint8_t max_tx_depth; + uint8_t bus; + uint32_t interrupts; + uint32_t reason; + uint8_t error_flag; + uint8_t speed; + task_id_t wait_task_id; +}; + +#endif /* __CROS_EC_ISH_I2C_H */ diff --git a/chip/ish/jtag.c b/chip/ish/jtag.c new file mode 100644 index 0000000000..168364b6e1 --- /dev/null +++ b/chip/ish/jtag.c @@ -0,0 +1,13 @@ +/* Copyright (c) 2016 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. + */ +/* Settings to enable JTAG debugging */ + +#include "jtag.h" +#include "registers.h" + +void jtag_pre_init(void) +{ + /* Not implemented yet */ +} diff --git a/chip/ish/registers.h b/chip/ish/registers.h new file mode 100644 index 0000000000..f92770ddbd --- /dev/null +++ b/chip/ish/registers.h @@ -0,0 +1,105 @@ +/* Copyright (c) 2016 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. + * + * Registers and interrupts for Intel(R) Integrated Sensor Hub + */ + +#ifndef __CROS_EC_REGISTERS_H +#define __CROS_EC_REGISTERS_H + +#ifndef __ASSEMBLER__ +#include "common.h" + +/* + * ISH3.0 has 3 controllers. Locking must occur by-controller (not by-port). + */ +enum ish_i2c_port { + ISH_I2C0 = 0, /* Controller 0 */ + ISH_I2C1 = 1, /* Controller 1 */ + ISH_I2C2 = 2, /* Controller 2 */ + I2C_PORT_COUNT, +}; +#endif + +#define ISH_I2C_PORT_COUNT I2C_PORT_COUNT + +/* In ISH, the devices are mapped to pre-defined addresses in the 32-bit + * linear address space. + */ +#define ISH_I2C0_BASE 0x00100000 +#define ISH_I2C1_BASE 0x00102000 +#define ISH_I2C2_BASE 0x00105000 +#define ISH_UART_BASE 0x00103000 +#define ISH_IPC_BASE 0x00B00000 +#define ISH_IOAPIC_BASE 0xFEC00000 +#define ISH_HPET_BASE 0xFED00000 +#define ISH_LAPIC_BASE 0xFEE00000 + +/* HW interrupt pins mapped to IOAPIC, from I/O sources */ +#define ISH_I2C0_IRQ 0 +#define ISH_I2C1_IRQ 1 +#define ISH_HPET_TIMER1_IRQ 8 +#define ISH_HPET_TIMER2_IRQ 11 +#define ISH_IPC_HOST2ISH_IRQ 12 +#define ISH_IPC_ISH2HOST_CLR_IRQ 24 +#define ISH_UART0_IRQ 34 +#define ISH_UART1_IRQ 35 +#define ISH_I2C2_IRQ 40 +#define ISH_HPET_TIMER0_IRQ 55 + +/* Interrupt vectors 0-31 are architecture reserved. + * Vectors 32-255 are user-defined. + */ +#define USER_VEC_START 32 +/* Map IRQs to vectors after offset 10 for certain APIC interrupts */ +#define IRQ_TO_VEC(irq) (irq + USER_VEC_START + 10) + +/* APIC interrupt vectors */ +#define ISH_TS_VECTOR 0x20 /* Task switch vector */ +#define LAPIC_LVT_ERROR_VECTOR 0x21 +#define LAPIC_SPURIOUS_INT_VECTOR 0xff + +/* Interrupt to vector mapping. To be programmed into IOAPIC */ +#define ISH_I2C0_VEC IRQ_TO_VEC(ISH_I2C0_IRQ) +#define ISH_I2C1_VEC IRQ_TO_VEC(ISH_I2C1_IRQ) +#define ISH_I2C2_VEC IRQ_TO_VEC(ISH_I2C2_IRQ) +#define ISH_HPET_TIMER0_VEC IRQ_TO_VEC(ISH_HPET_TIMER0_IRQ) +#define ISH_HPET_TIMER1_VEC IRQ_TO_VEC(ISH_HPET_TIMER1_IRQ) +#define ISH_HPET_TIMER2_VEC IRQ_TO_VEC(ISH_HPET_TIMER2_IRQ) +#define ISH_IPC_ISH2HOST_CLR_VEC IRQ_TO_VEC(ISH_IPC_ISH2HOST_CLR_IRQ) +#define ISH_UART0_VEC IRQ_TO_VEC(ISH_UART0_IRQ) +#define ISH_UART1_VEC IRQ_TO_VEC(ISH_UART1_IRQ) +#define ISH_IPC_VEC IRQ_TO_VEC(ISH_IPC_HOST2ISH_IRQ) + +/* IPC_Registers */ +#define IPC_PISR (ISH_IPC_BASE + 0x0) +#define IPC_PIMR (ISH_IPC_BASE + 0x4) +#define IPC_ISH2HOST_MSG_REGS (ISH_IPC_BASE + 0x60) +#define IPC_ISH_FWSTS (ISH_IPC_BASE + 0x34) +#define IPC_HOST2ISH_DOORBELL (ISH_IPC_BASE + 0x48) +#define IPC_HOST2ISH_MSG_REGS (ISH_IPC_BASE + 0xE0) +#define IPC_ISH2HOST_DOORBELL (ISH_IPC_BASE + 0x54) +#define IPC_BUSY_CLEAR (ISH_IPC_BASE + 0x378) + +/* IOAPIC registers */ +#define IOAPIC_IDX 0xFEC00000 +#define IOAPIC_WDW 0xFEC00010 +#define IOAPIC_EOI_REG 0xFEC00040 + +#define IOAPIC_VERSION 0x1 +#define IOAPIC_IOREDTBL 0x10 +#define IOAPIC_REDTBL_DELMOD_FIXED 0x00000000 +#define IOAPIC_REDTBL_DESTMOD_PHYS 0x00000000 +#define IOAPIC_REDTBL_INTPOL_HIGH 0x00000000 +#define IOAPIC_REDTBL_INTPOL_LOW 0x00002000 +#define IOAPIC_REDTBL_TRIGGER_EDGE 0x00000000 +#define IOAPIC_REDTBL_TRIGGER_LEVEL 0x00008000 +#define IOAPIC_REDTBL_MASK 0x00010000 + +/* LAPIC registers */ +#define LAPIC_EOI_REG 0xFEE000B0 +#define LAPIC_ISR_REG 0xFEE00170 +#define LAPIC_ICR_REG (ISH_LAPIC_BASE + 0x300) + +#endif /* __CROS_EC_REGISTERS_H */ diff --git a/chip/ish/system.c b/chip/ish/system.c new file mode 100644 index 0000000000..75f1d5a4a8 --- /dev/null +++ b/chip/ish/system.c @@ -0,0 +1,120 @@ +/* Copyright (c) 2016 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 ISH (Not implemented) */ + +#include "clock.h" +#include "common.h" +#include "console.h" +#include "cpu.h" +#include "gpio.h" +#include "host_command.h" +#include "registers.h" +#include "shared_mem.h" +#include "system.h" +#include "hooks.h" +#include "task.h" +#include "timer.h" +#include "util.h" +#include "spi.h" + +/* Indices for hibernate data registers (RAM backed by VBAT) */ +enum hibdata_index { + HIBDATA_INDEX_SCRATCHPAD = 0, /* General-purpose scratchpad */ + HIBDATA_INDEX_SAVED_RESET_FLAGS /* Saved reset flags */ +}; + +int system_is_reboot_warm(void) +{ + return 0; +} + +void system_pre_init(void) +{ +} + +void chip_save_reset_flags(int flags) +{ +} + +void _system_reset(int flags, int wake_from_hibernate) +{ +} + +void system_reset(int flags) +{ + _system_reset(flags, 0); +} + +const char *system_get_chip_vendor(void) +{ + return "intel"; +} + +const char *system_get_chip_name(void) +{ + return "intel"; +} + +static char to_hex(int x) +{ + if (x >= 0 && x <= 9) + return '0' + x; + return 'a' + x - 10; +} + +const char *system_get_chip_revision(void) +{ + static char buf[3]; + uint8_t rev = 0x86; + + buf[0] = to_hex(rev / 16); + buf[1] = to_hex(rev & 0xf); + buf[2] = '\0'; + return buf; +} + +int system_get_vbnvcontext(uint8_t *block) +{ + return EC_ERROR_UNIMPLEMENTED; +} + +int system_set_vbnvcontext(const uint8_t *block) +{ + return EC_ERROR_UNIMPLEMENTED; +} + +int system_set_scratchpad(uint32_t value) +{ + return EC_SUCCESS; +} + +uint32_t system_get_scratchpad(void) +{ + return 0; +} + +void system_hibernate(uint32_t seconds, uint32_t microseconds) +{ +} + +void htimer_interrupt(void) +{ + /* Time to wake up */ +} + +enum system_image_copy_t system_get_shrspi_image_copy(void) +{ + return 0; +} + +uint32_t system_get_lfw_address(void) +{ + return 0; +} + +void system_set_image_copy(enum system_image_copy_t copy) +{ +} diff --git a/chip/ish/uart.c b/chip/ish/uart.c new file mode 100644 index 0000000000..bb12624bcf --- /dev/null +++ b/chip/ish/uart.c @@ -0,0 +1,243 @@ +/* Copyright (c) 2016 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 ISH */ +#include "common.h" +#include "console.h" +#include "uart_defs.h" +#include "atomic.h" +#include "task.h" +#include "registers.h" +#include "uart.h" +#include "uart_defs.h" +#include "interrupts.h" + +#define CPUTS(outstr) cputs(CC_LPC, outstr) +#define CPRINTS(format, args...) cprints(CC_LPC, format, ## args) +#define CPRINTF(format, args...) cprintf(CC_LPC, format, ## args) + +static const uint32_t baud_conf[][BAUD_TABLE_MAX] = { + {B9600, 9600}, + {B57600, 57600}, + {B115200, 115200}, + {B921600, 921600}, + {B2000000, 2000000}, + {B3000000, 3000000}, + {B3250000, 3250000}, + {B3500000, 3500000}, + {B4000000, 4000000}, + {B19200, 19200}, +}; + +static struct uart_ctx uart_ctx[UART_DEVICES] = { + { + .id = 0, + .base = UART0_BASE, + .input_freq = UART_ISH_INPUT_FREQ, + .addr_interval = UART_ISH_ADDR_INTERVAL, + .uart_state = UART_STATE_CG, + }, + { + .id = 1, + .base = UART1_BASE, + .input_freq = UART_ISH_INPUT_FREQ, + .addr_interval = UART_ISH_ADDR_INTERVAL, + .uart_state = UART_STATE_CG, + } +}; + +static int init_done; + +int uart_init_done(void) +{ + return init_done; +} + +void uart_tx_start(void) +{ + /* TBD for RX and interrupt enabled TX */ +} + +void uart_tx_stop(void) +{ + /* TBD for RX and interrupt enabled TX */ +} + +void uart_tx_flush(void) +{ + /* TBD for RX and interrupt enabled TX */ +} + +int uart_tx_ready(void) +{ + return 1; +} + +int uart_rx_available(void) +{ + /* No RX FIFO */ + return 0; +} + +void uart_write_char(char c) +{ + int id = 1; /* In ISH, UART1 is assigned for console outpu */ + + /* Wait till reciever is ready */ + while ((REG8(LSR(id)) & LSR_TEMT) == 0) + ; + + REG8(THR(id)) = c; +} + +static int uart_return_baud_rate_by_id(int baud_rate_id) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(baud_conf); i++) { + if (baud_conf[i][BAUD_IDX] == baud_rate_id) + return baud_conf[i][BAUD_SPEED]; + } + + return -1; +} + +static void uart_hw_init(enum UART_PORT id) +{ + uint32_t divisor; /* baud rate divisor */ + uint8_t mcr = 0; + uint8_t fcr = 0; + struct uart_ctx *ctx = &uart_ctx[id]; + + interrupt_disable(); + + /* Calculate baud rate divisor */ + divisor = (ctx->input_freq / ctx->baud_rate) >> 4; + + REG32(MUL(ctx->id)) = (divisor * ctx->baud_rate); + REG32(DIV(ctx->id)) = (ctx->input_freq / 16); + REG32(PS(ctx->id)) = 16; + + /* Set the DLAB to access the baud rate divisor registers */ + REG8(LCR(ctx->id)) = LCR_DLAB; + REG8(DLL(ctx->id)) = (divisor & 0xff); + REG8(DLH(ctx->id)) = ((divisor >> 8) & 0xff); + + /* 8 data bits, 1 stop bit, no parity, clear DLAB */ + REG8(LCR(ctx->id)) = LCR_8BIT_CHR; + + if (ctx->client_flags & UART_CONFIG_HW_FLOW_CONTROL) + mcr = MCR_AUTO_FLOW_EN; + + mcr |= MCR_INTR_ENABLE; /* needs to be set regardless of flow control */ + + mcr |= (MCR_RTS | MCR_DTR); + REG8(MCR(ctx->id)) = mcr; + + fcr = FCR_FIFO_SIZE_64 | FCR_ITL_FIFO_64_BYTES_1; + + /* configure FIFOs */ + REG8(FCR(ctx->id)) = (fcr | FCR_FIFO_ENABLE + | FCR_RESET_RX | FCR_RESET_TX); + + /* enable UART unit */ + REG32(ABR(ctx->id)) = ABR_UUE; + + /* clear the port */ + REG8(RBR(ctx->id)); + REG8(IER(ctx->id)) = 0x00; + + interrupt_enable(); +} + +static void uart_stop_hw(enum UART_PORT id) +{ + int i; + uint32_t fifo_len; + + /* Manually clearing the fifo from possible noise. + * Entering D0i3 when fifo is not cleared may result in a hang. + */ + fifo_len = (REG32(FOR(id)) & FOR_OCCUPANCY_MASK) >> FOR_OCCUPANCY_OFFS; + + for (i = 0; i < fifo_len; i++) + (void)REG8(RBR(id)); + + /* No interrupts are enabled */ + REG8(IER(id)) = 0; + REG8(MCR(id)) = 0; + + /* Clear and disable FIFOs */ + REG8(FCR(id)) = (FCR_RESET_RX | FCR_RESET_TX); + + /* Disable uart unit */ + REG32(ABR(id)) = 0; +} + +static int uart_client_init(enum UART_PORT id, uint32_t baud_rate_id, int flags) +{ + + if ((uart_ctx[id].base == 0) || (id >= UART_DEVICES)) + return UART_ERROR; + + if (!bool_compare_and_swap_u32(&uart_ctx[id].is_open, 0, 1)) + return UART_BUSY; + + uart_ctx[id].baud_rate = uart_return_baud_rate_by_id(baud_rate_id); + + if ((uart_ctx[id].baud_rate == -1) || (uart_ctx[id].baud_rate == 0)) + uart_ctx[id].baud_rate = UART_DEFAULT_BAUD_RATE; + + uart_ctx[id].client_flags = flags; + + atomic_and(&uart_ctx[id].uart_state, ~UART_STATE_CG); + uart_hw_init(id); + + return EC_SUCCESS; +} + +static void uart_drv_init(void) +{ + int i; + uint32_t fifo_len; + + /* Disable UART */ + for (i = 0; i < UART_DEVICES; i++) + uart_stop_hw(i); + + /* Enable HSU global interrupts (DMA/U0/U1) and set PMEN bit + * to allow PMU to clock gate ISH + */ + REG32(HSU_BASE + HSU_REG_GIEN) = (GIEN_DMA_EN | GIEN_UART0_EN + | GIEN_UART1_EN | GIEN_PWR_MGMT); + + /* There is a by design HW "bug" where all UARTs are enabled by default + * but they must be disbled to enter clock gating. + * UART0 and UART1 are disabled during their init - but we don't init + * UART2 so as a w/a we disable UART2 even though it isn't being used. + * we also clear UART 2 fifo, which may cause problem entrying TCG is + * not empty (we do the same for UART0 and 1 in "uart_stop_hw" + */ + + fifo_len = (REG32(UART2_BASE + UART_REG_FOR) + & FOR_OCCUPANCY_MASK) >> FOR_OCCUPANCY_OFFS; + + for (i = 0; i < fifo_len; i++) + (void)REG8((UART2_BASE + UART_REG_RBR)); + + REG32(UART2_BASE + UART_REG_ABR) = 0; + + task_enable_irq(ISH_UART0_IRQ); + task_enable_irq(ISH_UART1_IRQ); +} + +void uart_init(void) +{ + + uart_drv_init(); + + uart_client_init(UART_PORT_1, B115200, 0); + init_done = 1; +} diff --git a/chip/ish/uart_defs.h b/chip/ish/uart_defs.h new file mode 100644 index 0000000000..8c431e21a8 --- /dev/null +++ b/chip/ish/uart_defs.h @@ -0,0 +1,222 @@ +/* Copyright (c) 2016 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 ISH */ + +#ifndef __CROS_EC_UART_DEFS_H_ +#define __CROS_EC_UART_DEFS_H_ + +#include <stdint.h> +#include <stddef.h> + +#define UART_ERROR -1 +#define UART_BUSY -2 +#define HSU_BASE ISH_UART_BASE +#define UART0_OFFS (0x80) +#define UART0_BASE (ISH_UART_BASE + UART0_OFFS) +#define UART0_SIZE (0x80) + +#define UART1_OFFS (0x100) +#define UART1_BASE (ISH_UART_BASE + UART1_OFFS) +#define UART1_SIZE (0x80) + +#define UART2_OFFS (0x180) +#define UART2_BASE (ISH_UART_BASE + UART2_OFFS) +#define UART2_SIZE (0x80) + +/* Register accesses */ +#define LSR(n) (uart_ctx[n].base + UART_REG_LSR * uart_ctx[n].addr_interval) +#define THR(n) (uart_ctx[n].base + UART_REG_THR * uart_ctx[n].addr_interval) +#define FOR(n) (uart_ctx[n].base + UART_REG_FOR * uart_ctx[n].addr_interval) +#define RBR(n) (uart_ctx[n].base + UART_REG_RBR * uart_ctx[n].addr_interval) +#define DLL(n) (uart_ctx[n].base + UART_REG_DLL * uart_ctx[n].addr_interval) +#define DLH(n) (uart_ctx[n].base + UART_REG_DLH * uart_ctx[n].addr_interval) +#define DLD(n) (uart_ctx[n].base + UART_REG_DLD * uart_ctx[n].addr_interval) +#define IER(n) (uart_ctx[n].base + UART_REG_IER * uart_ctx[n].addr_interval) +#define IIR(n) (uart_ctx[n].base + UART_REG_IIR * uart_ctx[n].addr_interval) +#define FCR(n) (uart_ctx[n].base + UART_REG_FCR * uart_ctx[n].addr_interval) +#define LCR(n) (uart_ctx[n].base + UART_REG_LCR * uart_ctx[n].addr_interval) +#define MCR(n) (uart_ctx[n].base + UART_REG_MCR * uart_ctx[n].addr_interval) +#define MSR(n) (uart_ctx[n].base + UART_REG_MSR * uart_ctx[n].addr_interval) +#define FCTR(n) (uart_ctx[n].base + UART_REG_FCTR * uart_ctx[n].addr_interval) +#define EFR(n) (uart_ctx[n].base + UART_REG_EFR * uart_ctx[n].addr_interval) +#define RXTRG(n) \ + (uart_ctx[n].base + UART_REG_RXTRG * uart_ctx[n].addr_interval) +#define ABR(n) (uart_ctx[n].base + UART_REG_ABR * uart_ctx[n].addr_interval) +#define PS(n) (uart_ctx[n].base + UART_REG_PS * uart_ctx[n].addr_interval) +#define MUL(n) (uart_ctx[n].base + UART_REG_MUL * uart_ctx[n].addr_interval) +#define DIV(n) (uart_ctx[n].base + UART_REG_DIV * uart_ctx[n].addr_interval) + +/* RBR: Receive Buffer register (BLAB bit = 0) */ +#define UART_REG_RBR (0) +/* THR: Transmit Holding register (BLAB bit = 0) */ +#define UART_REG_THR (0) +/* IER: Interrupt Enable register (BLAB bit = 0) */ +#define UART_REG_IER (1) + +#define FCR_FIFO_SIZE_16 (0x00) +#define FCR_FIFO_SIZE_64 (0x20) +#define FCR_ITL_FIFO_64_BYTES_1 (0x00) + +/* FCR: FIFO Control register */ +#define UART_REG_FCR (2) +#define FCR_FIFO_ENABLE (0x01) +#define FCR_RESET_RX (0x02) +#define FCR_RESET_TX (0x04) + +/* LCR: Line Control register */ +#define UART_REG_LCR (3) +#define LCR_DLAB (0x80) +#define LCR_5BIT_CHR (0x00) +#define LCR_6BIT_CHR (0x01) +#define LCR_7BIT_CHR (0x02) +#define LCR_8BIT_CHR (0x03) +#define LCR_BIT_CHR_MASK (0x03) +#define LCR_SB (0x40) /*Set Break */ + +/* MCR: Modem Control register */ +#define UART_REG_MCR (4) +#define MCR_DTR (0x1) +#define MCR_RTS (0x2) +#define MCR_LOO (0x10) +#define MCR_INTR_ENABLE (0x08) +#define MCR_AUTO_FLOW_EN (0x20) + +/* LSR: Line Status register */ +#define UART_REG_LSR (5) +#define LSR_DR (0x01) /* Data Ready */ +#define LSR_OE (0x02) /* Overrun error */ +#define LSR_PE (0x04) /* Parity error */ +#define LSR_FE (0x08) /* Framing error */ +#define LSR_BI (0x10) /* Breaking interrupt */ +#define LSR_THR_EMPTY (0x20) /* Non FIFO mode: Transmit holding + * register empty + */ +#define LSR_TDRQ (0x20) /* FIFO mode: Transmit Data request */ +#define LSR_TEMT (0x40) /* Transmitter empty */ + +#define FCR_ITL_FIFO_64_BYTES_56 (0xc0) + +#define IER_RECV (0x01) +#define IER_TDRQ (0x02) +#define IER_LINE_STAT (0x04) + +#define UART_REG_IIR (2) +/* MSR: Modem Status register */ +#define UART_REG_MSR (6) + +/* DLL: Divisor Latch Reg. low byte (BLAB bit = 1) */ +#define UART_REG_DLL (0) + +/* DLH: Divisor Latch Reg. high byte (BLAB bit = 1) */ +#define UART_REG_DLH (1) + +/* DLH: Divisor Latch Fractional. (BLAB bit = 1) */ +#define UART_REG_DLD (2) + +/* FOR: Fifo O Register (ISH only) */ +#define UART_REG_FOR (0x20) +#define FOR_OCCUPANCY_OFFS 0 +#define FOR_OCCUPANCY_MASK 0x7F + +/* ABR: Auto-Baud Control Register (ISH only) */ +#define UART_REG_ABR (0x24) +#define ABR_UUE (0x10) + +/* Pre-Scalar Register (ISH only) */ +#define UART_REG_PS (0x30) + +/* DDS registers (ISH only) */ +#define UART_REG_MUL (0x34) +#define UART_REG_DIV (0x38) + +/* G_IEN: Global Interrupt Enable (ISH only) */ +#define HSU_REG_GIEN (0) +#define HSU_REG_GIST (4) + +#define GIEN_PWR_MGMT (0x01000000) +#define GIEN_DMA_EN (0x00000020) +#define GIEN_UART2_EN (0x00000004) +#define GIEN_UART1_EN (0x00000002) +#define GIEN_UART0_EN (0x00000001) +#define GIST_DMA_EN (0x00000020) +#define GIST_UART2_EN (0x00000004) +#define GIST_UART1_EN (0x00000002) +#define GIST_UART0_EN (0x00000001) +#define GIST_UARTx_EN (GIST_UART0_EN|GIST_UART1_EN|GIST_UART2_EN) + +/* UART config flag, send to sc_io_control if the current UART line has HW + * flow control lines connected. + */ +#define UART_CONFIG_HW_FLOW_CONTROL (1<<0) + + /* UART config flag for sc_io_control. If defined a sc_io_event_rx_msg is + * raised only when the rx buffer is completely full. Otherwise, the event + * is raised after a timeout is received on the UART line, + * and all data received until now is provided. + */ +#define UART_CONFIG_DELIVER_FULL_RX_BUF (1<<1) + +/* UART config flag for sc_io_control. If defined a sc_io_event_rx_buf_depleted + * is raised when all rx buffers that were added are full. Otherwise, no + * event is raised. + */ +#define UART_CONFIG_ANNOUNCE_DEPLETED_BUF (1<<2) + +#define UART_INT_DEVICES 2 +#define UART_EXT_DEVICES 8 +#define UART_DEVICES UART_INT_DEVICES +#define UART_ISH_ADDR_INTERVAL 1 + +#define B9600 0x0000d +#define B57600 0x00000018 +#define B115200 0x00000011 +#define B921600 0x00000012 +#define B2000000 0x00000013 +#define B3000000 0x00000014 +#define B3250000 0x00000015 +#define B3500000 0x00000016 +#define B4000000 0x00000017 +#define B19200 0x0000e +#define B38400 0x0000f + +/* KHZ, MHZ */ +#define KHZ(x) ((x) * 1000) +#define MHZ(x) (KHZ(x) * 1000) +#define UART_ISH_INPUT_FREQ MHZ(120) +#define UART_DEFAULT_BAUD_RATE 115200 +#define UART_STATE_CG (1 << UART_OP_CG) + +enum UART_PORT { + UART_PORT_0, + UART_PORT_1, + UART_PORT_MAX +}; + +enum UART_OP { + UART_OP_READ, + UART_OP_WRITE, + UART_OP_CG, + UART_OP_MAX +}; + +enum { + BAUD_IDX, + BAUD_SPEED, + BAUD_TABLE_MAX +}; + +struct uart_ctx { + uint32_t id; + uint32_t base; + uint32_t addr_interval; + uint32_t uart_state; + uint32_t is_open; + uint32_t baud_rate; + uint32_t input_freq; + uint32_t client_flags; +}; + +#endif /* _CROS_EC_UART_DEFS_H_ */ |