From: eajames@linux.vnet.ibm.com
To: openbmc@lists.ozlabs.org
Cc: joel@jms.id.au, alistair@popple.id.au, benh@kernel.crashing.org,
"Edward A. James" <eajames@us.ibm.com>
Subject: [PATCH linux v1 8/8] drivers: fsi: i2c: boe engine
Date: Thu, 2 Feb 2017 17:26:01 -0600 [thread overview]
Message-ID: <1486077964-11346-6-git-send-email-eajames@linux.vnet.ibm.com> (raw)
In-Reply-To: <1486077964-11346-1-git-send-email-eajames@linux.vnet.ibm.com>
From: "Edward A. James" <eajames@us.ibm.com>
Signed-off-by: Edward A. James <eajames@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@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
next prev parent reply other threads:[~2017-02-02 23:26 UTC|newest]
Thread overview: 16+ messages / expand[flat|nested] mbox.gz Atom feed top
2017-02-02 23:25 [PATCH linux v1 0/8] drivers: fsi: interrupt polling, i2c client eajames
2017-02-02 23:25 ` [PATCH linux v1 4/8] drivers: fsi: Add i2c client driver eajames
2017-02-03 0:56 ` Alistair Popple
2017-02-03 0:58 ` Joel Stanley
2017-02-02 23:25 ` [PATCH linux v1 5/8] drivers: fsi: i2c: Add engine access wrappers eajames
2017-02-02 23:25 ` [PATCH linux v1 6/8] drivers: fsi: i2c: probe fsi device for i2c client eajames
2017-02-02 23:26 ` [PATCH linux v1 7/8] drivers: fsi: i2c: add driver file operations and bus locking eajames
2017-02-02 23:26 ` eajames [this message]
2017-02-02 23:26 ` [PATCH v2 1/3] drivers/fsi: Add slave interrupt polling eajames
2017-02-03 1:11 ` Alistair Popple
2017-02-03 16:18 ` eajames
2017-02-03 20:33 ` Benjamin Herrenschmidt
2017-02-02 23:26 ` [PATCH v2 2/3] drivers/fsi: Add Client IRQ Enable / Disable eajames
2017-02-02 23:26 ` [PATCH v2 3/3] drivers/fsi: Add sysfs file to adjust i-poll period eajames
2017-02-03 0:55 ` [PATCH linux v1 0/8] drivers: fsi: interrupt polling, i2c client Joel Stanley
2017-02-03 16:36 ` eajames
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1486077964-11346-6-git-send-email-eajames@linux.vnet.ibm.com \
--to=eajames@linux.vnet.ibm.com \
--cc=alistair@popple.id.au \
--cc=benh@kernel.crashing.org \
--cc=eajames@us.ibm.com \
--cc=joel@jms.id.au \
--cc=openbmc@lists.ozlabs.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.