// © 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 "fsi.h" #define DEBUG // General FSI register definitions for IBM CFAM slaves #define IBM_CFAM_FSI_SMODE 0x0800 #define IBM_CFAM_FSI_SISC 0x0802 #define IBM_CFAM_FSI_SSTAT 0x0805 // Boot-related CFAM register definitions for IBM POWER9 processors #define IBM_POWER9_FSI_A_SI1S 0x081c #define IBM_POWER9_LL_MODE_REG 0x0840 #define IBM_POWER9_FSI2PIB_CHIPID 0x100a #define IBM_POWER9_FSI2PIB_INTERRUPT 0x100b #define IBM_POWER9_FSI2PIB_TRUE_MASK 0x100d #define IBM_POWER9_CBS_CS 0x2801 #define IBM_POWER9_SBE_CTRL_STATUS 0x2808 #define IBM_POWER9_SBE_MSG_REGISTER 0x2809 #define IBM_POWER9_ROOT_CTRL0 0x2810 #define IBM_POWER9_PERV_CTRL0 0x281a #define IBM_POWER9_HB_MBX5_REG 0x283c #define IBM_POWER9_SCRATCH_REGISTER_8 0x283f #define IBM_POWER9_ROOT_CTRL8 0x2918 #define IBM_POWER9_ROOT_CTRL1_CLEAR 0x2931 // Platform data #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(); if ((devid_high == 0x7c525054) && (devid_low == 0x4653494d)) { return 0; } printf("Raptor OpenFSI master not found!\n"); return -1; } static int access_fsi_mem(uint8_t slave_id, uint32_t address, fsi_data_length_t data_length, uint8_t write, uint32_t* data) { int ret = 0; uint32_t word; uint8_t status_code; if (!data) return -1; // Set up request word = openfsi_master_interface_sid_adr_read(); 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); if (write) { openfsi_master_interface_tx_data_write(*data); } // Set direction and start operation word = openfsi_master_interface_control_reg_read(); 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); // Wait for operation to complete while (!(openfsi_master_interface_status_reg_read() & (FSI_MASTER_CTL_CYCLE_START_MASK << FSI_MASTER_CTL_CYCLE_START_SHIFT))); // Read status register word = openfsi_master_interface_status_reg_read(); 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(); #ifdef DEBUG printf("%s(): address 0x%06x, data: 0x%08x sta: 0x%08x\n", __FUNCTION__, address, *data, word); #endif if (status_code) { #ifdef DEBUG printf("[WARNING] FSI master returned error code %d on access to FSI address 0x%06x\n", status_code, address); #endif if (!ret) ret = -1; } // Clear any operation request flag word = openfsi_master_interface_control_reg_read(); 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); return ret; } static int access_cfam(uint8_t slave_id, uint32_t cfam_address, fsi_data_length_t data_length, uint8_t write, uint32_t* data) { // CFAM to FSI address mangling uint32_t fsi_address = (cfam_address & 0xfc00) | ((cfam_address & 0x3ff) << 2); return access_fsi_mem(slave_id, fsi_address, data_length, write, data); } static int initialize_fsi_master(void) { uint32_t word; if (check_device_id()) return -1; // Clear any pending operation requests word = openfsi_master_interface_control_reg_read(); 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); // 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)); // Set up ACK to CMD turnaround delay and enable CRC protection / enhanced error recovery word = openfsi_master_interface_control_reg_read(); 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); #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()); #endif return 0; } int run_pre_ipl_fixups(void) { uint32_t word; uint32_t fsi_cfam_error_register; // Set up FSI slave... // [31]=1: Warm start done // [29]=1: HW CRC check enabled // [26:24]: FSI slave ID (0) // [23:20]: Echo delay (7) // [19:16]: Send delay (7) // [11:8]: Clock ratio (8) word = 0xa0ff0800; if (access_cfam(IBM_POWER9_SLAVE_ID, IBM_CFAM_FSI_SMODE, FSI_DATA_LENGTH_WORD, FSI_DIRECTION_WRITE, &word)) goto fail; // Ensure asynchronous clock mode is set word = 0x0000001; if (access_cfam(IBM_POWER9_SLAVE_ID, IBM_POWER9_LL_MODE_REG, FSI_DATA_LENGTH_WORD, FSI_DIRECTION_WRITE, &word)) goto fail; // Configure SBE to pre-IPL state if (access_cfam(IBM_POWER9_SLAVE_ID, IBM_POWER9_LL_MODE_REG, FSI_DATA_LENGTH_WORD, FSI_DIRECTION_READ, &word)) goto fail; word &= ~(0x1 << 31); // Clear SBE IPL start flag word &= ~(0x1 << 29); // Run SCAN0 and CLOCKSTART during IPL word &= ~(0x1 << 28); // Clear SBE start prevention flag if (access_cfam(IBM_POWER9_SLAVE_ID, IBM_POWER9_LL_MODE_REG, FSI_DATA_LENGTH_WORD, FSI_DIRECTION_WRITE, &word)) goto fail; // Clear SBE status mailbox word = 0x00000000; if (access_cfam(IBM_POWER9_SLAVE_ID, IBM_POWER9_SBE_MSG_REGISTER, FSI_DATA_LENGTH_WORD, FSI_DIRECTION_WRITE, &word)) goto fail; // Read CFAM error IRQ register if (access_cfam(IBM_POWER9_SLAVE_ID, IBM_CFAM_FSI_SISC, FSI_DATA_LENGTH_WORD, FSI_DIRECTION_READ, &word)) goto fail; fsi_cfam_error_register = word; // Read CFAM status register if (access_cfam(IBM_POWER9_SLAVE_ID, IBM_CFAM_FSI_SSTAT, FSI_DATA_LENGTH_WORD, FSI_DIRECTION_READ, &word)) goto fail; // Clear CFAM error IRQ register if (access_cfam(IBM_POWER9_SLAVE_ID, IBM_CFAM_FSI_SISC, FSI_DATA_LENGTH_WORD, FSI_DIRECTION_WRITE, &fsi_cfam_error_register)) goto fail; return 0; fail: return -1; } int start_ipl(int side) { uint32_t word; if (initialize_fsi_master()) goto fail; if ((side < 0) || (side > 1)) { printf("Invalid side %d specified\n", side); return -2; } printf("Starting IPL on side %d\n", side); if (run_pre_ipl_fixups()) goto fail; // Ensure asynchronous clock mode is set word = 0x0000001; if (access_cfam(IBM_POWER9_SLAVE_ID, IBM_POWER9_LL_MODE_REG, FSI_DATA_LENGTH_WORD, FSI_DIRECTION_WRITE, &word)) goto fail; // Clock mux select override if (access_cfam(IBM_POWER9_SLAVE_ID, IBM_POWER9_ROOT_CTRL8, FSI_DATA_LENGTH_WORD, FSI_DIRECTION_READ, &word)) goto fail; word |= 0xc; if (access_cfam(IBM_POWER9_SLAVE_ID, IBM_POWER9_ROOT_CTRL8, FSI_DATA_LENGTH_WORD, FSI_DIRECTION_WRITE, &word)) goto fail; // Setup FSI2PIB to report checkstop word = 0x20000000; if (access_cfam(IBM_POWER9_SLAVE_ID, IBM_POWER9_FSI_A_SI1S, FSI_DATA_LENGTH_WORD, FSI_DIRECTION_WRITE, &word)) goto fail; // Enable XSTOP/ATTN interrupt word = 0x60000000; if (access_cfam(IBM_POWER9_SLAVE_ID, IBM_POWER9_FSI2PIB_TRUE_MASK, FSI_DATA_LENGTH_WORD, FSI_DIRECTION_WRITE, &word)) goto fail; // Arm XSTOP/ATTN interrupt word = 0xffffffff; if (access_cfam(IBM_POWER9_SLAVE_ID, IBM_POWER9_FSI2PIB_INTERRUPT, FSI_DATA_LENGTH_WORD, FSI_DIRECTION_WRITE, &word)) goto fail; // SBE boot side configuration if (access_cfam(IBM_POWER9_SLAVE_ID, IBM_POWER9_SBE_CTRL_STATUS, FSI_DATA_LENGTH_WORD, FSI_DIRECTION_READ, &word)) goto fail; if (side == 0) word &= ~(0x1 << 14); else word |= 0x1 << 14; if (access_cfam(IBM_POWER9_SLAVE_ID, IBM_POWER9_SBE_CTRL_STATUS, FSI_DATA_LENGTH_WORD, FSI_DIRECTION_WRITE, &word)) goto fail; // Ensure edge-triggered SBE start bit is deasserted prior to ignition... if (access_cfam(IBM_POWER9_SLAVE_ID, IBM_POWER9_CBS_CS, FSI_DATA_LENGTH_WORD, FSI_DIRECTION_READ, &word)) goto fail; word &= ~(0x1 << 31); if (access_cfam(IBM_POWER9_SLAVE_ID, IBM_POWER9_CBS_CS, FSI_DATA_LENGTH_WORD, FSI_DIRECTION_WRITE, &word)) goto fail; // ...and light the fuse! word |= 0x1 << 31; if (access_cfam(IBM_POWER9_SLAVE_ID, IBM_POWER9_CBS_CS, FSI_DATA_LENGTH_WORD, FSI_DIRECTION_WRITE, &word)) goto fail; return 0; fail: printf("IPL failed!\n"); return -1; } int get_sbe_status(void) { uint32_t word; // SBE if (access_cfam(IBM_POWER9_SLAVE_ID, IBM_POWER9_SBE_MSG_REGISTER, FSI_DATA_LENGTH_WORD, FSI_DIRECTION_READ, &word)) goto fail; printf("CFAM 0x%06x: 0x%08x\n", IBM_POWER9_SBE_MSG_REGISTER, word); // Hostboot if (access_cfam(IBM_POWER9_SLAVE_ID, IBM_POWER9_HB_MBX5_REG, FSI_DATA_LENGTH_WORD, FSI_DIRECTION_READ, &word)) goto fail; printf("CFAM 0x%06x: 0x%08x\n", IBM_POWER9_HB_MBX5_REG, word); return 0; fail: printf("Processor not responding!\n"); return -1; }