diff options
author | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2014-07-02 15:36:20 +1000 |
---|---|---|
committer | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2014-07-02 15:36:20 +1000 |
commit | 1d880992fd8c8457a2d990ac6622cfd58fb1b261 (patch) | |
tree | c4c843b12e96b5612c315db5a23c5da1a900618c /hw/centaur.c | |
download | skiboot-1d880992fd8c8457a2d990ac6622cfd58fb1b261.zip skiboot-1d880992fd8c8457a2d990ac6622cfd58fb1b261.tar.gz skiboot-1d880992fd8c8457a2d990ac6622cfd58fb1b261.tar.bz2 |
Initial commit of Open Source release
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Diffstat (limited to 'hw/centaur.c')
-rw-r--r-- | hw/centaur.c | 326 |
1 files changed, 326 insertions, 0 deletions
diff --git a/hw/centaur.c b/hw/centaur.c new file mode 100644 index 0000000..20f63cc --- /dev/null +++ b/hw/centaur.c @@ -0,0 +1,326 @@ +/* Copyright 2013-2014 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 <xscom.h> +#include <processor.h> +#include <device.h> +#include <chip.h> +#include <centaur.h> +#include <lock.h> +#include <fsi-master.h> + +/* + * Centaur chip IDs are using the XSCOM "partID" encoding + * described in xscom.h. recap: + * + * 0b1000.0000.0000.0000.0000.00NN.NCCC.MMMM + * N=Node, C=Chip, M=Memory Channel + * + * We currently use FSI exclusively for centaur access. We can + * start using MMIO on Centaur DD2.x when we have a way to handle + * machine checks happening inside Sapphire which we don't at the + * moment. + */ +struct centaur_chip { + bool valid; + uint8_t ec_level; + uint32_t fsi_master_chip_id; + uint32_t fsi_master_port; + uint32_t fsi_master_engine; + struct lock lock; +}; + +/* Is that correct ? */ +#define MAX_CENTAURS_PER_CHIP 8 + +/* + * FSI2PIB register definitions (this could be moved out if we were to + * support FSI master to other chips. + */ +#define FSI_DATA0_REG 0x1000 +#define FSI_DATA1_REG 0x1004 +#define FSI_CMD_REG 0x1008 +#define FSI_CMD_WR 0x80000000 +#define FSI_CMD_RD 0x00000000 +#define FSI_ENG_RESET_REG 0x1018 +#define FSI_STATUS_REG 0x101c +#define FSI_STATUS_ABORT 0x00100000 +#define FSI_STATUS_ERRORS 0x00007000 + +static int64_t centaur_fsiscom_complete(struct centaur_chip *centaur) +{ + int64_t rc; + uint32_t stat; + + rc = mfsi_read(centaur->fsi_master_chip_id, centaur->fsi_master_engine, + centaur->fsi_master_port, FSI_STATUS_REG, &stat); + if (rc) { + /* XXX Improve logging */ + prerror("CENTAUR: MFSI read error %lld reading STAT\n", rc); + return rc; + } + if ((stat & (FSI_STATUS_ABORT | FSI_STATUS_ERRORS)) == 0) + return OPAL_SUCCESS; + + prerror("CENTAUR: Remote FSI error, stat=0x%08x\n", stat); + + /* XXX Handle recovery */ + + return OPAL_HARDWARE; +} + +static int64_t centaur_fsiscom_read(struct centaur_chip *centaur, uint32_t pcb_addr, + uint64_t *val) +{ + int64_t rc; + uint32_t data0, data1; + + rc = mfsi_write(centaur->fsi_master_chip_id, centaur->fsi_master_engine, + centaur->fsi_master_port, FSI_CMD_REG, pcb_addr | FSI_CMD_RD); + if (rc) { + /* XXX Improve logging */ + prerror("CENTAUR: MFSI write error %lld writing CMD\n", rc); + return rc; + } + + rc = centaur_fsiscom_complete(centaur); + if (rc) + return rc; + + rc = mfsi_read(centaur->fsi_master_chip_id, centaur->fsi_master_engine, + centaur->fsi_master_port, FSI_DATA0_REG, &data0); + if (rc) { + /* XXX Improve logging */ + prerror("CENTAUR: MFSI read error %lld reading DATA0\n", rc); + return rc; + } + rc = mfsi_read(centaur->fsi_master_chip_id, centaur->fsi_master_engine, + centaur->fsi_master_port, FSI_DATA1_REG, &data1); + if (rc) { + /* XXX Improve logging */ + prerror("CENTAUR: MFSI read error %lld readking DATA1\n", rc); + return rc; + } + + *val = (((uint64_t)data0) << 32) | data1; + + return OPAL_SUCCESS; +} + +static struct centaur_chip *centaur_get(uint32_t part_id) +{ + uint32_t hchip_id, mchan; + struct proc_chip *hchip; + struct centaur_chip *centaur; + + if ((part_id >> 28) != 8) { + prerror("CENTAUR: Invalid part ID 0x%x\n", part_id); + return NULL; + } + hchip_id = (part_id & 0x0fffffff) >> 4; + mchan = part_id & 0xf; + + hchip = get_chip(hchip_id); + if (!hchip) { + prerror("CENTAUR: Centaur 0x%x not found on non-existing chip 0%x\n", + part_id, hchip_id); + return NULL; + } + if (mchan >= MAX_CENTAURS_PER_CHIP) { + prerror("CENTAUR: Centaur 0x%x channel out of bounds !\n", part_id); + return NULL; + } + if (!hchip->centaurs) { + prerror("CENTAUR: Centaur 0x%x not found on chip 0%x (no centaurs)\n", + part_id, hchip_id); + return NULL; + } + centaur = &hchip->centaurs[mchan]; + if (!centaur->valid) { + prerror("CENTAUR: Centaur 0x%x not valid on chip 0%x\n", + part_id, hchip_id); + return NULL; + } + return centaur; +} + +static int64_t centaur_fsiscom_write(struct centaur_chip *centaur, uint32_t pcb_addr, + uint64_t val) +{ + int64_t rc; + + rc = mfsi_write(centaur->fsi_master_chip_id, centaur->fsi_master_engine, + centaur->fsi_master_port, FSI_DATA0_REG, hi32(val)); + if (rc) { + /* XXX Improve logging */ + prerror("CENTAUR: MFSI write error %lld writing DATA0\n", rc); + return rc; + } + rc = mfsi_write(centaur->fsi_master_chip_id, centaur->fsi_master_engine, + centaur->fsi_master_port, FSI_DATA1_REG, lo32(val)); + if (rc) { + /* XXX Improve logging */ + prerror("CENTAUR: MFSI write error %lld writing DATA1\n", rc); + return rc; + } + rc = mfsi_write(centaur->fsi_master_chip_id, centaur->fsi_master_engine, + centaur->fsi_master_port, FSI_CMD_REG, pcb_addr | FSI_CMD_WR); + if (rc) { + /* XXX Improve logging */ + prerror("CENTAUR: MFSI write error %lld writing CMD\n", rc); + return rc; + } + + return centaur_fsiscom_complete(centaur); +} + +int64_t centaur_xscom_read(uint32_t id, uint64_t pcb_addr, uint64_t *val) +{ + struct centaur_chip *centaur = centaur_get(id); + int64_t rc; + + if (!centaur) + return OPAL_PARAMETER; + + lock(¢aur->lock); + rc = centaur_fsiscom_read(centaur, pcb_addr, val); + unlock(¢aur->lock); + + return rc; +} + +int64_t centaur_xscom_write(uint32_t id, uint64_t pcb_addr, uint64_t val) +{ + struct centaur_chip *centaur = centaur_get(id); + int64_t rc; + + if (!centaur) + return OPAL_PARAMETER; + + lock(¢aur->lock); + rc = centaur_fsiscom_write(centaur, pcb_addr, val); + unlock(¢aur->lock); + + return rc; +} + +static bool centaur_check_id(struct centaur_chip *centaur) +{ + int64_t rc; + uint64_t val; + + rc = centaur_fsiscom_read(centaur, 0xf000f, &val); + if (rc) { + prerror("CENTAUR: FSISCOM error %lld reading ID register\n", + rc); + return false; + } + + /* Extract CFAM id */ + val >>= 44; + + /* Identify chip */ + if ((val & 0xff) != 0xe9) { + prerror("CENTAUR: CFAM ID 0x%02x is not a Centaur !\n", + (unsigned int)(val & 0xff)); + return false; + } + + /* Get EC level from CFAM ID */ + centaur->ec_level = ((val >> 16) & 0xf) << 4; + centaur->ec_level |= (val >> 8) & 0xf; + + return true; +} + +static bool centaur_add(uint32_t part_id, uint32_t mchip, uint32_t meng, + uint32_t mport) +{ + uint32_t hchip_id, mchan; + struct proc_chip *hchip; + struct centaur_chip *centaur; + + if ((part_id >> 28) != 8) { + prerror("CENTAUR: Invalid part ID 0x%x\n", part_id); + return false; + } + hchip_id = (part_id & 0x0fffffff) >> 4; + mchan = part_id & 0xf; + + printf("CENTAUR: Found centaur for chip 0x%x channel %d\n", + hchip_id, mchan); + printf("CENTAUR: FSI host: 0x%x cMFSI%d port %d\n", + mchip, meng, mport); + + hchip = get_chip(hchip_id); + if (!hchip) { + prerror("CENTAUR: No such chip !!!\n"); + return false; + } + + if (mchan >= MAX_CENTAURS_PER_CHIP) { + prerror("CENTAUR: Channel out of bounds !\n"); + return false; + } + + if (!hchip->centaurs) { + hchip->centaurs = + zalloc(sizeof(struct centaur_chip) * + MAX_CENTAURS_PER_CHIP); + assert(hchip->centaurs); + } + + centaur = &hchip->centaurs[mchan]; + if (centaur->valid) { + prerror("CENTAUR: Duplicate centaur !\n"); + return false; + } + centaur->fsi_master_chip_id = mchip; + centaur->fsi_master_port = mport; + centaur->fsi_master_engine = meng ? MFSI_cMFSI1 : MFSI_cMFSI0; + init_lock(¢aur->lock); + + if (!centaur_check_id(centaur)) + return false; + + printf("CENTAUR: ChipID 0x%x [DD%x.%x]\n", part_id, + centaur->ec_level >> 4, + centaur->ec_level & 0xf); + + centaur->valid = true; + return true; +} + +void centaur_init(void) +{ + struct dt_node *cn; + + dt_for_each_compatible(dt_root, cn, "ibm,centaur-v10") { + uint32_t chip_id, mchip, meng, mport; + + chip_id = dt_prop_get_u32(cn, "ibm,chip-id"); + mchip = dt_prop_get_u32(cn, "ibm,fsi-master-chip-id"); + meng = dt_prop_get_cell(cn, "ibm,fsi-master-port", 0); + mport = dt_prop_get_cell(cn, "ibm,fsi-master-port", 1); + + /* + * If adding the centaur succeeds, we expose it to + * Linux as a scom-controller + */ + if (centaur_add(chip_id, mchip, meng, mport)) + dt_add_property(cn, "scom-controller", NULL, 0); + } +} |