diff options
author | 2023-10-10 14:33:42 +0000 | |
---|---|---|
committer | 2023-10-10 14:33:42 +0000 | |
commit | af1a266670d040d2f4083ff309d732d648afba2a (patch) | |
tree | 2fc46203448ddcc6f81546d379abfaeb323575e9 /roms/skiboot/hw/capp.c | |
parent | e02cda008591317b1625707ff8e115a4841aa889 (diff) |
Change-Id: Iaf8d18082d3991dec7c0ebbea540f092188eb4ec
Diffstat (limited to 'roms/skiboot/hw/capp.c')
-rw-r--r-- | roms/skiboot/hw/capp.c | 243 |
1 files changed, 243 insertions, 0 deletions
diff --git a/roms/skiboot/hw/capp.c b/roms/skiboot/hw/capp.c new file mode 100644 index 000000000..a1aa1caa9 --- /dev/null +++ b/roms/skiboot/hw/capp.c @@ -0,0 +1,243 @@ +// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later +/* + * CAPP unit (i.e. CAPI) + * + * Copyright 2013-2019 IBM Corp. + */ + +#include <skiboot.h> +#include <io.h> +#include <opal.h> +#include <chip.h> +#include <xscom.h> +#include <capp.h> + +#define PHBERR(opal_id, chip_id, index, fmt, a...) \ + prlog(PR_ERR, "PHB#%04x[%d:%d]: " fmt, \ + opal_id, chip_id, \ + index, ## a) + +static struct { + uint32_t ec_level; + struct capp_lid_hdr *lid; + size_t size; + int load_result; +} capp_ucode_info = { 0, NULL, 0, false }; + +#define CAPP_UCODE_MAX_SIZE 0x20000 + +struct lock capi_lock = LOCK_UNLOCKED; +struct capp_ops capi_ops = { NULL }; + +bool capp_ucode_loaded(struct proc_chip *chip, unsigned int index) +{ + return (chip->capp_ucode_loaded & (1 << index)); +} + +int preload_capp_ucode(void) +{ + struct dt_node *p; + struct proc_chip *chip; + uint32_t index; + uint64_t rc; + int ret; + + /* CAPI is supported on P8 and P9 only */ + p = dt_find_compatible_node(dt_root, NULL, "ibm,power8-pbcq"); + if (!p) + p = dt_find_compatible_node(dt_root, NULL, "ibm,power9-pbcq"); + if (!p) + return OPAL_SUCCESS; + + chip = get_chip(dt_get_chip_id(p)); + + rc = xscom_read_cfam_chipid(chip->id, &index); + if (rc) { + prerror("CAPP: Error reading cfam chip-id\n"); + ret = OPAL_HARDWARE; + return ret; + } + /* Keep ChipID and Major/Minor EC. Mask out the Location Code. */ + index = index & 0xf0fff; + + /* Assert that we're preloading */ + assert(capp_ucode_info.lid == NULL); + capp_ucode_info.load_result = OPAL_EMPTY; + + capp_ucode_info.ec_level = index; + + /* Is the ucode preloaded like for BML? */ + if (dt_has_node_property(p, "ibm,capp-ucode", NULL)) { + capp_ucode_info.lid = (struct capp_lid_hdr *)(u64) + dt_prop_get_u32(p, "ibm,capp-ucode"); + capp_ucode_info.load_result = OPAL_SUCCESS; + ret = OPAL_SUCCESS; + goto end; + } + /* If we successfully download the ucode, we leave it around forever */ + capp_ucode_info.size = CAPP_UCODE_MAX_SIZE; + capp_ucode_info.lid = malloc(CAPP_UCODE_MAX_SIZE); + if (!capp_ucode_info.lid) { + prerror("CAPP: Can't allocate space for ucode lid\n"); + ret = OPAL_NO_MEM; + goto end; + } + + prlog(PR_INFO, "CAPI: Preloading ucode %x\n", capp_ucode_info.ec_level); + + ret = start_preload_resource(RESOURCE_ID_CAPP, index, + capp_ucode_info.lid, + &capp_ucode_info.size); + + if (ret != OPAL_SUCCESS) { + prerror("CAPI: Failed to preload resource %d\n", ret); + capp_ucode_info.load_result = ret; + } + +end: + return ret; +} + +static int64_t capp_lid_download(void) +{ + int64_t ret; + + if (capp_ucode_info.load_result != OPAL_EMPTY) + return capp_ucode_info.load_result; + + capp_ucode_info.load_result = wait_for_resource_loaded( + RESOURCE_ID_CAPP, + capp_ucode_info.ec_level); + + if (capp_ucode_info.load_result != OPAL_SUCCESS) { + prerror("CAPP: Error loading ucode lid. index=%x\n", + capp_ucode_info.ec_level); + ret = OPAL_RESOURCE; + free(capp_ucode_info.lid); + capp_ucode_info.lid = NULL; + goto end; + } + + ret = OPAL_SUCCESS; +end: + return ret; +} + +int64_t capp_load_ucode(unsigned int chip_id, uint32_t opal_id, + unsigned int index, u64 lid_eyecatcher, + uint32_t reg_offset, + uint64_t apc_master_addr, uint64_t apc_master_write, + uint64_t snp_array_addr, uint64_t snp_array_write) +{ + struct proc_chip *chip = get_chip(chip_id); + struct capp_ucode_lid *ucode; + struct capp_ucode_data *data; + struct capp_lid_hdr *lid; + uint64_t rc, val, addr; + uint32_t chunk_count, offset; + int i; + + if (capp_ucode_loaded(chip, index)) + return OPAL_SUCCESS; + + rc = capp_lid_download(); + if (rc) + return rc; + + prlog(PR_INFO, "CHIP%i: CAPP ucode lid loaded at %p\n", + chip_id, capp_ucode_info.lid); + + lid = capp_ucode_info.lid; + /* + * If lid header is present (on FSP machines), it'll tell us where to + * find the ucode. Otherwise this is the ucode. + */ + ucode = (struct capp_ucode_lid *)lid; + if (be64_to_cpu(lid->eyecatcher) == lid_eyecatcher) { + if (be64_to_cpu(lid->version) != 0x1) { + PHBERR(opal_id, chip_id, index, + "capi ucode lid header invalid\n"); + return OPAL_HARDWARE; + } + ucode = (struct capp_ucode_lid *) + ((char *)ucode + be64_to_cpu(lid->ucode_offset)); + } + + /* 'CAPPULID' in ASCII */ + if ((be64_to_cpu(ucode->eyecatcher) != 0x43415050554C4944UL) || + (be64_to_cpu(ucode->version) != 1)) { + PHBERR(opal_id, chip_id, index, + "CAPP: ucode header invalid\n"); + return OPAL_HARDWARE; + } + + offset = 0; + while (offset < be64_to_cpu(ucode->data_size)) { + data = (struct capp_ucode_data *) + ((char *)&ucode->data + offset); + chunk_count = be32_to_cpu(data->hdr.chunk_count); + offset += sizeof(struct capp_ucode_data_hdr) + chunk_count * 8; + + /* 'CAPPUCOD' in ASCII */ + if (be64_to_cpu(data->hdr.eyecatcher) != 0x4341505055434F44UL) { + PHBERR(opal_id, chip_id, index, + "CAPP: ucode data header invalid:%i\n", + offset); + return OPAL_HARDWARE; + } + + switch (data->hdr.reg) { + case apc_master_cresp: + xscom_write(chip_id, apc_master_addr + reg_offset, + 0); + addr = apc_master_write; + break; + case apc_master_uop_table: + xscom_write(chip_id, apc_master_addr + reg_offset, + 0x180ULL << 52); + addr = apc_master_write; + break; + case snp_ttype: + xscom_write(chip_id, snp_array_addr + reg_offset, + 0x5000ULL << 48); + addr = snp_array_write; + break; + case snp_uop_table: + xscom_write(chip_id, snp_array_addr + reg_offset, + 0x4000ULL << 48); + addr = snp_array_write; + break; + default: + continue; + } + + for (i = 0; i < chunk_count; i++) { + val = be64_to_cpu(data->data[i]); + xscom_write(chip_id, addr + reg_offset, val); + } + } + + chip->capp_ucode_loaded |= (1 << index); + + return OPAL_SUCCESS; +} + +int64_t capp_get_info(int chip_id, struct phb *phb, struct capp_info *info) +{ + if (capi_ops.get_capp_info) + return capi_ops.get_capp_info(chip_id, phb, info); + + return OPAL_PARAMETER; +} + +int64_t capp_xscom_read(struct capp *capp, int64_t off, uint64_t *val) +{ + return capp == NULL ? OPAL_PARAMETER : + xscom_read(capp->chip_id, off + capp->capp_xscom_offset, val); +} + +int64_t capp_xscom_write(struct capp *capp, int64_t off, uint64_t val) +{ + return capp == NULL ? OPAL_PARAMETER : + xscom_write(capp->chip_id, off + capp->capp_xscom_offset, val); +} |