aboutsummaryrefslogtreecommitdiff
path: root/hw/fsi-master.c
diff options
context:
space:
mode:
authorBenjamin Herrenschmidt <benh@kernel.crashing.org>2014-07-02 15:36:20 +1000
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>2014-07-02 15:36:20 +1000
commit1d880992fd8c8457a2d990ac6622cfd58fb1b261 (patch)
treec4c843b12e96b5612c315db5a23c5da1a900618c /hw/fsi-master.c
downloadskiboot-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/fsi-master.c')
-rw-r--r--hw/fsi-master.c297
1 files changed, 297 insertions, 0 deletions
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 <skiboot.h>
+#include <xscom.h>
+#include <lock.h>
+#include <timebase.h>
+#include <chip.h>
+#include <fsi-master.h>
+
+//#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, &reg);
+ 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, &reg);
+ 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;
+ }
+ }
+}
+