aboutsummaryrefslogtreecommitdiff
path: root/hw/capp.c
diff options
context:
space:
mode:
authorChristophe Lombard <clombard@linux.vnet.ibm.com>2017-06-13 14:21:21 +0200
committerStewart Smith <stewart@linux.vnet.ibm.com>2017-06-19 14:49:29 +1000
commite50764d4f2b11fc9e9fe0b2fd0a4617b32593bfa (patch)
treef4e524e82083ea0693312b768a966b1115ecabe1 /hw/capp.c
parent37ea3cfdc8523cb5fbbde6e364eaed3c1c67f8bb (diff)
downloadskiboot-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.c231
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;
+}