254 lines
6.5 KiB
C
254 lines
6.5 KiB
C
/* 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;
|
|
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;
|
|
|
|
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);
|
|
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);
|
|
}
|