diff options
author | Christophe Lombard <clombard@linux.vnet.ibm.com> | 2017-06-13 14:21:21 +0200 |
---|---|---|
committer | Stewart Smith <stewart@linux.vnet.ibm.com> | 2017-06-19 14:49:29 +1000 |
commit | e50764d4f2b11fc9e9fe0b2fd0a4617b32593bfa (patch) | |
tree | f4e524e82083ea0693312b768a966b1115ecabe1 /hw/capp.c | |
parent | 37ea3cfdc8523cb5fbbde6e364eaed3c1c67f8bb (diff) | |
download | skiboot-e50764d4f2b11fc9e9fe0b2fd0a4617b32593bfa.zip skiboot-e50764d4f2b11fc9e9fe0b2fd0a4617b32593bfa.tar.gz skiboot-e50764d4f2b11fc9e9fe0b2fd0a4617b32593bfa.tar.bz2 |
capi: Load capp microcode
CAPP microcode flash download and CAPP upload for PHB4.
A new file 'capp.c' is created to receive common capp code for PHB3 and
PHB4.
Signed-off-by: Christophe Lombard <clombard@linux.vnet.ibm.com>
Reviewed-by: Frederic Barrat <fbarrat@linux.vnet.ibm.com>
Signed-off-by: Stewart Smith <stewart@linux.vnet.ibm.com>
Diffstat (limited to 'hw/capp.c')
-rw-r--r-- | hw/capp.c | 231 |
1 files changed, 231 insertions, 0 deletions
diff --git a/hw/capp.c b/hw/capp.c new file mode 100644 index 0000000..ae79b0f --- /dev/null +++ b/hw/capp.c @@ -0,0 +1,231 @@ +/* Copyright 2013-2017 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#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; + +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; + + 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) { + prlog(PR_INFO, "CAPI: WARNING: no compat thing found\n"); + 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); + +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) != 0x43415050554C4944) || + (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) != 0x4341505055434F44) { + 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; +} |