[PATCH linux v1 8/8] drivers: fsi: i2c: boe engine
eajames at linux.vnet.ibm.com
eajames at linux.vnet.ibm.com
Fri Feb 3 10:26:01 AEDT 2017
From: "Edward A. James" <eajames at us.ibm.com>
Signed-off-by: Edward A. James <eajames at us.ibm.com>
---
drivers/fsi/i2c/Makefile | 2 +-
drivers/fsi/i2c/iic-boe.c | 1597 +++++++++++++++++++++++++++++++++++++++++++++
drivers/fsi/i2c/iic-boe.h | 180 +++++
3 files changed, 1778 insertions(+), 1 deletion(-)
create mode 100644 drivers/fsi/i2c/iic-boe.c
create mode 100644 drivers/fsi/i2c/iic-boe.h
diff --git a/drivers/fsi/i2c/Makefile b/drivers/fsi/i2c/Makefile
index 4d04026..59ecf36 100644
--- a/drivers/fsi/i2c/Makefile
+++ b/drivers/fsi/i2c/Makefile
@@ -1 +1 @@
-obj-$(CONFIG_FSI_I2C) += iic-fsi.o iic-mstr.o iic-lock.o
+obj-$(CONFIG_FSI_I2C) += iic-fsi.o iic-mstr.o iic-lock.o iic-boe.o
diff --git a/drivers/fsi/i2c/iic-boe.c b/drivers/fsi/i2c/iic-boe.c
new file mode 100644
index 0000000..27dc8c1
--- /dev/null
+++ b/drivers/fsi/i2c/iic-boe.c
@@ -0,0 +1,1597 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006, 2010
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <asm/io.h>
+#include <asm/bitops.h>
+#include <linux/mm.h>
+#include <linux/highmem.h>
+#include "iic-int.h"
+#include "iic-boe.h"
+#include <linux/fsi.h>
+#include <asm/unaligned.h>
+
+/* Wrappers around the register access functions allow for access
+ * over different types of busses (i.e. FSI or OPB).
+ */
+#define iic_readb(eng, reg, val, ffdc)\
+ (eng->ra->bus_readb(eng, reg * 4, val, ffdc))
+#define iic_readh(eng, reg, val, ffdc)\
+ (eng->ra->bus_readh(eng, reg * 4, val, ffdc))
+#define iic_readw(eng, reg, val, ffdc)\
+ (eng->ra->bus_readw(eng, reg * 4, val, ffdc))
+
+#define iic_writeb(eng, reg, val, ffdc)\
+ (eng->ra->bus_writeb(eng, reg * 4, val, ffdc))
+#define iic_writeh(eng, reg, val, ffdc)\
+ (eng->ra->bus_writeh(eng, reg * 4, val, ffdc))
+#define iic_writew(eng, reg, val, ffdc)\
+ (eng->ra->bus_writew(eng, reg * 4, val, ffdc))
+
+/* Actual equation for the clock divider is
+ * (((lb_hz / (iic_hz)) - 1) / 4) - 1), but 1 is added in
+ * order to compensate for truncation errors -- It's better to be a little
+ * slow than to be a little too fast.
+ */
+#define IIC_BOE_HZ2DIV(lb_hz, iic_hz) _clock_divider
+#define IIC_BOE_DIV2HZ(lb_hz, d) \
+ ((lb_hz) / ((4 * ((d) + 1)) + 1))
+
+/* Bus address calc for larger SEEPROM devices
+ */
+#define IIC_BOE_CALC_BUS_ADDR(opts, xfr) \
+ opts->dev_addr + (((opts->offset + xfr->bytes_xfrd) & opts->inc_addr) \
+ >> ((opts->dev_width * 8) - 1));
+
+/* Externalized Master Functions */
+int iic_boe_use_dma(iic_xfr_t* xfr);
+int iic_boe_start(iic_xfr_t* xfr);
+int iic_boe_start_abort(iic_eng_t* eng, iic_ffdc_t**);
+int iic_boe_finish_abort(iic_eng_t* eng, iic_ffdc_t**);
+int iic_boe_rescue_timeout(iic_eng_t* eng, iic_xfr_t* xfr);
+int iic_boe_reset_bus(iic_bus_t* bus, iic_ffdc_t**);
+int iic_boe_reset_eng(iic_eng_t* eng, iic_ffdc_t**);
+int iic_boe_run_bat(iic_eng_t* eng, iic_ffdc_t**);
+int iic_boe_eng_init(iic_eng_t* eng, iic_ffdc_t**);
+int iic_boe_enable_int(iic_eng_t* eng, iic_ffdc_t**);
+int iic_boe_disable_int(iic_eng_t* eng, iic_ffdc_t**);
+int iic_boe_cleanup_eng(iic_eng_t* eng, iic_ffdc_t**);
+int iic_boe_int_handler(int, void*);
+int iic_boe_wait_for_idle(iic_eng_t* eng, int timeout, iic_ffdc_t**);
+void iic_boe_display_regs(iic_eng_t* eng, iic_ffdc_t**);
+int iic_boe_get_bus_state(iic_bus_t* bus, unsigned long* state, iic_ffdc_t**);
+int iic_boe_set_speed(iic_bus_t* bus, int speed);
+int iic_boe_get_speed(iic_bus_t* bus);
+
+#define IIC_BOE_BUS_RESET 1
+#define IIC_BOE_ENG_RESET 0
+int iic_boe_reset(iic_eng_t* eng, int type, iic_ffdc_t** ffdc);
+void iic_boe_dma_callback(int dma_rc, void* ffdc, void* data);
+int iic_boe_check_ddr4_nack(iic_xfr_t *xfr);
+
+static const char iic_boe_version[] = "3.0";
+static unsigned int _clock_divider = 6;
+
+static iic_eng_ops_t eng_ops = {
+ .use_dma = &iic_boe_use_dma,
+ .start = &iic_boe_start,
+ .start_abort = &iic_boe_start_abort,
+ .finish_abort = &iic_boe_finish_abort,
+ .start_rescue_timeout = &iic_boe_rescue_timeout,
+ .finish_rescue_timeout = &iic_boe_rescue_timeout,
+ .reset_bus = &iic_boe_reset_bus,
+ .reset_eng = &iic_boe_reset_eng,
+ .run_bat = &iic_boe_run_bat,
+ .init = &iic_boe_eng_init,
+ .enable_int = &iic_boe_enable_int,
+ .disable_int = &iic_boe_disable_int,
+ .int_handler = &iic_boe_int_handler,
+ .wait_for_idle = &iic_boe_wait_for_idle,
+ .cleanup_eng = 0,
+ .display_regs = iic_boe_display_regs,
+ .slv_on = 0,
+ .slv_off = 0,
+ .slv_recv = 0,
+ .slv_cont = 0,
+ .slv_set_addr = 0,
+ .slv_get_addr = 0,
+ .get_bus_state = iic_boe_get_bus_state,
+ .set_speed = iic_boe_set_speed,
+ .get_speed = iic_boe_get_speed,
+ .send = 0,
+};
+
+#define IIC_BOE_MAX_CLKDIV 0x0FFF
+int iic_boe_set_speed(iic_bus_t* bus, int i2c_hz)
+{
+ _clock_divider = i2c_hz;
+}
+
+int iic_boe_get_speed(iic_bus_t* bus)
+{
+ return _clock_divider;
+}
+
+void iic_boe_display_regs(iic_eng_t* eng, iic_ffdc_t** ffdc)
+{
+ int i;
+ unsigned long reg;
+ IENTER();
+ for(i = 1; i < IIC_BOE_MAX_OFFSET; i++)
+ {
+ if(eng->ra->bus_readw(eng, i * 4, ®, 0))
+ break;
+ printk("%02x: %08lx\n", i, reg);
+ }
+ IEXIT(0);
+}
+
+/* rc > 0 means use dma
+ * rc = 0 means don't use dma
+ * rc < 0 means dma setup failed
+ */
+#define IIC_BOE_MIN_DMA_SIZE 129
+int iic_boe_use_dma(iic_xfr_t* xfr)
+{
+ int rc = 0;
+ IEXIT(rc);
+ return rc;
+}
+
+#define IIC_BOE_SCL_SHIFT 11
+#define IIC_BOE_SDA_SHIFT 9
+/* Returns the logical state of the clock and data lines */
+int iic_boe_get_bus_state(iic_bus_t* bus, unsigned long* state, iic_ffdc_t** ffdc)
+{
+ unsigned long stat = 0;
+ unsigned long mode;
+ int rc = 0;
+
+ IENTER();
+ *state = 0;
+ rc = iic_readw(bus->eng, IIC_BOE_MODE, &mode, ffdc);
+ if(rc)
+ goto exit;
+
+ if(test_bit(IIC_ENG_Z7PLUS, &bus->eng->flags) ||
+ test_bit(IIC_ENG_P8_Z8_CENTAUR, &bus->eng->flags))
+ {
+ IFLDi(0, "iic_boe_get_bus_state: P8/Z7PLUS mode\n");
+ mode = (mode & ~IIC_BOE_Z7_PORT) | IIC_BOE_Z7_MK_PORT(bus->port);
+ }
+ else
+ {
+ IFLDi(0, "iic_boe_get_bus_state: Normal mode\n");
+ mode = (mode & ~IIC_BOE_PORT) | IIC_BOE_MK_PORT(bus->port);
+ }
+ rc = iic_writew(bus->eng, IIC_BOE_MODE, mode, ffdc);
+ if(rc)
+ goto exit;
+ rc = iic_readw(bus->eng, IIC_BOE_STAT, &stat, ffdc);
+ if(rc)
+ goto exit;
+
+ *state = ((stat & IIC_BOE_SCL_IN) >> IIC_BOE_SCL_SHIFT) |
+ ((stat & IIC_BOE_SDA_IN) >> IIC_BOE_SDA_SHIFT);
+ IDBGd(1, "bus state = %08lx\n", *state);
+exit:
+ IEXIT(rc);
+ return rc;
+}
+
+/* Translate BOE status bits into our standardized error codes.
+ */
+int iic_boe_get_error(iic_eng_t* eng, unsigned long stat, iic_ffdc_t** ffdc)
+{
+ int ret;
+ IENTER();
+ /* Use priority scheme in case multiple bits are enabled */
+ if(stat & IIC_BOE_S_BE_ERRS)
+ {
+ ret = -EIO;
+ }
+ else if(stat & IIC_BOE_S_NACK)
+ {
+ unsigned long cmd_len;
+ unsigned long fe_len;
+ /* compare cmd length and front end len to determine
+ * if we were sending the address or data. If the cmd
+ * len is the same as the front end len, then no data
+ * has been transfered and we must have been in the
+ * address phase, otherwise, we're in the data phase.
+ */
+ ret = iic_readw(eng, IIC_BOE_CMD, &cmd_len, ffdc);
+ if(ret)
+ goto exit;
+ ret = iic_readw(eng, IIC_BOE_RESID_LEN, &fe_len, ffdc);
+ if(ret)
+ goto exit;
+ cmd_len &= IIC_BOE_XFR_LEN;
+ fe_len = IIC_BOE_GET_FE(fe_len);
+ if(cmd_len == fe_len)
+ {
+ ret = -ENXIO;
+ }
+ else
+ {
+ if (!iic_boe_check_ddr4_nack(eng->cur_xfr))
+ ret = -ENODATA;
+ }
+ }
+ else if((stat & IIC_BOE_S_LOST_ARB) || (stat & IIC_BOE_PORT_BUSY))
+ {
+ ret = -EALREADY;
+ }
+ else
+ {
+ ret = -EIO;
+ }
+exit:
+ IEXIT(ret);
+ return ret;
+}
+
+int iic_boe_fifo_to_usr(iic_eng_t* eng, iic_xfr_t* xfr, unsigned long bytes)
+{
+ int rc = 0;
+ char* uptr;
+ unsigned long bytes_left;
+ unsigned long end = xfr->bytes_xfrd + bytes;
+ char dummy[4];
+
+ IENTER();
+ while((xfr->bytes_xfrd < end) && !rc)
+ {
+ uptr = &xfr->buf[xfr->bytes_xfrd];
+
+ bytes_left = end - xfr->bytes_xfrd;
+ if (xfr->bytes_xfrd >= xfr->size) {
+ IFLDe(2, "buffer is full, but fifo still has data\n");
+ uptr = dummy;
+ }
+ if(bytes_left >= 4) {
+ rc = iic_readw(eng, IIC_BOE_FIFO, (long *)dummy, &xfr->ffdc);
+ uptr[0] = dummy[3];
+ uptr[1] = dummy[2];
+ uptr[2] = dummy[1];
+ uptr[3] = dummy[0];
+ xfr->bytes_xfrd += 4;
+ } else if(bytes_left >= 2) {
+ iic_readh(eng, IIC_BOE_FIFO, (short *)dummy, &xfr->ffdc);
+ uptr[0] = dummy[1];
+ uptr[1] = dummy[0];
+ xfr->bytes_xfrd += 2;
+ } else {
+ iic_readb(eng, IIC_BOE_FIFO, uptr, &xfr->ffdc);
+ xfr->bytes_xfrd++;
+ }
+ }
+ if (xfr->bytes_xfrd > xfr->size)
+ xfr->bytes_xfrd = xfr->size;
+
+ IEXIT(rc);
+ return rc;
+}
+
+int iic_boe_usr_to_fifo(iic_eng_t* eng, iic_xfr_t* xfr, unsigned long bytes)
+{
+ int rc = 0;
+ u16 half;
+ u32 word;
+ char* uptr;
+ unsigned long bytes_left;
+ unsigned long end = xfr->bytes_xfrd + bytes;
+
+ IENTER();
+ while((xfr->bytes_xfrd < end) && !rc)
+ {
+ uptr = &xfr->buf[xfr->bytes_xfrd];
+
+ bytes_left = end - xfr->bytes_xfrd;
+ if(bytes_left >= 4) {
+ word = cpu_to_be32(get_unaligned((long *)uptr));
+ rc = iic_writew(eng, IIC_BOE_FIFO, word, &xfr->ffdc);
+ xfr->bytes_xfrd += 4;
+ } else if(bytes_left >= 2) {
+ half = cpu_to_be16(get_unaligned((short *)uptr));
+ iic_writeh(eng, IIC_BOE_FIFO, half, &xfr->ffdc);
+ xfr->bytes_xfrd += 2;
+ } else {
+ iic_writeb(eng, IIC_BOE_FIFO, *uptr, &xfr->ffdc);
+ xfr->bytes_xfrd++;
+ }
+ }
+
+ IEXIT(rc);
+ return rc;
+}
+
+/* Interrupt Handler
+ *
+ * Handles the following general cases:
+ *
+ * DATA REQUEST - This services the fifo
+ * CMD COMPLETE - Determines the next command to run for a transfer
+ * or notifies base code that transfer completed
+ * successfully.
+ * ERRORS - Gathers FFDC, cleans up bus, and notifies base code that
+ * transfer completed with errors.
+ */
+int iic_boe_int_handler(int irq,
+ void* device_data)
+{
+ int rc;
+ iic_ffdc_t* ffdc = 0;
+ unsigned long stat;
+ unsigned long cur_dev_addr;
+ struct device *dev = device_data;
+ iic_eng_t* eng = dev_get_drvdata(dev);
+ iic_xfr_t* xfr = eng->cur_xfr;
+ iic_xfr_opts_t* opts;
+ unsigned long cmd_left = 0;
+ unsigned long fifo_left = 0;
+
+ IENTER();
+
+ /* Mask all IIC interrupts for this engine */
+ rc = iic_writew(eng, IIC_BOE_INT_MASK, 0, &ffdc);
+ if(rc)
+ {
+ if(xfr)
+ {
+ goto xfr_err;
+ }
+ goto exit;
+ }
+
+ /* If the engine is locked and we get an interrupt,
+ * then we are in the process of aborting the transfer.
+ * Access of the xfr from the interrupt handler is forbidden
+ * as long as this flag is set.
+ * Wake up blocked threads waiting for the abort to complete
+ */
+ if(test_bit(IIC_ENG_ABORT, &eng->flags))
+ {
+ IDBGd(1, "xfr[%p] abort!\n", eng->cur_xfr);
+ wake_up_interruptible(&eng->waitq);
+ goto exit;
+ }
+
+ if(!xfr)
+ {
+ goto exit;
+ }
+
+ rc = iic_readw(eng, IIC_BOE_STAT, &stat, &ffdc);
+ if(rc)
+ goto xfr_err;
+
+ IDBGl(2, "eng[%08x]: status[%08lx]\n", eng->id, stat);
+
+ opts = &xfr->opts.xfr_opts;
+ cur_dev_addr = opts->dev_addr;
+
+ /* Check for errors
+ */
+ if(stat & IIC_BOE_S_ANY_ERR)
+ {
+ IDBGd(0, "error\n");
+
+ /* If status hasn't been set yet, Gather FFDC, & set status */
+ if(!xfr->status)
+ {
+ xfr->status = iic_boe_get_error(eng, stat, &xfr->ffdc);
+// iic_fill_xfr_ffdc(xfr, &xfr->ffdc);
+// iic_ffdc_loc(&xfr->ffdc);
+ }
+
+ /* clears error conditions and causes a stop
+ * command to be issued, which causes
+ * an interrupt when it completes.
+ * Also initiates cleanup of DMA transfer if
+ * one is submitted.
+ */
+ iic_abort_xfr(xfr);
+
+ set_bit(IIC_XFR_ENG_COMPLETED, &xfr->flags);
+ /* call xfr complete immediately if non-dma or completed
+ * dma xfr
+ */
+ iic_xfr_complete(xfr); // q ffdc
+ goto exit;
+ }
+
+ /* Check for cmd complete */
+ if(stat & IIC_BOE_S_CMD_COMP)
+ {
+ unsigned long cmd = 0;
+ unsigned long next_xfr;
+ unsigned long enable_ints = 0;
+
+ IDBGd(0, "cmd complete\n");
+
+ /* Special read handling in the offset phase for PLL/CRC chips*/
+ if(test_bit(IIC_XFR_SPECIAL_PHASE, &xfr->flags))
+ {
+ /* exit the special phase and enter the offset phase */
+ clear_bit(IIC_XFR_SPECIAL_PHASE, &xfr->flags);
+
+ /* Start a "write with no stop" command which will
+ * result in a data request interrupt.
+ */
+ cmd = opts->dev_width;
+ IFLDd(1, "cmd[%08lx]\n", cmd);
+ rc = iic_writew(eng, IIC_BOE_CMD, cmd, &xfr->ffdc);
+ if(rc)
+ goto xfr_err;
+
+ /* unmask interrupts and exit handler*/
+ rc = iic_writew(eng, IIC_BOE_INT_MASK,
+ IIC_BOE_ANY_ERR | IIC_BOE_CMD_COMP |
+ IIC_BOE_DAT_REQ, &xfr->ffdc);
+ if(rc)
+ goto xfr_err;
+ goto exit;
+ }
+
+ /* Check for xfr completion */
+ if((xfr->bytes_xfrd >= xfr->size) || xfr->status)
+ {
+ /* If this was a retry, the retry completed.
+ * clear flag so that
+ * iic_xfr_complete can do its work.
+ */
+ clear_bit(IIC_XFR_RETRY_IN_PROGRESS, &xfr->flags);
+
+ set_bit(IIC_XFR_ENG_COMPLETED, &xfr->flags);
+ iic_xfr_complete(xfr); // q ffdc
+ goto exit;
+ }
+
+ /* If there are bytes left to transfer and the last
+ * transfer sucessfully ended (with a stop), then
+ * this must be a split transfer, unless we are still
+ * in offset phase, in which case self_busy is inaccurate
+ */
+ if(!(stat & IIC_BOE_SELF_BUSY) &&
+ !test_bit(IIC_XFR_OFFSET_PHASE, &xfr->flags))
+ {
+ if(opts->rdelay)
+ {
+ iic_delay_xfr(xfr, opts->rdelay);
+ }
+ else
+ {
+ /* bus access errors are only possible
+ * failure
+ */
+ rc = iic_boe_start(xfr);
+ if(rc)
+ goto xfr_err;
+ }
+ goto exit;
+ }
+
+ /* Otherwise, we just need to issue a command to continue
+ * the current read or write transfer.
+ */
+ next_xfr = xfr->size - xfr->bytes_xfrd;
+
+ /* Calculate bytes left in this 'page' if xfr splits are
+ * enabled
+ */
+ if(opts->rsplit)
+ {
+ unsigned long pg_offset = (xfr->bytes_xfrd +
+ opts->offset) & opts->rsplit;
+ unsigned long page_bytes = opts->rsplit + 1;
+ if(pg_offset)
+ {
+ page_bytes -= pg_offset;
+ }
+ if(page_bytes < next_xfr)
+ {
+ next_xfr = page_bytes;
+ }
+ }
+ /* If the transfer is longer than supported by a single
+ * command (64k), set the 'read continue' bit so that
+ * the last byte in the command will get acked. Also,
+ * don't issue a stop.
+ */
+ if(next_xfr & ~IIC_BOE_XFR_LEN)
+ {
+ next_xfr = IIC_BOE_XFR_LEN;
+ cmd |= IIC_BOE_RD_CONT;
+ }
+ /* otherwise, issue a stop after the command completes */
+ else
+ {
+ if (!test_bit(IIC_REPEATED_START, &opts->flags))
+ {
+ cmd |= IIC_BOE_WITH_STOP;
+ }
+ else // Issue NO STOP
+ {
+ cmd |= IIC_BOE_RD_CONT;
+ }
+ }
+ cmd |= next_xfr;
+
+ /* set the read/!write bit */
+ if(test_bit(IIC_XFR_RD, &xfr->flags))
+ {
+ cmd |= IIC_BOE_READ;
+ }
+
+ /* special handling if the previous command was for
+ * setting a device offset pointer
+ */
+ if(test_bit(IIC_XFR_OFFSET_PHASE, &xfr->flags))
+ {
+ if(opts->dev_width && opts->inc_addr)
+ {
+ cur_dev_addr = IIC_BOE_CALC_BUS_ADDR(opts, xfr);
+ }
+
+ clear_bit(IIC_XFR_OFFSET_PHASE, &xfr->flags);
+ if(test_bit(IIC_XFR_RD, &xfr->flags) &&
+ !(opts->flags & IIC_SPECIAL_RD))
+ {
+ cmd |= IIC_BOE_WITH_START;
+ cmd |= IIC_BOE_WITH_ADDR;
+ cmd |= IIC_BOE_MK_ADDR(cur_dev_addr);
+ }
+ }
+
+ /* Issue cmd to iic engine prior to submitting dma */
+ IFLDd(1, "cmd[%08lx]\n", cmd);
+ rc = iic_writew(eng, IIC_BOE_CMD, cmd, &xfr->ffdc);
+ if(rc)
+ goto xfr_err;
+
+ enable_ints = IIC_BOE_DAT_REQ;
+ enable_ints |= IIC_BOE_CMD_COMP | IIC_BOE_ANY_ERR;
+
+
+ /* unmask interrupts */
+ IDBGd(1, "unmask ints[%08lx]\n", enable_ints);
+ rc = iic_writew(eng, IIC_BOE_INT_MASK, enable_ints, &xfr->ffdc);
+ if(rc)
+ goto xfr_err;
+
+ goto exit;
+ }
+
+ /* Check for data request, (this bit is masked for DMA transfers) */
+ if(stat & IIC_BOE_S_DAT_REQ)
+ {
+ unsigned long fifo_cnt = stat & IIC_BOE_FIFO_COUNT;
+
+ IDBGl(0, "data request\n");
+
+ /* Data request during the offset phase means we need to
+ * write the fifo with the offset address.
+ */
+ if(test_bit(IIC_XFR_OFFSET_PHASE, &xfr->flags))
+ {
+ unsigned long cur_offset = opts->offset +
+ xfr->bytes_xfrd;
+ IDBGl(1, "offset: %d\n", cur_offset);
+ fifo_cnt += opts->dev_width;
+
+ if(opts->dev_width == sizeof(long))
+ {
+ rc = iic_writew(eng, IIC_BOE_FIFO,
+ cur_offset, &xfr->ffdc);
+ }
+ else if(opts->dev_width == sizeof(short))
+ {
+ rc = iic_writeh(eng, IIC_BOE_FIFO,
+ cur_offset, &xfr->ffdc);
+ }
+ else
+ {
+ int i;
+ for(i = 0; !rc && (i < opts->dev_width); i++)
+ {
+ rc = iic_writeb(eng, IIC_BOE_FIFO,
+ cur_offset, &xfr->ffdc);
+ }
+ }
+ if(rc)
+ goto xfr_err;
+
+ /* also write data to fifo if this is a write */
+ if (test_bit(IIC_XFR_RD, &xfr->flags))
+ goto enable_interrupts;
+ else
+ clear_bit(IIC_XFR_OFFSET_PHASE, &xfr->flags);
+ }
+ else if(test_bit(IIC_XFR_RD, &xfr->flags)) /*read from fifo */
+ {
+ rc = iic_boe_fifo_to_usr(eng, xfr, fifo_cnt);
+ if(rc)
+ goto xfr_err;
+ goto enable_interrupts;
+ }
+
+ /* write to fifo */
+ fifo_left = eng->fifo_size - fifo_cnt;
+
+ /* 1st, determine how many bytes to write to the fifo,
+ * which is a minimum of the space left in the fifo
+ * and a maximum of the bytes left in the command.
+ */
+ rc = iic_readw(eng, IIC_BOE_RESID_LEN, &cmd_left, &xfr->ffdc);
+ if(rc)
+ goto xfr_err;
+ cmd_left = IIC_BOE_GET_BE(cmd_left);
+
+ if(cmd_left < fifo_left)
+ fifo_left = cmd_left;
+ rc = iic_boe_usr_to_fifo(eng, xfr, fifo_left);
+ if(rc)
+ goto xfr_err;
+
+enable_interrupts:
+ /* enable interrupts */
+ rc = iic_writew(eng, IIC_BOE_INT_MASK, IIC_BOE_ANY_ERR |
+ IIC_BOE_CMD_COMP |
+ IIC_BOE_DAT_REQ, &xfr->ffdc);
+ if(rc)
+ goto xfr_err;
+ }
+ goto exit;
+xfr_err:
+ IFLDe(2, "xfr[%p] rc=%d\n", xfr, rc);
+ xfr->status = rc;
+ set_bit(IIC_XFR_ENG_COMPLETED, &xfr->flags);
+
+ iic_xfr_complete(xfr);
+
+exit:
+ IEXIT(IRQ_HANDLED);
+ return IRQ_HANDLED;
+}
+
+/* Initialize the engine. This should only be called after the engine has
+ * been reset */
+#define IIC_BOE_LO_LVL 4
+#define IIC_BOE_HI_LVL 4
+#define IIC_BOE_DFLT_SPEED 100000
+#define IIC_BOE_CFAM_FIFO_SZ 8
+int iic_boe_eng_init(iic_eng_t* eng, iic_ffdc_t** ffdc)
+{
+ int rc = 0;
+ unsigned long clk_div = 0;
+ unsigned long water_mark = 0;
+ unsigned long mode;
+
+ IENTER();
+ /* Set up the clock divider, set port to 0, disable enhanced,
+ * diagnostic, pacing allow, and wrap modes
+ * Set to default speed of 400khz.
+ */
+ clk_div = IIC_BOE_HZ2DIV(eng->bus_speed, IIC_BOE_DFLT_SPEED);
+ if(!clk_div)
+ {
+ IFLDi(2, "eng[%08x], max speed = %ld\n",
+ eng->id, eng->bus_speed / 5);
+ }
+ if(clk_div & ~IIC_BOE_MAX_CLKDIV)
+ clk_div = IIC_BOE_MAX_CLKDIV;
+ IFLDd(3, "eng[%08x] speed[%ld] divisor[%ld]\n", eng->id,
+ IIC_BOE_DIV2HZ(eng->bus_speed, clk_div), clk_div);
+
+ if(test_bit(IIC_ENG_Z7PLUS, &eng->flags) ||
+ test_bit(IIC_ENG_P8_Z8_CENTAUR, &eng->flags))
+ {
+ IFLDi(0, "iic_boe_eng_init: P8/Z7PLUS mode\n");
+ mode = IIC_BOE_Z7_MK_CLKDIV(clk_div);
+ }
+ else
+ {
+ IFLDi(0, "iic_boe_eng_init: Normal mode\n");
+ mode = IIC_BOE_MK_CLKDIV(clk_div);
+ }
+
+ /* Set up the Water Mark register according to the fifo size */
+ rc = iic_readw(eng, IIC_BOE_ESTAT, &eng->fifo_size, ffdc);
+ if(rc)
+ goto exit;
+ eng->fifo_size = eng->fifo_size >> IIC_BOE_FIFO_SZ_SHIFT;
+
+ /* workaround for bad fifo size reported on CFAM dd1.0 */
+ if(eng->fifo_size == 16)
+ eng->fifo_size = IIC_BOE_CFAM_FIFO_SZ;
+
+ water_mark = IIC_BOE_MK_WATER_MRK(eng->fifo_size - IIC_BOE_HI_LVL,
+ IIC_BOE_LO_LVL);
+
+ /* handle differences between IOU and CFAM engines */
+ if(eng->fifo_size == IIC_BOE_CFAM_FIFO_SZ)
+ {
+ /* workaround for watermark mismatch between IOU and CFAM
+ * register layout
+ */
+ water_mark = water_mark << 4;
+
+ /* workaround for enhanced mode bit having opposite
+ * meaning in CFAM engines. (bug 37245)
+ */
+ mode |= IIC_BOE_ENHANCED;
+ }
+
+ rc = iic_writew(eng, IIC_BOE_MODE, mode, ffdc);
+ if(rc)
+ goto exit;
+
+ rc = iic_writew(eng, IIC_BOE_WATER_MARK, water_mark, ffdc);
+
+ /* all interrupts are masked coming out of a reset (which is what
+ * we want)
+ */
+exit:
+ IEXIT(rc);
+ return rc;
+}
+
+
+/* if dma_rc is -ECONNRESET then we notified DMA of an error.
+ * if dma_rc is -EIO then DMA discovered an error.
+ *
+ * It's assumed that this function will ALWAYS get called before
+ * the IIC_BOE_CMD_COMP interrupt. If an IIC error occurs in the
+ * middle of a transfer, the IIC interrupt handler will disable interrupts,
+ * collect ffdc, set xfr status, and then call dma_notify. Error recovery
+ * is started from here.
+ *
+ * In the good path case, this function is called prior to the
+ * IIC_BOE_CMD_COMP bit getting set. In this case this function
+ * does nothing but set the IIC_XFR_DMA_COMPLETED bit. iic_xfr_complete
+ * is still called from the IIC interrupt handler when the IIC_BOE_CMD_COMP
+ * bit gets set.
+ *
+ * Handles the following FFDC cases:
+ *
+ * 1) DMA ffdc, no IIC ffdc
+ * Create IIC ffdc element, add to existing ffdc chain
+ * 2) IIC ffdc, no DMA ffdc
+ * do nothing
+ * 3) both DMA and IIC ffdc
+ * combine both chains
+ */
+void iic_boe_dma_callback(int dma_rc, void* ffdc, void* data)
+{
+ iic_xfr_t* xfr = (iic_xfr_t*)data;
+
+ IENTER();
+ set_bit(IIC_XFR_DMA_COMPLETED, &xfr->flags);
+
+ /* Check for DMA/IIC failure */
+ if(dma_rc < 0 || dma_rc != xfr->size)
+ {
+ IFLDe(2, "dma_callback xfr[%p] dma_rc = %d\n", xfr, dma_rc);
+ if(!xfr->status)
+ {
+ xfr->status = -EIO;
+ }
+
+ iic_abort_xfr(xfr);
+ }
+ /* don't complete the xfr until both DMA and IIC operations have
+ * stopped, otherwise, xfr structure could still be in use.
+ */
+ if(test_bit(IIC_XFR_ENG_COMPLETED, &xfr->flags))
+ {
+ /* If this was a retry, the retry completed.
+ * clear flag so that
+ * iic_xfr_complete can do its work.
+ */
+ clear_bit(IIC_XFR_RETRY_IN_PROGRESS, &xfr->flags);
+
+ iic_xfr_complete(xfr);
+ }
+
+ IEXIT(0);
+ return;
+}
+
+/* Start a transfer from the beginning or from where it left off.
+ *
+ * xfr->bytes_xfrd is updated with each interrupt.
+ *
+ */
+int iic_boe_start(iic_xfr_t* xfr)
+{
+ int rc = -ENODEV;
+ iic_eng_t* eng = 0;
+ iic_xfr_opts_t* opts;
+ unsigned long cur_dev_addr;
+ unsigned long cmd = 0;
+ unsigned long enable_ints = 0;
+ unsigned long next_xfr = 0;
+ unsigned long mode;
+ unsigned long new_port = xfr->client->bus->port;
+ unsigned long new_clkdiv = 0;
+ unsigned long port = 0;
+ unsigned long clkdiv_hi = 0;
+ unsigned long clkdiv_lo = 0;
+ unsigned long port_mask = 0;
+ unsigned long port_encode = 0;
+ unsigned long clkdiv_encode = 0;
+ unsigned long clkdiv = 0;
+
+ IENTER();
+ opts = &xfr->opts.xfr_opts;
+ eng = xfr->client->bus->eng;
+ cur_dev_addr = opts->dev_addr;
+ new_clkdiv = IIC_BOE_HZ2DIV(eng->bus_speed, xfr->client->bus->i2c_hz);
+ if(new_clkdiv & ~IIC_BOE_MAX_CLKDIV)
+ new_clkdiv = IIC_BOE_MAX_CLKDIV;
+
+ /* update the target address if requested by user */
+ if(opts->dev_width && opts->inc_addr)
+ {
+ cur_dev_addr = IIC_BOE_CALC_BUS_ADDR(opts, xfr);
+ }
+
+ /* adjust the clock divider and set the port if necessary */
+ rc = iic_readw(eng, IIC_BOE_MODE, &mode, &xfr->ffdc);
+ if(rc)
+ goto error1;
+
+ if(test_bit(IIC_ENG_Z7PLUS, &eng->flags) ||
+ test_bit(IIC_ENG_P8_Z8_CENTAUR, &eng->flags))
+ {
+ IFLDi(0, "iic_boe_start: P8/Z7PLUS mode\n");
+ port = IIC_BOE_Z7_GET_PORT(mode);
+ clkdiv = IIC_BOE_Z7_GET_CLKDIV(mode);
+ clkdiv_hi = IIC_BOE_Z7_CLKDIV;
+ clkdiv_lo = 0;
+ port_mask = IIC_BOE_Z7_PORT;
+ port_encode = IIC_BOE_Z7_MK_PORT(new_port);
+ clkdiv_encode = IIC_BOE_Z7_MK_CLKDIV(new_clkdiv);
+ }
+ else
+ {
+ IFLDi(0, "iic_boe_start: Normal mode\n");
+ port = IIC_BOE_GET_PORT(mode);
+ clkdiv = IIC_BOE_GET_CLKDIV(mode);
+ clkdiv_hi = IIC_BOE_CLKDIV_HI;
+ clkdiv_lo = IIC_BOE_CLKDIV_LO;
+ port_mask = IIC_BOE_PORT;
+ port_encode = IIC_BOE_MK_PORT(new_port);
+ clkdiv_encode = IIC_BOE_MK_CLKDIV(new_clkdiv);
+ }
+
+ if((port != new_port) || (clkdiv != new_clkdiv))
+ {
+ /* clear out old clkdiv and port values */
+ mode &= ~(port_mask | clkdiv_hi | clkdiv_lo);
+
+ IDBGd(2, "new_port = %08lx, new_clkdiv = %08lx\n", new_port,
+ new_clkdiv);
+ /* set new values */
+ mode |= port_encode | clkdiv_encode;
+ rc = iic_writew(eng, IIC_BOE_MODE, mode, &xfr->ffdc);
+ if(rc)
+ goto error1;
+
+ /* reset the engine whenever the port is changed */
+ rc = iic_writew(eng, IIC_BOE_RESET_ERR, 0, &xfr->ffdc);
+ if(rc)
+ goto error1;
+ }
+
+ /* regardless of read or write, if offset is required, always
+ * start the xfr with a write of 1-4 bytes. Read transfers require
+ * a repeated start.
+ */
+ if(opts->dev_width)
+ {
+ /* This tells the interrupt handler that the last
+ * command was for setting the device offset
+ * (don't count the bytes as part of the actual
+ * transfer and reads require special handling)
+ */
+ IDBGd(0, "offset phase\n");
+ set_bit(IIC_XFR_OFFSET_PHASE, &xfr->flags);
+
+ /* writes must combine the dev_width xfr with real xfr */
+ if (test_bit(IIC_XFR_RD, &xfr->flags)) {
+ /* do a write command with no stop */
+ cmd |= IIC_BOE_WITH_START | IIC_BOE_WITH_ADDR |
+ IIC_BOE_MK_ADDR(cur_dev_addr) |
+ opts->dev_width;
+ if(test_bit(IIC_REPEATED_START, &opts->flags))
+ {
+ // Repeated start sent by the ioctl
+ cmd |= IIC_BOE_RD_CONT;
+ }
+
+ /* PLL/CRC chip workaround */
+ if (opts->flags & IIC_SPECIAL_RD)
+ {
+ set_bit(IIC_XFR_SPECIAL_PHASE, &xfr->flags);
+ cmd |= IIC_BOE_READ;
+ cmd &= ~IIC_BOE_XFR_LEN;
+ }
+
+ IFLDd(1, "cmd[%08lx]\n", cmd);
+ rc = iic_writew(eng, IIC_BOE_CMD, cmd, &xfr->ffdc);
+ if(rc)
+ goto error1;
+
+ /* Enable interrupts */
+ enable_ints |= IIC_BOE_CMD_COMP | IIC_BOE_ANY_ERR |
+ IIC_BOE_DAT_REQ;
+ IDBGd(1, "enable ints[%08lx]\n", enable_ints);
+ rc = iic_writew(eng, IIC_BOE_INT_MASK, enable_ints,
+ &xfr->ffdc);
+ if(rc)
+ goto error1;
+
+ goto exit;
+ }
+
+ }
+
+ /* otherwise, do a normal transfer */
+ cmd |= IIC_BOE_WITH_START | IIC_BOE_WITH_ADDR |
+ IIC_BOE_MK_ADDR(cur_dev_addr);
+ if(test_bit(IIC_XFR_RD, &xfr->flags))
+ {
+ cmd |= IIC_BOE_READ;
+ }
+ if(test_bit(IIC_REPEATED_START, &opts->flags))
+ {
+ // Repeated start sent by the ioctl
+ cmd |= IIC_BOE_RD_CONT;
+ }
+ next_xfr = xfr->size - xfr->bytes_xfrd;
+
+ /* Calculate bytes left in this 'page' if xfr splits are
+ * enabled
+ */
+ if(opts->rsplit)
+ {
+ unsigned long pg_offset = (xfr->bytes_xfrd +
+ opts->offset) & opts->rsplit;
+ unsigned long page_bytes = opts->rsplit + 1;
+ if(pg_offset)
+ {
+ page_bytes -= pg_offset;
+ }
+ if(page_bytes < next_xfr)
+ {
+ next_xfr = page_bytes;
+ }
+ }
+
+ next_xfr += opts->dev_width;
+
+ if(next_xfr & ~IIC_BOE_XFR_LEN)
+ {
+ next_xfr = IIC_BOE_XFR_LEN;
+ cmd |= IIC_BOE_RD_CONT;
+ }
+ else
+ {
+ if (!test_bit(IIC_REPEATED_START, &opts->flags))
+ {
+ cmd |= IIC_BOE_WITH_STOP;
+ }
+ }
+ cmd |= next_xfr;
+
+ /* Start the command prior to submitting dma */
+ IFLDd(1, "cmd[%08lx]\n", cmd);
+ rc = iic_writew(eng, IIC_BOE_CMD, cmd, &xfr->ffdc);
+ if(rc)
+ goto error1;
+
+ enable_ints = IIC_BOE_DAT_REQ;
+ enable_ints |= IIC_BOE_CMD_COMP | IIC_BOE_ANY_ERR;
+
+ /* unmask interrupts */
+ IDBGd(1, "enable ints[%08lx]\n", enable_ints);
+ rc = iic_writew(eng, IIC_BOE_INT_MASK, enable_ints, &xfr->ffdc);
+ if(rc)
+ goto error1;
+
+ goto exit;
+error1:
+ xfr->status = rc;
+
+exit:
+ IEXIT(rc);
+ return rc;
+}
+
+/* This function starts the process of aborting a xfr. It must be
+ * interrupt safe in case it gets called from a timer interrupt for a
+ * timeout.
+ */
+int iic_boe_start_abort(iic_eng_t* eng, iic_ffdc_t** ffdc)
+{
+ int rc;
+ unsigned long stat;
+ unsigned long cmd = IIC_BOE_WITH_STOP;
+ unsigned long additional_check = 0;
+
+ IENTER();
+
+ /* mask interrupts so that we don't get spurious interrupts.
+ * If we don't mask interrupts, the following 'reset errors'
+ * command can cause a 'CMD_COMPLETE' interrupt to be raised
+ * but then we issue the abort command which clears the
+ * CMD_COMPLETE interrupt bit before it can be
+ * handled.*/
+ rc = iic_writew(eng, IIC_BOE_INT_MASK, 0, ffdc);
+ if(rc)
+ goto exit;
+
+ rc = iic_readw(eng, IIC_BOE_STAT, &stat, ffdc);
+ if(rc)
+ goto exit;
+
+ IFLDi(1, "pre-abort status[%08lx]\n", stat);
+
+ if(test_bit(IIC_ENG_P8_Z8_CENTAUR, &eng->flags)) {
+ additional_check = IIC_BOE_S_LOST_ARB;
+
+ if (eng->cur_xfr && eng->cur_xfr->client && eng->cur_xfr->client->bus)
+ rc = iic_boe_reset_bus(eng->cur_xfr->client->bus, ffdc);
+ else
+ rc = iic_boe_reset_eng(eng, ffdc);
+
+ if(rc)
+ goto exit;
+
+ /* In order to find the potential defect in new IIC engiene,
+ * track stat for sure. */
+ {
+ unsigned long stat_tmp;
+ rc = iic_readw(eng, IIC_BOE_STAT, &stat_tmp, ffdc);
+ if(rc)
+ goto exit;
+ IFLDi(1, "after reset (P8,Z8,Centaur) status[%08lx]\n",
+ stat_tmp);
+ }
+ } else {
+ if (eng->cur_xfr && eng->cur_xfr->client && eng->cur_xfr->client->bus &&
+ ((eng->cur_xfr->retry_count % 4) == 0) &&
+ ((eng->cur_xfr->status != -ENODATA) ||
+ (eng->cur_xfr->retry_count > 0))) {
+ rc = iic_boe_reset_bus(eng->cur_xfr->client->bus, ffdc);
+ } else {
+ cmd |= IIC_BOE_FORCE_LAUNCH;
+
+ /* reset the engine (forces CMD_COMPLETE condition) */
+ rc = iic_boe_reset_eng(eng, ffdc);
+ }
+ if(rc)
+ goto exit;
+ }
+
+ /* don't bother issuing a stop command if we're recovering from
+ * a stop error, a parity error or arbitration lost. Otherwise,
+ * we can end up in a loop.
+ */
+ if(stat & (IIC_BOE_S_STOP_ERR | IIC_BOE_S_PARITY | additional_check))
+ {
+ IFLDi(1, "don't bother issuing a stop\n");
+ rc = -EIO;
+ goto exit;
+ }
+
+ /* issue a stop only command (clears CMD_COMPLETE condtion)*/
+ IFLDd(1, "cmd[%08x]\n", cmd);
+ rc = iic_writew(eng, IIC_BOE_CMD, cmd, ffdc);
+ if(rc)
+ goto exit;
+
+ /* unmask interrupts while stop command runs */
+ IDBGd(1, "enable ints[%08x]\n", IIC_BOE_CMD_COMP | IIC_BOE_ANY_ERR);
+ rc = iic_writew(eng, IIC_BOE_INT_MASK, IIC_BOE_CMD_COMP |
+ IIC_BOE_ANY_ERR, ffdc);
+exit:
+ IEXIT(rc);
+ return rc;
+}
+
+/* returns true if no transfer is pending on the specified engine.
+ */
+int iic_boe_xfr_not_pending(iic_eng_t* eng, iic_ffdc_t** ffdc)
+{
+ int rc;
+ unsigned long stat;
+ IENTER();
+ /* Note: if engine access fails, this function will indicate
+ * that no transfer is pending and wake up the waiter.
+ */
+ rc = iic_readw(eng, IIC_BOE_STAT, &stat, ffdc);
+ if(!rc)
+ rc = stat & IIC_BOE_S_CMD_COMP;
+ IDBGd(1, "status[%08lx]\n", stat);
+ IEXIT(rc);
+ return rc;
+}
+
+/* This function is not interrupt safe, it may require long delays!
+ *
+ * When we enter this function the engine is in a locked state and xfr_complete
+ * has already been called on the current xfr object. Any state information
+ * we need must be obtained from the engine object or the engine registers.
+ * If the transfer was a write operation, the halt command has been issued.
+ * Nothing has been done yet for read operations. Any master
+ * interrupts that occur after xfr_complete was called will not get handled
+ * except to clear the interrupt bit (i.e., status bits will not get cleared).
+ */
+#define IIC_BOE_ABORT_TIMEOUT ( msecs_to_jiffies(200) ) /* 200ms */
+int iic_boe_finish_abort(iic_eng_t* eng, iic_ffdc_t** ffdc)
+{
+ int rc = 0;
+ int rc2 = 0;
+
+ IENTER();
+ rc = wait_event_interruptible_timeout(eng->waitq,
+ (rc2 = iic_boe_xfr_not_pending(eng, ffdc)),
+ IIC_BOE_ABORT_TIMEOUT);
+ if(rc2 < 0)
+ {
+ rc = rc2;
+ goto exit;
+ }
+
+ if(rc <= 0)
+ {
+ if(!rc)
+ {
+ IFLDi(0, "abort timed out\n");
+ }
+ else
+ IFLDi(0, "abort interrupted?\n");
+
+ /* We were unable to abort an operation. Try
+ * Resetting the engine as a last attempt to leave
+ * the engine in a good state.
+ */
+ IFLDi(1, "Resetting eng[%08x]\n", eng->id);
+ rc = iic_boe_reset_eng(eng, ffdc);
+ }
+exit:
+ /* wake up threads waiting for engine to be available */
+ wake_up_interruptible(&eng->waitq);
+ IEXIT(rc);
+ return rc;
+
+}
+
+/* This function rescue the timeout event when the xfr is not really timeout
+ * Return "0" when this event is not really timeout event
+ * Return negative value when this event is really timeout event, or it
+ * is DMA transfer.
+ */
+int iic_boe_rescue_timeout(iic_eng_t *eng, iic_xfr_t* xfr)
+{
+ int retval = -1;
+ unsigned long stat;
+ unsigned long fifo_cnt;
+ int rc;
+
+ IENTER();
+
+ rc = iic_readw(eng, IIC_BOE_WATER_MARK, &stat, 0);
+ if(rc)
+ goto exit;
+ IDBGl(2, "eng[%08x]: Water mark status[%08lx]\n", eng->id, stat);
+
+ rc = iic_readw(eng, IIC_BOE_STAT, &stat, 0);
+ if(rc)
+ goto exit;
+ IFLDi(2, "eng[%08x]: status[%08lx]\n", eng->id, stat);
+
+ if(stat & IIC_BOE_S_ANY_ERR) {
+ IFLDd(0, "IIC_BOE_S_ANY_ERR\n");
+ goto exit;
+ }
+
+
+ if((fifo_cnt = stat & IIC_BOE_FIFO_COUNT) > 0) {
+ IFLDi(1, "fifo_cnt[%d]\n", fifo_cnt);
+
+ if(test_bit(IIC_XFR_RD, &xfr->flags))
+ { /* read operation */
+ unsigned long to_user = fifo_cnt;
+ if (xfr->size < to_user)
+ to_user = xfr->size;
+ rc = iic_boe_fifo_to_usr(eng, xfr, to_user);
+ if(rc)
+ goto exit;
+ retval = xfr->bytes_xfrd;
+ } else { /* write operation */
+ retval = xfr->bytes_xfrd;
+ }
+ }
+
+exit:
+ IEXIT(retval);
+ return retval;
+}
+
+/* Wait for pending transfers and abort operations to complete on
+ * the engine.
+ *
+ * NOTE: timeout must be in jiffies
+ */
+int iic_boe_wait_for_idle(iic_eng_t* eng, int timeout, iic_ffdc_t** ffdc)
+{
+ int rc;
+ int rc2 = 0;
+ IENTER();
+ rc = wait_event_interruptible_timeout(eng->waitq,
+ ((rc2 = iic_boe_xfr_not_pending(eng, ffdc)) &&
+ (!test_bit(IIC_ENG_ABORT, &eng->flags) || (rc2 < 0)) &&
+ !eng->cur_xfr),
+ timeout);
+ if(rc2 < 0)
+ {
+ rc = rc2;
+ goto exit;
+ }
+ if(!rc)
+ {
+ rc = -ETIME;
+ }
+ else if(rc < 0)
+ {
+ rc = -EINTR;
+ }
+ else if(rc > 0)
+ {
+ rc = 0;
+ }
+exit:
+ IEXIT(rc);
+ return rc;
+}
+
+/* This function is only called when SCL is still alive.
+ * it is a low level bus reset mechanism */
+int iic_boe_ll_bus_reset(iic_eng_t* eng, iic_ffdc_t** ffdc)
+{
+ int i, rc = 0;
+ iic_bus_t* bus = eng->cur_bus;
+ unsigned long stat = 0, mode = 0;
+
+ if (!bus) {
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ /* Enter diagnostic mode to enable the manual control of SDL & SCL */
+ rc = iic_readw(eng, IIC_BOE_MODE, &mode, ffdc);
+ if(rc)
+ goto exit;
+
+ mode |= IIC_BOE_DIAG;
+ rc = iic_writew(eng, IIC_BOE_MODE, mode, ffdc);
+ if(rc)
+ goto exit;
+ IFLDi(1, "iic_boe_ll_bus_reset: current mode[%08lx]\n", mode);
+
+ /* Send 9 clock cycle */
+ for (i = 0; i < 9; i++) {
+ /* SCL = 0 */
+ rc = iic_writew(eng, IIC_BOE_RESET_SCL, 0, ffdc);
+ if(rc)
+ goto exit;
+ /* SCL = 1 */
+ rc = iic_writew(eng, IIC_BOE_SET_SCL, 0, ffdc);
+ if(rc)
+ goto exit;
+ }
+ /* Send stop */
+ /* SCL = 0 */
+ rc = iic_writew(eng, IIC_BOE_RESET_SCL, 0, ffdc);
+ if(rc)
+ goto exit;
+ /* SDL = 0 */
+ rc = iic_writew(eng, IIC_BOE_RESET_SDA, 0, ffdc);
+ if(rc)
+ goto exit;
+ /* SCL = 1 */
+ rc = iic_writew(eng, IIC_BOE_SET_SCL, 0, ffdc);
+ if(rc)
+ goto exit;
+ /* SDL = 1 */
+ rc = iic_writew(eng, IIC_BOE_SET_SDA, 0, ffdc);
+ if(rc)
+ goto exit;
+
+ /* disable diagnostic mode */
+ mode &= ~IIC_BOE_DIAG;
+ rc = iic_writew(eng, IIC_BOE_MODE, mode, ffdc);
+ if(rc)
+ goto exit;
+
+ if((rc = iic_readw(eng, IIC_BOE_STAT, &stat, ffdc)))
+ goto exit;
+
+ IFLDi(1, "iic_boe_ll_bus_reset: current stat[%08lx]\n", stat);
+exit:
+ return rc;
+}
+
+/* Reset the engine and restore to a known state. Also attempts to free
+ * the bus if requested.
+ */
+#define IIC_BOE_BOTH_HI (IIC_BOE_SCL_IN | IIC_BOE_SDA_IN)
+int iic_boe_reset(iic_eng_t* eng, int type, iic_ffdc_t** ffdc)
+{
+ int rc = 0;
+ int i;
+ unsigned long stat;
+ int sample_count = 10; //0.1ms sample time
+ unsigned long bus_state = 0;
+ unsigned long mode = 0;
+
+ IENTER();
+ /* issue an immediate reset i2c command */
+ IFLDi(0, "iic_boe_reset\n");
+ rc = iic_writew(eng, IIC_BOE_RESET_I2C, 0, ffdc);
+ if(rc)
+ goto exit;
+
+ /* reinit the engine */
+ rc = iic_boe_eng_init(eng, ffdc);
+ if(rc)
+ goto exit;
+
+ /* write the bus port */
+ if (eng->cur_bus) {
+ rc = iic_readw(eng, IIC_BOE_MODE, &mode, ffdc);
+ if(rc)
+ goto exit;
+
+ /* encode the port number */
+ if (test_bit(IIC_ENG_Z7PLUS, &eng->flags) ||
+ test_bit(IIC_ENG_P8_Z8_CENTAUR, &eng->flags)) {
+ mode = (mode & ~IIC_BOE_Z7_PORT) |
+ IIC_BOE_Z7_MK_PORT(eng->cur_bus->port);
+ }
+ else {
+ mode = (mode & ~IIC_BOE_PORT) |
+ IIC_BOE_MK_PORT(eng->cur_bus->port);
+ }
+
+ rc = iic_writew(eng, IIC_BOE_MODE, mode, ffdc);
+ if(rc)
+ goto exit;
+ }
+
+ /* Workaround for HW237041 and SW243504 */
+ if(test_bit(IIC_ENG_P8_Z8_CENTAUR, &eng->flags)) {
+ unsigned long port_busy;
+
+ rc = iic_readw(eng, IIC_BOE_PORTBUSY, &port_busy, ffdc);
+ if(rc)
+ goto exit;
+ /* A start by anyone will assert i2c_busy.
+ * A start by itself will also asseret i2c_self_busy.
+ * A stop will deassert i2c_busy.
+ * A stop by itself will deassert i2c_self_busy.
+ * if self_busy is not 1 and i2c_busy is 1 then we cannot
+ * send STOP also.
+ * that's why we clear the I2C_busy from the PORT_BUSY_REGISTER.
+ * */
+ rc = iic_writew(eng, IIC_BOE_PORTBUSY, IIC_BOE_PORT_BUSY_REST,
+ ffdc);
+ if (rc)
+ goto exit;
+ IFLDi(1, "clean port busy port_busy[%08lx]\n", port_busy);
+ }
+
+ /* Test for stuck lines */
+ for(i = 0; ((i < sample_count) && (bus_state != IIC_BOE_BOTH_HI)); i++)
+ {
+ if((rc = iic_readw(eng, IIC_BOE_STAT, &stat, ffdc)))
+ goto exit;
+ bus_state |= stat & IIC_BOE_BOTH_HI;
+ udelay(100);
+ }
+
+ /* if both lines went high at least once during the sample time
+ * then the bus isn't stuck but we still can do bus reset.
+ * The reason is:
+ * In some case/iic mult-master case, the iic transfer is
+ * interrupted in mid of some operation, it will cause the
+ * iic salve in bad state machine/state. For example, the
+ * iic slave will keep SDA HIGH(wait ACK) or will keep SDA LOW
+ * (stuck BUS). So in order to recover iic slave back to normal,
+ * it is also make sense to send "STOP" on this bus.
+ */
+ if( bus_state != IIC_BOE_BOTH_HI)
+ {
+ IFLDi(1, "bus stuck, state[%08lx]\n", bus_state);
+ }
+
+ /* issue a stop command to reset the bus if requested and the
+ * clock line was sampled to be high.
+ */
+ if(type == IIC_BOE_BUS_RESET)
+ {
+ if(!(bus_state & IIC_BOE_SCL_IN) &&
+ (!eng->cur_xfr || eng->cur_xfr->retry_count))
+ {
+ IFLDi(0, "Clock stuck low. recovery not possible.\n");
+ goto exit;
+ }
+
+ IFLDi(0, "attempting bus reset\n");
+ rc = iic_boe_ll_bus_reset(eng, ffdc);
+ if(rc)
+ goto exit;
+
+ /* reset errors after low level bus reset */
+ rc = iic_writew(eng, IIC_BOE_RESET_ERR, 0, ffdc);
+ if (rc)
+ goto exit;
+
+ /* allow 1ms for the cmd to complete */
+ udelay(1000);
+
+ rc = iic_readw(eng, IIC_BOE_STAT, &stat, ffdc);
+ if(rc)
+ goto exit;
+ if((stat & IIC_BOE_S_CMD_COMP) && (stat & IIC_BOE_BOTH_HI))
+ {
+ IFLDi(0, "Bus Freed!\n");
+ goto exit;
+ }
+
+ /* if the stop didn't happen after 1ms,
+ * reset the engine
+ */
+ IFLDi(0, "Bus still stuck!\n");
+ rc = iic_writew(eng, IIC_BOE_RESET_I2C, 0, ffdc);
+ if(rc)
+ goto exit;
+ udelay(1);
+ rc = iic_boe_eng_init(eng, ffdc);
+ }
+exit:
+ IEXIT(rc);
+ return rc;
+}
+
+int iic_boe_reset_bus(iic_bus_t* bus, iic_ffdc_t** ffdc)
+{
+ int ret;
+ iic_eng_t* eng = bus->eng;
+
+ IENTER();
+ eng->cur_bus = bus;
+ ret = iic_boe_reset(eng, IIC_BOE_BUS_RESET, ffdc);
+ eng->cur_bus = NULL;
+ IEXIT(ret);
+ return ret;
+}
+
+int iic_boe_reset_eng(iic_eng_t* eng, iic_ffdc_t** ffdc)
+{
+ int ret;
+ IENTER();
+ ret = iic_boe_reset(eng, IIC_BOE_ENG_RESET, ffdc);
+ IEXIT(ret);
+ return ret;
+}
+
+/* This should be called when the engine is resumed */
+int iic_boe_run_bat(iic_eng_t* eng, iic_ffdc_t** ffdc)
+{
+ int rc = 0;
+
+ IENTER();
+ /* check DATA REQUEST bit is set properly after water mark
+ * register is set
+ */
+
+ /* check the 'and' and 'or' register functionality against the
+ * 'invalid command' bit.
+ */
+
+ /* force an invalid command and check the invalid command bit */
+
+ /* Verify that the 'reset err' command clears the invalid command
+ * bit.
+ */
+ IEXIT(rc);
+ return rc;
+}
+
+int iic_boe_enable_int(iic_eng_t* eng, iic_ffdc_t** ffdc)
+{
+ int rc = 0;
+ IENTER();
+ /* only enable interrupts when a command is in progress */
+
+ IEXIT(rc);
+ return rc;
+}
+
+int iic_boe_disable_int(iic_eng_t* eng, iic_ffdc_t** ffdc)
+{
+ int rc = 0;
+
+ IENTER();
+ rc = iic_writew(eng, IIC_BOE_INT_MASK, 0, ffdc);
+ IEXIT(rc);
+ return rc;
+}
+
+#define DDR4_NACK_DEV_ADDR 0x6C
+#define DDR4_NACK_REDO_POL 0x0002DD40
+int iic_boe_check_ddr4_nack(iic_xfr_t *xfr) {
+ int rc = 0;
+
+ if (xfr->opts.xfr_opts.dev_addr == DDR4_NACK_DEV_ADDR &&
+ xfr->opts.recovery.redo_pol == DDR4_NACK_REDO_POL &&
+ !test_bit(IIC_XFR_RD, &xfr->flags))
+ rc = 1;
+
+ return rc;
+}
+
+int __init iic_boe_init(void)
+{
+ int rc = 0;
+
+ IENTER();
+ /* Register the boe functions with the base driver */
+ iic_register_eng_ops(&eng_ops, FSI_ENGID_I2C);
+ iic_register_eng_ops(&eng_ops, FSI_ENGID_I2C_BB);
+
+ printk("IIC BOE engine support loaded. ver. %s\n", iic_boe_version);
+ IEXIT(rc);
+ return rc;
+}
+
+void __exit iic_boe_exit(void)
+{
+ IENTER();
+ iic_unregister_eng_ops(FSI_ENGID_I2C);
+ iic_unregister_eng_ops(FSI_ENGID_I2C_BB);
+ printk("IIC BOE engine support unloaded.\n");
+ IEXIT(0);
+}
+
+module_init(iic_boe_init);
+module_exit(iic_boe_exit);
+
+MODULE_AUTHOR("Eddie James <eajames at us.ibm.com>");
+MODULE_DESCRIPTION("FSP BOE IIC Driver");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/fsi/i2c/iic-boe.h b/drivers/fsi/i2c/iic-boe.h
new file mode 100644
index 0000000..81b7783
--- /dev/null
+++ b/drivers/fsi/i2c/iic-boe.h
@@ -0,0 +1,180 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/************************ BOE Engine offsets *********************************/
+#define IIC_BOE_FIFO 0x00
+#define IIC_BOE_CMD 0x01
+#define IIC_BOE_MODE 0x02
+#define IIC_BOE_WATER_MARK 0x03
+#define IIC_BOE_INT_MASK 0x04
+#define IIC_BOE_INT_COND 0x05
+#define IIC_BOE_OR_INT_MASK 0x05
+#define IIC_BOE_INTS 0x06
+#define IIC_BOE_AND_INT_MASK 0x06
+#define IIC_BOE_STAT 0x07
+#define IIC_BOE_RESET_I2C 0x07
+#define IIC_BOE_ESTAT 0x08
+#define IIC_BOE_RESET_ERR 0x08
+#define IIC_BOE_RESID_LEN 0x09
+#define IIC_BOE_SET_SCL 0x09
+#define IIC_BOE_RESERVED 0x0a
+#define IIC_BOE_PORTBUSY 0x0a
+#define IIC_BOE_RESET_SCL 0x0b
+#define IIC_BOE_SET_SDA 0x0c
+#define IIC_BOE_RESET_SDA 0x0d
+#define IIC_BOE_MAX_OFFSET IIC_BOE_RESET_SDA
+
+/************************ BOE Bit Definitions ********************************/
+
+#define IIC_BOE_MAX_BUSES 16
+#define IIC_BOE_MAX_FIFO_SIZE 32
+#define IIC_BOE_DFLT_FIFO_SIZE 8
+
+/* cmd register bits */
+#define IIC_BOE_WITH_START 0x80000000
+#define IIC_BOE_WITH_ADDR 0x40000000
+#define IIC_BOE_RD_CONT 0x20000000
+#define IIC_BOE_WITH_STOP 0x10000000
+#define IIC_BOE_FORCE_LAUNCH 0x08000000
+#define IIC_BOE_ADDR 0x00fe0000
+#define IIC_BOE_ADDR_SHIFT 16
+#define IIC_BOE_READ 0x00010000
+#define IIC_BOE_XFR_LEN 0x0000ffff
+
+#define IIC_BOE_MK_ADDR(addr) ((addr << IIC_BOE_ADDR_SHIFT) & IIC_BOE_ADDR)
+#define IIC_BOE_GET_ADDR(r) ((r & IIC_BOE_ADDR) >> IIC_BOE_ADDR_SHIFT)
+
+/* mode register bits */
+#define IIC_BOE_CLKDIV_LO 0xff000000
+#define IIC_BOE_CLKDIV_LO_SHIFT 24
+#define IIC_BOE_PORT 0x00ff0000
+#define IIC_BOE_PORT_SHIFT 16
+#define IIC_BOE_MODE_RSVD 0x0000ff00
+#define IIC_BOE_CLKDIV_HI 0x000000f0
+#define IIC_BOE_CLKDIV_HI_SHIFT 4
+#define IIC_BOE_ENHANCED 0x00000008
+#define IIC_BOE_DIAG 0x00000004
+#define IIC_BOE_PACE_ALLOW 0x00000002
+#define IIC_BOE_WRAP 0x00000001
+
+#define IIC_BOE_GET_PORT(r) ((r & IIC_BOE_PORT) >> IIC_BOE_PORT_SHIFT)
+#define IIC_BOE_MK_PORT(p) ((p << IIC_BOE_PORT_SHIFT) & IIC_BOE_PORT)
+
+#define IIC_BOE_MK_CLKDIV(val) \
+((val << IIC_BOE_CLKDIV_LO_SHIFT) | \
+((val >> IIC_BOE_CLKDIV_HI_SHIFT) & IIC_BOE_CLKDIV_HI))
+
+#define IIC_BOE_GET_CLKDIV(r) \
+(((r & IIC_BOE_CLKDIV_LO) >> IIC_BOE_CLKDIV_LO_SHIFT) | \
+((r & IIC_BOE_CLKDIV_HI) << IIC_BOE_CLKDIV_HI_SHIFT))
+
+/* Z7 plus mode register */
+#define IIC_BOE_Z7_CLKDIV 0xffff0000
+#define IIC_BOE_Z7_CLKDIV_SHIFT 16
+#define IIC_BOE_Z7_PORT 0x0000fc00
+#define IIC_BOE_Z7_PORT_SHIFT 10
+
+#define IIC_BOE_Z7_GET_PORT(r) ((r & IIC_BOE_Z7_PORT) >> IIC_BOE_Z7_PORT_SHIFT)
+#define IIC_BOE_Z7_MK_PORT(p) ((p << IIC_BOE_Z7_PORT_SHIFT) & IIC_BOE_Z7_PORT)
+
+#define IIC_BOE_Z7_MK_CLKDIV(val) ((val << IIC_BOE_Z7_CLKDIV_SHIFT))
+#define IIC_BOE_Z7_GET_CLKDIV(r) ((r & IIC_BOE_Z7_CLKDIV) >> IIC_BOE_Z7_CLKDIV_SHIFT)
+
+/* water mark register bits */
+#define IIC_BOE_HI_MRK 0x0000ff00
+#define IIC_BOE_HI_MRK_SHIFT 8
+#define IIC_BOE_LO_MRK 0x000000ff
+#define IIC_BOE_LO_MRK_SHIFT 0
+#define IIC_BOE_GET_LO_MRK(e) ((e->regs[IIC_BOE_WATER_MARK] & \
+ IIC_BOE_LO_MRK) >> \
+ IIC_BOE_LO_MRK_SHIFT)
+#define IIC_BOE_MK_WATER_MRK(hi, lo)\
+ ((((hi) << IIC_BOE_HI_MRK_SHIFT) & IIC_BOE_HI_MRK) |\
+ (((lo) << IIC_BOE_LO_MRK_SHIFT) & IIC_BOE_LO_MRK))
+#define IIC_BOE_GET_HI_MRK(e) ((e->regs[IIC_BOE_WATER_MARK] & \
+ IIC_BOE_HI_MRK) >> \
+ IIC_BOE_HI_MRK_SHIFT)
+
+/* interrupt, interrupt mask, interrupt condition bits */
+#define IIC_BOE_INVALID_CMD 0x00008000
+#define IIC_BOE_PARITY 0x00004000
+#define IIC_BOE_BE_OVERRUN 0x00002000
+#define IIC_BOE_BE_ACCESS 0x00001000
+#define IIC_BOE_LOST_ARB 0x00000800
+#define IIC_BOE_NACK 0x00000400
+#define IIC_BOE_DAT_REQ 0x00000200
+#define IIC_BOE_CMD_COMP 0x00000100
+#define IIC_BOE_STOP_ERR 0x00000080
+#define IIC_BOE_BUSY 0x00000040
+#define IIC_BOE_IDLE 0x00000020
+#define IIC_BOE_ALL_INTS 0x0000ffff
+#define IIC_BOE_FE_ERRS (IIC_BOE_LOST_ARB | IIC_BOE_NACK | \
+ IIC_BOE_STOP_ERR)
+#define IIC_BOE_BE_ERRS (IIC_BOE_INVALID_CMD | IIC_BOE_PARITY | \
+ IIC_BOE_BE_OVERRUN | IIC_BOE_BE_ACCESS)
+#define IIC_BOE_ANY_ERR (IIC_BOE_BE_ERRS | IIC_BOE_FE_ERRS)
+
+/* status register bits */
+#define IIC_BOE_S_INVALID_CMD 0x80000000
+#define IIC_BOE_S_PARITY 0x40000000
+#define IIC_BOE_S_BE_OVERRUN 0x20000000
+#define IIC_BOE_S_BE_ACCESS 0x10000000
+#define IIC_BOE_S_LOST_ARB 0x08000000
+#define IIC_BOE_S_NACK 0x04000000
+#define IIC_BOE_S_DAT_REQ 0x02000000
+#define IIC_BOE_S_CMD_COMP 0x01000000
+#define IIC_BOE_S_STOP_ERR 0x00800000
+#define IIC_BOE_STAT_SHIFT 16
+#define IIC_BOE_MAX_PORT 0x000f0000
+#define IIC_BOE_MAX_PORT_SHIFT 16
+#define IIC_BOE_ANY_INT 0x00008000
+#define IIC_BOE_SCL_IN 0x00000800
+#define IIC_BOE_SDA_IN 0x00000400
+#define IIC_BOE_PORT_BUSY 0x00000200
+#define IIC_BOE_SELF_BUSY 0x00000100
+#define IIC_BOE_FIFO_COUNT 0x000000ff
+
+#define IIC_BOE_S_BE_ERRS (IIC_BOE_BE_ERRS << IIC_BOE_STAT_SHIFT)
+#define IIC_BOE_S_FE_ERRS (IIC_BOE_FE_ERRS << IIC_BOE_STAT_SHIFT)
+#define IIC_BOE_S_ANY_ERR (IIC_BOE_ANY_ERR << IIC_BOE_STAT_SHIFT)
+
+/* extended status register bits */
+#define IIC_BOE_FIFO_SZ 0xff000000
+#define IIC_BOE_FIFO_SZ_SHIFT 24
+#define IIC_BOE_SCL_IN_SYN 0x00008000
+#define IIC_BOE_SDA_IN_SYN 0x00004000
+#define IIC_BOE_S_SCL 0x00002000
+#define IIC_BOE_S_SDA 0x00001000
+#define IIC_BOE_M_SCL 0x00000800
+#define IIC_BOE_M_SDA 0x00000400
+#define IIC_BOE_HI_WATER 0x00000200
+#define IIC_BOE_LO_WATER 0x00000100
+#define IIC_BOE_ES_PORT_BUSY 0x00000080
+#define IIC_BOE_ES_SELF_BUSY 0x00000040
+#define IIC_BOE_VERSION 0x0000001f
+
+/* residual length register bits */
+#define IIC_BOE_FE_LEN 0xffff0000
+#define IIC_BOE_FE_LEN_SHIFT 16
+#define IIC_BOE_BE_LEN 0x0000ffff
+#define IIC_BOE_GET_BE(r) (r & IIC_BOE_BE_LEN)
+#define IIC_BOE_GET_FE(r) (r >> IIC_BOE_FE_LEN_SHIFT)
+
+/* Murano/Centaur port busy register */
+#define IIC_BOE_PORT_BUSY_REST 0x80000000
+
--
1.8.3.1
More information about the openbmc
mailing list