From d5ea8c7b4babaf9b3ec97b21448cbb856b54cb16 Mon Sep 17 00:00:00 2001 From: Raptor Engineering Development Team <support@raptorengineering.com> Date: Fri, 15 Jan 2021 13:20:40 -0600 Subject: [PATCH] Sync from internal development repository GIT hash dfb40c41 Major update Verified to fully IPL attached Blackbird host to Petitboot shell using Versa ECP5 --- Makefile | 4 +- aquila.h | 160 ++++ fsi.c | 38 +- fsi.h | 69 +- ipmi_bt.h | 171 ++++ isr.c | 69 +- linker.ld | 37 +- main.c | 1943 ++++++++++++++++++++++++++++++++++++++++++- micron_n25q_flash.h | 32 + opencores_i2c.c | 210 +++++ opencores_i2c.h | 73 ++ tercel_spi.h | 44 + utility.c | 56 ++ utility.h | 11 + 14 files changed, 2822 insertions(+), 95 deletions(-) create mode 100644 aquila.h create mode 100644 ipmi_bt.h create mode 100644 micron_n25q_flash.h create mode 100644 opencores_i2c.c create mode 100644 opencores_i2c.h create mode 100644 utility.c create mode 100644 utility.h diff --git a/Makefile b/Makefile index 8568b40..28be304 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ BUILD_DIR=../build/versa_ecp5 include $(BUILD_DIR)/software/include/generated/variables.mak include $(SOC_DIRECTORY)/software/common.mak -OBJECTS=isr.o main.o fsi.o +OBJECTS=utility.o isr.o main.o fsi.o opencores_i2c.o all: firmware.bin @@ -18,7 +18,7 @@ firmware.elf: $(OBJECTS) $(LD) $(LDFLAGS) \ -T linker.ld \ -N -o $@ \ - $(BUILD_DIR)/software/libbase/crt0-ctr.o \ + $(BUILD_DIR)/software/libbase/crt0.o \ $(OBJECTS) \ -L$(BUILD_DIR)/software/libbase \ -L$(BUILD_DIR)/software/libcompiler_rt \ diff --git a/aquila.h b/aquila.h new file mode 100644 index 0000000..36b5ff1 --- /dev/null +++ b/aquila.h @@ -0,0 +1,160 @@ +// © 2020 Raptor Engineering, LLC +// +// Released under the terms of the AGPL v3 +// See the LICENSE file for full details + +#ifndef _AQUILA_H +#define _AQUILA_H + +#define AQUILA_LPC_CTL_BLOCK_OFFSET 0xf00000 +#define AQUILA_LPC_STATUS_CYC_WNR_MASK 0x1 +#define AQUILA_LPC_STATUS_CYC_WNR_SHIFT 1 +#define AQUILA_LPC_STATUS_ATTN_REQ_MASK 0x1 +#define AQUILA_LPC_STATUS_ATTN_REQ_SHIFT 0 +#define AQUILA_LPC_STATUS_CYCLE_TYPE_MASK 0x3 +#define AQUILA_LPC_STATUS_CYCLE_TYPE_SHIFT 2 +#define AQUILA_LPC_STATUS_FW_CYCLE_MSIZE_MASK 0xf +#define AQUILA_LPC_STATUS_FW_CYCLE_MSIZE_SHIFT 16 +#define AQUILA_LPC_STATUS_FW_CYCLE_IDSEL_MASK 0xf +#define AQUILA_LPC_STATUS_FW_CYCLE_IDSEL_SHIFT 20 +#define AQUILA_LPC_STATUS_ACT_ADDR_MASK 0xfffffff +#define AQUILA_LPC_STATUS_ACT_ADDR_SHIFT 0 +#define AQUILA_LPC_STATUS_ACT_WDATA_MASK 0xff +#define AQUILA_LPC_STATUS_ACT_WDATA_SHIFT 0 +#define AQUILA_LPC_CTL_ACT_RDATA_MASK 0xff +#define AQUILA_LPC_CTL_ACT_RDATA_SHIFT 8 +#define AQUILA_LPC_CTL_EN_IO_CYCLES_MASK 0x1 +#define AQUILA_LPC_CTL_EN_IO_CYCLES_SHIFT 3 +#define AQUILA_LPC_CTL_EN_FW_CYCLES_MASK 0x1 +#define AQUILA_LPC_CTL_EN_FW_CYCLES_SHIFT 1 +#define AQUILA_LPC_CTL_EN_VUART1_MASK 0x1 +#define AQUILA_LPC_CTL_EN_VUART1_SHIFT 4 +#define AQUILA_LPC_CTL_EN_VUART2_MASK 0x1 +#define AQUILA_LPC_CTL_EN_VUART2_SHIFT 5 +#define AQUILA_LPC_CTL_EN_IPMI_BT_MASK 0x1 +#define AQUILA_LPC_CTL_EN_IPMI_BT_SHIFT 6 +#define AQUILA_LPC_CTL_EN_IPMI_BT_IRQ_MASK 0x1 +#define AQUILA_LPC_CTL_EN_IPMI_BT_IRQ_SHIFT 16 +#define AQUILA_LPC_CTL_IPMI_BT_ADDR_MASK 0xff +#define AQUILA_LPC_CTL_IPMI_BT_ADDR_SHIFT 8 +#define AQUILA_LPC_CTL_EN_FW_CYCLE_IRQ_MASK 0x1 +#define AQUILA_LPC_CTL_EN_FW_CYCLE_IRQ_SHIFT 17 +#define AQUILA_LPC_CTL_EN_TPM_CYCLE_IRQ_MASK 0x1 +#define AQUILA_LPC_CTL_EN_TPM_CYCLE_IRQ_SHIFT 18 +#define AQUILA_LPC_CTL_EN_IO_CYCLE_IRQ_MASK 0x1 +#define AQUILA_LPC_CTL_EN_IO_CYCLE_IRQ_SHIFT 19 +#define AQUILA_LPC_CTL_XFER_ERR_MASK 0x1 +#define AQUILA_LPC_CTL_XFER_ERR_SHIFT 1 +#define AQUILA_LPC_CTL_XFER_CONT_MASK 0x1 +#define AQUILA_LPC_CTL_XFER_CONT_SHIFT 0 +#define AQUILA_LPC_RANGE_ENABLE_MASK 0x1 +#define AQUILA_LPC_RANGE_ENABLE_SHIFT 31 +#define AQUILA_LPC_RANGE_ALLOW_IO_MASK 0x1 +#define AQUILA_LPC_RANGE_ALLOW_IO_SHIFT 30 +#define AQUILA_LPC_RANGE_ALLOW_TPM_MASK 0x1 +#define AQUILA_LPC_RANGE_ALLOW_TPM_SHIFT 29 +#define AQUILA_LPC_RANGE_START_ADDR_MASK 0xfffffff +#define AQUILA_LPC_RANGE_START_ADDR_SHIFT 0 +#define AQUILA_LPC_RANGE_END_ADDR_MASK 0xfffffff +#define AQUILA_LPC_RANGE_END_ADDR_SHIFT 0 +#define AQUILA_LPC_CTL_EN_FW_DMA_R_MASK 0x1 +#define AQUILA_LPC_CTL_EN_FW_DMA_R_SHIFT 0 +#define AQUILA_LPC_CTL_EN_FW_DMA_W_MASK 0x1 +#define AQUILA_LPC_CTL_EN_FW_DMA_W_SHIFT 1 + +#define AQUILA_LPC_REG_DEVICE_ID_HIGH 0x0 +#define AQUILA_LPC_REG_DEVICE_ID_LOW 0x4 +#define AQUILA_LPC_REG_DEVICE_VERSION 0x8 +#define AQUILA_LPC_REG_CONTROL1 0xc +#define AQUILA_LPC_REG_CONTROL2 0x10 +#define AQUILA_LPC_REG_RANGE1_CONFIG 0x14 +#define AQUILA_LPC_REG_RANGE1_END 0x18 +#define AQUILA_LPC_REG_RANGE2_CONFIG 0x1c +#define AQUILA_LPC_REG_RANGE2_END 0x20 +#define AQUILA_LPC_REG_RANGE3_CONFIG 0x24 +#define AQUILA_LPC_REG_RANGE3_END 0x28 +#define AQUILA_LPC_REG_RANGE4_CONFIG 0x2c +#define AQUILA_LPC_REG_RANGE4_END 0x30 +#define AQUILA_LPC_REG_RANGE5_CONFIG 0x34 +#define AQUILA_LPC_REG_RANGE5_END 0x38 +#define AQUILA_LPC_REG_RANGE6_CONFIG 0x3c +#define AQUILA_LPC_REG_RANGE6_END 0x40 +#define AQUILA_LPC_REG_DMA_CONFIG1 0x44 +#define AQUILA_LPC_REG_DMA_CONFIG2 0x48 +#define AQUILA_LPC_REG_DMA_CONFIG3 0x4c +#define AQUILA_LPC_REG_DMA_CONFIG4 0x50 +#define AQUILA_LPC_REG_DMA_CONFIG5 0x54 +#define AQUILA_LPC_REG_DMA_CONFIG6 0x58 +#define AQUILA_LPC_REG_STATUS1 0x5c +#define AQUILA_LPC_REG_STATUS2 0x60 +#define AQUILA_LPC_REG_STATUS3 0x64 +#define AQUILA_LPC_REG_STATUS4 0x68 +#define AQUILA_LPC_REG_IPMI_BT_STATUS 0x6c + +#define AQUILA_LPC_DEVICE_ID_HIGH 0x7c525054 +#define AQUILA_LPC_DEVICE_ID_LOW 0x4c504353 + +#define AQUILA_LPC_VERSION_MAJOR_MASK 0xffff +#define AQUILA_LPC_VERSION_MAJOR_SHIFT 16 +#define AQUILA_LPC_VERSION_MINOR_MASK 0xff +#define AQUILA_LPC_VERSION_MINOR_SHIFT 8 +#define AQUILA_LPC_VERSION_PATCH_MASK 0xff +#define AQUILA_LPC_VERSION_PATCH_SHIFT 0 + +#define AQUILA_LPC_FW_DATA_BLOCK_OFFSET 0xc00000 + +#define AQUILA_LPC_IPMI_BT_DATA_BLOCK_OFFSET 0xd00000 + +#define AQUILA_LPC_STATUS_CYCLE_TYPE_IO 0 +#define AQUILA_LPC_STATUS_CYCLE_TYPE_FW 2 + +#define AQUILA_LPC_VUART1_FIFO_EMPTY 0x00000100 +#define AQUILA_LPC_VUART2_FIFO_EMPTY 0x01000000 + +#define AQUILA_LPC_IRQ_ASSERTED 0x00000001 +#define AQUILA_LPC_VUART1_IRQ_ASSERTED 0x00000002 +#define AQUILA_LPC_VUART2_IRQ_ASSERTED 0x00000004 +#define AQUILA_LPC_IPMI_BT_IRQ_ASSERTED 0x00000008 +#define AQUILA_LPC_FW_CYCLE_IRQ_ASSERTED 0x00000010 +#define AQUILA_LPC_TPM_CYCLE_IRQ_ASSERTED 0x00000020 +#define AQUILA_LPC_IO_CYCLE_IRQ_ASSERTED 0x00000040 + +#define AQUILA_LPC_VUART1_FIFO_READ_MASK 0xff +#define AQUILA_LPC_VUART1_FIFO_READ_SHIFT 0 + +#define AQUILA_LPC_VUART_BLOCK_OFFSET 0xe00000 +#define AQUILA_LPC_VUART1_STATUS_REG 0x4 +#define AQUILA_LPC_VUART1_CONTROL_REG 0x8 +#define AQUILA_LPC_VUART_FIFO_TRIG_LVL_MASK 0xff +#define AQUILA_LPC_VUART_FIFO_TRIG_LVL_SHIFT 8 +#define AQUILA_LPC_VUART_FIFO_IRQ_EN_MASK 0x1 +#define AQUILA_LPC_VUART_FIFO_IRQ_EN_SHIFT 30 +#define AQUILA_LPC_VUART_IRQ_EN_MASK 0x1 +#define AQUILA_LPC_VUART_IRQ_EN_SHIFT 31 +#define AQUILA_LPC_VUART_WFIFO_FULL_MASK 0x1 +#define AQUILA_LPC_VUART_WFIFO_FULL_SHIFT 24 + +#define AQUILA_EV_MASTER_IRQ 0x1 + +static inline uint32_t read_aquila_register(unsigned long base_address, uint8_t reg) { + return *((volatile uint32_t*)(base_address + AQUILA_LPC_CTL_BLOCK_OFFSET + reg)); +} + +static inline void write_aquila_register(unsigned long base_address, uint8_t reg, uint32_t data) { + *((volatile uint32_t*)(base_address + AQUILA_LPC_CTL_BLOCK_OFFSET + reg)) = data; +} + +static inline uint8_t lpc_fw_msize_to_bytes(uint8_t msize) { + switch (msize) { + case 0: return 1; + case 1: return 2; + case 2: return 4; + case 4: return 16; + case 7: return 128; + default: return 0; + } +} + +void lpc_slave_isr(void); + +#endif // _AQUILA_H \ No newline at end of file diff --git a/fsi.c b/fsi.c index bef9a71..c6d1397 100644 --- a/fsi.c +++ b/fsi.c @@ -7,7 +7,7 @@ #include <stdint.h> #include <stdlib.h> -#include <generated/csr.h> +#include <generated/mem.h> #include "fsi.h" @@ -38,8 +38,8 @@ #define IBM_POWER9_SLAVE_ID 0 static int check_device_id(void) { - uint32_t devid_high = openfsi_master_interface_device_id_high_read(); - uint32_t devid_low = openfsi_master_interface_device_id_low_read(); + uint32_t devid_high = read_openfsi_register(OPENFSIMASTER_BASE, FSI_MASTER_REG_DEVICE_ID_HIGH); + uint32_t devid_low = read_openfsi_register(OPENFSIMASTER_BASE, FSI_MASTER_REG_DEVICE_ID_LOW); if ((devid_high == 0x7c525054) && (devid_low == 0x4653494d)) { return 0; @@ -58,36 +58,36 @@ static int access_fsi_mem(uint8_t slave_id, uint32_t address, fsi_data_length_t return -1; // Set up request - word = openfsi_master_interface_sid_adr_read(); + word = read_openfsi_register(OPENFSIMASTER_BASE, FSI_MASTER_REG_SID_ADR); word &= ~(FSI_MASTER_SID_SLAVE_ID_MASK << FSI_MASTER_SID_SLAVE_ID_SHIFT); word |= ((slave_id & FSI_MASTER_SID_SLAVE_ID_MASK) << FSI_MASTER_SID_SLAVE_ID_SHIFT); word &= ~(FSI_MASTER_SID_ADDRESS_MASK << FSI_MASTER_SID_ADDRESS_SHIFT); word |= ((address & FSI_MASTER_SID_ADDRESS_MASK) << FSI_MASTER_SID_ADDRESS_SHIFT); word &= ~(FSI_MASTER_SID_DATA_LENGTH_MASK << FSI_MASTER_SID_DATA_LENGTH_SHIFT); word |= ((data_length & FSI_MASTER_SID_DATA_LENGTH_MASK) << FSI_MASTER_SID_DATA_LENGTH_SHIFT); - openfsi_master_interface_sid_adr_write(word); + write_openfsi_register(OPENFSIMASTER_BASE, FSI_MASTER_REG_SID_ADR, word); if (write) { - openfsi_master_interface_tx_data_write(*data); + write_openfsi_register(OPENFSIMASTER_BASE, FSI_MASTER_REG_TX_DATA, *data); } // Set direction and start operation - word = openfsi_master_interface_control_reg_read(); + word = read_openfsi_register(OPENFSIMASTER_BASE, FSI_MASTER_REG_CONTROL); word &= ~(FSI_MASTER_CTL_DATA_DIRECTION_MASK << FSI_MASTER_CTL_DATA_DIRECTION_SHIFT); word |= ((write & FSI_MASTER_CTL_DATA_DIRECTION_MASK) << FSI_MASTER_CTL_DATA_DIRECTION_SHIFT); word &= ~(FSI_MASTER_CTL_CYCLE_START_MASK << FSI_MASTER_CTL_CYCLE_START_SHIFT); word |= ((1 & FSI_MASTER_CTL_CYCLE_START_MASK) << FSI_MASTER_CTL_CYCLE_START_SHIFT); - openfsi_master_interface_control_reg_write(word); + write_openfsi_register(OPENFSIMASTER_BASE, FSI_MASTER_REG_CONTROL, word); // Wait for operation to complete - while (!(openfsi_master_interface_status_reg_read() & (FSI_MASTER_CTL_CYCLE_START_MASK << FSI_MASTER_CTL_CYCLE_START_SHIFT))); + while (!(read_openfsi_register(OPENFSIMASTER_BASE, FSI_MASTER_REG_STATUS) & (FSI_MASTER_CTL_CYCLE_START_MASK << FSI_MASTER_CTL_CYCLE_START_SHIFT))); // Read status register - word = openfsi_master_interface_status_reg_read(); + word = read_openfsi_register(OPENFSIMASTER_BASE, FSI_MASTER_REG_STATUS); status_code = (word >> FSI_MASTER_STAT_CYCLE_ERROR_SHIFT) & FSI_MASTER_STAT_CYCLE_ERROR_MASK; // Read data if (!write) - *data = openfsi_master_interface_rx_data_read(); + *data = read_openfsi_register(OPENFSIMASTER_BASE, FSI_MASTER_REG_RX_DATA); #ifdef DEBUG printf("%s(): address 0x%06x, data: 0x%08x sta: 0x%08x\n", __FUNCTION__, address, *data, word); @@ -102,10 +102,10 @@ static int access_fsi_mem(uint8_t slave_id, uint32_t address, fsi_data_length_t } // Clear any operation request flag - word = openfsi_master_interface_control_reg_read(); + word = read_openfsi_register(OPENFSIMASTER_BASE, FSI_MASTER_REG_CONTROL); word &= ~(FSI_MASTER_CTL_CYCLE_START_MASK << FSI_MASTER_CTL_CYCLE_START_SHIFT); word |= ((0 & FSI_MASTER_CTL_CYCLE_START_MASK) << FSI_MASTER_CTL_CYCLE_START_SHIFT); - openfsi_master_interface_control_reg_write(word); + write_openfsi_register(OPENFSIMASTER_BASE, FSI_MASTER_REG_CONTROL, word); return ret; } @@ -124,24 +124,24 @@ static int initialize_fsi_master(void) { return -1; // Clear any pending operation requests - word = openfsi_master_interface_control_reg_read(); + word = read_openfsi_register(OPENFSIMASTER_BASE, FSI_MASTER_REG_CONTROL); word &= ~(FSI_MASTER_CTL_CYCLE_START_MASK << FSI_MASTER_CTL_CYCLE_START_SHIFT); word |= ((0 & FSI_MASTER_CTL_CYCLE_START_MASK) << FSI_MASTER_CTL_CYCLE_START_SHIFT); - openfsi_master_interface_control_reg_write(word); + write_openfsi_register(OPENFSIMASTER_BASE, FSI_MASTER_REG_CONTROL, word); // Wait for any running operation(s) to complete - while (openfsi_master_interface_status_reg_read() & (FSI_MASTER_CTL_CYCLE_START_MASK << FSI_MASTER_CTL_CYCLE_START_SHIFT)); + while (read_openfsi_register(OPENFSIMASTER_BASE, FSI_MASTER_REG_STATUS) & (FSI_MASTER_CTL_CYCLE_START_MASK << FSI_MASTER_CTL_CYCLE_START_SHIFT)); // Set up ACK to CMD turnaround delay and enable CRC protection / enhanced error recovery - word = openfsi_master_interface_control_reg_read(); + word = read_openfsi_register(OPENFSIMASTER_BASE, FSI_MASTER_REG_CONTROL); word &= ~(FSI_MASTER_CTL_CMD_ISSUE_DELAY_MASK << FSI_MASTER_CTL_CMD_ISSUE_DELAY_SHIFT); word |= ((20 & FSI_MASTER_CTL_CMD_ISSUE_DELAY_MASK) << FSI_MASTER_CTL_CMD_ISSUE_DELAY_SHIFT); word |= 1 << FSI_MASTER_CTL_ENABLE_CRC_SHIFT; word |= 1 << FSI_MASTER_CTL_ENABLE_EER_SHIFT; - openfsi_master_interface_control_reg_write(word); + write_openfsi_register(OPENFSIMASTER_BASE, FSI_MASTER_REG_CONTROL, word); #ifdef DEBUG - printf("%s(): after setup: ctl 0x%08x, sta 0x%08x\n", __FUNCTION__, openfsi_master_interface_control_reg_read(), openfsi_master_interface_status_reg_read()); + printf("%s(): after setup: ctl 0x%08x, sta 0x%08x\n", __FUNCTION__, read_openfsi_register(OPENFSIMASTER_BASE, FSI_MASTER_REG_CONTROL), read_openfsi_register(OPENFSIMASTER_BASE, FSI_MASTER_REG_STATUS)); #endif return 0; diff --git a/fsi.h b/fsi.h index 37cc443..3451f9a 100644 --- a/fsi.h +++ b/fsi.h @@ -12,31 +12,50 @@ typedef enum { FSI_DATA_LENGTH_WORD = 2 } fsi_data_length_t; -#define FSI_MASTER_SID_SLAVE_ID_MASK 0x3 -#define FSI_MASTER_SID_SLAVE_ID_SHIFT 29 -#define FSI_MASTER_SID_ADDRESS_MASK 0x1fffff -#define FSI_MASTER_SID_ADDRESS_SHIFT 8 -#define FSI_MASTER_SID_DATA_LENGTH_MASK 0x3 -#define FSI_MASTER_SID_DATA_LENGTH_SHIFT 0 - -#define FSI_MASTER_CTL_CMD_ISSUE_DELAY_MASK 0xff -#define FSI_MASTER_CTL_CMD_ISSUE_DELAY_SHIFT 8 -#define FSI_MASTER_CTL_DATA_DIRECTION_MASK 0x1 -#define FSI_MASTER_CTL_DATA_DIRECTION_SHIFT 1 -#define FSI_MASTER_CTL_ENABLE_CRC_MASK 0x1 -#define FSI_MASTER_CTL_ENABLE_CRC_SHIFT 17 -#define FSI_MASTER_CTL_ENABLE_EER_MASK 0x1 -#define FSI_MASTER_CTL_ENABLE_EER_SHIFT 16 -#define FSI_MASTER_CTL_CYCLE_START_MASK 0x1 -#define FSI_MASTER_CTL_CYCLE_START_SHIFT 0 - -#define FSI_MASTER_STAT_CYCLE_ERROR_MASK 0x7 -#define FSI_MASTER_STAT_CYCLE_ERROR_SHIFT 8 -#define FSI_MASTER_STAT_CYCLE_COMPLETE_MASK 0x1 -#define FSI_MASTER_STAT_CYCLE_COMPLETE_SHIFT 0 - -#define FSI_DIRECTION_READ 0 -#define FSI_DIRECTION_WRITE 1 +// Peripheral registers +#define FSI_MASTER_REG_DEVICE_ID_HIGH 0x0 +#define FSI_MASTER_REG_DEVICE_ID_LOW 0x4 +#define FSI_MASTER_REG_DEVICE_VERSION 0x8 +#define FSI_MASTER_REG_SID_ADR 0xc +#define FSI_MASTER_REG_CONTROL 0x10 +#define FSI_MASTER_REG_STATUS 0x14 +#define FSI_MASTER_REG_TX_DATA 0x18 +#define FSI_MASTER_REG_RX_DATA 0x1c +#define FSI_MASTER_REG_DMA_IRQ 0x20 + +#define FSI_MASTER_SID_SLAVE_ID_MASK 0x3 +#define FSI_MASTER_SID_SLAVE_ID_SHIFT 29 +#define FSI_MASTER_SID_ADDRESS_MASK 0x1fffff +#define FSI_MASTER_SID_ADDRESS_SHIFT 8 +#define FSI_MASTER_SID_DATA_LENGTH_MASK 0x3 +#define FSI_MASTER_SID_DATA_LENGTH_SHIFT 0 + +#define FSI_MASTER_CTL_CMD_ISSUE_DELAY_MASK 0xff +#define FSI_MASTER_CTL_CMD_ISSUE_DELAY_SHIFT 8 +#define FSI_MASTER_CTL_DATA_DIRECTION_MASK 0x1 +#define FSI_MASTER_CTL_DATA_DIRECTION_SHIFT 1 +#define FSI_MASTER_CTL_ENABLE_CRC_MASK 0x1 +#define FSI_MASTER_CTL_ENABLE_CRC_SHIFT 17 +#define FSI_MASTER_CTL_ENABLE_EER_MASK 0x1 +#define FSI_MASTER_CTL_ENABLE_EER_SHIFT 16 +#define FSI_MASTER_CTL_CYCLE_START_MASK 0x1 +#define FSI_MASTER_CTL_CYCLE_START_SHIFT 0 + +#define FSI_MASTER_STAT_CYCLE_ERROR_MASK 0x7 +#define FSI_MASTER_STAT_CYCLE_ERROR_SHIFT 8 +#define FSI_MASTER_STAT_CYCLE_COMPLETE_MASK 0x1 +#define FSI_MASTER_STAT_CYCLE_COMPLETE_SHIFT 0 + +#define FSI_DIRECTION_READ 0 +#define FSI_DIRECTION_WRITE 1 + +static inline uint32_t read_openfsi_register(unsigned long base_address, uint8_t reg) { + return *((volatile uint32_t*)(base_address + reg)); +} + +static inline void write_openfsi_register(unsigned long base_address, uint8_t reg, uint32_t data) { + *((volatile uint32_t*)(base_address + reg)) = data; +} int run_pre_ipl_fixups(void); int start_ipl(int side); diff --git a/ipmi_bt.h b/ipmi_bt.h new file mode 100644 index 0000000..407f767 --- /dev/null +++ b/ipmi_bt.h @@ -0,0 +1,171 @@ +// © 2020 Raptor Engineering, LLC +// +// Released under the terms of the AGPL v3 +// See the LICENSE file for full details + +#ifndef _IPMI_BT_H +#define _IPMI_BT_H + +// Bitfield flag shifts +#define IPMI_BT_CTL_CLEAR_WR_PTR_SHIFT 0 +#define IPMI_BT_CTL_CLEAR_RD_PTR_SHIFT 1 +#define IPMI_BT_CTL_H2B_ATN_SHIFT 2 +#define IPMI_BT_CTL_B2H_ATN_SHIFT 3 +#define IPMI_BT_CTL_SMS_ATN_SHIFT 4 +#define IPMI_BT_CTL_OEM0_SHIFT 5 +#define IPMI_BT_CTL_H_BUSY_SHIFT 6 +#define IPMI_BT_CTL_B_BUSY_SHIFT 7 + +// Sensor mappings +// NOTE: These may change from platform to platform! +// The values below are valid for Raptor Computing Systems Talos II / Blackbird +#define IPMI_SENSOR_FW_BOOT 0x02 // XML ID /sys-0/fw_boot_sensor [W] +#define IPMI_SENSOR_CPU0_OCC_ACTIVE 0x03 // XML ID /sys-0/node-0/motherboard-0/proc_socket-0/module-0/p9_proc_s/occ/occ_active_sensor [W] +#define IPMI_SENSOR_CPU1_OCC_ACTIVE 0x04 // XML ID /sys-0/node-0/motherboard-0/proc_socket-1/module-0/p9_proc_s/occ/occ_active_sensor [W] +#define IPMI_SENSOR_CPU0_FUNC 0x08 // XML ID /sys-0/node-0/motherboard-0/proc_socket-0/module-0/p9_proc_s/cpu_func_sensor [W] +#define IPMI_SENSOR_CPU1_FUNC 0x09 // XML ID /sys-0/node-0/motherboard-0/proc_socket-1/module-0/p9_proc_s/cpu_func_sensor [W] +#define IPMI_SENSOR_BOOT_COUNT 0x8b // XML ID /sys-0/boot_count_sensor [RW] +#define IPMI_SENSOR_PLANAR_FAULT 0x8c // XML ID /sys-0/node-0/motherboard_fault_sensor [W] +#define IPMI_SENSOR_REF_CLK_FAULT 0x8d // XML ID /sys-0/node-0/ref_clk_sensor [W] +#define IPMI_SENSOR_PCIE_CLK_FAULT 0x8e // XML ID /sys-0/node-0/pci_clk_sensor [W] +#define IPMI_SENSOR_TOD_CLK_FAULT 0x8f // XML ID /sys-0/node-0/tod_clk_sensor [W] +#define IPMI_SENSOR_APSS_FAULT 0x93 // XML ID /sys-0/node-0/apss_fault_sensor [W] +#define IPMI_SENSOR_DERATING_FACTOR 0x96 // XML ID /sys-0/ps_derating_sensor [R] + +// Inventory bitfield sensor ranges +#define IPMI_SENSOR_INV_DIMM_FUNC_BEG 0x0b // XML ID /sys-0/node-0/motherboard-0/dimmconn-<X>/dimm-0/dimm_func_sensor [W] +#define IPMI_SENSOR_INV_DIMM_FUNC_END 0x1a +#define IPMI_SENSOR_INV_CPU0_FUNC_BEG 0x2b // XML ID /sys-0/node-0/motherboard-0/proc_socket-0/module-0/p9_proc_s/eq<X>/ex<Y>/core<Z>/cpucore_func_sensor [W] +#define IPMI_SENSOR_INV_CPU0_FUNC_END 0x42 +#define IPMI_SENSOR_INV_CPU1_FUNC_BEG 0x43 // XML ID /sys-0/node-0/motherboard-0/proc_socket-1/module-0/p9_proc_s/eq<X>/ex<Y>/core<Z>/cpucore_func_sensor [W] +#define IPMI_SENSOR_INV_CPU1_FUNC_END 0x5a + +// Event Data Byte bitfield mappings +// These are believed constant across hostboot versions, and are defined in hostboot src/include/usr/ipmi/ipmisensor.H +#define IPMI_OP_EVT_SHIFT_DIMM_DSBL 4 +#define IPMI_OP_EVT_SHIFT_DIMM_PRST 6 +#define IPMI_OP_EVT_SHIFT_PROC_PRST 7 +#define IPMI_OP_EVT_SHIFT_PROC_DSBL 8 + +// IPMI network function codes +#define IPMI_NETFN_SENS_ET_REQ 0x04 +#define IPMI_NETFN_APP_REQUEST 0x06 +#define IPMI_NETFN_STORAGE_REQ 0x0a +#define IPMI_NETFN_DCMI_GP_REQ 0x2c +#define IPMI_NETFN_OEM_IBM_REQ 0x3a + +// Sensor / Event commands +#define IPMI_CMD_GET_SNS_READNG 0x2d +#define IPMI_CMD_SET_SN_RD_EV_S 0x30 + +// Application commands +#define IPMI_CMD_GET_DEVICE_ID 0x01 +#define IPMI_CMD_GET_BT_INT_CAP 0x36 + +// Storage commands +#define IPMI_CMD_GET_SEL_TIME 0x48 +#define IPMI_CMD_SET_SEL_TIME 0x49 + +// IBM commands +#define IPMI_CMD_IBM_HIOMAP_REQ 0x5a + +#define IPMI_CC_NO_ERROR 0x00 +#define IPMI_CC_SNS_NOT_PRSNT 0xcb +#define IPMI_CC_INVALID_COMMAND 0xc1 + +#define BASE_IPMI_REQUEST_LENGTH 3 +#define BASE_IPMI_RESPONSE_LENGTH 4 +#define BASE_DCMI_RESPONSE_LENGTH (BASE_IPMI_RESPONSE_LENGTH + 1) +#define BASE_HIOMAP_RESPONSE_LENGTH (BASE_IPMI_RESPONSE_LENGTH + 2) + +// DCMI commands +#define DCMI_CMD_GET_CAPABILITIES 0x01 +#define DCMI_CMD_GET_POWER_CAP 0x03 + +// DCMI response codes +#define DCMI_CC_NO_ERROR 0x00 +#define DCMI_CC_INVALID_COMMAND 0xc1 + +// DCMI parameters +#define DCMI_ENFORCE_POWER_LIMIT 0 +#define DCMI_POWER_LIMIT_W 190 +#define DCMI_CORR_LIMIT_TIME_MS 500 +#define DCMI_PWR_SAMPLE_PERIOD_S 1 + +// HIOMAP commands +#define HIOMAP_CMD_RESET 0x01 +#define HIOMAP_CMD_GET_INFO 0x02 +#define HIOMAP_CMD_GET_FLASH_INFO 0x03 +#define HIOMAP_CMD_CREATE_RD_WIN 0x04 +#define HIOMAP_CMD_CLOSE_WINDOW 0x05 +#define HIOMAP_CMD_CREATE_WR_WIN 0x06 +#define HIOMAP_CMD_MARK_DIRTY 0x07 +#define HIOMAP_CMD_FLUSH 0x08 +#define HIOMAP_CMD_ACK 0x09 +#define HIOMAP_CMD_ERASE 0x0a +#define HIOMAP_CMD_GET_FLASH_NAME 0x0b +#define HIOMAP_CMD_LOCK 0x0c + +#define HIOMAP_WINDOW_TYPE_READ 0x0 +#define HIOMAP_WINDOW_TYPE_WRITE 0x1 +#define HIOMAP_WINDOW_INACTIVE 0x2 + +// HIOMAP parameters +#define HIOMAP_SUGGESTED_TIMEOUT_S 15 +#define HIOMAP_PNOR_DEVICE_COUNT 1 + +// 4k block size +// Requires subsector erase capability +#define FLASH_BLOCK_SIZE_SHIFT 12 +// 64MB Flash +#define FLASH_SIZE_BYTES 67108864 +// 256 byte pages +#define FLASH_PAGE_SIZE_BYTES 256 +// Minimum erase size is one block +#define FLASH_ERASE_GRAN_BLOCKS 1 +// Maximum cacheable / writeable chunk size +// Constrained by available BRAM in the FPGA +#define FLASH_MAX_WR_WINDOW_BYTES 4096 + +// Calculated and specification locked HIOMAP parameters +#define LPC_ADDRESS_BITS 28 +#define FLASH_SIZE_BLOCKS (FLASH_SIZE_BYTES >> FLASH_BLOCK_SIZE_SHIFT) +#define FLASH_ERASE_GRAN_BYTES (FLASH_ERASE_GRAN_BLOCKS << FLASH_BLOCK_SIZE_SHIFT) + +// OpenPOWER system constants +#define OPENPOWER_BOOT_COUNT_SENSOR_DEFAULT 2 + +typedef struct hiomap_pnor_dirty_range { + uint32_t start_address; + uint32_t bytes; + uint8_t erased; +} hiomap_pnor_dirty_range_t; + +typedef struct hiomap_configuration_data { + uint8_t protocol_version; + uint32_t window_start_address; + uint32_t window_length_bytes; + uint8_t active_device_id; + uint8_t window_type; + uint8_t dirty_range_count; + hiomap_pnor_dirty_range_t dirty_ranges[128]; +} hiomap_configuration_data_t; + +typedef struct __attribute__((packed)) ipmi_request_message { + uint8_t length; + uint8_t netfn_lun; + uint8_t sequence; + uint8_t command; + uint8_t data[256 - BASE_IPMI_REQUEST_LENGTH]; +} ipmi_request_message_t; + +typedef struct __attribute__((packed)) ipmi_response_message { + uint8_t length; + uint8_t netfn_lun; + uint8_t sequence; + uint8_t command; + uint8_t completion_code; + uint8_t data[256 - BASE_IPMI_REQUEST_LENGTH]; +} ipmi_response_message_t; + +#endif // _IPMI_BT_H diff --git a/isr.c b/isr.c index 670b6ba..b988e40 100644 --- a/isr.c +++ b/isr.c @@ -3,24 +3,81 @@ #include <irq.h> #include <uart.h> -void isr(void); +#include "aquila.h" + +uint32_t irq_unhandled_vector = 0; +uint32_t irq_unhandled_source = 0; +uint8_t irq_unhandled_vector_valid = 0; +uint8_t irq_unhandled_source_valid = 0; + +void isr(uint64_t vec); +void isr_dec(void); #ifdef CONFIG_CPU_HAS_INTERRUPT -void isr(void) +void isr(uint64_t vec) { - __attribute__((unused)) unsigned int irqs; + if (vec == 0x900) + { + // DEC interrupt + isr_dec(); + return; + } + + if (vec == 0x500) + { + // Read interrupt source + uint32_t xirr = xics_icp_readw(PPC_XICS_XIRR); + uint32_t irq_source = xirr & 0x00ffffff; - irqs = irq_pending() & irq_getmask(); + __attribute__((unused)) unsigned int irqs; + + // Handle IPI interrupts separately + if (irq_source == 2) { + // IPI interrupt + xics_icp_writeb(PPC_XICS_MFRR, 0xff); + } + else if (irq_source == 0) { + // Unknown source, slently ignore... + } + else { + // External interrupt + irqs = irq_pending() & irq_getmask(); #ifndef UART_POLLING - if(irqs & (1 << UART_INTERRUPT)) - uart_isr(); + if (irqs & (1 << UART_INTERRUPT)) + uart_isr(); #endif + + if (irqs & (1 << HOSTLPCSLAVE_INTERRUPT)) + lpc_slave_isr(); + + if (!irqs) { + irq_unhandled_source = irq_source; + irq_unhandled_source_valid = 1; + } + } + + // Clear interrupt + xics_icp_writew(PPC_XICS_XIRR, xirr); + + return; + } + + irq_unhandled_vector = vec; + irq_unhandled_vector_valid = 1; + while (1); +} + +void isr_dec(void) +{ + // For now, just set DEC back to a large enough value to slow the flood of DEC-initiated timer interrupts + mtdec(0x000000000ffffff); } #else void isr(void){}; +void isr_dec(void){}; #endif diff --git a/linker.ld b/linker.ld index 420a48d..9f2b7f4 100644 --- a/linker.ld +++ b/linker.ld @@ -1,8 +1,6 @@ INCLUDE generated/output_format.ld ENTRY(_start) -__DYNAMIC = 0; - INCLUDE generated/regions.ld SECTIONS @@ -10,33 +8,47 @@ SECTIONS .text : { _ftext = .; + *(.text .stub .text.* .gnu.linkonce.t.*) _etext = .; } > main_ram .rodata : { - . = ALIGN(4); + . = ALIGN(8); _frodata = .; *(.rodata .rodata.* .gnu.linkonce.r.*) *(.rodata1) + *(.got .got.*) + *(.toc .toc.*) + + /* Make sure the file is aligned on disk as well + as in memory; CRC calculation requires that. */ + FILL(0); + . = ALIGN(8); _erodata = .; } > main_ram .data : { - . = ALIGN(4); + . = ALIGN(8); _fdata = .; *(.data .data.* .gnu.linkonce.d.*) *(.data1) - _gp = ALIGN(16); *(.sdata .sdata.* .gnu.linkonce.s.*) + + /* Make sure the file is aligned on disk as well + as in memory; CRC calculation requires that. */ + FILL(0); + . = ALIGN(8); _edata = .; } > main_ram .bss : { - . = ALIGN(4); + . = ALIGN(8); + __rom_isr_address = .; + . = . + 8; _fbss = .; *(.dynsbss) *(.sbss .sbss.* .gnu.linkonce.sb.*) @@ -44,10 +56,19 @@ SECTIONS *(.dynbss) *(.bss .bss.* .gnu.linkonce.b.*) *(COMMON) - . = ALIGN(4); + . = ALIGN(8); _ebss = .; _end = .; } > sram + + /DISCARD/ : + { + *(.eh_frame) + *(.comment) + } } -PROVIDE(_fstack = ORIGIN(sram) + LENGTH(sram) - 4); +PROVIDE(_fstack = ORIGIN(sram) + LENGTH(sram) - 8); + +PROVIDE(_fdata_rom = LOADADDR(.data)); +PROVIDE(_edata_rom = LOADADDR(.data) + SIZEOF(.data)); diff --git a/main.c b/main.c index e6ed208..ce98ea1 100644 --- a/main.c +++ b/main.c @@ -1,8 +1,10 @@ -// © 2020 Raptor Engineering, LLC +// © 2020 - 2021 Raptor Engineering, LLC // // Released under the terms of the AGPL v3 // See the LICENSE file for full details +#define WITH_SPI 1 + #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -15,10 +17,92 @@ #include <generated/csr.h> #include <generated/mem.h> +#include "utility.h" + #include "fsi.h" +#ifdef WITH_SPI #include "tercel_spi.h" +#include "micron_n25q_flash.h" +#endif + +#include "aquila.h" +#include "ipmi_bt.h" +#include "opencores_i2c.h" + +// Performance controls +#define ENABLE_LPC_FW_CYCLE_IRQ_HANDLER +#define ENABLE_LPC_FW_CYCLE_DMA + +// Debug knobs +// #define DEBUG_HOST_SPI_FLASH_READ 1 + +// General RCS platform registers +#define HOST_PLATFORM_FPGA_I2C_REG_STATUS 0x7 +#define HOST_PLATFORM_FPGA_I2C_REG_MFR_OVR 0x33 + +// Host platform configuration +#define HOST_PLATFORM_FPGA_I2C_ADDRESS 0x31 + +extern uint32_t irq_unhandled_vector; +extern uint32_t irq_unhandled_source; +extern uint8_t irq_unhandled_vector_valid; +extern uint8_t irq_unhandled_source_valid; + +#define VUART_INTERRUPT_TRANSIENT_BUFFER_SIZE 32 + +// Interrupt transient VUART1 buffer +static uint8_t vuart1_incoming_interrupt_transient_buffer[VUART_INTERRUPT_TRANSIENT_BUFFER_SIZE]; +static int vuart1_incoming_interrupt_transient_buffer_pos = 0; +static uint8_t vuart1_incoming_interrupt_transient_buffer_overflow = 0; + +// BMC to host VUART1 buffer +static uint8_t vuart1_outgoing_buffer[512]; +static int vuart1_outgoing_buffer_read_pos = 0; +static int vuart1_outgoing_buffer_write_pos = 0; + +// Host to BMC VUART1 buffer +static uint8_t vuart1_incoming_buffer[512]; +static int vuart1_incoming_buffer_read_pos = 0; +static int vuart1_incoming_buffer_write_pos = 0; + +// Interrupt transient VUART2 buffer +static uint8_t vuart2_incoming_interrupt_transient_buffer[VUART_INTERRUPT_TRANSIENT_BUFFER_SIZE]; +static int vuart2_incoming_interrupt_transient_buffer_pos = 0; +static uint8_t vuart2_incoming_interrupt_transient_buffer_overflow = 0; + +// // BMC to host VUART2 buffer +// static uint8_t vuart2_outgoing_buffer[512]; +// static int vuart2_outgoing_buffer_read_pos = 0; +// static int vuart2_outgoing_buffer_write_pos = 0; -#define DEBUG_HOST_SPI_FLASH_READ 1 +// Host to BMC VUART2 buffer +static uint8_t vuart2_incoming_buffer[512]; +// static int vuart2_incoming_buffer_read_pos = 0; +static int vuart2_incoming_buffer_write_pos = 0; + +// IPMI BT buffer +static ipmi_request_message_t ipmi_bt_interrupt_transient_request; +static uint8_t ipmi_bt_interrupt_transient_request_valid = 0; +static ipmi_request_message_t ipmi_bt_current_request; + +// HIOMAP +static uint8_t* host_flash_buffer = NULL; +static hiomap_configuration_data_t hiomap_config; + +// Background service tasks +static uint8_t host_background_service_task_active = 0; +static uint8_t host_console_service_task_active = 0; +static int configured_cpu_count = 0; + +// POST codes +uint8_t post_code_high = 0; +uint8_t post_code_low = 0; + +// Global configuration +static uint8_t allow_flash_write = 0; +static uint8_t enable_post_code_console_output = 0; + +void primary_service_event_loop(void); static char *readstr(void) { @@ -55,6 +139,8 @@ static char *readstr(void) } } + primary_service_event_loop(); + return NULL; } @@ -83,9 +169,16 @@ static void help(void) { puts("Available commands:"); puts("help - this command"); - puts("reboot - reboot CPU"); + puts("reboot - reboot BMC CPU"); + puts("poweron - Turn chassis power on, start IPL, and attach to host console"); + puts("status - Print system status"); puts("ipl - Start IPL sequence"); + puts("chassison - Turn chassis power on and prepare for IPL"); + puts("chassisoff - Turn chassis power off"); puts("sbe_status - Get SBE status register"); + puts("post_codes - Enable or disable output of POST codes on console"); + puts("mr <address> <length> - Read data from BMC internal address in 32-bit words"); + puts("mw <address> <length> <data> - Write data from BMC internal address in 32-bit words"); } static void reboot(void) @@ -93,10 +186,1553 @@ static void reboot(void) ctrl_reset_write(1); } +static void display_character(char character, int dp) +{ + uint16_t value; + + // FIXME Only supports numbers for now + switch (character) { + case 0x30: value = 0x003f; break; // 0 + case 0x31: value = 0x0006; break; // 1 + case 0x32: value = 0x221b; break; // 2 + case 0x33: value = 0x220f; break; // 3 + case 0x34: value = 0x2226; break; // 4 + case 0x35: value = 0x222d; break; // 5 + case 0x36: value = 0x223d; break; // 6 + case 0x37: value = 0x0007; break; // 7 + case 0x38: value = 0x223f; break; // 8 + case 0x39: value = 0x222f; break; // 9 + default: value = 0x0000; break; // OFF + } + + gpio3_out_write(~(value | ((dp==0)?0x0000:0x4000))); +} + +static void set_led_bank_display(uint8_t bitfield) +{ + gpio1_out_write(~bitfield); +} + +static void gpio_init(void) +{ + // Set up discrete LED bank + set_led_bank_display(0x00); + gpio1_oe_write(0xff); + + // Set up alphanumeric display + gpio3_out_write(0xefff); + gpio3_oe_write(0xefff); +} + +static void set_lpc_slave_irq_enable(uint8_t enabled) +{ + if (!enabled) { + hostlpcslave_ev_enable_write(0); + irq_setmask(irq_getmask() & ~(1 << HOSTLPCSLAVE_INTERRUPT)); + } + + // Clear pending interrupts + hostlpcslave_ev_pending_write(hostlpcslave_ev_pending_read()); + + if (enabled) { + hostlpcslave_ev_enable_write(AQUILA_EV_MASTER_IRQ); + irq_setmask(irq_getmask() | (1 << HOSTLPCSLAVE_INTERRUPT)); + } +} + +void lpc_slave_isr(void) +{ +#ifdef ENABLE_LPC_FW_CYCLE_IRQ_HANDLER + int byte; + int word; +#endif + uint32_t dword; + uint32_t ev_status; + uint32_t address; + uint32_t physical_flash_address; + uint8_t write_not_read; + uint32_t status1_reg; + uint32_t status2_reg; + uint32_t status4_reg; + uint32_t vuart_status; + volatile ipmi_request_message_t* ipmi_bt_request_ptr; + + ev_status = hostlpcslave_ev_pending_read(); + if (ev_status & AQUILA_EV_MASTER_IRQ) { + // Master IRQ asserted + // Determine source within the LPC slave core + status4_reg = read_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_STATUS4); +#ifdef ENABLE_LPC_FW_CYCLE_IRQ_HANDLER + if (status4_reg & AQUILA_LPC_FW_CYCLE_IRQ_ASSERTED) { + // Firmware cycle request has caused IRQ assert + // This should remain at the beginning of the ISR for maximum transfer performance + status1_reg = read_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_STATUS1); + status2_reg = read_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_STATUS2); + address = (status2_reg >> AQUILA_LPC_STATUS_ACT_ADDR_SHIFT) & AQUILA_LPC_STATUS_ACT_ADDR_MASK; + write_not_read = (status1_reg >> AQUILA_LPC_STATUS_CYC_WNR_SHIFT) & AQUILA_LPC_STATUS_CYC_WNR_MASK; + + if (((status1_reg >> AQUILA_LPC_STATUS_CYCLE_TYPE_SHIFT) & AQUILA_LPC_STATUS_CYCLE_TYPE_MASK) == AQUILA_LPC_STATUS_CYCLE_TYPE_FW) { + uint8_t fw_cycle_idsel = (status1_reg >> AQUILA_LPC_STATUS_FW_CYCLE_IDSEL_SHIFT) & AQUILA_LPC_STATUS_FW_CYCLE_IDSEL_MASK; + uint8_t fw_cycle_msize = (status1_reg >> AQUILA_LPC_STATUS_FW_CYCLE_MSIZE_SHIFT) & AQUILA_LPC_STATUS_FW_CYCLE_MSIZE_MASK; + + if (fw_cycle_idsel == 0) { + // Limit firmware address to 64MB (wrap around) + address &= 0x3ffffff; + + physical_flash_address = address; + if ((address >= hiomap_config.window_start_address) && ((address - hiomap_config.window_start_address) < hiomap_config.window_length_bytes)) { + if (!write_not_read && ((hiomap_config.window_type == HIOMAP_WINDOW_TYPE_READ) || (hiomap_config.window_type == HIOMAP_WINDOW_TYPE_WRITE))) { + if (lpc_fw_msize_to_bytes(fw_cycle_msize) >= 4) { + for (word = 0; word < (lpc_fw_msize_to_bytes(fw_cycle_msize) / 4); word++) { + *((volatile uint32_t*)(HOSTLPCSLAVE_BASE + AQUILA_LPC_FW_DATA_BLOCK_OFFSET + (word * 4))) = *((uint32_t*)(host_flash_buffer + physical_flash_address + (word * 4))); + } + } + else { + for (byte = 0; byte < lpc_fw_msize_to_bytes(fw_cycle_msize); byte++) { + *((volatile uint8_t*)(HOSTLPCSLAVE_BASE + AQUILA_LPC_FW_DATA_BLOCK_OFFSET + byte)) = *((uint8_t*)(host_flash_buffer + physical_flash_address + byte)); + } + } + + // Transfer success -- do not send error + dword = read_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_CONTROL2); + dword &= ~((AQUILA_LPC_CTL_XFER_ERR_MASK) << AQUILA_LPC_CTL_XFER_ERR_SHIFT); + write_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_CONTROL2, dword); + } + else if (write_not_read && (hiomap_config.window_type == HIOMAP_WINDOW_TYPE_WRITE)) { + if (lpc_fw_msize_to_bytes(fw_cycle_msize) >= 4) { + for (word = 0; word < (lpc_fw_msize_to_bytes(fw_cycle_msize) / 4); word++) { + *((uint32_t*)(host_flash_buffer + physical_flash_address + (word * 4))) = *((volatile uint32_t*)(HOSTLPCSLAVE_BASE + AQUILA_LPC_FW_DATA_BLOCK_OFFSET + (word * 4))); + } + } + else { + for (byte = 0; byte < lpc_fw_msize_to_bytes(fw_cycle_msize); byte++) { + *((uint8_t*)(host_flash_buffer + physical_flash_address + byte)) = *((volatile uint8_t*)(HOSTLPCSLAVE_BASE + AQUILA_LPC_FW_DATA_BLOCK_OFFSET + byte)); + } + } + + // Transfer success -- do not send error + dword = read_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_CONTROL2); + dword &= ~((AQUILA_LPC_CTL_XFER_ERR_MASK) << AQUILA_LPC_CTL_XFER_ERR_SHIFT); + write_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_CONTROL2, dword); + } + else { + // Invalid access -- send error + dword = read_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_CONTROL2); + dword |= ((1 & AQUILA_LPC_CTL_XFER_ERR_MASK) << AQUILA_LPC_CTL_XFER_ERR_SHIFT); + write_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_CONTROL2, dword); + } + } + else { + // Invalid access -- send error + dword = read_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_CONTROL2); + dword |= ((1 & AQUILA_LPC_CTL_XFER_ERR_MASK) << AQUILA_LPC_CTL_XFER_ERR_SHIFT); + write_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_CONTROL2, dword); + } + } + else { + // Received firmware cycle request for unknown IDSEL! Dazed and confused, but trying to continue... + // Do not send error + dword = read_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_CONTROL2); + dword &= ~((AQUILA_LPC_CTL_XFER_ERR_MASK) << AQUILA_LPC_CTL_XFER_ERR_SHIFT); + write_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_CONTROL2, dword); + } + + // Acknowledge data transfer + dword = read_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_CONTROL2); + dword |= ((1 & AQUILA_LPC_CTL_XFER_CONT_MASK) << AQUILA_LPC_CTL_XFER_CONT_SHIFT); + write_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_CONTROL2, dword); + } + } +#endif + if (status4_reg & AQUILA_LPC_VUART1_IRQ_ASSERTED) { + // VUART1 has asserted its IRQ + // Copy received characters to IRQ receive buffer + do { + vuart_status = *((volatile uint32_t*)(HOSTLPCSLAVE_BASE + AQUILA_LPC_VUART_BLOCK_OFFSET + 0x0)); + vuart1_incoming_interrupt_transient_buffer[vuart1_incoming_interrupt_transient_buffer_pos] = (vuart_status >> AQUILA_LPC_VUART1_FIFO_READ_SHIFT) & AQUILA_LPC_VUART1_FIFO_READ_MASK; + vuart1_incoming_interrupt_transient_buffer_pos++; + if (vuart1_incoming_interrupt_transient_buffer_pos >= VUART_INTERRUPT_TRANSIENT_BUFFER_SIZE) { + // Transient buffer is full + // Disable VUART1 interrupts, since we are no longer able to service them, then exit the copy routine + dword = (*((volatile uint32_t*)(HOSTLPCSLAVE_BASE + AQUILA_LPC_VUART_BLOCK_OFFSET + AQUILA_LPC_VUART1_CONTROL_REG))); + dword &= ~((1 & AQUILA_LPC_VUART_IRQ_EN_MASK) << AQUILA_LPC_VUART_IRQ_EN_SHIFT); + (*((volatile uint32_t*)(HOSTLPCSLAVE_BASE + AQUILA_LPC_VUART_BLOCK_OFFSET + AQUILA_LPC_VUART1_CONTROL_REG))) = dword; + vuart1_incoming_interrupt_transient_buffer_overflow = 1; + break; + } + } while (!(vuart_status & AQUILA_LPC_VUART1_FIFO_EMPTY)); + } + if (status4_reg & AQUILA_LPC_VUART2_IRQ_ASSERTED) { + // VUART2 has asserted its IRQ + // Copy received characters to IRQ receive buffer + do { + vuart_status = *((volatile uint32_t*)(HOSTLPCSLAVE_BASE + AQUILA_LPC_VUART_BLOCK_OFFSET + 0x0)); + vuart2_incoming_interrupt_transient_buffer[vuart2_incoming_interrupt_transient_buffer_pos] = (vuart_status >> AQUILA_LPC_VUART1_FIFO_READ_SHIFT) & AQUILA_LPC_VUART1_FIFO_READ_MASK; + vuart2_incoming_interrupt_transient_buffer_pos++; + if (vuart2_incoming_interrupt_transient_buffer_pos >= VUART_INTERRUPT_TRANSIENT_BUFFER_SIZE) { + // Transient buffer is full + // Disable VUART2 interrupts, since we are no longer able to service them, then exit the copy routine + dword = (*((volatile uint32_t*)(HOSTLPCSLAVE_BASE + AQUILA_LPC_VUART_BLOCK_OFFSET + AQUILA_LPC_VUART1_CONTROL_REG))); + dword &= ~((1 & AQUILA_LPC_VUART_IRQ_EN_MASK) << AQUILA_LPC_VUART_IRQ_EN_SHIFT); + (*((volatile uint32_t*)(HOSTLPCSLAVE_BASE + AQUILA_LPC_VUART_BLOCK_OFFSET + AQUILA_LPC_VUART1_CONTROL_REG))) = dword; + vuart2_incoming_interrupt_transient_buffer_overflow = 1; + break; + } + } while (!(vuart_status & AQUILA_LPC_VUART1_FIFO_EMPTY)); + } + if (status4_reg & AQUILA_LPC_IPMI_BT_IRQ_ASSERTED) { + // The IPMI BT module has asserted its IRQ + // Copy IPMI BT request to IRQ receive buffer + + // Signal BMC read starting + dword = read_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_IPMI_BT_STATUS) & (1 << IPMI_BT_CTL_B_BUSY_SHIFT); + if (!(dword & (1 << IPMI_BT_CTL_B_BUSY_SHIFT))) { + // Set B_BUSY + dword |= (1 << IPMI_BT_CTL_B_BUSY_SHIFT); + } + write_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_IPMI_BT_STATUS, dword); + + // Clear H2B_ATN + dword = 0; + dword |= (1 << IPMI_BT_CTL_H2B_ATN_SHIFT); + write_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_IPMI_BT_STATUS, dword); + + ipmi_bt_request_ptr = (ipmi_request_message_t*)(HOSTLPCSLAVE_BASE + AQUILA_LPC_IPMI_BT_DATA_BLOCK_OFFSET); + ipmi_bt_interrupt_transient_request = *ipmi_bt_request_ptr; + + // Signal BMC read complete + dword = read_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_IPMI_BT_STATUS) & (1 << IPMI_BT_CTL_B_BUSY_SHIFT); + if (dword & (1 << IPMI_BT_CTL_B_BUSY_SHIFT)) { + // Clear B_BUSY + dword |= (1 << IPMI_BT_CTL_B_BUSY_SHIFT); + } + write_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_IPMI_BT_STATUS, dword); + + ipmi_bt_interrupt_transient_request_valid = 1; + } + } + + hostlpcslave_ev_pending_write(AQUILA_EV_MASTER_IRQ); +} + +uint8_t uart_register_bank[8]; + +static uint8_t ipmi_bt_transaction_state; + +static void configure_flash_write_enable(uint8_t enable_writes) { + // Set user mode + write_tercel_register(HOSTSPIFLASHCFG_BASE, TERCEL_SPI_REG_SYS_CORE_CTL1, read_tercel_register(HOSTSPIFLASHCFG_BASE, TERCEL_SPI_REG_SYS_CORE_CTL1) | (TERCEL_SPI_ENABLE_USER_MODE_MASK << TERCEL_SPI_ENABLE_USER_MODE_SHIFT)); + + if (enable_writes) { + // Send write enable command + *((volatile uint8_t*)HOSTSPIFLASH_BASE) = 0x06; + } + else { + // Send write disable command + *((volatile uint8_t*)HOSTSPIFLASH_BASE) = 0x04; + } + + // Clear user mode + write_tercel_register(HOSTSPIFLASHCFG_BASE, TERCEL_SPI_REG_SYS_CORE_CTL1, read_tercel_register(HOSTSPIFLASHCFG_BASE, TERCEL_SPI_REG_SYS_CORE_CTL1) & ~(TERCEL_SPI_ENABLE_USER_MODE_MASK << TERCEL_SPI_ENABLE_USER_MODE_SHIFT)); +} + +static uint8_t read_flash_flag_status_register(void) { + uint8_t flag_status = 0; + + // Set user mode + write_tercel_register(HOSTSPIFLASHCFG_BASE, TERCEL_SPI_REG_SYS_CORE_CTL1, read_tercel_register(HOSTSPIFLASHCFG_BASE, TERCEL_SPI_REG_SYS_CORE_CTL1) | (TERCEL_SPI_ENABLE_USER_MODE_MASK << TERCEL_SPI_ENABLE_USER_MODE_SHIFT)); + + // Send Read Flag Status Register command + *((volatile uint8_t*)HOSTSPIFLASH_BASE) = 0x70; + + // Read response + flag_status = *((volatile uint8_t*)HOSTSPIFLASH_BASE); + + // Clear user mode + write_tercel_register(HOSTSPIFLASHCFG_BASE, TERCEL_SPI_REG_SYS_CORE_CTL1, read_tercel_register(HOSTSPIFLASHCFG_BASE, TERCEL_SPI_REG_SYS_CORE_CTL1) & ~(TERCEL_SPI_ENABLE_USER_MODE_MASK << TERCEL_SPI_ENABLE_USER_MODE_SHIFT)); + + return flag_status; +} + +static void reset_flash_device(void) { + // Set user mode + write_tercel_register(HOSTSPIFLASHCFG_BASE, TERCEL_SPI_REG_SYS_CORE_CTL1, read_tercel_register(HOSTSPIFLASHCFG_BASE, TERCEL_SPI_REG_SYS_CORE_CTL1) | (TERCEL_SPI_ENABLE_USER_MODE_MASK << TERCEL_SPI_ENABLE_USER_MODE_SHIFT)); + + // Issue RESET ENABLE command + *((volatile uint8_t*)HOSTSPIFLASH_BASE) = 0x66; + + // Clear user mode + write_tercel_register(HOSTSPIFLASHCFG_BASE, TERCEL_SPI_REG_SYS_CORE_CTL1, read_tercel_register(HOSTSPIFLASHCFG_BASE, TERCEL_SPI_REG_SYS_CORE_CTL1) & ~(TERCEL_SPI_ENABLE_USER_MODE_MASK << TERCEL_SPI_ENABLE_USER_MODE_SHIFT)); + + // Set user mode + write_tercel_register(HOSTSPIFLASHCFG_BASE, TERCEL_SPI_REG_SYS_CORE_CTL1, read_tercel_register(HOSTSPIFLASHCFG_BASE, TERCEL_SPI_REG_SYS_CORE_CTL1) | (TERCEL_SPI_ENABLE_USER_MODE_MASK << TERCEL_SPI_ENABLE_USER_MODE_SHIFT)); + + // Issue RESET MEMORY command + *((volatile uint8_t*)HOSTSPIFLASH_BASE) = 0x99; + + // Clear user mode + write_tercel_register(HOSTSPIFLASHCFG_BASE, TERCEL_SPI_REG_SYS_CORE_CTL1, read_tercel_register(HOSTSPIFLASHCFG_BASE, TERCEL_SPI_REG_SYS_CORE_CTL1) & ~(TERCEL_SPI_ENABLE_USER_MODE_MASK << TERCEL_SPI_ENABLE_USER_MODE_SHIFT)); +} + +static void configure_flash_device(void) { + uint8_t config_byte; + + // Set user mode + write_tercel_register(HOSTSPIFLASHCFG_BASE, TERCEL_SPI_REG_SYS_CORE_CTL1, read_tercel_register(HOSTSPIFLASHCFG_BASE, TERCEL_SPI_REG_SYS_CORE_CTL1) | (TERCEL_SPI_ENABLE_USER_MODE_MASK << TERCEL_SPI_ENABLE_USER_MODE_SHIFT)); + + // Enable 4 byte addressing mode + *((volatile uint8_t*)HOSTSPIFLASH_BASE) = 0xb7; + + // Clear user mode + write_tercel_register(HOSTSPIFLASHCFG_BASE, TERCEL_SPI_REG_SYS_CORE_CTL1, read_tercel_register(HOSTSPIFLASHCFG_BASE, TERCEL_SPI_REG_SYS_CORE_CTL1) & ~(TERCEL_SPI_ENABLE_USER_MODE_MASK << TERCEL_SPI_ENABLE_USER_MODE_SHIFT)); + + configure_flash_write_enable(1); + + // Set user mode + write_tercel_register(HOSTSPIFLASHCFG_BASE, TERCEL_SPI_REG_SYS_CORE_CTL1, read_tercel_register(HOSTSPIFLASHCFG_BASE, TERCEL_SPI_REG_SYS_CORE_CTL1) | (TERCEL_SPI_ENABLE_USER_MODE_MASK << TERCEL_SPI_ENABLE_USER_MODE_SHIFT)); + + // Initialize volatile configuration register + *((volatile uint8_t*)HOSTSPIFLASH_BASE) = 0x81; + + config_byte = 0; + config_byte |= (MICRON_N25Q_SPI_FAST_READ_DUMMY_CLOCK_CYCLES & 0xf) << 4; + config_byte |= (1 & 0x1) << 3; + config_byte |= (0 & 0x1) << 2; + config_byte |= (3 & 0x3) << 0; + + *((volatile uint8_t*)HOSTSPIFLASH_BASE) = config_byte; + + // Clear user mode + write_tercel_register(HOSTSPIFLASHCFG_BASE, TERCEL_SPI_REG_SYS_CORE_CTL1, read_tercel_register(HOSTSPIFLASHCFG_BASE, TERCEL_SPI_REG_SYS_CORE_CTL1) & ~(TERCEL_SPI_ENABLE_USER_MODE_MASK << TERCEL_SPI_ENABLE_USER_MODE_SHIFT)); + + configure_flash_write_enable(0); +} + +static void erase_flash_subsector(uint32_t address) { + // Limit Flash address to active memory + address = address & 0x0fffffff; + + while (!(read_flash_flag_status_register() & 0x80)) { + // Wait for pending operation to complete + } + + // Set user mode + write_tercel_register(HOSTSPIFLASHCFG_BASE, TERCEL_SPI_REG_SYS_CORE_CTL1, read_tercel_register(HOSTSPIFLASHCFG_BASE, TERCEL_SPI_REG_SYS_CORE_CTL1) | (TERCEL_SPI_ENABLE_USER_MODE_MASK << TERCEL_SPI_ENABLE_USER_MODE_SHIFT)); + + // Send subsector erase command + *((volatile uint8_t*)HOSTSPIFLASH_BASE) = 0x21; + + // Send address + *((volatile uint8_t*)HOSTSPIFLASH_BASE) = (address >> 24) & 0xff; + *((volatile uint8_t*)HOSTSPIFLASH_BASE) = (address >> 16) & 0xff; + *((volatile uint8_t*)HOSTSPIFLASH_BASE) = (address >> 8) & 0xff; + *((volatile uint8_t*)HOSTSPIFLASH_BASE) = address & 0xff; + + // Clear user mode + write_tercel_register(HOSTSPIFLASHCFG_BASE, TERCEL_SPI_REG_SYS_CORE_CTL1, read_tercel_register(HOSTSPIFLASHCFG_BASE, TERCEL_SPI_REG_SYS_CORE_CTL1) & ~(TERCEL_SPI_ENABLE_USER_MODE_MASK << TERCEL_SPI_ENABLE_USER_MODE_SHIFT)); + + while (!(read_flash_flag_status_register() & 0x80)) { + // Wait for pending operation to complete + } +} + +static int write_data_to_flash(uint8_t* write_buffer, uint32_t bytes, uint32_t flash_offset, uint8_t erase_before_write) { + uint32_t flash_address; + uint32_t bytes_remaining; + + // Limit Flash address to active memory + flash_offset = flash_offset & 0x0fffffff; + + if (allow_flash_write) { + // Flash erase if needed, then write data + if (erase_before_write) { + for (flash_address = flash_offset; (flash_address - flash_offset) < bytes; flash_address = flash_address + FLASH_ERASE_GRAN_BYTES) { + configure_flash_write_enable(1); + erase_flash_subsector(flash_address); + } + + configure_flash_write_enable(0); + } + + for (flash_address = flash_offset; (flash_address - flash_offset) < bytes; flash_address = flash_address + FLASH_PAGE_SIZE_BYTES) { + bytes_remaining = bytes - (flash_address - flash_offset); + configure_flash_write_enable(1); + while (!(read_flash_flag_status_register() & 0x80)) { + // Wait for pending operation to complete + } + memcpy((uint8_t*)(HOSTSPIFLASH_BASE + flash_address), write_buffer + (flash_address - flash_offset), (bytes_remaining > 256)?256:bytes_remaining); + while (!(read_flash_flag_status_register() & 0x80)) { + // Wait for pending operation to complete + } + } + + configure_flash_write_enable(0); + + return -1; + } + else { + return 0; + } +} + +// NOTE +// The POWER9 host uses true multitasking (kernel preemptive), so it is entirely possible to receive various LPC commands +// during processing of others. As a result, we need at least a primitive multitasking system for the BMC. +// For now, use cooperative multitasking in this basic bare metal firmware... +// All functions called from the main TX/RX loop should return within some timeframe, e.g. 10ms +static void process_host_to_bmc_ipmi_bt_transactions(void) { + uint32_t dword; + + static uint8_t unhandled_ipmi_command; + volatile ipmi_response_message_t* response_ptr; + static ipmi_response_message_t response; + static uint8_t request_netfn; + static uint8_t request_lun; + + uint32_t offset_bytes = 0; + uint32_t length_bytes = 0; + uint8_t flags = 0; + + int i; + + switch (ipmi_bt_transaction_state) { + case 0: + // Idle + break; + case 1: + // Extract NETFN/LUN from request + request_netfn = ipmi_bt_current_request.netfn_lun >> 2; + request_lun = ipmi_bt_current_request.netfn_lun & 0x3; + + // Set up basic response parameters + response.netfn_lun = (((request_netfn + 1) & 0x3f) << 2) | (request_lun & 0x3); + response.sequence = ipmi_bt_current_request.sequence; + response.command = ipmi_bt_current_request.command; + response.length = BASE_IPMI_RESPONSE_LENGTH; + response.completion_code = IPMI_CC_INVALID_COMMAND; + memset(response.data, 0, sizeof(response.data)); + + unhandled_ipmi_command = 0; + switch (request_netfn) { + case IPMI_NETFN_SENS_ET_REQ: + unhandled_ipmi_command = 1; + break; + case IPMI_NETFN_APP_REQUEST: + switch (ipmi_bt_current_request.command) { + case IPMI_CMD_GET_DEVICE_ID: + response.data[0] = 0x00; + response.data[1] = 0x00; + response.data[2] = 0x00; + response.data[3] = 0x00; + response.data[4] = 0x02; + response.data[5] = 0x00; + response.data[6] = 0x05; + response.data[7] = 0xcb; + response.data[8] = 0x00; + response.data[9] = 0x01; + response.data[10] = 0x00; + response.data[11] = 0x00; + response.data[12] = 0x00; + response.data[13] = 0x00; + response.data[14] = 0x00; + response.length = BASE_IPMI_RESPONSE_LENGTH + 15; + response.completion_code = IPMI_CC_NO_ERROR; + break; + case IPMI_CMD_GET_BT_INT_CAP: + response.data[0] = 0x01; + response.data[1] = 0x3f; + response.data[2] = 0x3f; + response.data[3] = 0x01; + response.data[4] = 0x01; + response.length = BASE_IPMI_RESPONSE_LENGTH + 5; + response.completion_code = IPMI_CC_NO_ERROR; + break; + default: + unhandled_ipmi_command = 1; + break; + } + break; + case IPMI_NETFN_STORAGE_REQ: + unhandled_ipmi_command = 1; + break; + case IPMI_NETFN_DCMI_GP_REQ: + unhandled_ipmi_command = 1; + break; + case IPMI_NETFN_OEM_IBM_REQ: + switch (ipmi_bt_current_request.command) { + case IPMI_CMD_IBM_HIOMAP_REQ: + switch (ipmi_bt_current_request.data[0]) { + case HIOMAP_CMD_GET_INFO: + if (ipmi_bt_current_request.data[2] > 3) { + // We only support up to the HIOMAP v3 protocol + hiomap_config.protocol_version = 3; + } + else { + hiomap_config.protocol_version = ipmi_bt_current_request.data[2]; + } + switch (hiomap_config.protocol_version) { + case 1: + response.data[2] = hiomap_config.protocol_version; + response.data[3] = FLASH_SIZE_BLOCKS & 0xff; + response.data[4] = (FLASH_SIZE_BLOCKS >> 8) & 0xff; + response.data[5] = FLASH_SIZE_BLOCKS & 0xff; + response.data[6] = (FLASH_SIZE_BLOCKS >> 8) & 0xff; + response.length = BASE_HIOMAP_RESPONSE_LENGTH + 5; + break; + case 2: + response.data[2] = hiomap_config.protocol_version; + response.data[3] = FLASH_BLOCK_SIZE_SHIFT; + response.data[4] = HIOMAP_SUGGESTED_TIMEOUT_S & 0xff; + response.data[5] = (HIOMAP_SUGGESTED_TIMEOUT_S >> 8) & 0xff; + response.length = BASE_HIOMAP_RESPONSE_LENGTH + 4; + break; + case 3: + response.data[2] = hiomap_config.protocol_version; + response.data[3] = FLASH_BLOCK_SIZE_SHIFT; + response.data[4] = HIOMAP_SUGGESTED_TIMEOUT_S & 0xff; + response.data[5] = (HIOMAP_SUGGESTED_TIMEOUT_S >> 8) & 0xff; + response.data[6] = HIOMAP_PNOR_DEVICE_COUNT; + response.length = BASE_HIOMAP_RESPONSE_LENGTH + 5; + break; + } + response.data[0] = ipmi_bt_current_request.data[0]; + response.data[1] = ipmi_bt_current_request.data[1]; + response.completion_code = IPMI_CC_NO_ERROR; + break; + case HIOMAP_CMD_GET_FLASH_INFO: + switch (hiomap_config.protocol_version) { + case 1: + response.data[2] = FLASH_SIZE_BYTES & 0xff; + response.data[3] = (FLASH_SIZE_BYTES >> 8) & 0xff; + response.data[4] = (FLASH_SIZE_BYTES >> 16) & 0xff; + response.data[5] = (FLASH_SIZE_BYTES >> 24) & 0xff; + response.data[6] = FLASH_ERASE_GRAN_BYTES & 0xff; + response.data[7] = (FLASH_ERASE_GRAN_BYTES >> 8) & 0xff; + response.data[8] = (FLASH_ERASE_GRAN_BYTES >> 16) & 0xff; + response.data[9] = (FLASH_ERASE_GRAN_BYTES >> 24) & 0xff; + response.length = BASE_HIOMAP_RESPONSE_LENGTH + 8; + break; + case 2: + // Fall through, same format as protocol version 3 + case 3: + response.data[2] = FLASH_SIZE_BLOCKS & 0xff; + response.data[3] = (FLASH_SIZE_BLOCKS >> 8) & 0xff; + response.data[4] = FLASH_ERASE_GRAN_BLOCKS & 0xff; + response.data[5] = (FLASH_ERASE_GRAN_BLOCKS >> 8) & 0xff; + response.length = BASE_HIOMAP_RESPONSE_LENGTH + 4; + break; + } + response.data[0] = ipmi_bt_current_request.data[0]; + response.data[1] = ipmi_bt_current_request.data[1]; + response.completion_code = IPMI_CC_NO_ERROR; + break; + case HIOMAP_CMD_CREATE_RD_WIN: + case HIOMAP_CMD_CREATE_WR_WIN: + // Parse request data + hiomap_config.window_start_address = (((((uint32_t)ipmi_bt_current_request.data[3]) << 8) | ipmi_bt_current_request.data[2]) << FLASH_BLOCK_SIZE_SHIFT) & ((1 << LPC_ADDRESS_BITS) - 1); + hiomap_config.window_length_bytes = (((((uint32_t)ipmi_bt_current_request.data[5]) << 8) | ipmi_bt_current_request.data[4]) << FLASH_BLOCK_SIZE_SHIFT) & ((1 << LPC_ADDRESS_BITS) - 1); + hiomap_config.active_device_id = ipmi_bt_current_request.data[6]; + if (ipmi_bt_current_request.data[0] == HIOMAP_CMD_CREATE_RD_WIN) + hiomap_config.window_type = HIOMAP_WINDOW_TYPE_READ; + else if (ipmi_bt_current_request.data[0] == HIOMAP_CMD_CREATE_WR_WIN) + hiomap_config.window_type = HIOMAP_WINDOW_TYPE_WRITE; + else + hiomap_config.window_type = HIOMAP_WINDOW_INACTIVE; + + // Sanitize input + switch (hiomap_config.protocol_version) { + case 1: + if (ipmi_bt_current_request.data[0] == HIOMAP_CMD_CREATE_RD_WIN) { + // Size unspecified, use one block as the size + hiomap_config.window_length_bytes = 1 << FLASH_BLOCK_SIZE_SHIFT; + } + if (ipmi_bt_current_request.data[0] == HIOMAP_CMD_CREATE_WR_WIN) { + // Size unspecified, use one block or the maximum write + // cache size as the returned size, whichever is smaller... + if (FLASH_MAX_WR_WINDOW_BYTES < (1 << FLASH_BLOCK_SIZE_SHIFT)) { + hiomap_config.window_length_bytes = FLASH_MAX_WR_WINDOW_BYTES; + } + else { + hiomap_config.window_length_bytes = 1 << FLASH_BLOCK_SIZE_SHIFT; + } + } + break; + case 2: + case 3: + if (ipmi_bt_current_request.data[0] == HIOMAP_CMD_CREATE_RD_WIN) { + // Zero sized window indicates undefined size, but must be at least one block + // Just use one block as the size in this corner case... + if (hiomap_config.window_length_bytes == 0) { + hiomap_config.window_length_bytes = 1 << FLASH_BLOCK_SIZE_SHIFT; + } + } + if (ipmi_bt_current_request.data[0] == HIOMAP_CMD_CREATE_WR_WIN) { + // Zero sized window indicates undefined size, but must be at least one block + // Just use one block as the size in this corner case... + if (hiomap_config.window_length_bytes == 0) { + hiomap_config.window_length_bytes = 1 << FLASH_BLOCK_SIZE_SHIFT; + } + else { + // The host can only request a window size, not demand one + // If the request is larger than our write cache size, limit + // the returned window to the write cache size... + if (hiomap_config.window_length_bytes > FLASH_MAX_WR_WINDOW_BYTES) { + hiomap_config.window_length_bytes = FLASH_MAX_WR_WINDOW_BYTES; + } + } + } + break; + } + +#ifdef ENABLE_LPC_FW_CYCLE_DMA + // Disable DMA engine + dword = read_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_DMA_CONFIG1); + dword &= ~((1 & AQUILA_LPC_CTL_EN_FW_DMA_R_MASK) << AQUILA_LPC_CTL_EN_FW_DMA_R_SHIFT); + dword &= ~((1 & AQUILA_LPC_CTL_EN_FW_DMA_W_MASK) << AQUILA_LPC_CTL_EN_FW_DMA_W_SHIFT); + write_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_DMA_CONFIG1, dword); + + // Reconfigure LPC firmware cycle DMA ranges + write_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_DMA_CONFIG2, (uintptr_t)host_flash_buffer); + write_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_DMA_CONFIG3, FLASH_SIZE_BYTES); + write_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_DMA_CONFIG4, hiomap_config.window_start_address); + write_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_DMA_CONFIG5, hiomap_config.window_start_address + hiomap_config.window_length_bytes); + write_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_DMA_CONFIG6, FLASH_SIZE_BYTES - 1); + + // Enable DMA engine + dword = read_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_DMA_CONFIG1); + dword |= ((1 & AQUILA_LPC_CTL_EN_FW_DMA_R_MASK) << AQUILA_LPC_CTL_EN_FW_DMA_R_SHIFT); + if (hiomap_config.window_type == HIOMAP_WINDOW_TYPE_WRITE) { + dword |= ((1 & AQUILA_LPC_CTL_EN_FW_DMA_W_MASK) << AQUILA_LPC_CTL_EN_FW_DMA_W_SHIFT); + } + else { + dword &= ~((1 & AQUILA_LPC_CTL_EN_FW_DMA_W_MASK) << AQUILA_LPC_CTL_EN_FW_DMA_W_SHIFT); + } + write_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_DMA_CONFIG1, dword); +#endif + + // Generate response + switch (hiomap_config.protocol_version) { + case 1: + // Use 1:1 mapping between LPC firmware address and SPI Flash address + response.data[2] = (hiomap_config.window_start_address >> FLASH_BLOCK_SIZE_SHIFT) & 0xff; + response.data[3] = ((hiomap_config.window_start_address >> FLASH_BLOCK_SIZE_SHIFT) >> 8) & 0xff; + response.length = BASE_HIOMAP_RESPONSE_LENGTH + 2; + break; + case 2: + case 3: + // Use 1:1 mapping between LPC firmware address and SPI Flash address + response.data[2] = (hiomap_config.window_start_address >> FLASH_BLOCK_SIZE_SHIFT) & 0xff; + response.data[3] = ((hiomap_config.window_start_address >> FLASH_BLOCK_SIZE_SHIFT) >> 8) & 0xff; + // Echo configured Flash window start / length + response.data[4] = (hiomap_config.window_length_bytes >> FLASH_BLOCK_SIZE_SHIFT) & 0xff; + response.data[5] = ((hiomap_config.window_length_bytes >> FLASH_BLOCK_SIZE_SHIFT) >> 8) & 0xff; + response.data[6] = (hiomap_config.window_start_address >> FLASH_BLOCK_SIZE_SHIFT) & 0xff; + response.data[7] = ((hiomap_config.window_start_address >> FLASH_BLOCK_SIZE_SHIFT) >> 8) & 0xff; + response.length = BASE_HIOMAP_RESPONSE_LENGTH + 6; + break; + } + + response.data[0] = ipmi_bt_current_request.data[0]; + response.data[1] = ipmi_bt_current_request.data[1]; + response.completion_code = IPMI_CC_NO_ERROR; + break; + case HIOMAP_CMD_MARK_DIRTY: + flags = 0; + switch (hiomap_config.protocol_version) { + case 1: + offset_bytes = (((((uint32_t)ipmi_bt_current_request.data[3]) << 8) | ipmi_bt_current_request.data[2]) << FLASH_BLOCK_SIZE_SHIFT) & ((1 << LPC_ADDRESS_BITS) - 1); + length_bytes = ((((uint32_t)ipmi_bt_current_request.data[7]) << 24) | (((uint32_t)ipmi_bt_current_request.data[6]) << 16) | (((uint32_t)ipmi_bt_current_request.data[5]) << 8) | ipmi_bt_current_request.data[4]); + break; + case 2: + case 3: + offset_bytes = hiomap_config.window_start_address + ((((((uint32_t)ipmi_bt_current_request.data[3]) << 8) | ipmi_bt_current_request.data[2]) << FLASH_BLOCK_SIZE_SHIFT) & ((1 << LPC_ADDRESS_BITS) - 1)); + length_bytes = (((((uint32_t)ipmi_bt_current_request.data[5]) << 8) | ipmi_bt_current_request.data[4]) << FLASH_BLOCK_SIZE_SHIFT) & ((1 << LPC_ADDRESS_BITS) - 1); + if (hiomap_config.protocol_version == 3) { + flags = ipmi_bt_current_request.data[6]; + } + break; + } + + // Record dirty page + hiomap_config.dirty_ranges[hiomap_config.dirty_range_count].start_address = offset_bytes; + hiomap_config.dirty_ranges[hiomap_config.dirty_range_count].bytes = length_bytes; + hiomap_config.dirty_ranges[hiomap_config.dirty_range_count].erased = flags & 0x1; + hiomap_config.dirty_range_count++; + + response.data[0] = ipmi_bt_current_request.data[0]; + response.data[1] = ipmi_bt_current_request.data[1]; + response.length = BASE_HIOMAP_RESPONSE_LENGTH; + response.completion_code = IPMI_CC_NO_ERROR; + break; + case HIOMAP_CMD_FLUSH: + if (hiomap_config.protocol_version == 1) { + // Only HIOMAP protocol v1 has the ability to mark a page dirty in the FLUSH command + offset_bytes = (((((uint32_t)ipmi_bt_current_request.data[3]) << 8) | ipmi_bt_current_request.data[2]) << FLASH_BLOCK_SIZE_SHIFT) & ((1 << LPC_ADDRESS_BITS) - 1); + length_bytes = ((((uint32_t)ipmi_bt_current_request.data[7]) << 24) | (((uint32_t)ipmi_bt_current_request.data[6]) << 16) | (((uint32_t)ipmi_bt_current_request.data[5]) << 8) | ipmi_bt_current_request.data[4]); + + // Record dirty page + hiomap_config.dirty_ranges[hiomap_config.dirty_range_count].start_address = offset_bytes; + hiomap_config.dirty_ranges[hiomap_config.dirty_range_count].bytes = length_bytes; + hiomap_config.dirty_ranges[hiomap_config.dirty_range_count].erased = 0; + hiomap_config.dirty_range_count++; + } + + for(i = 0; i < hiomap_config.dirty_range_count; i++) { + write_data_to_flash(((uint8_t*)(host_flash_buffer + hiomap_config.dirty_ranges[i].start_address)), hiomap_config.dirty_ranges[i].bytes, hiomap_config.dirty_ranges[i].start_address, !hiomap_config.dirty_ranges[i].erased); + } + hiomap_config.dirty_range_count = 0; + + response.data[0] = ipmi_bt_current_request.data[0]; + response.data[1] = ipmi_bt_current_request.data[1]; + response.length = BASE_HIOMAP_RESPONSE_LENGTH; + response.completion_code = IPMI_CC_NO_ERROR; + case HIOMAP_CMD_ACK: + // Mask is in ipmi_bt_current_request.data[2] + // For now just ignore and claim sucess + response.data[0] = ipmi_bt_current_request.data[0]; + response.data[1] = ipmi_bt_current_request.data[1]; + response.length = BASE_HIOMAP_RESPONSE_LENGTH; + response.completion_code = IPMI_CC_NO_ERROR; + break; + default: + unhandled_ipmi_command = 1; + break; + } + break; + default: + unhandled_ipmi_command = 1; + break; + } + break; + default: + unhandled_ipmi_command = 1; + break; + } + + if (unhandled_ipmi_command) { + response.length = BASE_IPMI_RESPONSE_LENGTH; + response.completion_code = IPMI_CC_INVALID_COMMAND; + } + + ipmi_bt_transaction_state = 2; + break; + case 2: + // Wait for H_BUSY clear + if (!(read_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_IPMI_BT_STATUS) & (1 << IPMI_BT_CTL_H_BUSY_SHIFT))) { + ipmi_bt_transaction_state = 3; + } + break; + case 3: + // Initialize pointer + response_ptr = (ipmi_response_message_t*)(HOSTLPCSLAVE_BASE + AQUILA_LPC_IPMI_BT_DATA_BLOCK_OFFSET); + + // Send response + // A full copy is done so as to ensure any potentially sensitive data stored in the IPMI BT buffer from a previous request is overwritten + *response_ptr = response; + + // Signal BMC data ready + dword = 0; + dword |= (1 << IPMI_BT_CTL_B2H_ATN_SHIFT); + write_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_IPMI_BT_STATUS, dword); + + ipmi_bt_transaction_state = 4; + break; + case 4: + // Wait for processing to complete + // If B2H_ATN, and H_BUSY are both clear, processing has been completed + dword = read_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_IPMI_BT_STATUS); + if ((!(dword & (1 << IPMI_BT_CTL_B2H_ATN_SHIFT))) && (!(dword & (1 << IPMI_BT_CTL_H_BUSY_SHIFT)))) { + ipmi_bt_transaction_state = 0; + } + break; + default: + ipmi_bt_transaction_state = 0; + break; + } +} + +#ifndef ENABLE_LPC_FW_CYCLE_IRQ_HANDLER +static uint32_t previous_fw_read_address; +#endif + +static void process_interrupts_stage2(void) { + uint32_t dword; + int read_position; + + // Deactivate interrupts on entering critical section + irq_setie(0); + + // CRITICAL SECTION + // No interrupts can fire here! + // All code in this section must be able to run in bounded time -- do NOT wait on external events etc. here, just move and enqueue data as needed for further processing at a later time + + // Process incoming VUART data + if (vuart1_incoming_interrupt_transient_buffer_pos > 0) { + read_position = 0; + while (read_position < vuart1_incoming_interrupt_transient_buffer_pos) { + vuart1_incoming_buffer[vuart1_incoming_buffer_write_pos] = vuart1_incoming_interrupt_transient_buffer[read_position]; + vuart1_incoming_buffer_write_pos++; + if (vuart1_incoming_buffer_write_pos >= 512) { + vuart1_incoming_buffer_write_pos = 0; + } + read_position++; + if (read_position >= 512) { + break; + } + } + vuart1_incoming_interrupt_transient_buffer_pos = 0; + if (vuart1_incoming_interrupt_transient_buffer_overflow) { + // Reenable VUART1 interrupts + dword = (*((volatile uint32_t*)(HOSTLPCSLAVE_BASE + AQUILA_LPC_VUART_BLOCK_OFFSET + AQUILA_LPC_VUART1_CONTROL_REG))); + dword |= (1 & AQUILA_LPC_VUART_IRQ_EN_MASK) << AQUILA_LPC_VUART_IRQ_EN_SHIFT; + (*((volatile uint32_t*)(HOSTLPCSLAVE_BASE + AQUILA_LPC_VUART_BLOCK_OFFSET + AQUILA_LPC_VUART1_CONTROL_REG))) = dword; + vuart1_incoming_interrupt_transient_buffer_overflow = 0; + } + } + if (vuart2_incoming_interrupt_transient_buffer_pos > 0) { + read_position = 0; + while (read_position < vuart2_incoming_interrupt_transient_buffer_pos) { + vuart2_incoming_buffer[vuart2_incoming_buffer_write_pos] = vuart2_incoming_interrupt_transient_buffer[read_position]; + vuart2_incoming_buffer_write_pos++; + if (vuart2_incoming_buffer_write_pos >= 512) { + vuart2_incoming_buffer_write_pos = 0; + } + read_position++; + if (read_position >= 512) { + break; + } + } + vuart2_incoming_interrupt_transient_buffer_pos = 0; + if (vuart2_incoming_interrupt_transient_buffer_overflow) { + // Reenable VUART1 interrupts + dword = (*((volatile uint32_t*)(HOSTLPCSLAVE_BASE + AQUILA_LPC_VUART_BLOCK_OFFSET + AQUILA_LPC_VUART1_CONTROL_REG))); + dword |= (1 & AQUILA_LPC_VUART_IRQ_EN_MASK) << AQUILA_LPC_VUART_IRQ_EN_SHIFT; + (*((volatile uint32_t*)(HOSTLPCSLAVE_BASE + AQUILA_LPC_VUART_BLOCK_OFFSET + AQUILA_LPC_VUART1_CONTROL_REG))) = dword; + vuart2_incoming_interrupt_transient_buffer_overflow = 0; + } + } + + // Process incoming IPMI BT request data + if (ipmi_bt_interrupt_transient_request_valid) { + if (ipmi_bt_transaction_state == 0) { + ipmi_bt_current_request = ipmi_bt_interrupt_transient_request; + ipmi_bt_interrupt_transient_request_valid = 0; + ipmi_bt_transaction_state = 1; + } + } + + // Re-activate interupts on exiting critical section + irq_setie(1); +} + +static void run_pre_ipl_bmc_peripheral_setup(void) +{ + uint32_t dword; + + // Reset POST codes and display + post_code_high = 0; + post_code_low = 0; + set_led_bank_display(0x00); + + // Deactivate interrupts on entering critical section + irq_setie(0); + + // Reset VUART1 FIFO pointers + vuart1_incoming_interrupt_transient_buffer_pos = 0; + vuart1_incoming_interrupt_transient_buffer_overflow = 0; + vuart1_outgoing_buffer_read_pos = 0; + vuart1_outgoing_buffer_write_pos = 0; + vuart1_incoming_buffer_read_pos = 0; + vuart1_incoming_buffer_write_pos = 0; + + // Re-activate interupts on exiting critical section + irq_setie(1); + + // Configure VUART1 + dword = 0; + dword |= (1 & AQUILA_LPC_VUART_FIFO_TRIG_LVL_MASK) << AQUILA_LPC_VUART_FIFO_TRIG_LVL_SHIFT; + dword |= (1 & AQUILA_LPC_VUART_IRQ_EN_MASK) << AQUILA_LPC_VUART_IRQ_EN_SHIFT; + dword |= (1 & AQUILA_LPC_VUART_FIFO_IRQ_EN_MASK) << AQUILA_LPC_VUART_FIFO_IRQ_EN_SHIFT; + (*((volatile uint32_t*)(HOSTLPCSLAVE_BASE + AQUILA_LPC_VUART_BLOCK_OFFSET + AQUILA_LPC_VUART1_CONTROL_REG))) = dword; + + // Enable LPC slave IRQs + set_lpc_slave_irq_enable(1); + + // Clear IPMI BT B_BUSY flag + dword = read_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_IPMI_BT_STATUS) & (1 << IPMI_BT_CTL_B_BUSY_SHIFT); + if (dword & (1 << IPMI_BT_CTL_B_BUSY_SHIFT)) { + dword |= (1 << IPMI_BT_CTL_B_BUSY_SHIFT); + } + write_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_IPMI_BT_STATUS, dword); + + // Enable IPMI BT IRQ + dword = read_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_CONTROL1); + dword |= ((1 & AQUILA_LPC_CTL_EN_IPMI_BT_IRQ_MASK) << AQUILA_LPC_CTL_EN_IPMI_BT_IRQ_SHIFT); + write_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_CONTROL1, dword); + +#ifdef ENABLE_LPC_FW_CYCLE_IRQ_HANDLER + // Enable LPC firmware cycle IRQ + dword = read_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_CONTROL1); + dword |= ((1 & AQUILA_LPC_CTL_EN_FW_CYCLE_IRQ_MASK) << AQUILA_LPC_CTL_EN_FW_CYCLE_IRQ_SHIFT); + write_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_CONTROL1, dword); +#endif + + // Reset HIOMAP windows + hiomap_config.protocol_version = 0; + hiomap_config.window_start_address = 0; + hiomap_config.window_length_bytes = FLASH_SIZE_BYTES; + hiomap_config.active_device_id = 0; + hiomap_config.window_type = HIOMAP_WINDOW_TYPE_READ; + hiomap_config.dirty_range_count = 0; + +#ifdef ENABLE_LPC_FW_CYCLE_DMA + // Configure and enable LPC firmware cycle DMA + // Set up default window with address masking based on physical ROM size + write_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_DMA_CONFIG2, (uintptr_t)host_flash_buffer); + write_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_DMA_CONFIG3, FLASH_SIZE_BYTES); + write_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_DMA_CONFIG4, 0x0); + write_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_DMA_CONFIG5, FLASH_SIZE_BYTES); + write_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_DMA_CONFIG6, FLASH_SIZE_BYTES - 1); + + // Enable DMA engine + dword = read_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_DMA_CONFIG1); + dword |= ((1 & AQUILA_LPC_CTL_EN_FW_DMA_R_MASK) << AQUILA_LPC_CTL_EN_FW_DMA_R_SHIFT); + dword &= ~((1 & AQUILA_LPC_CTL_EN_FW_DMA_W_MASK) << AQUILA_LPC_CTL_EN_FW_DMA_W_SHIFT); + write_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_DMA_CONFIG1, dword); +#endif + + // Enable host background service task + host_background_service_task_active = 1; + + // Assume console service task is inactive at startup + host_console_service_task_active = 0; +} + +static void run_post_shutdown_bmc_peripheral_teardown(void) +{ + uint32_t dword; + + // Disable host and console background service tasks + host_background_service_task_active = 0; + host_console_service_task_active = 0; + + // Reset internal state variables + ipmi_bt_transaction_state = 0; + + // Set IPMI BT B_BUSY flag + dword = read_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_IPMI_BT_STATUS) & (1 << IPMI_BT_CTL_B_BUSY_SHIFT); + if (!(dword & (1 << IPMI_BT_CTL_B_BUSY_SHIFT))) { + // Set B_BUSY + dword |= (1 << IPMI_BT_CTL_B_BUSY_SHIFT); + } + write_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_IPMI_BT_STATUS, dword); + +#ifdef ENABLE_LPC_FW_CYCLE_DMA + // Disable DMA engine + dword = read_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_DMA_CONFIG1); + dword &= ~((1 & AQUILA_LPC_CTL_EN_FW_DMA_R_MASK) << AQUILA_LPC_CTL_EN_FW_DMA_R_SHIFT); + dword &= ~((1 & AQUILA_LPC_CTL_EN_FW_DMA_W_MASK) << AQUILA_LPC_CTL_EN_FW_DMA_W_SHIFT); + write_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_DMA_CONFIG1, dword); +#endif + +#ifdef ENABLE_LPC_FW_CYCLE_IRQ_HANDLER + // Disable LPC firmware cycle IRQ + dword = read_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_CONTROL1); + dword &= ~((1 & AQUILA_LPC_CTL_EN_FW_CYCLE_IRQ_MASK) << AQUILA_LPC_CTL_EN_FW_CYCLE_IRQ_SHIFT); + write_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_CONTROL1, dword); +#endif + + // Disable IPMI BT IRQ + dword = read_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_CONTROL1); + dword &= ~((1 & AQUILA_LPC_CTL_EN_IPMI_BT_IRQ_MASK) << AQUILA_LPC_CTL_EN_IPMI_BT_IRQ_SHIFT); + write_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_CONTROL1, dword); + + // Disable LPC slave IRQs + set_lpc_slave_irq_enable(0); + + // Reset POST codes and display + post_code_high = 0; + post_code_low = 0; + set_led_bank_display(0x00); +} + +static int apply_avsbus_workarounds(int cpu_count) { + printf("Applying AVSBus workarounds...\n"); + + printf("\tVDD/VCS 0: Enabling AVSBus CLK/MDAT pullups and selecting VIH/VIL 0x2 (0.65V/0.55V)\n"); + if (i2c_write_register_byte((uint8_t*)I2CMASTER1_BASE, 0x28, 0x2e, 0x23)) { + return -1; + } + + printf("\tVDN 0: Enabling AVSBus CLK/MDAT pullups and selecting VIH/VIL 0x2 (0.65V/0.55V)\n"); + if (i2c_write_register_byte((uint8_t*)I2CMASTER1_BASE, 0x2b, 0x2e, 0x23)) { + return -1; + } + + if (cpu_count > 1) { + printf("\tVDD/VCS 1: Enabling AVSBus CLK/MDAT pullups and selecting VIH/VIL 0x2 (0.65V/0.55V)\n"); + if (i2c_write_register_byte((uint8_t*)I2CMASTER2_BASE, 0x28, 0x2e, 0x23)) { + return -1; + } + + printf("\tVDN 1: Enabling AVSBus CLK/MDAT pullups and selecting VIH/VIL 0x2 (0.65V/0.55V)\n"); + if (i2c_write_register_byte((uint8_t*)I2CMASTER2_BASE, 0x2b, 0x2e, 0x23)) { + return -1; + } + } + + printf("\tAVSBus workaround application complete!\n"); + return 0; +} + +static int enable_avsbus_pmbus(int cpu_count) { + printf("Enabling AVSbus PMBUS functionality...\n"); + + printf("\tVDD 0: Placing device in AVSBus voltage command mode\n"); + if (i2c_write_register_byte((uint8_t*)I2CMASTER1_BASE, 0x70, 0x00, 0x00)) { + return -1; + } + if (i2c_write_register_byte((uint8_t*)I2CMASTER1_BASE, 0x70, 0x01, 0xb0)) { + return -1; + } + + printf("\tVCS 0: Placing device in AVSBus voltage command mode\n"); + if (i2c_write_register_byte((uint8_t*)I2CMASTER1_BASE, 0x70, 0x00, 0x01)) { + return -1; + } + if (i2c_write_register_byte((uint8_t*)I2CMASTER1_BASE, 0x70, 0x01, 0xb0)) { + return -1; + } + + printf("\tVDN 0: Placing device in AVSBus voltage command mode\n"); + if (i2c_write_register_byte((uint8_t*)I2CMASTER1_BASE, 0x73, 0x00, 0x00)) { + return -1; + } + if (i2c_write_register_byte((uint8_t*)I2CMASTER1_BASE, 0x73, 0x01, 0xb0)) { + return -1; + } + + if (cpu_count > 1) { + printf("\tVDD 1: Placing device in AVSBus voltage command mode\n"); + if (i2c_write_register_byte((uint8_t*)I2CMASTER2_BASE, 0x70, 0x00, 0x00)) { + return -1; + } + if (i2c_write_register_byte((uint8_t*)I2CMASTER2_BASE, 0x70, 0x01, 0xb0)) { + return -1; + } + + printf("\tVCS 1: Placing device in AVSBus voltage command mode\n"); + if (i2c_write_register_byte((uint8_t*)I2CMASTER2_BASE, 0x70, 0x00, 0x01)) { + return -1; + } + if (i2c_write_register_byte((uint8_t*)I2CMASTER2_BASE, 0x70, 0x01, 0xb0)) { + return -1; + } + + printf("\tVDN 1: Placing device in AVSBus voltage command mode\n"); + if (i2c_write_register_byte((uint8_t*)I2CMASTER2_BASE, 0x73, 0x00, 0x00)) { + return -1; + } + if (i2c_write_register_byte((uint8_t*)I2CMASTER2_BASE, 0x73, 0x01, 0xb0)) { + return -1; + } + } + + printf("\tAVSBus PMBUS functionality enabled!\n"); + return 0; +} + +static int disable_avsbus_pmbus(int cpu_count) { + printf("Disabling AVSbus PMBUS functionality...\n"); + + printf("\tVDD 0: Placing device in immediate off mode\n"); + if (i2c_write_register_byte((uint8_t*)I2CMASTER1_BASE, 0x70, 0x00, 0x00)) { + return -1; + } + if (i2c_write_register_byte((uint8_t*)I2CMASTER1_BASE, 0x70, 0x01, 0xb0)) { + return -1; + } + + printf("\tVCS 0: Placing device in immediate off mode\n"); + if (i2c_write_register_byte((uint8_t*)I2CMASTER1_BASE, 0x70, 0x00, 0x01)) { + return -1; + } + if (i2c_write_register_byte((uint8_t*)I2CMASTER1_BASE, 0x70, 0x01, 0xb0)) { + return -1; + } + + printf("\tVDN 0: Placing device in immediate off mode\n"); + if (i2c_write_register_byte((uint8_t*)I2CMASTER1_BASE, 0x73, 0x00, 0x00)) { + return -1; + } + if (i2c_write_register_byte((uint8_t*)I2CMASTER1_BASE, 0x73, 0x01, 0xb0)) { + return -1; + } + + if (cpu_count > 1) { + printf("\tVDD 1: Placing device in immediate off mode\n"); + if (i2c_write_register_byte((uint8_t*)I2CMASTER2_BASE, 0x70, 0x00, 0x00)) { + return -1; + } + if (i2c_write_register_byte((uint8_t*)I2CMASTER2_BASE, 0x70, 0x01, 0xb0)) { + return -1; + } + + printf("\tVCS 1: Placing device in immediate off mode\n"); + if (i2c_write_register_byte((uint8_t*)I2CMASTER2_BASE, 0x70, 0x00, 0x01)) { + return -1; + } + if (i2c_write_register_byte((uint8_t*)I2CMASTER2_BASE, 0x70, 0x01, 0xb0)) { + return -1; + } + + printf("\tVDN 1: Placing device in immediate off mode\n"); + if (i2c_write_register_byte((uint8_t*)I2CMASTER2_BASE, 0x73, 0x00, 0x00)) { + return -1; + } + if (i2c_write_register_byte((uint8_t*)I2CMASTER2_BASE, 0x73, 0x01, 0xb0)) { + return -1; + } + } + + printf("\tAVSBus PMBUS functionality disabled!\n"); + return 0; +} + +static void power_off_chassis(void) { + // Disable PMBUS + if (disable_avsbus_pmbus(configured_cpu_count)) { + printf("PMBUS disable failed!\n"); + } + + // Power off host via platform FPGA commands + i2c_write_register_byte((uint8_t*)I2CMASTER4_BASE, HOST_PLATFORM_FPGA_I2C_ADDRESS, HOST_PLATFORM_FPGA_I2C_REG_MFR_OVR, 0x00); + + run_post_shutdown_bmc_peripheral_teardown(); +} + +static int power_on_chassis(void) { + uint8_t platform_fpga_identifier[4]; + int platform_power_on_timeout_counter; + int cpu_count = 1; + int i2c_read_retcode; + uint8_t byte; + + // Verify communication with platform control FPGA + platform_fpga_identifier[0] = i2c_read_register_byte((uint8_t*)I2CMASTER4_BASE, HOST_PLATFORM_FPGA_I2C_ADDRESS, 0x0c, NULL); + if (platform_fpga_identifier[0] == 0xff) { + return -1; + } + platform_fpga_identifier[1] = i2c_read_register_byte((uint8_t*)I2CMASTER4_BASE, HOST_PLATFORM_FPGA_I2C_ADDRESS, 0x0d, NULL); + platform_fpga_identifier[2] = i2c_read_register_byte((uint8_t*)I2CMASTER4_BASE, HOST_PLATFORM_FPGA_I2C_ADDRESS, 0x0e, NULL); + platform_fpga_identifier[3] = i2c_read_register_byte((uint8_t*)I2CMASTER4_BASE, HOST_PLATFORM_FPGA_I2C_ADDRESS, 0x0f, NULL); + if ((platform_fpga_identifier[0] != 0x52) || (platform_fpga_identifier[1] != 0x43) || (platform_fpga_identifier[2] != 0x53) || (platform_fpga_identifier[3] != 0x20)) { + return -1; + } + printf("Platform FPGA communication verified\n"); + + // Enable BMC runtime support tasks + run_pre_ipl_bmc_peripheral_setup(); + + // Power on host via platform FPGA commands + printf("Commanding chassis power ON...\n"); + i2c_write_register_byte((uint8_t*)I2CMASTER4_BASE, HOST_PLATFORM_FPGA_I2C_ADDRESS, HOST_PLATFORM_FPGA_I2C_REG_MFR_OVR, 0x01); + platform_power_on_timeout_counter = 0; + byte = i2c_read_register_byte((uint8_t*)I2CMASTER4_BASE, HOST_PLATFORM_FPGA_I2C_ADDRESS, HOST_PLATFORM_FPGA_I2C_REG_STATUS, &i2c_read_retcode); + while (i2c_read_retcode || (((byte) & 0x03) != 0x03)) { + if (platform_power_on_timeout_counter > 20000) { + printf("Chassis poweron timeout!\n"); + power_off_chassis(); + return -2; + } + usleep(100); + platform_power_on_timeout_counter++; + byte = i2c_read_register_byte((uint8_t*)I2CMASTER4_BASE, HOST_PLATFORM_FPGA_I2C_ADDRESS, HOST_PLATFORM_FPGA_I2C_REG_STATUS, &i2c_read_retcode); + } + if (i2c_read_retcode) { + printf("FPGA communication failure during poweron!\n"); + return -3; + } + if (byte & 0x20) { + cpu_count = 2; + } + printf("Chassis power verified active\n"); + configured_cpu_count = cpu_count; + printf("%d CPU(s) installed\n", cpu_count); + + // Apply AVSBus workarounds + if (apply_avsbus_workarounds(cpu_count)) { + printf("AVSBus setup failed!\n"); + power_off_chassis(); + return -4; + } + + // Enable PMBUS + if (enable_avsbus_pmbus(cpu_count)) { + printf("PMBUS enable failed!\n"); + power_off_chassis(); + return -5; + } + + return 0; +} + +static int power_on_host(void) { + if (power_on_chassis()) { + return -1; + } + + if (start_ipl(0)) { + return -1; + } + + return 0; +} + +static void print_chassis_status(void) { + int i2c_read_retcode; + uint8_t byte; + + byte = i2c_read_register_byte((uint8_t*)I2CMASTER4_BASE, HOST_PLATFORM_FPGA_I2C_ADDRESS, HOST_PLATFORM_FPGA_I2C_REG_STATUS, &i2c_read_retcode); + if (i2c_read_retcode) { + printf("Unable to communicate with platform control FPGA!\n"); + } + + printf("Platform FPGA status:\n"); + if (byte & 0x1) { + printf("\tPSU commanded ON\n"); + } + else { + printf("\tPSU commanded OFF\n"); + } + if (byte & 0x2) { + printf("\tPSU PGOOD asserted\n"); + } + else { + printf("\tPSU PGOOD deasserted\n"); + } + printf("Platform overall status:\n"); + if (byte & 0x20) { + printf("\t2 CPUs installed\n"); + } + else { + printf("\t1 CPU installed\n"); + } + printf("BMC status:\n"); + if (host_background_service_task_active) { + printf("\tBackground host services active\n"); + } + else { + printf("\tBackground host services inactive\n"); + } +} + +static void host_background_service_task_event_loop(void) { + uint32_t address; + uint8_t write_not_read; + uint32_t dword; +#ifndef ENABLE_LPC_FW_CYCLE_IRQ_HANDLER + int byte; + int word; + uint32_t physical_flash_address; +#endif + + // IRQ debugging routines + if (irq_unhandled_source_valid) { + printf("[WARNING] Interrupt triggered without external IRQ set! source: %d\n", irq_unhandled_source); + irq_unhandled_source_valid = 0; + } + if (irq_unhandled_vector_valid) { + printf("[ERROR] Unhandled exception 0x%03d\n", irq_unhandled_vector); + irq_unhandled_vector_valid = 0; + } + + if (read_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_STATUS1) & (AQUILA_LPC_STATUS_ATTN_REQ_MASK << AQUILA_LPC_STATUS_ATTN_REQ_SHIFT)) { + // Store / retrieve data + uint32_t status1_reg = read_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_STATUS1); + uint32_t status2_reg = read_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_STATUS2); + address = (status2_reg >> AQUILA_LPC_STATUS_ACT_ADDR_SHIFT) & AQUILA_LPC_STATUS_ACT_ADDR_MASK; + write_not_read = (status1_reg >> AQUILA_LPC_STATUS_CYC_WNR_SHIFT) & AQUILA_LPC_STATUS_CYC_WNR_MASK; +#ifndef ENABLE_LPC_FW_CYCLE_IRQ_HANDLER + if (((status1_reg >> AQUILA_LPC_STATUS_CYCLE_TYPE_SHIFT) & AQUILA_LPC_STATUS_CYCLE_TYPE_MASK) == AQUILA_LPC_STATUS_CYCLE_TYPE_FW) { + uint8_t fw_cycle_idsel = (status1_reg >> AQUILA_LPC_STATUS_FW_CYCLE_IDSEL_SHIFT) & AQUILA_LPC_STATUS_FW_CYCLE_IDSEL_MASK; + uint8_t fw_cycle_msize = (status1_reg >> AQUILA_LPC_STATUS_FW_CYCLE_MSIZE_SHIFT) & AQUILA_LPC_STATUS_FW_CYCLE_MSIZE_MASK; + + if (fw_cycle_idsel == 0) { + // Limit firmware address to 64MB (wrap around) + address &= 0x3ffffff; + + previous_fw_read_address = address; + physical_flash_address = address; + if ((address >= hiomap_config.window_start_address) && ((address - hiomap_config.window_start_address) < hiomap_config.window_length_bytes)) { + if (!write_not_read && ((hiomap_config.window_type == HIOMAP_WINDOW_TYPE_READ) || (hiomap_config.window_type == HIOMAP_WINDOW_TYPE_WRITE))) { + if (lpc_fw_msize_to_bytes(fw_cycle_msize) >= 4) { + for (word = 0; word < (lpc_fw_msize_to_bytes(fw_cycle_msize) / 4); word++) { + *((volatile uint32_t*)(HOSTLPCSLAVE_BASE + AQUILA_LPC_FW_DATA_BLOCK_OFFSET + (word * 4))) = *((uint32_t*)(host_flash_buffer + physical_flash_address + (word * 4))); + } + } + else { + for (byte = 0; byte < lpc_fw_msize_to_bytes(fw_cycle_msize); byte++) { + *((volatile uint8_t*)(HOSTLPCSLAVE_BASE + AQUILA_LPC_FW_DATA_BLOCK_OFFSET + byte)) = *((uint8_t*)(host_flash_buffer + physical_flash_address + byte)); + } + } + + // Transfer success -- do not send error + dword = read_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_CONTROL2); + dword &= ~((AQUILA_LPC_CTL_XFER_ERR_MASK) << AQUILA_LPC_CTL_XFER_ERR_SHIFT); + write_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_CONTROL2, dword); + } + else if (write_not_read && (hiomap_config.window_type == HIOMAP_WINDOW_TYPE_WRITE)) { + if (lpc_fw_msize_to_bytes(fw_cycle_msize) >= 4) { + for (word = 0; word < (lpc_fw_msize_to_bytes(fw_cycle_msize) / 4); word++) { + *((uint32_t*)(host_flash_buffer + physical_flash_address + (word * 4))) = *((volatile uint32_t*)(HOSTLPCSLAVE_BASE + AQUILA_LPC_FW_DATA_BLOCK_OFFSET + (word * 4))); + } + } + else { + for (byte = 0; byte < lpc_fw_msize_to_bytes(fw_cycle_msize); byte++) { + *((uint8_t*)(host_flash_buffer + physical_flash_address + byte)) = *((volatile uint8_t*)(HOSTLPCSLAVE_BASE + AQUILA_LPC_FW_DATA_BLOCK_OFFSET + byte)); + } + } + + // Transfer success -- do not send error + dword = read_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_CONTROL2); + dword &= ~((AQUILA_LPC_CTL_XFER_ERR_MASK) << AQUILA_LPC_CTL_XFER_ERR_SHIFT); + write_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_CONTROL2, dword); + } + else { + printf("[WARNING] Data transfer attempted without active HIOMAP window! Returning error to host...\n"); + + // Invalid access -- send error + dword = read_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_CONTROL2); + dword |= ((1 & AQUILA_LPC_CTL_XFER_ERR_MASK) << AQUILA_LPC_CTL_XFER_ERR_SHIFT); + write_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_CONTROL2, dword); + } + } + else { + printf("[WARNING] Data transfer attempted outside configured HIOMAP window! Returning error to host...\n"); + + // Invalid access -- send error + dword = read_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_CONTROL2); + dword |= ((1 & AQUILA_LPC_CTL_XFER_ERR_MASK) << AQUILA_LPC_CTL_XFER_ERR_SHIFT); + write_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_CONTROL2, dword); + } + } + else { + printf("[WARNING] Received firmware cycle request for IDSEL 0x%02x (address 0x%08x)! Dazed and confused, but trying to continue...\n", fw_cycle_idsel, address); + + // Do not send error + dword = read_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_CONTROL2); + dword &= ~((AQUILA_LPC_CTL_XFER_ERR_MASK) << AQUILA_LPC_CTL_XFER_ERR_SHIFT); + write_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_CONTROL2, dword); + } + + // Acknowledge data transfer + dword = read_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_CONTROL2); + dword |= ((1 & AQUILA_LPC_CTL_XFER_CONT_MASK) << AQUILA_LPC_CTL_XFER_CONT_SHIFT); + write_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_CONTROL2, dword); + } + else +#endif + if (((status1_reg >> AQUILA_LPC_STATUS_CYCLE_TYPE_SHIFT) & AQUILA_LPC_STATUS_CYCLE_TYPE_MASK) == AQUILA_LPC_STATUS_CYCLE_TYPE_IO) { + if ((address >= 0x80) && (address <= 0x82)) { + if (write_not_read) { + uint8_t post_code = (read_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_STATUS3) >> AQUILA_LPC_STATUS_ACT_WDATA_SHIFT) & AQUILA_LPC_STATUS_ACT_WDATA_MASK; + if (enable_post_code_console_output) { + printf("[POST CODE] %d:%d\n", address - 0x80, post_code); + } + if (address == 0x80) { + post_code_high = post_code; + } + else if (address == 0x81) { + post_code_low = post_code; + set_led_bank_display(((post_code_high & 0xf) << 4) | (post_code_low & 0xf)); + } + } + + // Transfer success -- do not send error + dword = read_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_CONTROL2); + dword &= ~((AQUILA_LPC_CTL_XFER_ERR_MASK) << AQUILA_LPC_CTL_XFER_ERR_SHIFT); + write_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_CONTROL2, dword); + + // Acknowledge data transfer + dword = read_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_CONTROL2); + dword |= ((1 & AQUILA_LPC_CTL_XFER_CONT_MASK) << AQUILA_LPC_CTL_XFER_CONT_SHIFT); + write_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_CONTROL2, dword); + } + else { + printf("[WARNING] LPC I/O transfer attempted to invalid address 0x%04x\n", address); + + // Transfer failed -- send error + dword = read_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_CONTROL2); + dword |= ((1 & AQUILA_LPC_CTL_XFER_ERR_MASK) << AQUILA_LPC_CTL_XFER_ERR_SHIFT); + write_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_CONTROL2, dword); + } + } + else { + // Transfer failed -- send error + dword = read_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_CONTROL2); + dword |= ((1 & AQUILA_LPC_CTL_XFER_ERR_MASK) << AQUILA_LPC_CTL_XFER_ERR_SHIFT); + write_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_CONTROL2, dword); + + // Acknowledge data transfer + dword = read_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_CONTROL2); + dword |= ((1 & AQUILA_LPC_CTL_XFER_CONT_MASK) << AQUILA_LPC_CTL_XFER_CONT_SHIFT); + write_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_CONTROL2, dword); + } + } +} + +void primary_service_event_loop(void) { + // =================================================================================== + // Main service loop + // =================================================================================== + // This loop is called as frequently as practical to keep response times low + // All background tasks, from LPC I/O transfers to IPMI requests, are handled here + // =================================================================================== + + if (host_background_service_task_active) { + // Run background service task event loop + host_background_service_task_event_loop(); + + // Process queued interrupt tasks + process_interrupts_stage2(); + + // Process cooperative multitasking threads + process_host_to_bmc_ipmi_bt_transactions(); + } +} + +static void attach_to_host_console(void) { + uint8_t escape_sequence_state; + uint8_t character; + uint8_t character_read; + + // Deactivate interrupts on entering critical section + irq_setie(0); + + // Reset VUART1 FIFO pointers + vuart1_incoming_interrupt_transient_buffer_pos = 0; + vuart1_incoming_interrupt_transient_buffer_overflow = 0; + vuart1_outgoing_buffer_read_pos = 0; + vuart1_outgoing_buffer_write_pos = 0; + vuart1_incoming_buffer_read_pos = 0; + vuart1_incoming_buffer_write_pos = 0; + + // Re-activate interupts on exiting critical section + irq_setie(1); + + // Signal host console service is now active + host_console_service_task_active = 1; + + // Enter polling loop + escape_sequence_state = 0; + + while (1) { + // Escape sequence handler + character_read = 0; + if (readchar_nonblock()) { + character = readchar(); + character_read = 1; + switch (escape_sequence_state) { + case 0: + if (character == '\n') { + escape_sequence_state = 1; + } + break; + case 1: + if (character == '~') { + escape_sequence_state = 2; + } + else { + escape_sequence_state = 0; + } + break; + case 2: + if (character == '.') { + escape_sequence_state = 3; + } + else { + escape_sequence_state = 0; + } + break; + default: + escape_sequence_state = 0; + } + } + if (escape_sequence_state == 3) { + break; + } + + while (vuart1_outgoing_buffer_write_pos != vuart1_outgoing_buffer_read_pos) { + uint32_t vuart1_status_register = *((volatile uint32_t*)(HOSTLPCSLAVE_BASE + AQUILA_LPC_VUART_BLOCK_OFFSET + AQUILA_LPC_VUART1_STATUS_REG)); + if (!((vuart1_status_register >> AQUILA_LPC_VUART_WFIFO_FULL_SHIFT) & AQUILA_LPC_VUART_WFIFO_FULL_MASK)) { + // VUART FIFO now has room, send queued character to VUART hardware + *((volatile uint8_t*)(HOSTLPCSLAVE_BASE + AQUILA_LPC_VUART_BLOCK_OFFSET + 0x0)) = vuart1_outgoing_buffer[vuart1_outgoing_buffer_read_pos]; + vuart1_outgoing_buffer_read_pos++; + if (vuart1_outgoing_buffer_read_pos >= 512) { + vuart1_outgoing_buffer_read_pos = 0; + } + } + else { + break; + } + } + + if (character_read) { + // Attempt to send character to host + uint32_t vuart1_status_register = *((volatile uint32_t*)(HOSTLPCSLAVE_BASE + AQUILA_LPC_VUART_BLOCK_OFFSET + AQUILA_LPC_VUART1_STATUS_REG)); + if ((vuart1_status_register >> AQUILA_LPC_VUART_WFIFO_FULL_SHIFT) & AQUILA_LPC_VUART_WFIFO_FULL_MASK) { + // VUART FIFO full, add to soft buffer + vuart1_outgoing_buffer[vuart1_outgoing_buffer_write_pos] = character; + vuart1_outgoing_buffer_write_pos++; + if (vuart1_outgoing_buffer_write_pos >= 512) { + vuart1_outgoing_buffer_write_pos = 0; + } + } + else { + // VUART FIFO still has room, send character to VUART hardware + *((volatile uint8_t*)(HOSTLPCSLAVE_BASE + AQUILA_LPC_VUART_BLOCK_OFFSET + 0x0)) = character; + } + } + + // Send any queued VUART output from host to BMC console + while (vuart1_incoming_buffer_write_pos != vuart1_incoming_buffer_read_pos) { + printf("%c", vuart1_incoming_buffer[vuart1_incoming_buffer_read_pos]); + vuart1_incoming_buffer_read_pos++; + if (vuart1_incoming_buffer_read_pos >= 512) { + vuart1_incoming_buffer_read_pos = 0; + } + } + + primary_service_event_loop(); + } + + // Signal host console service is now inactive + host_console_service_task_active = 0; +} + +static uint64_t parse_user_provided_number(const char* string) { + if (((*(string+0)) == '0') && (((*(string+1)) == 'x') || ((*(string+1)) == 'X'))) { + return strtoul(string, NULL, 16); + } + return strtoul(string, NULL, 10); +} + +static uint8_t sanitize_ascii(uint8_t char_in) { + if ((char_in >= 32) && (char_in <= 126)) { + return char_in; + } + return '.'; +} + static void console_service(void) { char *str; char *token; + uint64_t address; + uint32_t data; + unsigned int i; + unsigned int length; str = readstr(); if(str == NULL) return; @@ -109,6 +1745,111 @@ static void console_service(void) start_ipl(0); else if(strcmp(token, "sbe_status") == 0) get_sbe_status(); + else if(strcmp(token, "mr") == 0) { + if (*str) { + token = get_token(&str); + address = parse_user_provided_number(token); + if (*str) { + token = get_token(&str); + length = parse_user_provided_number(token); + } + else { + length = 1; + } + for (i=0; i < length; i++) { + printf("0x%08x: 0x%08x\t%02x%02x%02x%02x\t%c%c%c%c\n", address + (i * 4), *((volatile uint32_t*)(address + (i * 4))), *((volatile uint8_t*)(address + (i * 4)+0)), *((volatile uint8_t*)(address + (i * 4)+1)), *((volatile uint8_t*)(address + (i * 4)+2)), *((volatile uint8_t*)(address + (i * 4)+3)), sanitize_ascii(*((volatile uint8_t*)(address + (i * 4)+0))), sanitize_ascii(*((volatile uint8_t*)(address + (i * 4)+1))), sanitize_ascii(*((volatile uint8_t*)(address + (i * 4)+2))), sanitize_ascii(*((volatile uint8_t*)(address + (i * 4)+3)))); + } + } + else { + printf("USAGE: mr <memory address>\n"); + } + } + else if(strcmp(token, "mw") == 0) { + if (*str) { + token = get_token(&str); + address = parse_user_provided_number(token); + if (*str) { + token = get_token(&str); + length = parse_user_provided_number(token); + } + else { + length = 1; + } + i = 0; + while (*str) { + token = get_token(&str); + data = parse_user_provided_number(token); + *((volatile uint32_t*)(address)) = data; + i++; + if (i >= length) { + break; + } + } + } + else { + printf("USAGE: mr <memory address>\n"); + } + } + else if(strcmp(token, "flash_write") == 0) { + if (*str) { + token = get_token(&str); + if (strcmp(token, "enable") == 0) { + allow_flash_write = 1; + printf("Flash write ENABLED\n"); + } + else if (strcmp(token, "disable") == 0) { + allow_flash_write = 0; + printf("Flash write DISABLED\n"); + } + else { + printf("USAGE: flash_write <enable|disable>\n"); + } + } + else { + printf("USAGE: flash_write <enable|disable>\n"); + } + } + else if(strcmp(token, "chassison") == 0) { + power_on_chassis(); + } + else if(strcmp(token, "chassisoff") == 0) { + power_off_chassis(); + printf("Chassis power commanded OFF\n"); + } + else if(strcmp(token, "status") == 0) { + print_chassis_status(); + } + else if(strcmp(token, "poweron") == 0) { + if (power_on_host() == 0) { + attach_to_host_console(); + } + else { + printf("Host poweron procedure FAILED\n"); + } + } + else if(strcmp(token, "post_codes") == 0) { + if (*str) { + token = get_token(&str); + if (strcmp(token, "enable") == 0) { + enable_post_code_console_output = 1; + printf("POST code console output ENABLED\n"); + } + else if (strcmp(token, "disable") == 0) { + enable_post_code_console_output = 0; + printf("POST code console output DISABLED\n"); + } + else { + printf("USAGE: post_codes <enable|disable>\n"); + } + } + else { + printf("USAGE: post_codes <enable|disable>\n"); + } + } + else if(strcmp(token, "console") == 0) + attach_to_host_console(); + else if(strcmp(token, "") != 0) + printf("%s: command not found\n", token); prompt(); } @@ -121,11 +1862,20 @@ static void memcpy32(uint32_t *destination, uint32_t *source, int words) { } } +static void memset32(uint32_t *destination, uint32_t value, int words) { + int word; + for (word = 0; word < words; word++) { + *destination = value; + destination++; + } +} + +#ifdef WITH_SPI static uint32_t read_host_spi_flash_id(void) { uint32_t flash_id = 0; // Set user mode - hostspiflash_core_ctl1_write(hostspiflash_core_ctl1_read() | (TERCEL_SPI_ENABLE_USER_MODE_MASK << TERCEL_SPI_ENABLE_USER_MODE_SHIFT)); + write_tercel_register(HOSTSPIFLASHCFG_BASE, TERCEL_SPI_REG_SYS_CORE_CTL1, read_tercel_register(HOSTSPIFLASHCFG_BASE, TERCEL_SPI_REG_SYS_CORE_CTL1) | (TERCEL_SPI_ENABLE_USER_MODE_MASK << TERCEL_SPI_ENABLE_USER_MODE_SHIFT)); // Send Flash ID command *((volatile uint8_t*)HOSTSPIFLASH_BASE) = 0x9e; @@ -137,104 +1887,213 @@ static uint32_t read_host_spi_flash_id(void) { flash_id = (flash_id << 8) | (*((volatile uint8_t*)HOSTSPIFLASH_BASE) & 0xff); // Clear user mode - hostspiflash_core_ctl1_write(hostspiflash_core_ctl1_read() & ~(TERCEL_SPI_ENABLE_USER_MODE_MASK << TERCEL_SPI_ENABLE_USER_MODE_SHIFT)); + write_tercel_register(HOSTSPIFLASHCFG_BASE, TERCEL_SPI_REG_SYS_CORE_CTL1, read_tercel_register(HOSTSPIFLASHCFG_BASE, TERCEL_SPI_REG_SYS_CORE_CTL1) & ~(TERCEL_SPI_ENABLE_USER_MODE_MASK << TERCEL_SPI_ENABLE_USER_MODE_SHIFT)); return flash_id; } static int host_spi_flash_init(void) { + int i; uint32_t dword; + uint32_t flash_device_id; - if (!(hostspiflash_device_id_read() == 0x7c5250545350494d)) { + if ((read_tercel_register(HOSTSPIFLASHCFG_BASE, TERCEL_SPI_REG_DEVICE_ID_HIGH) != TERCEL_SPI_DEVICE_ID_HIGH) + || (read_tercel_register(HOSTSPIFLASHCFG_BASE, TERCEL_SPI_REG_DEVICE_ID_LOW) != TERCEL_SPI_DEVICE_ID_LOW)) { return -1; } - printf("Raptor Tercel SPI master found\n"); + uint32_t tercel_version = read_tercel_register(HOSTSPIFLASHCFG_BASE, TERCEL_SPI_REG_DEVICE_VERSION); + printf("Raptor Tercel SPI master found, device version %0d.%0d.%d\n", (tercel_version >> TERCEL_SPI_VERSION_MAJOR_SHIFT) & TERCEL_SPI_VERSION_MAJOR_MASK, (tercel_version >> TERCEL_SPI_VERSION_MINOR_SHIFT) & TERCEL_SPI_VERSION_MINOR_MASK, (tercel_version >> TERCEL_SPI_VERSION_PATCH_SHIFT) & TERCEL_SPI_VERSION_PATCH_MASK); + + flash_device_id = read_host_spi_flash_id(); + for (i = 0; i < (sizeof(micron_n25q_spi_device_ids) / sizeof(micron_n25q_spi_device_ids[0])); i++) { + if (flash_device_id == micron_n25q_spi_device_ids[i]) { + printf("%s Flash device detected, configuring\n", micron_n25q_spi_device_names[i]); + + // Set up Flash-specific commands + dword = 0; + dword |= (MICRON_N25Q_SPI_4BA_QSPI_READ_CMD & TERCEL_SPI_4BA_QSPI_CMD_MASK) << TERCEL_SPI_4BA_QSPI_CMD_SHIFT; + dword |= (MICRON_N25Q_SPI_3BA_QSPI_READ_CMD & TERCEL_SPI_3BA_QSPI_CMD_MASK) << TERCEL_SPI_3BA_QSPI_CMD_SHIFT; + dword |= (MICRON_N25Q_SPI_4BA_SPI_READ_CMD & TERCEL_SPI_4BA_SPI_CMD_MASK) << TERCEL_SPI_4BA_SPI_CMD_SHIFT; + dword |= (MICRON_N25Q_SPI_3BA_SPI_READ_CMD & TERCEL_SPI_3BA_SPI_CMD_MASK) << TERCEL_SPI_3BA_SPI_CMD_SHIFT; + write_tercel_register(HOSTSPIFLASHCFG_BASE, TERCEL_SPI_REG_SYS_FLASH_CFG1, dword); + + dword = 0; + dword |= (MICRON_N25Q_SPI_4BA_QSPI_FAST_READ_CMD & TERCEL_SPI_4BA_QSPI_CMD_MASK) << TERCEL_SPI_4BA_QSPI_CMD_SHIFT; + dword |= (MICRON_N25Q_SPI_3BA_QSPI_FAST_READ_CMD & TERCEL_SPI_3BA_QSPI_CMD_MASK) << TERCEL_SPI_3BA_QSPI_CMD_SHIFT; + dword |= (MICRON_N25Q_SPI_4BA_SPI_FAST_READ_CMD & TERCEL_SPI_4BA_SPI_CMD_MASK) << TERCEL_SPI_4BA_SPI_CMD_SHIFT; + dword |= (MICRON_N25Q_SPI_3BA_SPI_FAST_READ_CMD & TERCEL_SPI_3BA_SPI_CMD_MASK) << TERCEL_SPI_3BA_SPI_CMD_SHIFT; + write_tercel_register(HOSTSPIFLASHCFG_BASE, TERCEL_SPI_REG_SYS_FLASH_CFG2, dword); + + dword = 0; + dword |= (MICRON_N25Q_SPI_4BA_QSPI_PAGE_PROGRAM_CMD & TERCEL_SPI_4BA_QSPI_CMD_MASK) << TERCEL_SPI_4BA_QSPI_CMD_SHIFT; + dword |= (MICRON_N25Q_SPI_3BA_QSPI_PAGE_PROGRAM_CMD & TERCEL_SPI_3BA_QSPI_CMD_MASK) << TERCEL_SPI_3BA_QSPI_CMD_SHIFT; + dword |= (MICRON_N25Q_SPI_4BA_SPI_PAGE_PROGRAM_CMD & TERCEL_SPI_4BA_SPI_CMD_MASK) << TERCEL_SPI_4BA_SPI_CMD_SHIFT; + dword |= (MICRON_N25Q_SPI_3BA_SPI_PAGE_PROGRAM_CMD & TERCEL_SPI_3BA_SPI_CMD_MASK) << TERCEL_SPI_3BA_SPI_CMD_SHIFT; + write_tercel_register(HOSTSPIFLASHCFG_BASE, TERCEL_SPI_REG_SYS_FLASH_CFG3, dword); + + // Enable extended QSPI read/write operations + dword = read_tercel_register(HOSTSPIFLASHCFG_BASE, TERCEL_SPI_REG_SYS_PHY_CFG1); + dword |= TERCEL_SPI_PHY_QSPI_EXT_READ_EN_MASK << TERCEL_SPI_PHY_QSPI_EXT_READ_EN_SHIFT; + dword |= TERCEL_SPI_PHY_QSPI_EXT_WRITE_EN_MASK << TERCEL_SPI_PHY_QSPI_EXT_WRITE_EN_SHIFT; + write_tercel_register(HOSTSPIFLASHCFG_BASE, TERCEL_SPI_REG_SYS_PHY_CFG1, dword); + + break; + } + } // Set SPI core to automatic mode - hostspiflash_core_ctl1_write(hostspiflash_core_ctl1_read() & ~(TERCEL_SPI_ENABLE_USER_MODE_MASK << TERCEL_SPI_ENABLE_USER_MODE_SHIFT)); + write_tercel_register(HOSTSPIFLASHCFG_BASE, TERCEL_SPI_REG_SYS_CORE_CTL1, read_tercel_register(HOSTSPIFLASHCFG_BASE, TERCEL_SPI_REG_SYS_CORE_CTL1) & ~(TERCEL_SPI_ENABLE_USER_MODE_MASK << TERCEL_SPI_ENABLE_USER_MODE_SHIFT)); // Set extra CS delay cycle count to 0 - dword = hostspiflash_phy_cfg1_read(); + dword = read_tercel_register(HOSTSPIFLASHCFG_BASE, TERCEL_SPI_REG_SYS_PHY_CFG1); dword &= ~(TERCEL_SPI_PHY_CS_EXTRA_IDLE_CYC_MASK << TERCEL_SPI_PHY_CS_EXTRA_IDLE_CYC_SHIFT); dword |= ((0 & TERCEL_SPI_PHY_CS_EXTRA_IDLE_CYC_MASK) << TERCEL_SPI_PHY_CS_EXTRA_IDLE_CYC_SHIFT); - hostspiflash_phy_cfg1_write(dword); + write_tercel_register(HOSTSPIFLASHCFG_BASE, TERCEL_SPI_REG_SYS_PHY_CFG1, dword); // Set maximum CS assert cycle count to 10000 - dword = hostspiflash_flash_cfg4_read(); + dword = read_tercel_register(HOSTSPIFLASHCFG_BASE, TERCEL_SPI_REG_SYS_FLASH_CFG4); dword &= ~(TERCEL_SPI_FLASH_CS_EN_LIMIT_CYC_MASK << TERCEL_SPI_FLASH_CS_EN_LIMIT_CYC_SHIFT); dword |= ((10000 & TERCEL_SPI_FLASH_CS_EN_LIMIT_CYC_MASK) << TERCEL_SPI_FLASH_CS_EN_LIMIT_CYC_SHIFT); - hostspiflash_flash_cfg4_write(dword); + write_tercel_register(HOSTSPIFLASHCFG_BASE, TERCEL_SPI_REG_SYS_FLASH_CFG4, dword); - // Set SPI fast read dummy cycles to 10 - dword = hostspiflash_phy_cfg1_read(); + // Set SPI fast read dummy cycles to MICRON_N25Q_SPI_FAST_READ_DUMMY_CLOCK_CYCLES + dword = read_tercel_register(HOSTSPIFLASHCFG_BASE, TERCEL_SPI_REG_SYS_PHY_CFG1); dword &= ~(TERCEL_SPI_PHY_DUMMY_CYCLES_MASK << TERCEL_SPI_PHY_DUMMY_CYCLES_SHIFT); - dword |= ((10 & TERCEL_SPI_PHY_DUMMY_CYCLES_MASK) << TERCEL_SPI_PHY_DUMMY_CYCLES_SHIFT); - hostspiflash_phy_cfg1_write(dword); + dword |= ((MICRON_N25Q_SPI_FAST_READ_DUMMY_CLOCK_CYCLES & TERCEL_SPI_PHY_DUMMY_CYCLES_MASK) << TERCEL_SPI_PHY_DUMMY_CYCLES_SHIFT); + write_tercel_register(HOSTSPIFLASHCFG_BASE, TERCEL_SPI_REG_SYS_PHY_CFG1, dword); // Enable SPI fast read functionality - dword = hostspiflash_phy_cfg1_read(); + dword = read_tercel_register(HOSTSPIFLASHCFG_BASE, TERCEL_SPI_REG_SYS_PHY_CFG1); dword &= ~(TERCEL_SPI_PHY_FAST_READ_ENABLE_MASK << TERCEL_SPI_PHY_FAST_READ_ENABLE_SHIFT); dword |= ((1 & TERCEL_SPI_PHY_FAST_READ_ENABLE_MASK) << TERCEL_SPI_PHY_FAST_READ_ENABLE_SHIFT); - hostspiflash_phy_cfg1_write(dword); + write_tercel_register(HOSTSPIFLASHCFG_BASE, TERCEL_SPI_REG_SYS_PHY_CFG1, dword); // Set SPI controller to 4BA mode - dword = hostspiflash_phy_cfg1_read(); + dword = read_tercel_register(HOSTSPIFLASHCFG_BASE, TERCEL_SPI_REG_SYS_PHY_CFG1); dword &= ~(TERCEL_SPI_PHY_4BA_ENABLE_MASK << TERCEL_SPI_PHY_4BA_ENABLE_SHIFT); dword |= ((TERCEL_SPI_PHY_4BA_MODE & TERCEL_SPI_PHY_4BA_ENABLE_MASK) << TERCEL_SPI_PHY_4BA_ENABLE_SHIFT); - hostspiflash_phy_cfg1_write(dword); + write_tercel_register(HOSTSPIFLASHCFG_BASE, TERCEL_SPI_REG_SYS_PHY_CFG1, dword); // Set SPI controller to QSPI mode - dword = hostspiflash_phy_cfg1_read(); + dword = read_tercel_register(HOSTSPIFLASHCFG_BASE, TERCEL_SPI_REG_SYS_PHY_CFG1); dword &= ~(TERCEL_SPI_PHY_IO_TYPE_MASK << TERCEL_SPI_PHY_IO_TYPE_SHIFT); dword |= ((TERCEL_SPI_PHY_IO_TYPE_QUAD & TERCEL_SPI_PHY_IO_TYPE_MASK) << TERCEL_SPI_PHY_IO_TYPE_SHIFT); - hostspiflash_phy_cfg1_write(dword); + write_tercel_register(HOSTSPIFLASHCFG_BASE, TERCEL_SPI_REG_SYS_PHY_CFG1, dword); - // Set SPI clock cycle divider to 4 - dword = hostspiflash_phy_cfg1_read(); + // Set SPI clock cycle divider to 8 + dword = read_tercel_register(HOSTSPIFLASHCFG_BASE, TERCEL_SPI_REG_SYS_PHY_CFG1); dword &= ~(TERCEL_SPI_PHY_CLOCK_DIVISOR_MASK << TERCEL_SPI_PHY_CLOCK_DIVISOR_SHIFT); - dword |= ((4 & TERCEL_SPI_PHY_CLOCK_DIVISOR_MASK) << TERCEL_SPI_PHY_CLOCK_DIVISOR_SHIFT); - hostspiflash_phy_cfg1_write(dword); + dword |= ((8 & TERCEL_SPI_PHY_CLOCK_DIVISOR_MASK) << TERCEL_SPI_PHY_CLOCK_DIVISOR_SHIFT); + write_tercel_register(HOSTSPIFLASHCFG_BASE, TERCEL_SPI_REG_SYS_PHY_CFG1, dword); // Calculate and dump configured SPI clock speed - uint8_t spi_divisor = (hostspiflash_phy_cfg1_read() >> TERCEL_SPI_PHY_CLOCK_DIVISOR_SHIFT) & TERCEL_SPI_PHY_CLOCK_DIVISOR_MASK; + uint8_t spi_divisor = (read_tercel_register(HOSTSPIFLASHCFG_BASE, TERCEL_SPI_REG_SYS_PHY_CFG1) >> TERCEL_SPI_PHY_CLOCK_DIVISOR_SHIFT) & TERCEL_SPI_PHY_CLOCK_DIVISOR_MASK; spi_divisor = (spi_divisor + 1) * 2; - uint8_t spi_dummy_cycles = (hostspiflash_phy_cfg1_read() >> TERCEL_SPI_PHY_DUMMY_CYCLES_SHIFT) & TERCEL_SPI_PHY_DUMMY_CYCLES_MASK; - printf("Flash controller frequency configured to %d MHz (bus frequency %d MHz, dummy cycles %d)\n", (hostspiflash_sys_clk_freq_read() / spi_divisor) / 1000000, hostspiflash_sys_clk_freq_read() / 1000000, spi_dummy_cycles); + uint8_t spi_dummy_cycles = (read_tercel_register(HOSTSPIFLASHCFG_BASE, TERCEL_SPI_REG_SYS_PHY_CFG1) >> TERCEL_SPI_PHY_DUMMY_CYCLES_SHIFT) & TERCEL_SPI_PHY_DUMMY_CYCLES_MASK; + printf("Flash controller frequency configured to %d MHz (bus frequency %d MHz, dummy cycles %d)\n", (read_tercel_register(HOSTSPIFLASHCFG_BASE, TERCEL_SPI_REG_SYS_CLK_FREQ) / spi_divisor) / 1000000, read_tercel_register(HOSTSPIFLASHCFG_BASE, TERCEL_SPI_REG_SYS_CLK_FREQ) / 1000000, spi_dummy_cycles); // Enable read merging - hostspiflash_flash_cfg5_write(hostspiflash_flash_cfg5_read() | (TERCEL_SPI_FLASH_EN_MULTCYC_READ_MASK << TERCEL_SPI_FLASH_EN_MULTCYC_READ_SHIFT)); + write_tercel_register(HOSTSPIFLASHCFG_BASE, TERCEL_SPI_REG_SYS_FLASH_CFG5, read_tercel_register(HOSTSPIFLASHCFG_BASE, TERCEL_SPI_REG_SYS_FLASH_CFG5) | (TERCEL_SPI_FLASH_EN_MULTCYC_READ_MASK << TERCEL_SPI_FLASH_EN_MULTCYC_READ_SHIFT)); // Enable write merging - hostspiflash_flash_cfg5_write(hostspiflash_flash_cfg5_read() | (TERCEL_SPI_FLASH_EN_MULTCYC_WRITE_MASK << TERCEL_SPI_FLASH_EN_MULTCYC_WRITE_SHIFT)); + write_tercel_register(HOSTSPIFLASHCFG_BASE, TERCEL_SPI_REG_SYS_FLASH_CFG5, read_tercel_register(HOSTSPIFLASHCFG_BASE, TERCEL_SPI_REG_SYS_FLASH_CFG5) | (TERCEL_SPI_FLASH_EN_MULTCYC_WRITE_MASK << TERCEL_SPI_FLASH_EN_MULTCYC_WRITE_SHIFT)); return 0; } +#endif int main(void) { + uint32_t dword; + #ifdef CONFIG_CPU_HAS_INTERRUPT + // Redirect exception handler to the ISR provided by this application + __rom_isr_address = &isr; + + // Mask external interrupts / enable global interrupts irq_setmask(0); irq_setie(1); #endif uart_init(); + gpio_init(); + + display_character(0x30, 0); // STATUS CODE: 0 + + initialize_i2c_master((uint8_t*)I2CMASTER1_BASE, 100000); + initialize_i2c_master((uint8_t*)I2CMASTER2_BASE, 100000); + // initialize_i2c_master((uint8_t*)I2CMASTER3_BASE, 100000); + initialize_i2c_master((uint8_t*)I2CMASTER4_BASE, 100000); + + // Check for Aquila core presence + if ((read_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_DEVICE_ID_HIGH) == AQUILA_LPC_DEVICE_ID_HIGH) + && (read_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_DEVICE_ID_LOW) == AQUILA_LPC_DEVICE_ID_LOW)) { + uint32_t aquila_version = read_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_DEVICE_VERSION); + printf("Raptor Aquila LPC slave found, device version %0d.%0d.%d\n", (aquila_version >> AQUILA_LPC_VERSION_MAJOR_SHIFT) & AQUILA_LPC_VERSION_MAJOR_MASK, (aquila_version >> AQUILA_LPC_VERSION_MINOR_SHIFT) & AQUILA_LPC_VERSION_MINOR_MASK, (aquila_version >> AQUILA_LPC_VERSION_PATCH_SHIFT) & AQUILA_LPC_VERSION_PATCH_MASK); + + // Configure Aquila core to intercept I/O port ranges 0x80-0x82 and 0x3f8-0x3ff + // 0x80 + dword = 0; + dword |= ((0x82 & AQUILA_LPC_RANGE_END_ADDR_MASK) << AQUILA_LPC_RANGE_END_ADDR_SHIFT); + write_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_RANGE1_END, dword); + dword = 0; + dword |= ((0x80 & AQUILA_LPC_RANGE_START_ADDR_MASK) << AQUILA_LPC_RANGE_START_ADDR_SHIFT); + dword |= ((1 & AQUILA_LPC_RANGE_ALLOW_IO_MASK) << AQUILA_LPC_RANGE_ALLOW_IO_SHIFT); + dword |= ((1 & AQUILA_LPC_RANGE_ENABLE_MASK) << AQUILA_LPC_RANGE_ENABLE_SHIFT); + write_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_RANGE1_CONFIG, dword); + + // Enable I/O cycle intercept + dword = read_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_CONTROL1); + dword |= ((1 & AQUILA_LPC_CTL_EN_IO_CYCLES_MASK) << AQUILA_LPC_CTL_EN_IO_CYCLES_SHIFT); + write_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_CONTROL1, dword); + + // Enable VUART1 + dword = read_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_CONTROL1); + dword |= ((1 & AQUILA_LPC_CTL_EN_VUART1_MASK) << AQUILA_LPC_CTL_EN_VUART1_SHIFT); + write_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_CONTROL1, dword); + + // Enable IPMI BT functionality + dword = read_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_CONTROL1); + dword |= ((1 & AQUILA_LPC_CTL_EN_IPMI_BT_MASK) << AQUILA_LPC_CTL_EN_IPMI_BT_SHIFT); + dword &= ~((AQUILA_LPC_CTL_IPMI_BT_ADDR_MASK) << AQUILA_LPC_CTL_IPMI_BT_ADDR_SHIFT); + dword |= ((0xe4 & AQUILA_LPC_CTL_IPMI_BT_ADDR_MASK) << AQUILA_LPC_CTL_IPMI_BT_ADDR_SHIFT); + write_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_CONTROL1, dword); + // Enable firmware cycle intercept + dword = read_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_CONTROL1); + dword |= ((1 & AQUILA_LPC_CTL_EN_FW_CYCLES_MASK) << AQUILA_LPC_CTL_EN_FW_CYCLES_SHIFT); + write_aquila_register(HOSTLPCSLAVE_BASE, AQUILA_LPC_REG_CONTROL1, dword); + } + + // Allocate internal host SPI Flash ROM buffer + host_flash_buffer = (uint8_t*)(MAIN_RAM_BASE + 0x4000000); + + // Clear SPI Flash buffer + printf("Clearing host ROM internal buffer..."); + memset32((uint32_t*)host_flash_buffer, 0xdeadbeef, 0x4000000 / 4); + printf("\rInternal host ROM buffer cleared \n"); + + display_character(0x31, 0); // STATUS CODE: 1 + +#ifdef WITH_SPI host_spi_flash_init(); // Detect and print attached host SPI Flash ID printf("Host SPI flash ID: 0x%08x\n", read_host_spi_flash_id()); + reset_flash_device(); + configure_flash_device(); + // Allocate internal host SPI Flash ROM buffer - uint8_t* host_flash_buffer = (uint8_t*)(MAIN_RAM_BASE + 0x4000000); printf("Copying host Flash ROM (%p) to internal buffer (%p)...\n", HOSTSPIFLASH_BASE, host_flash_buffer); int chunk; for (chunk = 0; chunk < 64; chunk++) { - memcpy32((uint32_t*)host_flash_buffer, (uint32_t*)HOSTSPIFLASH_BASE, (1 * 1024 * 1024LL) / 4); + memcpy32((uint32_t*)(host_flash_buffer + (chunk * (1 * 1024 * 1024LL))), (uint32_t*)(HOSTSPIFLASH_BASE + (chunk * (1 * 1024 * 1024LL))), (1 * 1024 * 1024LL) / 4); printf("\r[%d/64]", chunk + 1); // Reset ongoing multibyte access due to die switch requirements on the N25Q Flash devices - hostspiflash_flash_cfg5_write(hostspiflash_flash_cfg5_read() & ~(TERCEL_SPI_FLASH_EN_MULTCYC_READ_MASK << TERCEL_SPI_FLASH_EN_MULTCYC_READ_SHIFT)); - hostspiflash_flash_cfg5_write(hostspiflash_flash_cfg5_read() | (TERCEL_SPI_FLASH_EN_MULTCYC_READ_MASK << TERCEL_SPI_FLASH_EN_MULTCYC_READ_SHIFT)); + write_tercel_register(HOSTSPIFLASHCFG_BASE, TERCEL_SPI_REG_SYS_FLASH_CFG5, read_tercel_register(HOSTSPIFLASHCFG_BASE, TERCEL_SPI_REG_SYS_FLASH_CFG5) & ~(TERCEL_SPI_FLASH_EN_MULTCYC_READ_MASK << TERCEL_SPI_FLASH_EN_MULTCYC_READ_SHIFT)); + write_tercel_register(HOSTSPIFLASHCFG_BASE, TERCEL_SPI_REG_SYS_FLASH_CFG5, read_tercel_register(HOSTSPIFLASHCFG_BASE, TERCEL_SPI_REG_SYS_FLASH_CFG5) | (TERCEL_SPI_FLASH_EN_MULTCYC_READ_MASK << TERCEL_SPI_FLASH_EN_MULTCYC_READ_SHIFT)); } printf("\r%dMB copied\n", chunk); @@ -254,8 +2113,22 @@ int main(void) printf("[3/5] CRC of first 1KB: %08x (next %08x, next %08x)\n", crc32(host_flash_buffer, 1024LL), crc32(host_flash_buffer + 1024LL, 1024LL), crc32(host_flash_buffer + (2 * 1024LL), 1024LL)); printf("[4/5] CRC of first 1MB: %08x\n", crc32(host_flash_buffer, 1 * 1024 * 1024LL)); printf("[5/5] CRC of full 64MB: %08x\n", crc32(host_flash_buffer, 64 * 1024 * 1024LL)); + + // HBBL on test sytem is from 0x206200 to 0x207388 inclusive + printf("\nHBBL region:\n"); + for (row = 0; row < 141; row++) { + for (byte = 0; byte < 32; byte++) { + printf("%02x ", *(host_flash_buffer + 0x206200ULL + (byte + (row * 32)))); + //printf("%02x ", *(host_flash_buffer + 0x1000ULL + (byte + (row * 32)))); + } + printf("\n"); + } + printf("CRC of HBBL region: %08x\n", crc32(host_flash_buffer + 0x206200ULL, 4488)); +#endif #endif + display_character(0x32, 0); // STATUS CODE: 2 + puts("\nRaptor Open FSP\nBuilt "__DATE__" "__TIME__"\n"); help(); prompt(); diff --git a/micron_n25q_flash.h b/micron_n25q_flash.h new file mode 100644 index 0000000..f5c041b --- /dev/null +++ b/micron_n25q_flash.h @@ -0,0 +1,32 @@ +// © 2020 Raptor Engineering, LLC +// +// Released under the terms of the AGPL v3 +// See the LICENSE file for full details + +uint32_t micron_n25q_spi_device_ids[] = { + 0x20ba2010 + }; + +const char* micron_n25q_spi_device_names[] = { + "Micron N25Q 512MB" + }; + +#define MICRON_N25Q_SPI_FAST_READ_DUMMY_CLOCK_CYCLES 10 + +#define MICRON_N25Q_SPI_3BA_SPI_READ_CMD 0x03 +#define MICRON_N25Q_SPI_4BA_SPI_READ_CMD 0x13 + +// NOTE: QSPI mode unavailable for single read, use Write Disable command as plaecholder +#define MICRON_N25Q_SPI_3BA_QSPI_READ_CMD 0x04 +#define MICRON_N25Q_SPI_4BA_QSPI_READ_CMD 0x04 + +#define MICRON_N25Q_SPI_3BA_SPI_FAST_READ_CMD 0x0b +#define MICRON_N25Q_SPI_4BA_SPI_FAST_READ_CMD 0x0c +#define MICRON_N25Q_SPI_3BA_QSPI_FAST_READ_CMD 0xeb +#define MICRON_N25Q_SPI_4BA_QSPI_FAST_READ_CMD 0xec + +// NOTE: The same command code is used for both QSPI 3BA and QSPI 4BA extended quad input writes, thus the device must be placed in either 3BA or 4BA mode prior to issuing PAGE PROGRAM +#define MICRON_N25Q_SPI_3BA_SPI_PAGE_PROGRAM_CMD 0x02 +#define MICRON_N25Q_SPI_4BA_SPI_PAGE_PROGRAM_CMD 0x12 +#define MICRON_N25Q_SPI_3BA_QSPI_PAGE_PROGRAM_CMD 0x38 +#define MICRON_N25Q_SPI_4BA_QSPI_PAGE_PROGRAM_CMD 0x38 \ No newline at end of file diff --git a/opencores_i2c.c b/opencores_i2c.c new file mode 100644 index 0000000..bee6600 --- /dev/null +++ b/opencores_i2c.c @@ -0,0 +1,210 @@ +// © 2020 Raptor Engineering, LLC +// +// Released under the terms of the AGPL v3 +// See the LICENSE file for full details + +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> + +#include <generated/csr.h> +#include <generated/soc.h> + +#include "utility.h" + +#include "opencores_i2c.h" + +int initialize_i2c_master(uint8_t* base_address, int i2c_bus_frequency) { + printf("Configuring I2C master at address %p...\n", base_address); + + if ((*((volatile uint32_t*)(base_address + OPENCORES_I2C_MASTER_DEVICE_ID_HIGH)) != OPENCORES_I2C_DEVICE_ID_HIGH) + || (*((volatile uint32_t*)(base_address + OPENCORES_I2C_MASTER_DEVICE_ID_LOW)) != OPENCORES_I2C_DEVICE_ID_LOW)) { + return -1; + } + uint32_t opencores_spi_version = *((volatile uint32_t*)(base_address + OPENCORES_I2C_MASTER_DEVICE_VERSION)); + printf("OpenCores I2C master found, device version %0d.%0d.%d\n", (opencores_spi_version >> OPENCORES_I2C_VERSION_MAJOR_SHIFT) & OPENCORES_I2C_VERSION_MAJOR_MASK, (opencores_spi_version >> OPENCORES_I2C_VERSION_MINOR_SHIFT) & OPENCORES_I2C_VERSION_MINOR_MASK, (opencores_spi_version >> OPENCORES_I2C_VERSION_PATCH_SHIFT) & OPENCORES_I2C_VERSION_PATCH_MASK); + + // Compute prescale value from system clock and desired I2C frequency in HZ + uint16_t i2c_prescale = (CONFIG_CLOCK_FREQUENCY / (5LL * i2c_bus_frequency)) - 1; + printf("Desired prescale register: 0x%04x (system clock %dMHz, bus frequency %dkHz)\n", i2c_prescale, CONFIG_CLOCK_FREQUENCY / 1000000LL, i2c_bus_frequency / 1000LL); + *((volatile uint8_t*)(base_address + OPENCORES_I2C_MASTER_PRESCALE_LOW)) = i2c_prescale & 0xff; + *((volatile uint8_t*)(base_address + OPENCORES_I2C_MASTER_PRESCALE_HIGH)) = (i2c_prescale >> 8) & 0xff; + if ((*((volatile uint8_t*)(base_address + OPENCORES_I2C_MASTER_PRESCALE_LOW)) == (i2c_prescale & 0xff)) + && (*((volatile uint8_t*)(base_address + OPENCORES_I2C_MASTER_PRESCALE_HIGH)) == ((i2c_prescale >> 8) & 0xff))) { + printf("Enabling I2C core\n", i2c_prescale); + *((volatile uint8_t*)(base_address + OPENCORES_I2C_MASTER_PRESCALE_CTL)) = (OPENCORES_I2C_MASTER_CTL_CORE_EN_MASK << OPENCORES_I2C_MASTER_CTL_CORE_EN_SHIFT); + + return 0; + } + + return 1; +} + +int write_i2c_data(uint8_t* base_address, uint8_t slave_address, uint8_t* data, int data_length, uint8_t send_stop_signal) { + uint32_t i2c_op_timeout_counter; + uint8_t i2c_op_failed; + int active_byte; + uint8_t byte; + + i2c_op_failed = 0; + *((volatile uint8_t*)(base_address + OPENCORES_I2C_MASTER_TX_RX)) = (slave_address << 1) | (OPENCORES_I2C_MASTER_TX_RX_WRITE_MASK << OPENCORES_I2C_MASTER_TX_RX_WRITE_SHIFT); + *((volatile uint8_t*)(base_address + OPENCORES_I2C_MASTER_CMD_STATUS)) = (OPENCORES_I2C_MASTER_CMD_STA_MASK << OPENCORES_I2C_MASTER_CMD_STA_SHIFT) | (OPENCORES_I2C_MASTER_CMD_WR_MASK << OPENCORES_I2C_MASTER_CMD_WR_SHIFT); + i2c_op_timeout_counter = 0; + while ((*((volatile uint8_t*)(base_address + OPENCORES_I2C_MASTER_CMD_STATUS)) >> OPENCORES_I2C_MASTER_STATUS_TIP_SHIFT) & OPENCORES_I2C_MASTER_STATUS_TIP_MASK) { + if (i2c_op_timeout_counter > I2C_MASTER_OPERATION_TIMEOUT_VALUE) { + printf("[WARNING] I2C operation timed out in device select!\n"); + i2c_op_failed = 1; + break; + } + usleep(100); + i2c_op_timeout_counter++; + } + + if (!i2c_op_failed) { + if ((*((volatile uint8_t*)(base_address + OPENCORES_I2C_MASTER_CMD_STATUS)) >> OPENCORES_I2C_MASTER_STATUS_RXACK_SHIFT) & OPENCORES_I2C_MASTER_STATUS_RXACK_MASK) { + printf("[WARNING] I2C operation failed in device select!\n"); + i2c_op_failed = 1; + } + } + + for (active_byte = 0; active_byte < data_length; active_byte++) { + if (!i2c_op_failed) { + *((volatile uint8_t*)(base_address + OPENCORES_I2C_MASTER_TX_RX)) = *(data + active_byte); + byte = OPENCORES_I2C_MASTER_CMD_WR_MASK << OPENCORES_I2C_MASTER_CMD_WR_SHIFT; + if ((active_byte + 1) == data_length) { + // Final byte + if (send_stop_signal) { + byte |= OPENCORES_I2C_MASTER_CMD_STO_MASK << OPENCORES_I2C_MASTER_CMD_STO_SHIFT; + } + } + *((volatile uint8_t*)(base_address + OPENCORES_I2C_MASTER_CMD_STATUS)) = byte; + i2c_op_timeout_counter = 0; + while ((*((volatile uint8_t*)(base_address + OPENCORES_I2C_MASTER_CMD_STATUS)) >> OPENCORES_I2C_MASTER_STATUS_TIP_SHIFT) & OPENCORES_I2C_MASTER_STATUS_TIP_MASK) { + if (i2c_op_timeout_counter > I2C_MASTER_OPERATION_TIMEOUT_VALUE) { + printf("[WARNING] I2C operation timed out in register write!\n"); + i2c_op_failed = 1; + break; + } + usleep(100); + i2c_op_timeout_counter++; + } + } + + if (!i2c_op_failed) { + if ((*((volatile uint8_t*)(base_address + OPENCORES_I2C_MASTER_CMD_STATUS)) >> OPENCORES_I2C_MASTER_STATUS_RXACK_SHIFT) & OPENCORES_I2C_MASTER_STATUS_RXACK_MASK) { + printf("[WARNING] I2C operation failed in register write!\n"); + i2c_op_failed = 1; + } + } + + if (i2c_op_failed) { + break; + } + } + + return i2c_op_failed; +} + +int read_i2c_data(uint8_t* base_address, uint8_t slave_address, uint8_t* data, int* data_length, int max_data_length, uint8_t send_stop_signal) { + uint32_t i2c_op_timeout_counter; + uint8_t i2c_op_failed; + int active_byte; + uint8_t byte; + + i2c_op_failed = 0; + *((volatile uint8_t*)(base_address + OPENCORES_I2C_MASTER_TX_RX)) = (slave_address << 1) | (OPENCORES_I2C_MASTER_TX_RX_READ_MASK << OPENCORES_I2C_MASTER_TX_RX_READ_SHIFT); + *((volatile uint8_t*)(base_address + OPENCORES_I2C_MASTER_CMD_STATUS)) = (OPENCORES_I2C_MASTER_CMD_STA_MASK << OPENCORES_I2C_MASTER_CMD_STA_SHIFT) | (OPENCORES_I2C_MASTER_CMD_WR_MASK << OPENCORES_I2C_MASTER_CMD_WR_SHIFT); + i2c_op_timeout_counter = 0; + while ((*((volatile uint8_t*)(base_address + OPENCORES_I2C_MASTER_CMD_STATUS)) >> OPENCORES_I2C_MASTER_STATUS_TIP_SHIFT) & OPENCORES_I2C_MASTER_STATUS_TIP_MASK) { + if (i2c_op_timeout_counter > I2C_MASTER_OPERATION_TIMEOUT_VALUE) { + printf("[WARNING] I2C operation timed out in device select!\n"); + i2c_op_failed = 1; + break; + } + usleep(100); + i2c_op_timeout_counter++; + } + + if (!i2c_op_failed) { + if ((*((volatile uint8_t*)(base_address + OPENCORES_I2C_MASTER_CMD_STATUS)) >> OPENCORES_I2C_MASTER_STATUS_RXACK_SHIFT) & OPENCORES_I2C_MASTER_STATUS_RXACK_MASK) { + printf("[WARNING] I2C operation failed in device select!\n"); + i2c_op_failed = 1; + } + } + + if (data_length) { + *data_length = 0; + } + for (active_byte = 0; active_byte < max_data_length; active_byte++) { + if (!i2c_op_failed) { + if ((active_byte + 1) == max_data_length) { + // Final byte, send NACK + byte = (OPENCORES_I2C_MASTER_CMD_RD_MASK << OPENCORES_I2C_MASTER_CMD_RD_SHIFT) | (OPENCORES_I2C_MASTER_CMD_NACK_MASK << OPENCORES_I2C_MASTER_CMD_NACK_SHIFT); + if (send_stop_signal) { + byte |= OPENCORES_I2C_MASTER_CMD_STO_MASK << OPENCORES_I2C_MASTER_CMD_STO_SHIFT; + } + *((volatile uint8_t*)(base_address + OPENCORES_I2C_MASTER_CMD_STATUS)) = byte; + } + else { + // More bytes expected, send ACK + *((volatile uint8_t*)(base_address + OPENCORES_I2C_MASTER_CMD_STATUS)) = (OPENCORES_I2C_MASTER_CMD_RD_MASK << OPENCORES_I2C_MASTER_CMD_RD_SHIFT) | (OPENCORES_I2C_MASTER_CMD_ACK_MASK << OPENCORES_I2C_MASTER_CMD_ACK_SHIFT); + } + while ((*((volatile uint8_t*)(base_address + OPENCORES_I2C_MASTER_CMD_STATUS)) >> OPENCORES_I2C_MASTER_STATUS_TIP_SHIFT) & OPENCORES_I2C_MASTER_STATUS_TIP_MASK) { + if (i2c_op_timeout_counter > I2C_MASTER_OPERATION_TIMEOUT_VALUE) { + printf("[WARNING] I2C operation timed out in register read!\n"); + i2c_op_failed = 1; + break; + } + usleep(100); + i2c_op_timeout_counter++; + } + if (!i2c_op_failed) { + *(data + active_byte) = *((volatile uint8_t*)(base_address + OPENCORES_I2C_MASTER_TX_RX)); + if (data_length) { + *data_length = *data_length + 1; + } + } + } + + if (i2c_op_failed) { + break; + } + } + + return i2c_op_failed; +} + +uint8_t i2c_read_register_byte(uint8_t* base_address, uint8_t slave_address, uint8_t slave_register, int* error) { + uint8_t retval = 0xff; + uint8_t byte; + + if (error) { + *error = -1; + } + + if (!write_i2c_data(base_address, slave_address, &slave_register, 1, 0)) { + if (!read_i2c_data(base_address, slave_address, &byte, NULL, 1, 1)) { + retval = byte; + + if (error) { + *error = 0; + } + } + } + + return retval; +} + +int i2c_write_register_byte(uint8_t* base_address, uint8_t slave_address, uint8_t slave_register, uint8_t data) { + uint8_t tx_data[2]; + + // Assemble TX data structure + tx_data[0] = slave_register; + tx_data[1] = data; + + if (!write_i2c_data(base_address, slave_address, tx_data, 2, 1)) { + return 0; + } + + return 1; +} \ No newline at end of file diff --git a/opencores_i2c.h b/opencores_i2c.h new file mode 100644 index 0000000..493dea5 --- /dev/null +++ b/opencores_i2c.h @@ -0,0 +1,73 @@ +// © 2020 Raptor Engineering, LLC +// +// Released under the terms of the AGPL v3 +// See the LICENSE file for full details + +#ifndef _OPENCORES_I2C_H +#define _OPENCORES_I2C_H + +#define OPENCORES_I2C_MASTER_DEVICE_ID_LOW 0x0 +#define OPENCORES_I2C_MASTER_DEVICE_ID_HIGH 0x4 +#define OPENCORES_I2C_MASTER_DEVICE_VERSION 0x8 +#define OPENCORES_I2C_MASTER_PRESCALE_LOW 0x10 +#define OPENCORES_I2C_MASTER_PRESCALE_HIGH 0x11 +#define OPENCORES_I2C_MASTER_PRESCALE_CTL 0x12 +#define OPENCORES_I2C_MASTER_TX_RX 0x13 +#define OPENCORES_I2C_MASTER_CMD_STATUS 0x14 + +#define OPENCORES_I2C_DEVICE_ID_HIGH 0x4932434d +#define OPENCORES_I2C_DEVICE_ID_LOW 0x4f504e43 + +#define OPENCORES_I2C_VERSION_MAJOR_MASK 0xffff +#define OPENCORES_I2C_VERSION_MAJOR_SHIFT 16 +#define OPENCORES_I2C_VERSION_MINOR_MASK 0xff +#define OPENCORES_I2C_VERSION_MINOR_SHIFT 8 +#define OPENCORES_I2C_VERSION_PATCH_MASK 0xff +#define OPENCORES_I2C_VERSION_PATCH_SHIFT 0 + +#define OPENCORES_I2C_MASTER_CTL_CORE_EN_MASK 0x1 +#define OPENCORES_I2C_MASTER_CTL_CORE_EN_SHIFT 7 +#define OPENCORES_I2C_MASTER_CTL_IRQ_EN_MASK 0x1 +#define OPENCORES_I2C_MASTER_CTL_IRQ_EN_SHIFT 6 + +#define OPENCORES_I2C_MASTER_CMD_STA_MASK 0x1 +#define OPENCORES_I2C_MASTER_CMD_STA_SHIFT 7 +#define OPENCORES_I2C_MASTER_CMD_STO_MASK 0x1 +#define OPENCORES_I2C_MASTER_CMD_STO_SHIFT 6 +#define OPENCORES_I2C_MASTER_CMD_RD_MASK 0x1 +#define OPENCORES_I2C_MASTER_CMD_RD_SHIFT 5 +#define OPENCORES_I2C_MASTER_CMD_WR_MASK 0x1 +#define OPENCORES_I2C_MASTER_CMD_WR_SHIFT 4 +#define OPENCORES_I2C_MASTER_CMD_ACK_MASK 0x0 +#define OPENCORES_I2C_MASTER_CMD_ACK_SHIFT 3 +#define OPENCORES_I2C_MASTER_CMD_NACK_MASK 0x1 +#define OPENCORES_I2C_MASTER_CMD_NACK_SHIFT 3 +#define OPENCORES_I2C_MASTER_CMD_IACK_MASK 0x1 +#define OPENCORES_I2C_MASTER_CMD_IACK_SHIFT 0 + +#define OPENCORES_I2C_MASTER_STATUS_RXACK_MASK 0x1 +#define OPENCORES_I2C_MASTER_STATUS_RXACK_SHIFT 7 +#define OPENCORES_I2C_MASTER_STATUS_BUSY_MASK 0x1 +#define OPENCORES_I2C_MASTER_STATUS_BUSY_SHIFT 6 +#define OPENCORES_I2C_MASTER_STATUS_ARBL_MASK 0x1 +#define OPENCORES_I2C_MASTER_STATUS_ARBL_SHIFT 5 +#define OPENCORES_I2C_MASTER_STATUS_TIP_MASK 0x1 +#define OPENCORES_I2C_MASTER_STATUS_TIP_SHIFT 1 +#define OPENCORES_I2C_MASTER_STATUS_IRQP_MASK 0x1 +#define OPENCORES_I2C_MASTER_STATUS_IRQP_SHIFT 0 + +#define OPENCORES_I2C_MASTER_TX_RX_WRITE_MASK 0x0 +#define OPENCORES_I2C_MASTER_TX_RX_WRITE_SHIFT 0 +#define OPENCORES_I2C_MASTER_TX_RX_READ_MASK 0x1 +#define OPENCORES_I2C_MASTER_TX_RX_READ_SHIFT 0 + +#define I2C_MASTER_4_ADDR 0xc0010000L +#define I2C_MASTER_OPERATION_TIMEOUT_VALUE 10000 + +int initialize_i2c_master(uint8_t* base_address, int i2c_bus_frequency); +int write_i2c_data(uint8_t* base_address, uint8_t slave_address, uint8_t* data, int data_length, uint8_t send_stop_signal); +int read_i2c_data(uint8_t* base_address, uint8_t slave_address, uint8_t* data, int* data_length, int max_data_length, uint8_t send_stop_signal); +uint8_t i2c_read_register_byte(uint8_t* base_address, uint8_t slave_address, uint8_t slave_register, int* error); +int i2c_write_register_byte(uint8_t* base_address, uint8_t slave_address, uint8_t slave_register, uint8_t data); + +#endif // _OPENCORES_I2C_H \ No newline at end of file diff --git a/tercel_spi.h b/tercel_spi.h index deb230a..38200ee 100644 --- a/tercel_spi.h +++ b/tercel_spi.h @@ -3,6 +3,29 @@ // Released under the terms of the AGPL v3 // See the LICENSE file for full details +#define TERCEL_SPI_REG_DEVICE_ID_HIGH 0x0 +#define TERCEL_SPI_REG_DEVICE_ID_LOW 0x4 +#define TERCEL_SPI_REG_DEVICE_VERSION 0x8 +#define TERCEL_SPI_REG_SYS_CLK_FREQ 0xc +#define TERCEL_SPI_REG_SYS_PHY_CFG1 0x10 +#define TERCEL_SPI_REG_SYS_FLASH_CFG1 0x14 +#define TERCEL_SPI_REG_SYS_FLASH_CFG2 0x18 +#define TERCEL_SPI_REG_SYS_FLASH_CFG3 0x1c +#define TERCEL_SPI_REG_SYS_FLASH_CFG4 0x20 +#define TERCEL_SPI_REG_SYS_FLASH_CFG5 0x24 +#define TERCEL_SPI_REG_SYS_CORE_CTL1 0x28 +#define TERCEL_SPI_REG_SYS_CORE_DATA1 0x2c + +#define TERCEL_SPI_DEVICE_ID_HIGH 0x7c525054 +#define TERCEL_SPI_DEVICE_ID_LOW 0x5350494d + +#define TERCEL_SPI_VERSION_MAJOR_MASK 0xffff +#define TERCEL_SPI_VERSION_MAJOR_SHIFT 16 +#define TERCEL_SPI_VERSION_MINOR_MASK 0xff +#define TERCEL_SPI_VERSION_MINOR_SHIFT 8 +#define TERCEL_SPI_VERSION_PATCH_MASK 0xff +#define TERCEL_SPI_VERSION_PATCH_SHIFT 0 + #define TERCEL_SPI_ENABLE_USER_MODE_MASK 0x1 #define TERCEL_SPI_ENABLE_USER_MODE_SHIFT 0x0 #define TERCEL_SPI_PHY_DUMMY_CYCLES_MASK 0xff @@ -15,6 +38,10 @@ #define TERCEL_SPI_PHY_4BA_ENABLE_SHIFT 18 #define TERCEL_SPI_PHY_FAST_READ_ENABLE_MASK 0x1 #define TERCEL_SPI_PHY_FAST_READ_ENABLE_SHIFT 19 +#define TERCEL_SPI_PHY_QSPI_EXT_READ_EN_MASK 0x1 +#define TERCEL_SPI_PHY_QSPI_EXT_READ_EN_SHIFT 20 +#define TERCEL_SPI_PHY_QSPI_EXT_WRITE_EN_MASK 0x1 +#define TERCEL_SPI_PHY_QSPI_EXT_WRITE_EN_SHIFT 21 #define TERCEL_SPI_PHY_CS_EXTRA_IDLE_CYC_MASK 0xff #define TERCEL_SPI_PHY_CS_EXTRA_IDLE_CYC_SHIFT 24 #define TERCEL_SPI_FLASH_EN_MULTCYC_WRITE_MASK 0x1 @@ -24,8 +51,25 @@ #define TERCEL_SPI_FLASH_CS_EN_LIMIT_CYC_MASK 0xffffffff #define TERCEL_SPI_FLASH_CS_EN_LIMIT_CYC_SHIFT 0 +#define TERCEL_SPI_3BA_SPI_CMD_MASK 0xff +#define TERCEL_SPI_3BA_SPI_CMD_SHIFT 0 +#define TERCEL_SPI_4BA_SPI_CMD_MASK 0xff +#define TERCEL_SPI_4BA_SPI_CMD_SHIFT 8 +#define TERCEL_SPI_3BA_QSPI_CMD_MASK 0xff +#define TERCEL_SPI_3BA_QSPI_CMD_SHIFT 16 +#define TERCEL_SPI_4BA_QSPI_CMD_MASK 0xff +#define TERCEL_SPI_4BA_QSPI_CMD_SHIFT 24 + #define TERCEL_SPI_PHY_IO_TYPE_SINGLE 0x0 #define TERCEL_SPI_PHY_IO_TYPE_QUAD 0x2 #define TERCEL_SPI_PHY_3BA_MODE 0x0 #define TERCEL_SPI_PHY_4BA_MODE 0x1 + +static inline uint32_t read_tercel_register(unsigned long base_address, uint8_t reg) { + return *((volatile uint32_t*)(base_address + reg)); +} + +static inline void write_tercel_register(unsigned long base_address, uint8_t reg, uint32_t data) { + *((volatile uint32_t*)(base_address + reg)) = data; +} \ No newline at end of file diff --git a/utility.c b/utility.c new file mode 100644 index 0000000..64d17a6 --- /dev/null +++ b/utility.c @@ -0,0 +1,56 @@ +// © 2020 Raptor Engineering, LLC +// +// Released under the terms of the AGPL v3 +// See the LICENSE file for full details + +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <limits.h> + +#include <generated/soc.h> + +#define TICKS_PER_US (CONFIG_CLOCK_FREQUENCY / 1000000LL) + +#include "utility.h" + +static __inline__ unsigned long long rdtsc(void) +{ + unsigned long long int tsc; + unsigned long int high; + unsigned long int low; + unsigned long int scratch; + __asm__ volatile( + "0: \n" + "\tmftbu %0 \n" + "\tmftb %1 \n" + "\tmftbu %2 \n" + "\tcmpw %2,%0 \n" + "\tbne 0b \n" + : "=r"(high), "=r"(low), "=r"(scratch) + ); + tsc = high << 32; + tsc |= low; + + return tsc; +} + +void usleep(int usecs) { + unsigned long long start_tsc; + unsigned long long current_tsc; + unsigned long long offset; + + offset = 0; + start_tsc = rdtsc(); + + while (1) { + current_tsc = rdtsc(); + if (current_tsc < start_tsc) { + offset = ULONG_MAX - start_tsc; + start_tsc = 0; + } + if (((current_tsc - start_tsc) + offset) >= (TICKS_PER_US * usecs)) { + break; + } + } +} \ No newline at end of file diff --git a/utility.h b/utility.h new file mode 100644 index 0000000..0d8aae4 --- /dev/null +++ b/utility.h @@ -0,0 +1,11 @@ +// © 2020 Raptor Engineering, LLC +// +// Released under the terms of the AGPL v3 +// See the LICENSE file for full details + +#ifndef _UTILITY_H +#define _UTILITY_H + +void usleep(int usecs); + +#endif // _UTILITY_H \ No newline at end of file -- GitLab