aboutsummaryrefslogtreecommitdiffstats
path: root/roms/skiboot/hw/p8-i2c.c
diff options
context:
space:
mode:
Diffstat (limited to 'roms/skiboot/hw/p8-i2c.c')
-rw-r--r--roms/skiboot/hw/p8-i2c.c1688
1 files changed, 1688 insertions, 0 deletions
diff --git a/roms/skiboot/hw/p8-i2c.c b/roms/skiboot/hw/p8-i2c.c
new file mode 100644
index 000000000..45815858e
--- /dev/null
+++ b/roms/skiboot/hw/p8-i2c.c
@@ -0,0 +1,1688 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/*
+ * P8 i2c master
+ *
+ * Copyright 2013-2019 IBM Corp.
+ */
+
+#undef DEBUG
+
+#include <opal.h>
+#include <skiboot.h>
+#include <mem_region-malloc.h>
+#include <lock.h>
+#include <chip.h>
+#include <i2c.h>
+#include <xscom.h>
+#include <timebase.h>
+#include <timer.h>
+#include <opal-msg.h>
+#include <errorlog.h>
+#include <centaur.h>
+#include <debug_descriptor.h>
+
+DEFINE_LOG_ENTRY(OPAL_RC_I2C_INIT, OPAL_PLATFORM_ERR_EVT, OPAL_I2C,
+ OPAL_IO_SUBSYSTEM, OPAL_PREDICTIVE_ERR_DEGRADED_PERF,
+ OPAL_NA);
+DEFINE_LOG_ENTRY(OPAL_RC_I2C_START_REQ, OPAL_INPUT_OUTPUT_ERR_EVT, OPAL_I2C,
+ OPAL_IO_SUBSYSTEM, OPAL_INFO, OPAL_NA);
+DEFINE_LOG_ENTRY(OPAL_RC_I2C_TIMEOUT, OPAL_INPUT_OUTPUT_ERR_EVT, OPAL_I2C,
+ OPAL_IO_SUBSYSTEM, OPAL_INFO, OPAL_NA);
+DEFINE_LOG_ENTRY(OPAL_RC_I2C_TRANSFER, OPAL_INPUT_OUTPUT_ERR_EVT, OPAL_I2C,
+ OPAL_IO_SUBSYSTEM, OPAL_INFO, OPAL_NA);
+DEFINE_LOG_ENTRY(OPAL_RC_I2C_RESET, OPAL_INPUT_OUTPUT_ERR_EVT, OPAL_I2C,
+ OPAL_IO_SUBSYSTEM, OPAL_INFO, OPAL_NA);
+
+#ifdef DEBUG
+#define DBG(fmt...) prlog(PR_ERR, "I2C: " fmt)
+#define I2C_TIMEOUT_IRQ_MS 100 /* 100ms/byte timeout */
+#define I2C_TIMEOUT_POLL_MS 4000 /* 4s/byte timeout */
+#else
+#define DBG(fmt...) prlog(PR_TRACE, "I2C: " fmt)
+#define I2C_TIMEOUT_IRQ_MS 1 /* 1ms/byte timeout */
+#define I2C_TIMEOUT_POLL_MS 4000 /* 4s/byte timeout */
+#endif
+
+/* How long to keep the sensor cache disabled after an access
+ * in milliseconds
+ */
+#define SENSOR_CACHE_EN_DELAY 10
+
+#define USEC_PER_SEC 1000000
+#define USEC_PER_MSEC 1000
+#define I2C_RESET_DELAY_MS 5 /* 5 msecs */
+#define I2C_FIFO_HI_LVL 4
+#define I2C_FIFO_LO_LVL 4
+
+/*
+ * I2C registers set.
+ * Below is the offset of registers from base which is stored in the
+ * 'struct p8_i2c_master'
+ */
+
+/* I2C FIFO register */
+#define I2C_FIFO_REG 0x4
+#define I2C_FIFO PPC_BITMASK(0, 7)
+
+/* I2C command register */
+#define I2C_CMD_REG 0x5
+#define I2C_CMD_WITH_START PPC_BIT(0)
+#define I2C_CMD_WITH_ADDR PPC_BIT(1)
+#define I2C_CMD_READ_CONT PPC_BIT(2)
+#define I2C_CMD_WITH_STOP PPC_BIT(3)
+#define I2C_CMD_INTR_STEERING PPC_BITMASK(6,7) /* P9 */
+#define I2C_CMD_INTR_STEER_HOST 1
+#define I2C_CMD_INTR_STEER_OCC 2
+#define I2C_CMD_DEV_ADDR PPC_BITMASK(8, 14)
+#define I2C_CMD_READ_NOT_WRITE PPC_BIT(15)
+#define I2C_CMD_LEN_BYTES PPC_BITMASK(16, 31)
+#define I2C_MAX_TFR_LEN 0xfff0ull
+
+/* I2C mode register */
+#define I2C_MODE_REG 0x6
+#define I2C_MODE_BIT_RATE_DIV PPC_BITMASK(0, 15)
+#define I2C_MODE_PORT_NUM PPC_BITMASK(16, 21)
+#define I2C_MODE_ENHANCED PPC_BIT(28)
+#define I2C_MODE_DIAGNOSTIC PPC_BIT(29)
+#define I2C_MODE_PACING_ALLOW PPC_BIT(30)
+#define I2C_MODE_WRAP PPC_BIT(31)
+
+/* I2C watermark register */
+#define I2C_WATERMARK_REG 0x7
+#define I2C_WATERMARK_HIGH PPC_BITMASK(16, 19)
+#define I2C_WATERMARK_LOW PPC_BITMASK(24, 27)
+
+/*
+ * I2C interrupt mask and condition registers
+ *
+ * NB: The function of 0x9 and 0xa changes depending on whether you're reading
+ * or writing to them. When read they return the interrupt condition bits
+ * and on writes they update the interrupt mask register.
+ *
+ * The bit definitions are the same for all the interrupt registers.
+ */
+#define I2C_INTR_MASK_REG 0x8
+
+#define I2C_INTR_RAW_COND_REG 0x9 /* read */
+#define I2C_INTR_MASK_OR_REG 0x9 /* write*/
+
+#define I2C_INTR_COND_REG 0xa /* read */
+#define I2C_INTR_MASK_AND_REG 0xa /* write */
+
+#define I2C_INTR_ALL PPC_BITMASK(16, 31)
+#define I2C_INTR_INVALID_CMD PPC_BIT(16)
+#define I2C_INTR_LBUS_PARITY_ERR PPC_BIT(17)
+#define I2C_INTR_BKEND_OVERRUN_ERR PPC_BIT(18)
+#define I2C_INTR_BKEND_ACCESS_ERR PPC_BIT(19)
+#define I2C_INTR_ARBT_LOST_ERR PPC_BIT(20)
+#define I2C_INTR_NACK_RCVD_ERR PPC_BIT(21)
+#define I2C_INTR_DATA_REQ PPC_BIT(22)
+#define I2C_INTR_CMD_COMP PPC_BIT(23)
+#define I2C_INTR_STOP_ERR PPC_BIT(24)
+#define I2C_INTR_I2C_BUSY PPC_BIT(25)
+#define I2C_INTR_NOT_I2C_BUSY PPC_BIT(26)
+#define I2C_INTR_SCL_EQ_1 PPC_BIT(28)
+#define I2C_INTR_SCL_EQ_0 PPC_BIT(29)
+#define I2C_INTR_SDA_EQ_1 PPC_BIT(30)
+#define I2C_INTR_SDA_EQ_0 PPC_BIT(31)
+
+/* I2C status register */
+#define I2C_RESET_I2C_REG 0xb
+#define I2C_RESET_ERRORS 0xc
+#define I2C_STAT_REG 0xb
+#define I2C_STAT_INVALID_CMD PPC_BIT(0)
+#define I2C_STAT_LBUS_PARITY_ERR PPC_BIT(1)
+#define I2C_STAT_BKEND_OVERRUN_ERR PPC_BIT(2)
+#define I2C_STAT_BKEND_ACCESS_ERR PPC_BIT(3)
+#define I2C_STAT_ARBT_LOST_ERR PPC_BIT(4)
+#define I2C_STAT_NACK_RCVD_ERR PPC_BIT(5)
+#define I2C_STAT_DATA_REQ PPC_BIT(6)
+#define I2C_STAT_CMD_COMP PPC_BIT(7)
+#define I2C_STAT_STOP_ERR PPC_BIT(8)
+#define I2C_STAT_UPPER_THRS PPC_BITMASK(9, 15)
+#define I2C_STAT_ANY_I2C_INTR PPC_BIT(16)
+#define I2C_STAT_PORT_HISTORY_BUSY PPC_BIT(19)
+#define I2C_STAT_SCL_INPUT_LEVEL PPC_BIT(20)
+#define I2C_STAT_SDA_INPUT_LEVEL PPC_BIT(21)
+#define I2C_STAT_PORT_BUSY PPC_BIT(22)
+#define I2C_STAT_INTERFACE_BUSY PPC_BIT(23)
+#define I2C_STAT_FIFO_ENTRY_COUNT PPC_BITMASK(24, 31)
+
+#define I2C_STAT_ANY_ERR (I2C_STAT_INVALID_CMD | I2C_STAT_LBUS_PARITY_ERR | \
+ I2C_STAT_BKEND_OVERRUN_ERR | \
+ I2C_STAT_BKEND_ACCESS_ERR | I2C_STAT_ARBT_LOST_ERR | \
+ I2C_STAT_NACK_RCVD_ERR | I2C_STAT_STOP_ERR)
+
+
+#define I2C_INTR_ACTIVE \
+ ((I2C_STAT_ANY_ERR >> 16) | I2C_INTR_CMD_COMP | I2C_INTR_DATA_REQ)
+
+/* Pseudo-status used for timeouts */
+#define I2C_STAT_PSEUDO_TIMEOUT PPC_BIT(63)
+
+
+/* I2C extended status register */
+#define I2C_EXTD_STAT_REG 0xc
+#define I2C_EXTD_STAT_FIFO_SIZE PPC_BITMASK(0, 7)
+#define I2C_EXTD_STAT_MSM_CURSTATE PPC_BITMASK(11, 15)
+#define I2C_EXTD_STAT_SCL_IN_SYNC PPC_BIT(16)
+#define I2C_EXTD_STAT_SDA_IN_SYNC PPC_BIT(17)
+#define I2C_EXTD_STAT_S_SCL PPC_BIT(18)
+#define I2C_EXTD_STAT_S_SDA PPC_BIT(19)
+#define I2C_EXTD_STAT_M_SCL PPC_BIT(20)
+#define I2C_EXTD_STAT_M_SDA PPC_BIT(21)
+#define I2C_EXTD_STAT_HIGH_WATER PPC_BIT(22)
+#define I2C_EXTD_STAT_LOW_WATER PPC_BIT(23)
+#define I2C_EXTD_STAT_I2C_BUSY PPC_BIT(24)
+#define I2C_EXTD_STAT_SELF_BUSY PPC_BIT(25)
+#define I2C_EXTD_STAT_I2C_VERSION PPC_BITMASK(27, 31)
+
+/* I2C residual front end/back end length */
+#define I2C_RESIDUAL_LEN_REG 0xd
+#define I2C_RESIDUAL_FRONT_END PPC_BITMASK(0, 15)
+#define I2C_RESIDUAL_BACK_END PPC_BITMASK(16, 31)
+
+/* Port busy register */
+#define I2C_PORT_BUSY_REG 0xe
+#define I2C_SET_S_SCL_REG 0xd
+#define I2C_RESET_S_SCL_REG 0xf
+#define I2C_SET_S_SDA_REG 0x10
+#define I2C_RESET_S_SDA_REG 0x11
+
+enum p8_i2c_master_type {
+ I2C_POWER8,
+ I2C_CENTAUR,
+ MAX_I2C_TYPE,
+};
+
+struct p8_i2c_master {
+ struct dt_node *dt_node;
+ struct lock lock; /* Lock to guard the members */
+ enum p8_i2c_master_type type; /* P8 vs. Centaur */
+ uint64_t start_time; /* Request start time */
+ uint64_t last_update;
+ uint64_t poll_interval; /* Polling interval */
+ uint64_t xscom_base; /* xscom base of i2cm */
+ uint32_t fifo_size; /* Maximum size of FIFO */
+ uint32_t chip_id; /* Chip the i2cm sits on */
+ uint32_t engine_id; /* Engine# on chip */
+ uint8_t obuf[4]; /* Offset buffer */
+ uint32_t bytes_sent;
+ bool irq_ok; /* Interrupt working ? */
+ bool occ_cache_dis; /* I have disabled the cache */
+ bool occ_lock_acquired; /* Acquired lock from OCC */
+ enum request_state {
+ state_idle,
+ state_occache_dis,
+ state_offset,
+ state_data,
+ state_error,
+ state_recovery,
+ } state;
+ struct list_head req_list; /* Request queue head */
+ struct timer poller;
+ struct timer timeout;
+ struct timer recovery;
+ struct timer sensor_cache;
+ uint8_t recovery_pass;
+ struct list_node link;
+ struct list_head ports;
+};
+
+struct p8_i2c_master_port {
+ struct i2c_bus bus; /* Abstract bus struct for the client */
+ struct p8_i2c_master *master;
+ uint32_t port_num;
+ uint32_t bit_rate_div; /* Divisor to set bus speed*/
+ uint64_t byte_timeout; /* Timeout per byte */
+ uint64_t poll_interval; /* Polling interval */
+ struct list_node link;
+};
+
+static int occ_i2c_unlock(struct p8_i2c_master *master);
+
+static int64_t i2cm_read_reg(struct p8_i2c_master *m, int reg, uint64_t *val)
+{
+ return xscom_read(m->chip_id, m->xscom_base + reg, val);
+}
+
+static int64_t i2cm_write_reg(struct p8_i2c_master *m, int reg, uint64_t val)
+{
+ return xscom_write(m->chip_id, m->xscom_base + reg, val);
+}
+
+static void p8_i2c_print_debug_info(struct p8_i2c_master_port *port,
+ struct i2c_request *req, uint64_t end_time)
+{
+ struct p8_i2c_master *master = port->master;
+ uint64_t cmd, mode, stat, estat, intm, intc;
+
+ /* Print master and request structure bits */
+ log_simple_error(&e_info(OPAL_RC_I2C_TRANSFER),
+ "I2C: Chip %08x Eng. %d Port %d--\n"
+ " xscom_base=0x%016llx\tstate=%d\tbytes_sent=%d\n",
+ master->chip_id, master->engine_id, port->port_num,
+ master->xscom_base, master->state, master->bytes_sent);
+
+ log_simple_error(&e_info(OPAL_RC_I2C_TRANSFER), "I2C: Request info--\n"
+ " addr=0x%04x\toffset_bytes=%d\toffset=%d\tlen=%d\n",
+ req->dev_addr, req->offset_bytes, req->offset,
+ req->rw_len);
+
+ log_simple_error(&e_info(OPAL_RC_I2C_TRANSFER), "I2C: "
+ " start_time=%016llx end_time=%016llx (duration=%016llx)\n",
+ master->start_time, end_time, end_time - master->start_time);
+
+ /* initialise to some fake value in case of read errors */
+ cmd = mode = stat = estat = intm = intc = 0xDEAD;
+
+ /* Dump the current state of i2c registers */
+ i2cm_read_reg(master, I2C_CMD_REG, &cmd);
+ i2cm_read_reg(master, I2C_MODE_REG, &mode);
+ i2cm_read_reg(master, I2C_MODE_REG, &mode);
+ i2cm_read_reg(master, I2C_STAT_REG, &stat);
+ i2cm_read_reg(master, I2C_EXTD_STAT_REG, &estat);
+ i2cm_read_reg(master, I2C_INTR_MASK_REG, &intm);
+ i2cm_read_reg(master, I2C_INTR_RAW_COND_REG, &intc);
+
+ log_simple_error(&e_info(OPAL_RC_I2C_TRANSFER), "I2C: Register dump--\n"
+ " cmd:0x%016llx\tmode:0x%016llx\tstat:0x%016llx\n"
+ " estat:0x%016llx\tintm:0x%016llx\tintc:0x%016llx\n",
+ cmd, mode, stat, estat, intm, intc);
+
+ log_simple_error(&e_info(OPAL_RC_I2C_TRANSFER),
+ "I2C: Error bits set: %s%s%s%s%s%s%s\n",
+ (stat & I2C_STAT_NACK_RCVD_ERR) ? "nack, " : "",
+ (stat & I2C_STAT_INVALID_CMD) ? "cmd invalid, " : "",
+ (stat & I2C_STAT_LBUS_PARITY_ERR) ? "interal parity, " : "",
+ (stat & I2C_STAT_BKEND_OVERRUN_ERR) ? "backend overrun, " : "",
+ (stat & I2C_STAT_BKEND_ACCESS_ERR) ? "backend access, " : "",
+ (stat & I2C_STAT_ARBT_LOST_ERR) ? "arbitration loss, " : "",
+ (stat & I2C_STAT_STOP_ERR) ? "stop error, " : "");
+}
+
+static bool p8_i2c_has_irqs(struct p8_i2c_master *master)
+{
+ struct proc_chip *chip;
+
+ /* Centaur I2C doesn't have interrupts */
+ if (master->type == I2C_CENTAUR)
+ return false;
+
+ chip = get_chip(master->chip_id);
+
+ /* The i2c interrupts was only added to Murano DD2.1 and Venice
+ * DD2.0. When operating without interrupts, we need to bump the
+ * timeouts as we rely solely on the polls from Linux which can
+ * be up to 2s apart !
+ */
+ if (proc_gen >= proc_gen_p9)
+ return true;
+ else if (chip->type == PROC_CHIP_P8_MURANO)
+ return chip->ec_level >= 0x21;
+ else if (chip->type == PROC_CHIP_P8_VENICE)
+ return chip->ec_level >= 0x20;
+
+ return true;
+}
+
+static int p8_i2c_enable_irqs(struct p8_i2c_master *master)
+{
+ int rc;
+
+ /* enable interrupts we're interested in */
+ rc = i2cm_write_reg(master, I2C_INTR_MASK_OR_REG, I2C_INTR_ACTIVE);
+ if (rc)
+ prlog(PR_ERR, "I2C: Failed to enable the interrupts\n");
+
+ return rc;
+}
+
+static void p8_i2c_reset_timeout(struct p8_i2c_master *master,
+ struct i2c_request *req)
+{
+ uint64_t now = mftb();
+
+ master->last_update = now;
+ schedule_timer_at(&master->timeout, now + msecs_to_tb(req->timeout));
+}
+
+static int p8_i2c_prog_watermark(struct p8_i2c_master *master)
+{
+ uint64_t watermark;
+ int rc;
+
+ rc = xscom_read(master->chip_id, master->xscom_base + I2C_WATERMARK_REG,
+ &watermark);
+ if (rc) {
+ prlog(PR_ERR, "I2C: Failed to read the WATERMARK_REG\n");
+ return rc;
+ }
+
+ /* Set the high/low watermark */
+ watermark = SETFIELD(I2C_WATERMARK_HIGH, watermark, I2C_FIFO_HI_LVL);
+ watermark = SETFIELD(I2C_WATERMARK_LOW, watermark, I2C_FIFO_LO_LVL);
+ rc = xscom_write(master->chip_id, master->xscom_base +
+ I2C_WATERMARK_REG, watermark);
+ if (rc)
+ prlog(PR_ERR, "I2C: Failed to set high/low watermark level\n");
+
+ return rc;
+}
+
+static int p8_i2c_prog_mode(struct p8_i2c_master_port *port, bool enhanced_mode)
+{
+ struct p8_i2c_master *master = port->master;
+ uint64_t mode, omode;
+ int rc;
+
+ rc = xscom_read(master->chip_id, master->xscom_base +
+ I2C_MODE_REG, &mode);
+ if (rc) {
+ prlog(PR_ERR, "I2C: Failed to read the MODE_REG\n");
+ return rc;
+ }
+ omode = mode;
+ mode = SETFIELD(I2C_MODE_PORT_NUM, mode, port->port_num);
+ mode = SETFIELD(I2C_MODE_BIT_RATE_DIV, mode, port->bit_rate_div);
+ if (enhanced_mode)
+ mode |= I2C_MODE_ENHANCED;
+ else
+ mode &= ~I2C_MODE_ENHANCED;
+ if (mode == omode)
+ return 0;
+
+ rc = xscom_write(master->chip_id, master->xscom_base + I2C_MODE_REG,
+ mode);
+ if (rc)
+ prlog(PR_ERR, "I2C: Failed to write the MODE_REG\n");
+
+ return rc;
+}
+
+static void p8_i2c_complete_request(struct p8_i2c_master *master,
+ struct i2c_request *req, int ret)
+{
+ /* We only complete the current top level request */
+ assert(req == list_top(&master->req_list, struct i2c_request, link));
+
+ cancel_timer_async(&master->timeout);
+
+ list_del(&req->link);
+ master->state = state_idle;
+ req->result = ret;
+ req->req_state = i2c_req_done;
+
+ /* Schedule re-enabling of sensor cache */
+ if (master->occ_cache_dis)
+ schedule_timer(&master->sensor_cache,
+ msecs_to_tb(SENSOR_CACHE_EN_DELAY));
+
+ /* If we're done with i2c master, allow OCC to use it */
+ if (master->occ_lock_acquired && list_empty(&master->req_list))
+ occ_i2c_unlock(master);
+
+ unlock(&master->lock);
+ if (req->completion)
+ req->completion(ret, req);
+ /* req might have been freed at this point */
+ lock(&master->lock);
+}
+
+
+static int p8_i2c_engine_reset(struct p8_i2c_master_port *port)
+{
+ struct p8_i2c_master *master = port->master;
+ int rc;
+
+ /* Reset the i2c engine */
+ rc = xscom_write(master->chip_id, master->xscom_base +
+ I2C_RESET_I2C_REG, 0);
+ if (rc) {
+ log_simple_error(&e_info(OPAL_RC_I2C_RESET), "I2C: Failed "
+ "to reset the i2c engine\n");
+ return rc;
+ }
+
+ /* Reprogram the watermark and mode */
+ rc = p8_i2c_prog_watermark(port->master);
+ if (rc) {
+ log_simple_error(&e_info(OPAL_RC_I2C_RESET), "I2C: Failed to"
+ "program the WATERMARK_REG\n");
+ return rc;
+ }
+
+ rc = p8_i2c_prog_mode(port, false);
+ if (rc)
+ log_simple_error(&e_info(OPAL_RC_I2C_RESET), "I2C: Failed to"
+ "program the MODE_REG\n");
+
+ return rc;
+}
+
+static void p8_i2c_translate_error(struct i2c_request *req, uint64_t status)
+{
+ /* Assuming there are not more than one type of error simultaneously */
+ if (status & I2C_STAT_NACK_RCVD_ERR)
+ req->result = OPAL_I2C_NACK_RCVD;
+ else if (status & I2C_STAT_INVALID_CMD)
+ req->result = OPAL_I2C_INVALID_CMD;
+ else if (status & I2C_STAT_LBUS_PARITY_ERR)
+ req->result = OPAL_I2C_LBUS_PARITY;
+ else if (status & I2C_STAT_BKEND_OVERRUN_ERR)
+ req->result = OPAL_I2C_BKEND_OVERRUN;
+ else if (status & I2C_STAT_BKEND_ACCESS_ERR)
+ req->result = OPAL_I2C_BKEND_ACCESS;
+ else if (status & I2C_STAT_ARBT_LOST_ERR)
+ req->result = OPAL_I2C_ARBT_LOST;
+ else if (status & I2C_STAT_STOP_ERR)
+ req->result = OPAL_I2C_STOP_ERR;
+ else if (status & I2C_STAT_PSEUDO_TIMEOUT)
+ req->result = OPAL_I2C_TIMEOUT;
+}
+
+static int p8_i2c_reset_port(struct p8_i2c_master_port *p)
+{
+ struct p8_i2c_master *master = p->master;
+ int reset_loops, rc;
+ uint64_t status;
+
+ /* FIXME: this should per per-port rather than per-master */
+ master->state = state_error;
+
+ /*
+ * Put the master into enhanced STOP mode when recovering the
+ * port. This causes the master to send additional STOP conditions
+ * to work around some particularly stupid I2C devices and it's
+ * required on secure I2C masters since they will not send a bare
+ * stop condition.
+ */
+ rc = p8_i2c_prog_mode(p, true);
+ if (rc) {
+ log_simple_error(&e_info(OPAL_RC_I2C_RESET),
+ "I2C: Failed to enable enhanced mode\n");
+ return -1;
+ }
+
+ rc = xscom_write(master->chip_id, master->xscom_base +
+ I2C_CMD_REG, I2C_CMD_WITH_STOP);
+ if (rc)
+ goto err;
+
+ /* Wait for COMMAND COMPLETE */
+ for (reset_loops = 0; reset_loops < 10; reset_loops++) {
+ time_wait_ms(10);
+
+ rc = xscom_read(master->chip_id,
+ master->xscom_base + I2C_STAT_REG,
+ &status);
+ if (rc)
+ goto err;
+
+ if (status & I2C_STAT_CMD_COMP)
+ break;
+ }
+
+ if (status & I2C_STAT_CMD_COMP)
+ return 0;
+err:
+ prerror("I2C: Failed to reset c%de%dp%d\n",
+ master->chip_id, master->engine_id, p->port_num);
+ return -1;
+}
+
+static void p8_i2c_status_error(struct p8_i2c_master_port *port,
+ struct i2c_request *req,
+ uint64_t status, uint64_t end_time)
+{
+ struct p8_i2c_master *master = port->master;
+ int rc;
+
+ /* Display any error other than I2C_INTR_NACK_RCVD_ERR or
+ * timeout since getting NACK's is normal if Linux is probing
+ * the bus and timeouts will have already logged something.
+ */
+ if (!(status & (I2C_STAT_NACK_RCVD_ERR | I2C_STAT_PSEUDO_TIMEOUT))) {
+ log_simple_error(&e_info(OPAL_RC_I2C_TRANSFER),
+ "I2C: Transfer error occurred\n");
+ p8_i2c_print_debug_info(port, req, end_time);
+ } else if (status == I2C_STAT_PSEUDO_TIMEOUT) {
+ log_simple_error(&e_info(OPAL_RC_I2C_TIMEOUT),
+ "I2C: request timed out!\n");
+ p8_i2c_print_debug_info(port, req, end_time);
+ }
+
+ p8_i2c_translate_error(req, status);
+
+ rc = p8_i2c_engine_reset(port);
+ if (rc)
+ goto exit;
+
+ if (status & (I2C_STAT_LBUS_PARITY_ERR | I2C_STAT_ARBT_LOST_ERR |
+ I2C_STAT_STOP_ERR)) {
+ /*
+ * Don't bother issuing a STOP command for those errors
+ * just get rid of the current request and start off with
+ * the fresh one in the list
+ */
+ p8_i2c_complete_request(master, req, req->result);
+ } else {
+ if (p8_i2c_reset_port(port))
+ goto exit;
+ /* Enable the interrupt */
+ p8_i2c_enable_irqs(master);
+ }
+ return;
+
+exit:
+ p8_i2c_complete_request(master, req, req->result);
+}
+
+static int p8_i2c_fifo_read(struct p8_i2c_master *master,
+ uint8_t *buf, uint32_t count)
+{
+ uint64_t fifo;
+ uint32_t i;
+ int rc = 0;
+
+ for (i = 0; i < count; i++, buf++) {
+ rc = xscom_read(master->chip_id, master->xscom_base +
+ I2C_FIFO_REG, &fifo);
+ if (rc) {
+ log_simple_error(&e_info(OPAL_RC_I2C_TRANSFER),
+ "I2C: Failed to read the fifo\n");
+ break;
+ }
+
+ *buf = GETFIELD(I2C_FIFO, fifo);
+ }
+ return rc;
+}
+
+static int p8_i2c_fifo_write(struct p8_i2c_master *master,
+ uint8_t *buf, uint32_t count)
+{
+ uint64_t fifo;
+ uint32_t i;
+ int rc = 0;
+
+ for (i = 0; i < count; i++, buf++) {
+ fifo = SETFIELD(I2C_FIFO, 0ull, *buf);
+ rc = xscom_write(master->chip_id, master->xscom_base +
+ I2C_FIFO_REG, fifo);
+ if (rc) {
+ log_simple_error(&e_info(OPAL_RC_I2C_TRANSFER),
+ "I2C: Failed to write the fifo\n");
+ break;
+ }
+ }
+ return rc;
+}
+
+static void p8_i2c_status_data_request(struct p8_i2c_master *master,
+ struct i2c_request *req,
+ uint64_t status)
+{
+ uint32_t fifo_count, fifo_free, count;
+ uint8_t *buf;
+ int rc = 0;
+
+ fifo_count = GETFIELD(I2C_STAT_FIFO_ENTRY_COUNT, status);
+ fifo_free = master->fifo_size - fifo_count;
+
+ DBG("Data request, state=%d fifo_count=%d/%d bytes_sent=%d\n",
+ master->state, fifo_count, master->fifo_size, master->bytes_sent);
+
+ switch(master->state) {
+ case state_offset:
+ /* We assume the offset can always be written in one go */
+ if (fifo_free < req->offset_bytes) {
+ log_simple_error(&e_info(OPAL_RC_I2C_TRANSFER),
+ "I2C: Fifo too small for offset !\n");
+ rc = OPAL_HARDWARE;
+ } else {
+ rc = p8_i2c_fifo_write(master, master->obuf,
+ req->offset_bytes);
+ }
+
+ /* For read, wait address phase to complete */
+ if (rc || req->op != SMBUS_WRITE)
+ break;
+
+ /* For writes, transition to data phase now */
+ master->state = state_data;
+ fifo_free -= req->offset_bytes;
+ /* Fall through */
+ case state_data:
+ /* Sanity check */
+ if (master->bytes_sent >= req->rw_len) {
+ log_simple_error(&e_info(OPAL_RC_I2C_TRANSFER), "I2C: "
+ "Data req with no data to send sent=%d "
+ "req=%d\n", master->bytes_sent,
+ req->rw_len);
+ rc = OPAL_HARDWARE;
+ break;
+ }
+
+ /* Get next chunk */
+ buf = req->rw_buf + master->bytes_sent;
+ count = req->rw_len - master->bytes_sent;
+
+ /* Check direction */
+ if (req->op == I2C_READ || req->op == SMBUS_READ) {
+ if (count > fifo_count)
+ count = fifo_count;
+ rc = p8_i2c_fifo_read(master, buf, count);
+ } else {
+ if (count > fifo_free)
+ count = fifo_free;
+ rc = p8_i2c_fifo_write(master, buf, count);
+ }
+ if (rc == 0)
+ master->bytes_sent += count;
+ break;
+ default:
+ log_simple_error(&e_info(OPAL_RC_I2C_TRANSFER), "I2C: Invalid "
+ "state %d in data req !\n", master->state);
+ rc = OPAL_WRONG_STATE;
+ }
+
+ if (rc) {
+ p8_i2c_complete_request(master, req, rc);
+ } else {
+ p8_i2c_enable_irqs(master);
+ p8_i2c_reset_timeout(master, req);
+ }
+}
+
+static void p8_i2c_complete_offset(struct p8_i2c_master *master,
+ struct i2c_request *req)
+{
+ uint64_t cmd;
+ int rc = 0;
+
+ DBG("Completing offset phase\n");
+
+ /* If it's a write, we should only get here for empty
+ * write commands
+ */
+ if (req->op == SMBUS_WRITE && req->rw_len != 0) {
+ log_simple_error(&e_info(OPAL_RC_I2C_TRANSFER), "I2C: Write "
+ "completion in offset state !\n");
+ rc = OPAL_HARDWARE;
+ goto complete;
+ }
+
+ /* Switch to data phase */
+ master->state = state_data;
+
+ /* If it's not a read command, or there are no data to read,
+ * then we complete the command
+ */
+ if (req->op != SMBUS_READ || req->rw_len == 0)
+ goto complete;
+
+ /* Otherwise, let's start the data phase */
+ cmd = I2C_CMD_WITH_START | I2C_CMD_WITH_ADDR |
+ I2C_CMD_WITH_STOP | I2C_CMD_READ_NOT_WRITE;
+ cmd = SETFIELD(I2C_CMD_DEV_ADDR, cmd, req->dev_addr);
+ cmd = SETFIELD(I2C_CMD_LEN_BYTES, cmd, req->rw_len);
+ cmd = SETFIELD(I2C_CMD_INTR_STEERING, cmd, I2C_CMD_INTR_STEER_HOST);
+
+ DBG("Command: %016llx, state: %d\n", cmd, master->state);
+
+ /* Send command */
+ rc = xscom_write(master->chip_id, master->xscom_base + I2C_CMD_REG,
+ cmd);
+ if (rc) {
+ log_simple_error(&e_info(OPAL_RC_I2C_TRANSFER), "I2C: Failed "
+ "to write the CMD_REG\n");
+ goto complete;
+ }
+
+ /* Enable the interrupts */
+ p8_i2c_enable_irqs(master);
+ p8_i2c_reset_timeout(master, req);
+ return;
+
+ complete:
+ p8_i2c_complete_request(master, req, rc);
+}
+
+static void p8_i2c_status_cmd_completion(struct p8_i2c_master *master,
+ struct i2c_request *req,
+ uint64_t end_time __unused)
+{
+ int rc;
+
+ DBG("Command completion, state=%d bytes_sent=%d\n",
+ master->state, master->bytes_sent);
+ DBG(" start_time=%016llx end_time=%016llx (duration=%016llx)\n",
+ master->start_time, end_time, end_time - master->start_time);
+
+ /* If we complete an offset, we probably need to transition
+ * do a data read, check if that all makes sense
+ */
+ if (master->state == state_offset) {
+ p8_i2c_complete_offset(master, req);
+ return;
+ }
+
+ /* If we are not already in error state, check if we have
+ * completed our data transfer properly
+ */
+ if (master->state != state_error && master->bytes_sent != req->rw_len) {
+ log_simple_error(&e_info(OPAL_RC_I2C_TRANSFER), "I2C: Request "
+ "complete with residual data req=%d done=%d\n",
+ req->rw_len, master->bytes_sent);
+ /* Should we error out here ? */
+ }
+ rc = master->state == state_error ? req->result : OPAL_SUCCESS;
+ p8_i2c_complete_request(master, req, rc);
+}
+
+static void p8_i2c_check_status(struct p8_i2c_master *master)
+{
+ struct p8_i2c_master_port *port;
+ uint64_t status, deadline, now;
+ struct i2c_request *req;
+ int rc;
+
+ /*
+ * When idle or waiting for the occ to release the bus there's
+ * nothing to check. Also ignore recovery state, as the bus
+ * can be reset in that state, and a request can think it's
+ * complete when it just means the reset is complete.
+ * Error states are handled when starting a new request.
+ */
+ if (master->state == state_idle || master->state == state_occache_dis ||
+ master->state == state_recovery)
+ return;
+
+ /* A non-idle master should always have a pending request */
+ req = list_top(&master->req_list, struct i2c_request, link);
+ if (!req) {
+ prerror("I2C: Master is not idle and has no pending request\n");
+ return;
+ }
+
+ rc = i2cm_read_reg(master, I2C_STAT_REG, &status);
+ if (rc) {
+ log_simple_error(&e_info(OPAL_RC_I2C_TRANSFER),
+ "I2C: Failed to read the STAT_REG\n");
+ return;
+ }
+
+ /* mask interrupts while we're mucking with the master */
+ rc = i2cm_write_reg(master, I2C_INTR_MASK_AND_REG, ~I2C_INTR_ALL);
+ if (rc) {
+ log_simple_error(&e_info(OPAL_RC_I2C_TRANSFER),
+ "I2C: Failed to disable the interrupts\n");
+ return;
+ }
+
+ /* Get port for current request */
+ port = container_of(req->bus, struct p8_i2c_master_port, bus);
+ now = mftb();
+
+ deadline = master->last_update + msecs_to_tb(req->timeout);
+
+ if (status & I2C_STAT_ANY_ERR)
+ p8_i2c_status_error(port, req, status & I2C_STAT_ANY_ERR, now);
+ else if (status & I2C_STAT_DATA_REQ)
+ p8_i2c_status_data_request(master, req, status);
+ else if (status & I2C_STAT_CMD_COMP)
+ p8_i2c_status_cmd_completion(master, req, now);
+ else if (tb_compare(now, deadline) == TB_AAFTERB)
+ p8_i2c_status_error(port, req, I2C_STAT_PSEUDO_TIMEOUT, now);
+ else
+ p8_i2c_enable_irqs(master);
+}
+
+static int p8_i2c_check_initial_status(struct p8_i2c_master_port *port)
+{
+ struct p8_i2c_master *master = port->master;
+ uint64_t status, estat;
+ int rc;
+
+ master->recovery_pass++;
+
+ /* Read status register */
+ rc = xscom_read(master->chip_id, master->xscom_base + I2C_STAT_REG,
+ &status);
+ if (rc) {
+ log_simple_error(&e_info(OPAL_RC_I2C_START_REQ), "I2C: Failed "
+ "to read the STAT_REG\n");
+ return rc;
+ }
+
+ rc = xscom_read(master->chip_id,
+ master->xscom_base + I2C_EXTD_STAT_REG,
+ &estat);
+ if (rc) {
+ log_simple_error(&e_info(OPAL_RC_I2C_START_REQ), "I2C: Failed "
+ "to read the EXTD_STAT_REG\n");
+ return rc;
+ }
+ if (estat & (I2C_EXTD_STAT_I2C_BUSY | I2C_EXTD_STAT_SELF_BUSY)) {
+ DBG("Initial estat busy ! %016llx\n", estat);
+ /* Just a warning for now */
+ }
+
+ /* Nothing happened ? Go back */
+ if (status & I2C_STAT_ANY_ERR) {
+ log_simple_error(&e_info(OPAL_RC_I2C_START_REQ), "I2C: "
+ "Initial error status 0x%016llx\n", status);
+
+ if (master->recovery_pass > 1) {
+ log_simple_error(&e_info(OPAL_RC_I2C_START_REQ), "I2C: "
+ "Error stuck, aborting !!\n");
+ return OPAL_HARDWARE;
+ }
+
+ /* Mark state as "recovery" to block any other activity */
+ master->state = state_recovery;
+
+ /* Reset the engine */
+ p8_i2c_engine_reset(port);
+
+ /* Delay 5ms for bus to settle */
+ schedule_timer(&master->recovery, msecs_to_tb(5));
+ return OPAL_BUSY;
+ }
+
+ /* Still busy ? */
+ if (!(status & I2C_STAT_CMD_COMP)) {
+ log_simple_error(&e_info(OPAL_RC_I2C_START_REQ), "I2C: Initial "
+ "command complete not set\n");
+
+ if (master->recovery_pass > 5) {
+ log_simple_error(&e_info(OPAL_RC_I2C_START_REQ), "I2C: "
+ "Command stuck, aborting !!\n");
+ return OPAL_HARDWARE;
+ }
+
+
+ master->state = state_recovery;
+
+ /* Delay 5ms for bus to settle */
+ schedule_timer(&master->recovery, msecs_to_tb(5));
+ return OPAL_BUSY;
+ }
+
+ master->recovery_pass = 0;
+ return 0;
+}
+
+/*
+ * On POWER9, the I2C may also wish to use some of the i2cm engines,
+ * to do things like read sensor data. There's a couple of shared
+ * registers with the OCC to negotiate locking of the i2cm engines.
+ * See occ/src/occ_405/lock/lock.c
+ */
+static bool occ_uses_master(struct p8_i2c_master *master)
+{
+ /* OCC uses I2CM Engines 1,2 and 3, only on POWER9/10 */
+ if (master->type == I2C_POWER8 && proc_gen >= proc_gen_p9)
+ return master->engine_id >= 1;
+
+ return false;
+}
+
+static uint32_t occflg;
+#define OCCFLG_BASE 0
+#define OCCFLG_CLEAR 1
+#define OCCFLG_SET 2
+
+static int occ_i2c_lock(struct p8_i2c_master *master)
+{
+ u64 occflags, busflag;
+ int rc;
+
+ if (!occ_uses_master(master) || !occflg)
+ return 0;
+
+ if (master->occ_lock_acquired)
+ return 0;
+
+ rc = xscom_read(master->chip_id, occflg, &occflags);
+ if (rc) {
+ prerror("I2C: Failed to read OCC FLAG register\n");
+ return rc;
+ }
+
+ assert(master->engine_id > 0);
+
+ busflag = PPC_BIT(16 + (master->engine_id - 1) * 2);
+
+ DBG("I2C: c%de%d: occflags = %llx (locks = %x:%x:%x)\n",
+ master->chip_id, master->engine_id, (u64) occflags,
+ (u32) GETFIELD(PPC_BITMASK(16, 17), occflags),
+ (u32) GETFIELD(PPC_BITMASK(18, 19), occflags),
+ (u32) GETFIELD(PPC_BITMASK(20, 21), occflags));
+
+ rc = xscom_write(master->chip_id, occflg + OCCFLG_SET, busflag);
+ if (rc) {
+ prerror("I2C: Failed to write OCC FLAG register\n");
+ return rc;
+ }
+
+ /* If the OCC also has this bus locked then wait for IRQ */
+ if (occflags & (busflag >> 1)) {
+ DBG("I2C: c%de%d: Master in use by OCC\n",
+ master->chip_id, master->engine_id);
+ return 1;
+ }
+
+ master->occ_lock_acquired = true;
+
+ return 0;
+}
+
+static int occ_i2c_unlock(struct p8_i2c_master *master)
+{
+ u64 busflag, occflags;
+ int rc;
+
+ if (!occ_uses_master(master) || !occflg)
+ return 0;
+
+ rc = xscom_read(master->chip_id, occflg, &occflags);
+ if (rc) {
+ prerror("I2C: Failed to read OCC Flag register\n");
+ return rc;
+ }
+
+ busflag = PPC_BIT(16 + (master->engine_id - 1) * 2);
+
+ if (!(occflags & busflag)) {
+ DBG("I2C: spurious unlock for c%de%d already cleared (flags = %.16llx)",
+ master->chip_id, master->engine_id, occflags);
+ }
+
+ rc = xscom_write(master->chip_id, occflg + OCCFLG_CLEAR, busflag);
+ if (rc)
+ prerror("I2C: Failed to write OCC Flag register\n");
+
+ master->occ_lock_acquired = false;
+
+ return rc;
+}
+
+static int p8_i2c_start_request(struct p8_i2c_master *master,
+ struct i2c_request *req)
+{
+ struct p8_i2c_master_port *port;
+ uint64_t cmd;
+ int64_t rc;
+
+ DBG("Starting req %d len=%d addr=%02x (offset=%x)\n",
+ req->op, req->rw_len, req->dev_addr, req->offset);
+
+ /* Get port */
+ port = container_of(req->bus, struct p8_i2c_master_port, bus);
+
+ /* Check if we need to disable the OCC cache first */
+ if (master->type == I2C_CENTAUR && !master->occ_cache_dis) {
+ DBG("Disabling OCC cache...\n");
+ rc = centaur_disable_sensor_cache(master->chip_id);
+
+ if (rc < 0) {
+ log_simple_error(&e_info(OPAL_RC_I2C_START_REQ),
+ "I2C: Failed "
+ "to disable the sensor cache\n");
+ return rc;
+ }
+ master->occ_cache_dis = true;
+
+ /* Do we need to wait ? */
+ if (rc > 0) {
+ DBG("Waiting %lld\n", rc);
+ master->state = state_occache_dis;
+ schedule_timer(&master->recovery, rc);
+ return 0;
+ }
+ }
+
+ /*
+ * on P9 we need to set the "I2C master using bit" so we don't
+ * conflict with the OCC's use of the i2c master.
+ */
+ rc = occ_i2c_lock(master);
+ if (rc < 0) {
+ log_simple_error(&e_info(OPAL_RC_I2C_START_REQ),
+ "I2C: Failed to get I2CM lock from OCC\n");
+ return rc;
+ }
+ if (rc > 0) {
+ /* Wait for OCC IRQ */
+ master->state = state_occache_dis;
+ schedule_timer(&master->recovery, msecs_to_tb(10));
+ return 0;
+ }
+
+ /* Convert the offset if needed */
+ if (req->offset_bytes) {
+ int i;
+
+ for (i = 0; i < req->offset_bytes; i++) {
+ uint8_t b;
+
+ b = req->offset >> (8 * (req->offset_bytes - i - 1));
+ master->obuf[i] = b;
+ }
+ DBG("Offset %d bytes: %02x %02x %02x %02x\n",
+ req->offset_bytes, master->obuf[0], master->obuf[1],
+ master->obuf[2], master->obuf[3]);
+ }
+
+ /* Program mode register */
+ rc = p8_i2c_prog_mode(port, false);
+ if (rc) {
+ log_simple_error(&e_info(OPAL_RC_I2C_START_REQ), "I2C: Failed "
+ "to program the MODE_REG\n");
+ return rc;
+ }
+
+ /* Check status */
+ rc = p8_i2c_check_initial_status(port);
+ if (rc != OPAL_BUSY)
+ master->recovery_pass = 0;
+ if (rc)
+ return rc;
+
+ /* program the watermark register */
+ rc = p8_i2c_prog_watermark(master);
+ if (rc) {
+ log_simple_error(&e_info(OPAL_RC_I2C_INIT),
+ "I2C: Failed to program the WATERMARK_REG\n");
+ return rc;
+ }
+
+ /* Initialize bytes_sent */
+ master->bytes_sent = 0;
+
+ /* Set up the command register */
+ cmd = I2C_CMD_WITH_START | I2C_CMD_WITH_ADDR;
+ cmd = SETFIELD(I2C_CMD_DEV_ADDR, cmd, req->dev_addr);
+ cmd = SETFIELD(I2C_CMD_INTR_STEERING, cmd, I2C_CMD_INTR_STEER_HOST);
+ switch (req->op) {
+ case I2C_READ:
+ cmd |= I2C_CMD_READ_NOT_WRITE;
+ /* Fall through */
+ case I2C_WRITE:
+ cmd |= I2C_CMD_WITH_STOP;
+ cmd = SETFIELD(I2C_CMD_LEN_BYTES, cmd, req->rw_len);
+ master->state = state_data;
+ break;
+ case SMBUS_READ:
+ cmd = SETFIELD(I2C_CMD_LEN_BYTES, cmd, req->offset_bytes);
+ master->state = state_offset;
+ break;
+ case SMBUS_WRITE:
+ cmd |= I2C_CMD_WITH_STOP;
+ cmd = SETFIELD(I2C_CMD_LEN_BYTES, cmd,
+ req->rw_len + req->offset_bytes);
+ master->state = state_offset;
+ break;
+ default:
+ return OPAL_PARAMETER;
+ }
+ DBG("Command: %016llx, state: %d\n", cmd, master->state);
+
+ master->start_time = mftb();
+
+ /* Send command */
+ rc = xscom_write(master->chip_id, master->xscom_base + I2C_CMD_REG,
+ cmd);
+ if (rc) {
+ log_simple_error(&e_info(OPAL_RC_I2C_START_REQ), "I2C: Failed "
+ "to write the CMD_REG\n");
+ return rc;
+ }
+
+ /* Enable the interrupts */
+ p8_i2c_enable_irqs(master);
+
+ /* Run a poll timer for boot cases or non-working interrupts
+ * cases
+ */
+ if (!opal_booting() && master->irq_ok)
+ master->poll_interval = TIMER_POLL;
+ else
+ master->poll_interval = port->poll_interval;
+ schedule_timer(&master->poller, master->poll_interval);
+
+ /* If we don't have a user-set timeout then use the master's default */
+ if (!req->timeout)
+ req->timeout = port->byte_timeout;
+
+ /* Start the timeout */
+ p8_i2c_reset_timeout(master, req);
+
+ return OPAL_SUCCESS;
+}
+
+static void p8_i2c_check_work(struct p8_i2c_master *master)
+{
+ struct i2c_request *req;
+ int rc;
+
+ while (master->state == state_idle && !list_empty(&master->req_list)) {
+ req = list_top(&master->req_list, struct i2c_request, link);
+ rc = p8_i2c_start_request(master, req);
+ if (rc) {
+ /*
+ * If it didn't work the first three times then
+ * odds are it's not going to work on the 4th.
+ */
+ if (rc && req->retries > 3)
+ p8_i2c_complete_request(master, req, rc);
+ else
+ req->retries++;
+ }
+ }
+}
+
+/* OCC IRQ Handler for I2C Ownership Change*/
+void p9_i2c_bus_owner_change(u32 chip_id)
+{
+ struct proc_chip *chip = get_chip(chip_id);
+ struct p8_i2c_master *master = NULL;
+
+ assert(chip);
+ list_for_each(&chip->i2cms, master, link) {
+ lock(&master->lock);
+
+ /* spurious */
+ if (master->state != state_occache_dis)
+ goto done;
+
+ /* Can we now lock this master? */
+ if (occ_i2c_lock(master))
+ goto done;
+
+ /* clear the existing wait timer */
+ cancel_timer_async(&master->recovery);
+
+ /* re-start the request now that we own the master */
+ master->state = state_idle;
+
+ p8_i2c_check_work(master);
+ p8_i2c_check_status(master);
+done:
+ unlock(&master->lock);
+ }
+}
+
+static int p8_i2c_queue_request(struct i2c_request *req)
+{
+ struct i2c_bus *bus = req->bus;
+ struct p8_i2c_master_port *port =
+ container_of(bus, struct p8_i2c_master_port, bus);
+ struct p8_i2c_master *master = port->master;
+ int rc = 0;
+
+ /* Parameter check */
+ if (req->rw_len > I2C_MAX_TFR_LEN) {
+ prlog(PR_ERR, "I2C: Too large transfer %d bytes\n", req->rw_len);
+ return OPAL_PARAMETER;
+ }
+
+ if (req->offset_bytes > 4) {
+ prlog(PR_ERR, "I2C: Invalid offset size %d\n", req->offset_bytes);
+ return OPAL_PARAMETER;
+ }
+ lock(&master->lock);
+ list_add_tail(&master->req_list, &req->link);
+ p8_i2c_check_work(master);
+ unlock(&master->lock);
+
+ return rc;
+}
+
+static uint64_t p8_i2c_run_request(struct i2c_request *req)
+{
+ struct i2c_bus *bus = req->bus;
+ struct p8_i2c_master_port *port =
+ container_of(bus, struct p8_i2c_master_port, bus);
+ struct p8_i2c_master *master = port->master;
+ uint64_t poll_interval = 0;
+
+ lock(&master->lock);
+ p8_i2c_check_status(master);
+ p8_i2c_check_work(master);
+ poll_interval = master->poll_interval;
+ unlock(&master->lock);
+
+ return poll_interval;
+}
+
+static inline uint32_t p8_i2c_get_bit_rate_divisor(uint32_t lb_freq,
+ uint32_t bus_speed)
+{
+ assert(bus_speed > 0);
+ return (((lb_freq / bus_speed) - 1) / 4);
+}
+
+static inline uint64_t p8_i2c_get_poll_interval(uint32_t bus_speed)
+{
+ uint64_t usec;
+
+ assert(bus_speed > 0);
+
+ /* Polling Interval = 8 * (1/bus_speed) * (1/10) -> convert to uSec */
+ usec = ((8 * USEC_PER_SEC) / (10 * bus_speed));
+ return usecs_to_tb(usec);
+}
+
+static void p8_i2c_timeout(struct timer *t __unused, void *data,
+ uint64_t __unused now)
+{
+ struct p8_i2c_master *master = data;
+
+ lock(&master->lock);
+
+ DBG("timeout on c%de%d\n", master->chip_id, master->engine_id);
+
+ /*
+ * Run through the usual status checks. It's possible to get spurious
+ * timeouts due to races between the interrupt/poller paths and the
+ * timeout handler. So we do all the checking, all the time.
+ */
+ p8_i2c_check_status(master);
+ p8_i2c_check_work(master);
+
+ unlock(&master->lock);
+}
+
+static void p8_i2c_recover(struct timer *t __unused, void *data,
+ uint64_t now __unused)
+{
+ struct p8_i2c_master *master = data;
+
+ lock(&master->lock);
+
+ /*
+ * The recovery timer can race with the OCC interrupt. If the interrupt
+ * comes in just before this is called, then we'll get a spurious
+ * timeout which we need to ignore.
+ */
+ if (master->state != state_recovery &&
+ master->state != state_occache_dis) {
+ unlock(&master->lock);
+ return;
+ }
+
+ master->state = state_idle;
+
+ /* We may or may not still have work pending, re-enable the sensor cache
+ * immediately if we don't (we just waited the recovery time so there is
+ * little point waiting longer).
+ */
+ if (master->occ_cache_dis && list_empty(&master->req_list)) {
+ DBG("Re-enabling OCC cache after recovery\n");
+ centaur_enable_sensor_cache(master->chip_id);
+ master->occ_cache_dis = false;
+ }
+
+ if (master->occ_lock_acquired && list_empty(&master->req_list))
+ occ_i2c_unlock(master);
+
+ /* Re-check for new work */
+ p8_i2c_check_work(master);
+ unlock(&master->lock);
+}
+
+static void p8_i2c_enable_scache(struct timer *t __unused, void *data,
+ uint64_t now __unused)
+{
+ struct p8_i2c_master *master = data;
+
+ lock(&master->lock);
+
+ /* Check if we are still idle */
+ if (master->state == state_idle && master->occ_cache_dis) {
+ DBG("Re-enabling OCC cache\n");
+ centaur_enable_sensor_cache(master->chip_id);
+ master->occ_cache_dis = false;
+ }
+ unlock(&master->lock);
+}
+
+static void p8_i2c_poll(struct timer *t __unused, void *data, uint64_t now)
+{
+ struct p8_i2c_master *master = data;
+
+ /*
+ * This is called when the interrupt isn't functional or
+ * generally from the opal pollers, so fast while booting
+ * and slowly when Linux is up.
+ */
+
+ /* Lockless fast bailout */
+ if (master->state == state_idle)
+ return;
+
+ lock(&master->lock);
+ p8_i2c_check_status(master);
+ if (master->state != state_idle)
+ schedule_timer_at(&master->poller, now + master->poll_interval);
+ p8_i2c_check_work(master);
+ unlock(&master->lock);
+}
+
+void p8_i2c_interrupt(uint32_t chip_id)
+{
+ struct proc_chip *chip = get_chip(chip_id);
+ struct p8_i2c_master *master = NULL;
+
+ assert(chip);
+ list_for_each(&chip->i2cms, master, link) {
+
+ /* Lockless fast bailout (shared interrupt) */
+ if (master->state == state_idle)
+ continue;
+
+ lock(&master->lock);
+
+ /* Run the state machine */
+ p8_i2c_check_status(master);
+
+ /* Check for new work */
+ p8_i2c_check_work(master);
+
+ unlock(&master->lock);
+ }
+}
+
+static const char *compat[] = {
+ "ibm,power8-i2cm",
+ "ibm,centaur-i2cm"
+};
+
+static void p8_i2c_add_bus_prop(struct p8_i2c_master_port *port)
+{
+ const struct dt_property *c, *p;
+ struct dt_node *np = port->bus.dt_node;
+ char name[32];
+
+ c = dt_find_property(np, "compatible");
+ p = dt_find_property(np, "ibm,port-name");
+
+ if (!c) {
+ if (port->master->type == I2C_POWER8)
+ dt_add_property_strings(np, "compatible",
+ "ibm,power8-i2c-port",
+ "ibm,opal-i2c");
+ else if (port->master->type == I2C_CENTAUR)
+ dt_add_property_strings(np, "compatible",
+ "ibm,centaur-i2c-port",
+ "ibm,opal-i2c");
+ }
+
+ if (!p) {
+ if (port->master->type == I2C_POWER8)
+ snprintf(name, sizeof(name), "p8_%08x_e%dp%d",
+ port->master->chip_id, port->master->engine_id,
+ port->port_num);
+ else if (port->master->type == I2C_CENTAUR)
+ snprintf(name, sizeof(name), "cen_%08x_e%dp%d",
+ port->master->chip_id, port->master->engine_id,
+ port->port_num);
+
+ dt_add_property_string(np, "ibm,port-name", name);
+ }
+}
+
+static struct p8_i2c_master_port *p8_i2c_init_one_port(struct p8_i2c_master *m,
+ struct dt_node *n)
+{
+ struct p8_i2c_master_port *port;
+ uint64_t def_timeout, lb_freq;
+ uint32_t speed, div;
+
+ port = zalloc(sizeof(*port));
+ if (!port)
+ return NULL;
+
+ def_timeout = m->irq_ok ? I2C_TIMEOUT_IRQ_MS : I2C_TIMEOUT_POLL_MS;
+
+ lb_freq = dt_prop_get_u32_def(m->dt_node, "clock-frequency", 150000000);
+ speed = dt_prop_get_u32_def(n, "bus-frequency", 100000);
+ div = p8_i2c_get_bit_rate_divisor(lb_freq, speed);
+
+ /* p8-i2c stuff */
+ port->master = m;
+ port->bit_rate_div = div;
+ port->poll_interval = p8_i2c_get_poll_interval(speed);
+ port->port_num = dt_prop_get_u32(n, "reg");
+ port->byte_timeout = dt_prop_get_u32_def(n, "timeout-ms", def_timeout);
+ list_add_tail(&m->ports, &port->link);
+
+ /* core i2c stuff */
+ port->bus.dt_node = n;
+ port->bus.queue_req = p8_i2c_queue_request;
+ port->bus.run_req = p8_i2c_run_request;
+ i2c_add_bus(&port->bus);
+
+ /* add the bus name and compatible (if needed) */
+ p8_i2c_add_bus_prop(port);
+
+ prlog(PR_INFO, " P%d: <%s> %d kHz\n", port->port_num,
+ (char *) dt_prop_get(n, "ibm,port-name"), speed / 1000);
+
+ return port;
+}
+
+static struct p8_i2c_master *p8_i2c_init_one(struct dt_node *i2cm,
+ enum p8_i2c_master_type type)
+{
+ struct p8_i2c_master *master;
+ struct list_head *chip_list;
+ struct dt_node *i2cm_port;
+ uint64_t ex_stat;
+ uint32_t lb_freq;
+ int64_t rc;
+
+ master = zalloc(sizeof(*master));
+ if (!master) {
+ log_simple_error(&e_info(OPAL_RC_I2C_INIT),
+ "I2C: Failed to allocate master "
+ "structure\n");
+ return NULL;
+ }
+ master->type = type;
+
+ /* Local bus speed in Hz */
+ lb_freq = dt_prop_get_u32(i2cm, "clock-frequency");
+
+ /* Initialise the i2c master structure */
+ master->state = state_idle;
+ master->chip_id = dt_get_chip_id(i2cm);
+ master->engine_id = dt_prop_get_u32(i2cm, "chip-engine#");
+ master->xscom_base = dt_get_address(i2cm, 0, NULL);
+ master->dt_node = i2cm;
+ if (master->type == I2C_CENTAUR) {
+ struct centaur_chip *centaur = get_centaur(master->chip_id);
+ if (centaur == NULL) {
+ log_simple_error(&e_info(OPAL_RC_I2C_INIT),
+ "I2C: Failed to get centaur 0x%x ",
+ master->chip_id);
+ free(master);
+ return NULL;
+ }
+ chip_list = &centaur->i2cms;
+
+ /* Detect bad device-tree from HostBoot giving us bogus
+ * i2c masters
+ */
+ if (master->engine_id > 0) {
+ prlog(PR_ERR, "I2C: Skipping Centaur Master #1\n");
+ free(master);
+ return NULL;
+ }
+ } else {
+ struct proc_chip *chip = get_chip(master->chip_id);
+ assert(chip);
+ chip_list = &chip->i2cms;
+ }
+ init_timer(&master->timeout, p8_i2c_timeout, master);
+ init_timer(&master->poller, p8_i2c_poll, master);
+ init_timer(&master->recovery, p8_i2c_recover, master);
+ init_timer(&master->sensor_cache, p8_i2c_enable_scache, master);
+
+ master->irq_ok = p8_i2c_has_irqs(master);
+
+ prlog(PR_INFO, "I2C: Chip %08x Eng. %d Clock %d Mhz %s\n",
+ master->chip_id, master->engine_id, lb_freq / 1000000,
+ master->irq_ok ? "" : "(no interrupt)");
+
+ /* Disable OCC cache during inits */
+ if (master->type == I2C_CENTAUR) {
+ rc = centaur_disable_sensor_cache(master->chip_id);
+ if (rc < 0) {
+ log_simple_error(&e_info(OPAL_RC_I2C_INIT), "I2C: "
+ "Error %lld disabling sensor cache\n",
+ rc);
+ /* Ignore error and move on ... */
+ } else
+ time_wait(rc);
+ }
+ rc = xscom_read(master->chip_id, master->xscom_base +
+ I2C_EXTD_STAT_REG, &ex_stat);
+ if (rc) {
+ log_simple_error(&e_info(OPAL_RC_I2C_INIT), "I2C: "
+ "Failed to read EXTD_STAT_REG\n");
+ if (master->type == I2C_CENTAUR)
+ centaur_enable_sensor_cache(master->chip_id);
+
+ free(master);
+ return NULL;
+ }
+
+ master->fifo_size = GETFIELD(I2C_EXTD_STAT_FIFO_SIZE, ex_stat);
+ list_head_init(&master->req_list);
+ list_head_init(&master->ports);
+
+ /* Re-enable the sensor cache, we aren't touching HW anymore */
+ if (master->type == I2C_CENTAUR)
+ centaur_enable_sensor_cache(master->chip_id);
+
+ /* Add master to chip's list */
+ list_add_tail(chip_list, &master->link);
+
+ /* initialise ports */
+ dt_for_each_child(i2cm, i2cm_port)
+ p8_i2c_init_one_port(master, i2cm_port);
+
+ return master;
+}
+
+void p8_i2c_init(void)
+{
+ struct dt_node *i2cm;
+ int i;
+
+ /* setup the handshake reg */
+ if (proc_gen <= proc_gen_p9)
+ occflg = 0x6C08A;
+ else if (proc_gen == proc_gen_p10)
+ occflg = 0x6C0AC;
+ else
+ return;
+
+ prlog(PR_INFO, "I2C: OCC flag reg: %x\n", occflg);
+
+ for (i = 0; i < MAX_I2C_TYPE; i++) {
+ dt_for_each_compatible(dt_root, i2cm, compat[i])
+ p8_i2c_init_one(i2cm, i);
+ }
+}
+
+struct i2c_bus *p8_i2c_find_bus_by_port(uint32_t chip_id, int eng, int port_num)
+{
+ struct proc_chip *chip = get_chip(chip_id);
+ struct p8_i2c_master *m, *master = NULL;
+ struct p8_i2c_master_port *port;
+
+ if (!chip)
+ return NULL;
+
+ list_for_each(&chip->i2cms, m, link) {
+ if (m->engine_id == eng) {
+ master = m;
+ break;
+ }
+ }
+
+ if (!master)
+ return NULL;
+
+ list_for_each(&master->ports, port, link)
+ if (port->port_num == port_num)
+ return &port->bus;
+
+ return NULL;
+}
+
+/* Adds a new i2c port to the DT and initialises it */
+struct i2c_bus *p8_i2c_add_bus(uint32_t chip_id, int eng_id, int port_id,
+ uint32_t bus_speed)
+{
+ struct proc_chip *c = get_chip(chip_id);
+ struct p8_i2c_master *m, *master = NULL;
+ struct p8_i2c_master_port *port;
+ struct dt_node *pn;
+
+ if (!c) {
+ prerror("I2C: Unable to add i2c bus: c%de%dp%d: chip doesn't exist\n",
+ chip_id, eng_id, port_id);
+ return NULL;
+ }
+
+ list_for_each(&c->i2cms, m, link) {
+ if (m->engine_id == eng_id) {
+ master = m;
+ break;
+ }
+ }
+
+ if (!master) {
+ struct dt_node *mn;
+
+ mn = p8_i2c_add_master_node(c->devnode, eng_id);
+ if (!mn) {
+ prerror("I2C: Unable to add DT node for I2CM c%xe%d\n",
+ chip_id, eng_id);
+ return NULL;
+ }
+
+ master = p8_i2c_init_one(mn, I2C_POWER8);
+ if (!master) {
+ prerror("I2C: Unable to initialise I2CM c%xe%d\n",
+ chip_id, eng_id);
+ return NULL;
+ }
+ }
+
+ list_for_each(&master->ports, port, link)
+ if (port->port_num == port_id)
+ return &port->bus;
+
+ pn = __p8_i2c_add_port_node(master->dt_node, port_id, bus_speed);
+ if (!pn) {
+ prerror("I2C: Unable to add dt node for bus c%xe%dp%d\n",
+ chip_id, eng_id, port_id);
+ return NULL;
+ }
+
+ port = p8_i2c_init_one_port(master, pn);
+ if (!port) {
+ prerror("I2C: Unable to init bus c%xe%dp%d\n",
+ chip_id, eng_id, port_id);
+ return NULL;
+ }
+
+ return &port->bus;
+}