From 1d880992fd8c8457a2d990ac6622cfd58fb1b261 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Wed, 2 Jul 2014 15:36:20 +1000 Subject: Initial commit of Open Source release Signed-off-by: Benjamin Herrenschmidt --- hw/fsi-master.c | 297 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 297 insertions(+) create mode 100644 hw/fsi-master.c (limited to 'hw/fsi-master.c') diff --git a/hw/fsi-master.c b/hw/fsi-master.c new file mode 100644 index 0000000..67d337a --- /dev/null +++ b/hw/fsi-master.c @@ -0,0 +1,297 @@ +/* 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 +#include +#include +#include +#include +#include + +//#define DBG(fmt...) printf("MFSI: " fmt) +#define DBG(fmt...) do { } while(0) + + +/* + * FSI Masters sit on OPB busses behind PIB2OPB bridges + * + * There are two cMFSI behind two different bridges at + * different XSCOM addresses. For now we don't have them in + * the device-tree so we hard code the address + */ +#define PIB2OPB_MFSI0_ADDR 0x20000 +#define PIB2OPB_MFSI1_ADDR 0x30000 + +/* + * Bridge registers on XSCOM that allow generatoin + * of OPB cycles + */ +#define PIB2OPB_REG_CMD 0x0 +#define OPB_CMD_WRITE 0x80000000 +#define OPB_CMD_READ 0x00000000 +#define OPB_CMD_8BIT 0x00000000 +#define OPB_CMD_16BIT 0x20000000 +#define OPB_CMD_32BIT 0x60000000 +#define PIB2OPB_REG_STAT 0x1 +#define OPB_STAT_BUSY 0x00010000 +#define OPB_STAT_READ_VALID 0x00020000 +#define OPB_STAT_ERR_OPB 0x09F00000 +#define OPB_STAT_ERR_CMFSI 0x0000FC00 +#define OPB_STAT_ERR_MFSI 0x000000FC +#define OPB_STAT_ERR_ANY (OPB_STAT_ERR_OPB | \ + OPB_STAT_ERR_CMFSI | \ + OPB_STAT_ERR_MFSI) +#define PIB2OPB_REG_LSTAT 0x2 + +/* + * PIB2OPB 0 has 2 MFSIs, cMFSI and hMFSI, PIB2OPB 1 only + * has cMFSI + */ +#define cMFSI_OPB_PORT_BASE 0x40000 +#define cMFSI_OPB_REG_BASE 0x03000 +#define hMFSI_OPB_PORT_BASE 0x80000 +#define hMFSI_OPB_REG_BASE 0x03400 +#define MFSI_OPB_PORT_STRIDE 0x08000 + + +/* + * Use a global FSI lock for now. Beware of re-entrancy + * if we ever add support for normal chip XSCOM via FSI, in + * which case we'll probably have to consider either per chip + * lock (which can have AB->BA deadlock issues) or a re-entrant + * global lock + */ +static struct lock fsi_lock = LOCK_UNLOCKED; +static uint32_t mfsi_valid_err = OPB_STAT_ERR_ANY; + +/* + * OPB accessors + */ + +#define MFSI_OPB_MAX_TRIES 120 + +static int64_t mfsi_handle_opb_error(uint32_t chip, uint32_t xscom_base, + uint32_t stat) +{ + int64_t rc; + + prerror("MFSI: Error status=0x%08x !\n", stat); + + /* XXX Dump a bunch of data, create an error log ... */ + + /* Clean error */ + rc = xscom_write(chip, xscom_base + PIB2OPB_REG_STAT, 0); + if (rc) + prerror("MFSI: XSCOM error %lld clearing status\n", rc); + + /* + * XXX HB resets the ports here, but that's broken as it will + * re-enter the opb accessors ... the HW is a mess here, it mixes + * the OPB stuff with the FSI stuff in horrible ways. + * If we want to reset the port and generally handle FSI specific + * errors we should do that at the upper level and leave only the + * OPB error handling here. + * + * We probably need to return "stat" to the callers too for that + * to work + */ + + return OPAL_HARDWARE; +} + +static int64_t mfsi_opb_poll(uint32_t chip, uint32_t xscom_base, + uint32_t *read_data) +{ + unsigned long retries = MFSI_OPB_MAX_TRIES; + uint64_t sval; + uint32_t stat; + int64_t rc; + + /* We try again every 10us for a bit more than 1ms */ + for (;;) { + /* Read OPB status register */ + rc = xscom_read(chip, xscom_base + PIB2OPB_REG_STAT, &sval); + if (rc) { + /* Do something here ? */ + prerror("MFSI: XSCOM error %lld read OPB STAT\n", rc); + return rc; + } + DBG(" STAT=0x%16llx...\n", sval); + + stat = sval >> 32; + + /* Complete */ + if (!(stat & OPB_STAT_BUSY)) + break; + /* Error */ + if (stat & mfsi_valid_err) + break; + if (retries-- == 0) { + /* XXX What should we do here ? reset it ? */ + prerror("MFSI: OPB POLL timeout !\n"); + return OPAL_HARDWARE; + } + time_wait_us(10); + } + + /* Did we have an error ? */ + if (stat & mfsi_valid_err) + return mfsi_handle_opb_error(chip, xscom_base, stat); + + if (read_data) { + if (!(stat & OPB_STAT_READ_VALID)) { + prerror("MFSI: Read successful but no data !\n"); + /* What do do here ? can it actually happen ? */ + sval |= 0xffffffff; + } + *read_data = sval & 0xffffffff; + } + + return OPAL_SUCCESS; +} + +static int64_t mfsi_opb_read(uint32_t chip, uint32_t xscom_base, + uint32_t addr, uint32_t *data) +{ + uint64_t opb_cmd = OPB_CMD_READ | OPB_CMD_32BIT; + int64_t rc; + + if (addr > 0x00ffffff) + return OPAL_PARAMETER; + + opb_cmd |= addr; + opb_cmd <<= 32; + + DBG("MFSI_OPB_READ: Writing 0x%16llx to XSCOM %x\n", + opb_cmd, xscom_base); + + rc = xscom_write(chip, xscom_base + PIB2OPB_REG_CMD, opb_cmd); + if (rc) { + prerror("MFSI: XSCOM error %lld writing OPB CMD\n", rc); + return rc; + } + return mfsi_opb_poll(chip, xscom_base, data); +} + +static int64_t mfsi_opb_write(uint32_t chip, uint32_t xscom_base, + uint32_t addr, uint32_t data) +{ + uint64_t opb_cmd = OPB_CMD_WRITE | OPB_CMD_32BIT; + int64_t rc; + + if (addr > 0x00ffffff) + return OPAL_PARAMETER; + + opb_cmd |= addr; + opb_cmd <<= 32; + opb_cmd |= data; + + DBG("MFSI_OPB_WRITE: Writing 0x%16llx to XSCOM %x\n", + opb_cmd, xscom_base); + + rc = xscom_write(chip, xscom_base + PIB2OPB_REG_CMD, opb_cmd); + if (rc) { + prerror("MFSI: XSCOM error %lld writing OPB CMD\n", rc); + return rc; + } + return mfsi_opb_poll(chip, xscom_base, NULL); +} + +static int64_t mfsi_get_addrs(uint32_t mfsi, uint32_t port, + uint32_t *xscom_base, uint32_t *port_base, + uint32_t *reg_base) +{ + if (port > 7) + return OPAL_PARAMETER; + + /* We hard code everything for now */ + switch(mfsi) { + case MFSI_cMFSI0: + *xscom_base = PIB2OPB_MFSI0_ADDR; + *port_base = cMFSI_OPB_PORT_BASE + port * MFSI_OPB_PORT_STRIDE; + *reg_base = cMFSI_OPB_REG_BASE; + break; + case MFSI_cMFSI1: + *xscom_base = PIB2OPB_MFSI1_ADDR; + *port_base = cMFSI_OPB_PORT_BASE + port * MFSI_OPB_PORT_STRIDE; + *reg_base = cMFSI_OPB_REG_BASE; + break; + case MFSI_hMFSI0: + *xscom_base = PIB2OPB_MFSI0_ADDR; + *port_base = hMFSI_OPB_PORT_BASE + port * MFSI_OPB_PORT_STRIDE; + *reg_base = hMFSI_OPB_REG_BASE; + break; + default: + return OPAL_PARAMETER; + } + return OPAL_SUCCESS; +} + +int64_t mfsi_read(uint32_t chip, uint32_t mfsi, uint32_t port, + uint32_t fsi_addr, uint32_t *data) +{ + int64_t rc; + uint32_t xscom, port_addr, reg; + + rc = mfsi_get_addrs(mfsi, port, &xscom, &port_addr, ®); + if (rc) + return rc; + lock(&fsi_lock); + rc = mfsi_opb_read(chip, xscom, port_addr + fsi_addr, data); + /* XXX Handle FSI level errors here, maybe reset port */ + unlock(&fsi_lock); + + return rc; +} + +int64_t mfsi_write(uint32_t chip, uint32_t mfsi, uint32_t port, + uint32_t fsi_addr, uint32_t data) +{ + int64_t rc; + uint32_t xscom, port_addr, reg; + + rc = mfsi_get_addrs(mfsi, port, &xscom, &port_addr, ®); + if (rc) + return rc; + lock(&fsi_lock); + rc = mfsi_opb_write(chip, xscom, port_addr + fsi_addr, data); + /* XXX Handle FSI level errors here, maybe reset port */ + unlock(&fsi_lock); + + return rc; +} + +void mfsi_init(void) +{ + struct proc_chip *chip; + + /* For now assume all chips are the same DD... might need + * fixing. + */ + chip = next_chip(NULL); + assert(chip); + if (chip->type == PROC_CHIP_P8_MURANO) { + /* Hardware Bug HW222712 on Murano DD1.0 causes the + * any_error bit to be un-clearable so we just + * have to ignore it + */ + if (chip->ec_level < 0x20) { + /* 16: cMFSI any-master-error */ + /* 24: hMFSI any-master-error */ + mfsi_valid_err &= 0xFFFF7F7F; + } + } +} + -- cgit v1.1