Skip to contentSkip to conversation

drivers: i2c: FSI-attached I2C master algorithm

PATCH Superseded Eddie James 2 participants 10 messages May 4, 2017 – May 10, 2017

Subsystems (4)

v1 of 2 Superseded Latest is v2 →

lore
Share & tools
lore
#0 lore raw

cover drivers: i2c: FSI-attached I2C master algorithm

From: "Edward A. James" <[email protected]>

This patch series adds an I2C bus algorithm to drive the I2C master located on POWER CPUs. The master is accessed over FSI bus from the service processor.

The driver creates one chardev entry for each port off an I2C master by registering one I2C adapter for port. This implementation requires locking of the master in the driver to prevent transfers running into each other. The master registers are shared between ports.

This patchset also adds the latest in FSI dt bindings from Jeremy Kerr. This describes the entire FSI tree structure (master -> slaves -> engines -> engine data (such as i2c master ports)). These bindings are required for the I2C driver in order to enumerate the ports off each I2C master.

Edward A. James (6):
drivers: i2c: Add FSI-attached I2C master algorithm
drivers: i2c: Add port structure to FSI algorithm
drivers: i2c: Add transfer implementation for FSI algorithm
drivers: i2c: Add I2C master locking to FSI algorithm
drivers: i2c: Add bus recovery for FSI algorithm
dts: aspeed: witherspoon: Add I2C master under FSI masters

 arch/arm/boot/dts/aspeed-bmc-opp-witherspoon.dts | 156 ++++++
 drivers/Makefile                                 |   2 +-
 drivers/i2c/busses/Kconfig                       |  11 +
 drivers/i2c/busses/Makefile                      |   1 +
 drivers/i2c/busses/i2c-fsi.c                     | 681 +++++++++++++++++++++++
 5 files changed, 850 insertions(+), 1 deletion(-)
 create mode 100644 drivers/i2c/busses/i2c-fsi.c
Signature · 1 line
1.8.3.1
#1 lore raw

PATCH 1/6 drivers: i2c: Add FSI-attached I2C master algorithm

From: "Edward A. James" <[email protected]>

Initial startup code for the I2C algorithm to drive the I2C master located on POWER CPUs over FSI bus.

---
 drivers/Makefile             |   2 +-
 drivers/i2c/busses/Kconfig   |  11 ++
 drivers/i2c/busses/Makefile  |   1 +
 drivers/i2c/busses/i2c-fsi.c | 275 +++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 288 insertions(+), 1 deletion(-)
 create mode 100644 drivers/i2c/busses/i2c-fsi.c
drivers/Makefile +1 -1
index 67ce51d..278f109 100644
@@ -105,6 +105,7 @@ obj-$(CONFIG_SERIO)		+= input/serio/
 obj-$(CONFIG_GAMEPORT)		+= input/gameport/
 obj-$(CONFIG_INPUT)		+= input/
 obj-$(CONFIG_RTC_LIB)		+= rtc/
+obj-$(CONFIG_FSI)		+= fsi/
 obj-y				+= i2c/ media/
 obj-$(CONFIG_PPS)		+= pps/
 obj-y				+= ptp/
@@ -173,4 +174,3 @@ obj-$(CONFIG_STM)		+= hwtracing/stm/
 obj-$(CONFIG_ANDROID)		+= android/
 obj-$(CONFIG_NVMEM)		+= nvmem/
 obj-$(CONFIG_FPGA)		+= fpga/
-obj-$(CONFIG_FSI)		+= fsi/
drivers/i2c/busses/Kconfig +11 -0
index 7e34901d..0c714e0 100644
@@ -1245,4 +1245,15 @@ config I2C_OPAL
 	  This driver can also be built as a module. If so, the module will be
 	  called as i2c-opal.
 
+config I2C_FSI
+	tristate "FSI I2C driver"
+	depends on FSI
+	help
+	  Driver for FSI bus attached I2C masters. These are I2C masters that
+	  are connected to the system over a FSI bus, instead of the more
+	  common PCI or MMIO interface.
+
+	  This driver can also be built as a module. If so, the module will be
+	  called as i2c-fsi.
+
 endmenu
drivers/i2c/busses/Makefile +1 -0
index 5798b4e..547773b 100644
@@ -124,5 +124,6 @@ obj-$(CONFIG_I2C_PCA_ISA)	+= i2c-pca-isa.o
 obj-$(CONFIG_I2C_SIBYTE)	+= i2c-sibyte.o
 obj-$(CONFIG_I2C_XGENE_SLIMPRO) += i2c-xgene-slimpro.o
 obj-$(CONFIG_SCx200_ACB)	+= scx200_acb.o
+obj-$(CONFIG_I2C_FSI)		+= i2c-fsi.o
 
 ccflags-$(CONFIG_I2C_DEBUG_BUS) := -DDEBUG
drivers/i2c/busses/i2c-fsi.c new file +275 -0

275 lines not on this page · raw message

Signature · 1 line
1.8.3.1
maintainer
#2 lore raw

Re: [PATCH linux dev-4.10 1/6] drivers: i2c: Add FSI-attached I2C master algorithm

Quoted 79 lines · Eddie James

From: "Edward A. James" <[email protected]>

Initial startup code for the I2C algorithm to drive the I2C master located on POWER CPUs over FSI bus.

---
 drivers/Makefile             |   2 +-
 drivers/i2c/busses/Kconfig   |  11 ++
 drivers/i2c/busses/Makefile  |   1 +
 drivers/i2c/busses/i2c-fsi.c | 275 +++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 288 insertions(+), 1 deletion(-)
 create mode 100644 drivers/i2c/busses/i2c-fsi.c
drivers/Makefile +1 -1
index 67ce51d..278f109 100644
@@ -105,6 +105,7 @@ obj-$(CONFIG_SERIO)         += input/serio/
 obj-$(CONFIG_GAMEPORT)         += input/gameport/
 obj-$(CONFIG_INPUT)            += input/
 obj-$(CONFIG_RTC_LIB)          += rtc/
+obj-$(CONFIG_FSI)              += fsi/
 obj-y                          += i2c/ media/
 obj-$(CONFIG_PPS)              += pps/
 obj-y                          += ptp/
@@ -173,4 +174,3 @@ obj-$(CONFIG_STM)           += hwtracing/stm/
 obj-$(CONFIG_ANDROID)          += android/
 obj-$(CONFIG_NVMEM)            += nvmem/
 obj-$(CONFIG_FPGA)             += fpga/
-obj-$(CONFIG_FSI)              += fsi/
drivers/i2c/busses/Kconfig +11 -0
index 7e34901d..0c714e0 100644
@@ -1245,4 +1245,15 @@ config I2C_OPAL
          This driver can also be built as a module. If so, the module will be
          called as i2c-opal.
 
+config I2C_FSI
+       tristate "FSI I2C driver"
+       depends on FSI
+       help
+         Driver for FSI bus attached I2C masters. These are I2C masters that
+         are connected to the system over a FSI bus, instead of the more
+         common PCI or MMIO interface.
+
+         This driver can also be built as a module. If so, the module will be
+         called as i2c-fsi.
+
 endmenu
drivers/i2c/busses/Makefile +1 -0
index 5798b4e..547773b 100644
@@ -124,5 +124,6 @@ obj-$(CONFIG_I2C_PCA_ISA)   += i2c-pca-isa.o
 obj-$(CONFIG_I2C_SIBYTE)       += i2c-sibyte.o
 obj-$(CONFIG_I2C_XGENE_SLIMPRO) += i2c-xgene-slimpro.o
 obj-$(CONFIG_SCx200_ACB)       += scx200_acb.o
+obj-$(CONFIG_I2C_FSI)          += i2c-fsi.o
 
 ccflags-$(CONFIG_I2C_DEBUG_BUS) := -DDEBUG
drivers/i2c/busses/i2c-fsi.c new file +11 -0
new file mode 100644
index 0000000..bb12fa9
@@ -0,0 +1,275 @@
+/*
+ * Copyright 2017 IBM Corporation
+ *
+ * Eddie James <[email protected]>
+ *
+ * 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.
+ */
+

drivers/i2c/busses/i2c-fsi.c line 21

Quoted 10 lines
drivers/i2c/busses/i2c-fsi.c new file +10 -0
+#include <linux/fsi.h>
+#include <linux/i2c.h>
+#include <linux/jiffies.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/sched.h>
+#include <linux/semaphore.h>
+#include <linux/wait.h>
+
+#define FSI_ENGID_I2C_FSI      0x7

Is the engine ID something that is part of the FSI spec? If so, we should put the IDs in the FSI headers.

drivers/i2c/busses/i2c-fsi.c line 31

Quoted 10 lines
drivers/i2c/busses/i2c-fsi.c new file +10 -0
+
+/* Find left shift from first set bit in m */
+#define MASK_TO_LSH(m)         (__builtin_ffsll(m) - 1ULL)
+
+/* Extract field m from v */
+#define GETFIELD(m, v)         (((v) & (m)) >> MASK_TO_LSH(m))
+
+/* Set field m of v to val */
+#define SETFIELD(m, v, val)    \
+       (((v) & ~(m)) | ((((typeof(v))(val)) << MASK_TO_LSH(m)) & (m)))

My eyes :)

Are these going to be used by all of our FSI drivers? Perhaps we could get Chris to put them in the common FSI header.

Quoted 102 lines
drivers/i2c/busses/i2c-fsi.c new file +102 -0
+
+#define I2C_DEFAULT_CLK_DIV    6
+#define I2C_DEFAULT_CLK                333333
+
+/* i2c registers */
+#define I2C_FSI_FIFO           0x00
+#define I2C_FSI_CMD            0x04
+#define I2C_FSI_MODE           0x08
+#define I2C_FSI_WATER_MARK     0x0C
+#define I2C_FSI_INT_MASK       0x10
+#define I2C_FSI_INT_COND       0x14
+#define I2C_FSI_OR_INT_MASK    0x14
+#define I2C_FSI_INTS           0x18
+#define I2C_FSI_AND_INT_MASK   0x18
+#define I2C_FSI_STAT           0x1C
+#define I2C_FSI_RESET_I2C      0x1C
+#define I2C_FSI_ESTAT          0x20
+#define I2C_FSI_RESET_ERR      0x20
+#define I2C_FSI_RESID_LEN      0x24
+#define I2C_FSI_SET_SCL                0x24
+#define I2C_FSI_PORT_BUSY      0x28
+#define I2C_FSI_RESET_SCL      0x2C
+#define I2C_FSI_SET_SDA                0x30
+#define I2C_FSI_RESET_SDA      0x34
+
+/* cmd register */
+#define I2C_CMD_WITH_START     0x80000000
+#define I2C_CMD_WITH_ADDR      0x40000000
+#define I2C_CMD_RD_CONT                0x20000000
+#define I2C_CMD_WITH_STOP      0x10000000
+#define I2C_CMD_FORCELAUNCH    0x08000000
+#define I2C_CMD_ADDR           0x00fe0000
+#define I2C_CMD_READ           0x00010000
+#define I2C_CMD_LEN            0x0000ffff
+
+/* mode register */
+#define I2C_MODE_CLKDIV                0xffff0000
+#define I2C_MODE_PORT          0x0000fc00
+#define I2C_MODE_ENHANCED      0x00000008
+#define I2C_MODE_DIAG          0x00000004
+#define I2C_MODE_PACE_ALLOW    0x00000002
+#define I2C_MODE_WRAP          0x00000001
+
+/* watermark register */
+#define I2C_WATERMARK_HI       0x0000f000
+#define I2C_WATERMARK_LO       0x000000f0
+
+#define I2C_FIFO_HI_LVL                4
+#define I2C_FIFO_LO_LVL                4
+
+/* interrupt register */
+#define I2C_INT_INV_CMD                0x00008000
+#define I2C_INT_PARITY         0x00004000
+#define I2C_INT_BE_OVERRUN     0x00002000
+#define I2C_INT_BE_ACCESS      0x00001000
+#define I2C_INT_LOST_ARB       0x00000800
+#define I2C_INT_NACK           0x00000400
+#define I2C_INT_DAT_REQ                0x00000200
+#define I2C_INT_CMD_COMP       0x00000100
+#define I2C_INT_STOP_ERR       0x00000080
+#define I2C_INT_BUSY           0x00000040
+#define I2C_INT_IDLE           0x00000020
+
+#define I2C_INT_ENABLE         0x0000ff80
+#define I2C_INT_ERR            0x0000fcc0
+
+/* status register */
+#define I2C_STAT_INV_CMD       0x80000000
+#define I2C_STAT_PARITY                0x40000000
+#define I2C_STAT_BE_OVERRUN    0x20000000
+#define I2C_STAT_BE_ACCESS     0x10000000
+#define I2C_STAT_LOST_ARB      0x08000000
+#define I2C_STAT_NACK          0x04000000
+#define I2C_STAT_DAT_REQ       0x02000000
+#define I2C_STAT_CMD_COMP      0x01000000
+#define I2C_STAT_STOP_ERR      0x00800000
+#define I2C_STAT_MAX_PORT      0x000f0000
+#define I2C_STAT_ANY_INT       0x00008000
+#define I2C_STAT_SCL_IN                0x00000800
+#define I2C_STAT_SDA_IN                0x00000400
+#define I2C_STAT_PORT_BUSY     0x00000200
+#define I2C_STAT_SELF_BUSY     0x00000100
+#define I2C_STAT_FIFO_COUNT    0x000000ff
+
+#define I2C_STAT_ERR           0xfc800000
+#define I2C_STAT_ANY_RESP      0xff800000
+
+/* extended status register */
+#define I2C_ESTAT_FIFO_SZ      0xff000000
+#define I2C_ESTAT_SCL_IN_SY    0x00008000
+#define I2C_ESTAT_SDA_IN_SY    0x00004000
+#define I2C_ESTAT_S_SCL                0x00002000
+#define I2C_ESTAT_S_SDA                0x00001000
+#define I2C_ESTAT_M_SCL                0x00000800
+#define I2C_ESTAT_M_SDA                0x00000400
+#define I2C_ESTAT_HI_WATER     0x00000200
+#define I2C_ESTAT_LO_WATER     0x00000100
+#define I2C_ESTAT_PORT_BUSY    0x00000080
+#define I2C_ESTAT_SELF_BUSY    0x00000040
+#define I2C_ESTAT_VERSION      0x0000001f
+
+struct fsi_i2c_master {

drivers/i2c/busses/i2c-fsi.c line 143

Quoted 10 lines
drivers/i2c/busses/i2c-fsi.c new file +10 -0
+       struct fsi_device       *fsi;
+       u8                      fifo_size;
+       unsigned long           clk;
+};
+
+static int fsi_i2c_read_reg(struct fsi_device *fsi, unsigned int reg,
+                           u32 *data)
+{
+       int rc;
+       u32 _data;

Not a fan of the underscores. Perhaps raw_data?

Quoted 6 lines
drivers/i2c/busses/i2c-fsi.c new file +6 -0
+
+       rc = fsi_device_read(fsi, reg, &_data, sizeof(_data));
+       if (rc)
+               return rc;
+
+       *data = be32_to_cpu(_data);

drivers/i2c/busses/i2c-fsi.c line 159

Quoted 10 lines
drivers/i2c/busses/i2c-fsi.c new file +10 -0
+
+       return 0;
+}
+
+static int fsi_i2c_write_reg(struct fsi_device *fsi, unsigned int reg,
+                            u32 *data)
+{
+       u32 _data = cpu_to_be32(*data);
+
+       return fsi_device_write(fsi, reg, &_data, sizeof(_data));

It would be nice if the FSI api checked that you were passing it data in the correct endiannes.

drivers/i2c/busses/i2c-fsi.c line 167

Quoted 8 lines
drivers/i2c/busses/i2c-fsi.c new file +8 -0
+}
+
+static unsigned long fsi_i2c_get_clk_div(struct fsi_i2c_master *i2c)
+{
+       unsigned long clk_div = I2C_DEFAULT_CLK_DIV;
+
+       return clk_div;
+}

A bit of overkill. You could just use the #define where you need it.

Quoted 94 lines
drivers/i2c/busses/i2c-fsi.c new file +94 -0
+
+static int fsi_i2c_dev_init(struct fsi_i2c_master *i2c)
+{
+       int rc;
+       unsigned long clk_div;
+       u32 mode = I2C_MODE_ENHANCED, extended_status, watermark = 0;
+       u32 interrupt = 0;
+
+       /* since we use polling, disable interrupts */
+       rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_INT_MASK, &interrupt);
+       if (rc)
+               return rc;
+
+       clk_div = fsi_i2c_get_clk_div(i2c);
+       mode = SETFIELD(I2C_MODE_CLKDIV, mode, clk_div);
+       rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_MODE, &mode);
+       if (rc)
+               return rc;
+
+       rc = fsi_i2c_read_reg(i2c->fsi, I2C_FSI_ESTAT, &extended_status);
+       if (rc)
+               return rc;
+
+       i2c->fifo_size = GETFIELD(I2C_ESTAT_FIFO_SZ, extended_status);
+       watermark = SETFIELD(I2C_WATERMARK_HI, watermark,
+                            i2c->fifo_size - I2C_FIFO_HI_LVL);
+       watermark = SETFIELD(I2C_WATERMARK_LO, watermark,
+                            I2C_FIFO_LO_LVL);
+
+       rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_WATER_MARK, &watermark);
+
+       return rc;
+}
+
+static int fsi_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
+                       int num)
+{
+       return -ENOSYS;
+}
+
+static u32 fsi_i2c_functionality(struct i2c_adapter *adap)
+{
+       return I2C_FUNC_I2C | I2C_FUNC_PROTOCOL_MANGLING | I2C_FUNC_10BIT_ADDR;
+}
+
+static const struct i2c_algorithm fsi_i2c_algorithm = {
+       .master_xfer = fsi_i2c_xfer,
+       .functionality = fsi_i2c_functionality,
+};
+
+static int fsi_i2c_probe(struct device *dev)
+{
+       struct fsi_i2c_master *i2c;
+       int rc;
+       u32 clk;
+
+       i2c = devm_kzalloc(dev, sizeof(*i2c), GFP_KERNEL);
+       if (!i2c)
+               return -ENOMEM;
+
+       i2c->fsi = to_fsi_dev(dev);
+       i2c->clk = I2C_DEFAULT_CLK;
+
+       if (dev->of_node) {
+               if (!of_property_read_u32(dev->of_node, "clock-frequency",
+                                         &clk))
+                       i2c->clk = clk;
+       }
+
+       rc = fsi_i2c_dev_init(i2c);
+       if (rc)
+               return rc;
+
+       dev_set_drvdata(dev, i2c);
+
+       return 0;
+}
+
+static const struct fsi_device_id fsi_i2c_ids[] = {
+       { FSI_ENGID_I2C_FSI, FSI_VERSION_ANY },
+       { 0 }
+};
+
+static struct fsi_driver fsi_i2c_driver = {
+       .id_table = fsi_i2c_ids,
+       .drv = {
+               .name = "i2c_master_fsi",
+               .bus = &fsi_bus_type,
+               .probe = fsi_i2c_probe,
+       },
+};
+
+static int fsi_i2c_init(void)
+{

drivers/i2c/busses/i2c-fsi.c line 271

Quoted 10 lines
drivers/i2c/busses/i2c-fsi.c new file +10 -0
+       return fsi_driver_register(&fsi_i2c_driver);
+}
+
+static void fsi_i2c_exit(void)
+{
+       fsi_driver_unregister(&fsi_i2c_driver);
+}
+
+module_init(fsi_i2c_init);
+module_exit(fsi_i2c_exit);

Please use module_fsi_driver() instead.

Quoted 7 lines
drivers/i2c/busses/i2c-fsi.c new file +4 -1
+
+MODULE_AUTHOR("Eddie James <[email protected]>");
+MODULE_DESCRIPTION("FSI attached I2C master");
+MODULE_LICENSE("GPL");
--

1.8.3.1

#3 lore raw

Re: [PATCH linux dev-4.10 1/6] drivers: i2c: Add FSI-attached I2C master algorithm

Quoted 84 lines · Joel Stanley
Quoted 83 lines · Eddie James

From: "Edward A. James" <[email protected]>

Initial startup code for the I2C algorithm to drive the I2C master located on POWER CPUs over FSI bus.

---
  drivers/Makefile             |   2 +-
  drivers/i2c/busses/Kconfig   |  11 ++
  drivers/i2c/busses/Makefile  |   1 +
  drivers/i2c/busses/i2c-fsi.c | 275 +++++++++++++++++++++++++++++++++++++++++++
  4 files changed, 288 insertions(+), 1 deletion(-)

create mode 100644 drivers/i2c/busses/i2c-fsi.c

drivers/Makefile +1 -1
index 67ce51d..278f109 100644
@@ -105,6 +105,7 @@ obj-$(CONFIG_SERIO)         += input/serio/
  obj-$(CONFIG_GAMEPORT)         += input/gameport/
  obj-$(CONFIG_INPUT)            += input/
  obj-$(CONFIG_RTC_LIB)          += rtc/
+obj-$(CONFIG_FSI)              += fsi/
  obj-y                          += i2c/ media/
  obj-$(CONFIG_PPS)              += pps/
  obj-y                          += ptp/
@@ -173,4 +174,3 @@ obj-$(CONFIG_STM)           += hwtracing/stm/
  obj-$(CONFIG_ANDROID)          += android/
  obj-$(CONFIG_NVMEM)            += nvmem/
  obj-$(CONFIG_FPGA)             += fpga/
-obj-$(CONFIG_FSI)              += fsi/
drivers/i2c/busses/Kconfig +11 -0
index 7e34901d..0c714e0 100644
@@ -1245,4 +1245,15 @@ config I2C_OPAL
           This driver can also be built as a module. If so, the module will be
           called as i2c-opal.
 
+config I2C_FSI
+       tristate "FSI I2C driver"
+       depends on FSI
+       help
+         Driver for FSI bus attached I2C masters. These are I2C masters that
+         are connected to the system over a FSI bus, instead of the more
+         common PCI or MMIO interface.
+
+         This driver can also be built as a module. If so, the module will be
+         called as i2c-fsi.
+
  endmenu
drivers/i2c/busses/Makefile +1 -0
index 5798b4e..547773b 100644
@@ -124,5 +124,6 @@ obj-$(CONFIG_I2C_PCA_ISA)   += i2c-pca-isa.o
  obj-$(CONFIG_I2C_SIBYTE)       += i2c-sibyte.o
  obj-$(CONFIG_I2C_XGENE_SLIMPRO) += i2c-xgene-slimpro.o
  obj-$(CONFIG_SCx200_ACB)       += scx200_acb.o
+obj-$(CONFIG_I2C_FSI)          += i2c-fsi.o
 
  ccflags-$(CONFIG_I2C_DEBUG_BUS) := -DDEBUG
drivers/i2c/busses/i2c-fsi.c new file +15 -0
new file mode 100644
index 0000000..bb12fa9
@@ -0,0 +1,275 @@
+/*
+ * Copyright 2017 IBM Corporation
+ *
+ * Eddie James <[email protected]>
+ *
+ * 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.
+ */
+
+#include <linux/fsi.h>
+#include <linux/i2c.h>
+#include <linux/jiffies.h>
+#include <linux/module.h>
Quoted 8 lines

drivers/i2c/busses/i2c-fsi.c line 21

Quoted 6 lines
drivers/i2c/busses/i2c-fsi.c new file +6 -0
+#include <linux/of.h>
+#include <linux/sched.h>
+#include <linux/semaphore.h>
+#include <linux/wait.h>
+
+#define FSI_ENGID_I2C_FSI      0x7

Is the engine ID something that is part of the FSI spec? If so, we should put the IDs in the FSI headers.

It's defined in the engine code everywhere else... SCOM driver, hub master code, etc. So I think we should leave it here for now.

Quoted 13 lines

drivers/i2c/busses/i2c-fsi.c line 31

Quoted 11 lines
drivers/i2c/busses/i2c-fsi.c new file +10 -0
+
+/* Find left shift from first set bit in m */
+#define MASK_TO_LSH(m)         (__builtin_ffsll(m) - 1ULL)
+
+/* Extract field m from v */
+#define GETFIELD(m, v)         (((v) & (m)) >> MASK_TO_LSH(m))
+
+/* Set field m of v to val */
+#define SETFIELD(m, v, val)    \
+       (((v) & ~(m)) | ((((typeof(v))(val)) << MASK_TO_LSH(m)) & (m)))

My eyes :)

Quoted 2 lines

Are these going to be used by all of our FSI drivers? Perhaps we could get Chris to put them in the common FSI header.

Agreed, it's ugly :) I haven't seen these in any other FSI drivers though... would prefer to leave it here.

Quoted 106 lines
Quoted 106 lines
drivers/i2c/busses/i2c-fsi.c new file +105 -0
+
+#define I2C_DEFAULT_CLK_DIV    6
+#define I2C_DEFAULT_CLK                333333
+
+/* i2c registers */
+#define I2C_FSI_FIFO           0x00
+#define I2C_FSI_CMD            0x04
+#define I2C_FSI_MODE           0x08
+#define I2C_FSI_WATER_MARK     0x0C
+#define I2C_FSI_INT_MASK       0x10
+#define I2C_FSI_INT_COND       0x14
+#define I2C_FSI_OR_INT_MASK    0x14
+#define I2C_FSI_INTS           0x18
+#define I2C_FSI_AND_INT_MASK   0x18
+#define I2C_FSI_STAT           0x1C
+#define I2C_FSI_RESET_I2C      0x1C
+#define I2C_FSI_ESTAT          0x20
+#define I2C_FSI_RESET_ERR      0x20
+#define I2C_FSI_RESID_LEN      0x24
+#define I2C_FSI_SET_SCL                0x24
+#define I2C_FSI_PORT_BUSY      0x28
+#define I2C_FSI_RESET_SCL      0x2C
+#define I2C_FSI_SET_SDA                0x30
+#define I2C_FSI_RESET_SDA      0x34
+
+/* cmd register */
+#define I2C_CMD_WITH_START     0x80000000
+#define I2C_CMD_WITH_ADDR      0x40000000
+#define I2C_CMD_RD_CONT                0x20000000
+#define I2C_CMD_WITH_STOP      0x10000000
+#define I2C_CMD_FORCELAUNCH    0x08000000
+#define I2C_CMD_ADDR           0x00fe0000
+#define I2C_CMD_READ           0x00010000
+#define I2C_CMD_LEN            0x0000ffff
+
+/* mode register */
+#define I2C_MODE_CLKDIV                0xffff0000
+#define I2C_MODE_PORT          0x0000fc00
+#define I2C_MODE_ENHANCED      0x00000008
+#define I2C_MODE_DIAG          0x00000004
+#define I2C_MODE_PACE_ALLOW    0x00000002
+#define I2C_MODE_WRAP          0x00000001
+
+/* watermark register */
+#define I2C_WATERMARK_HI       0x0000f000
+#define I2C_WATERMARK_LO       0x000000f0
+
+#define I2C_FIFO_HI_LVL                4
+#define I2C_FIFO_LO_LVL                4
+
+/* interrupt register */
+#define I2C_INT_INV_CMD                0x00008000
+#define I2C_INT_PARITY         0x00004000
+#define I2C_INT_BE_OVERRUN     0x00002000
+#define I2C_INT_BE_ACCESS      0x00001000
+#define I2C_INT_LOST_ARB       0x00000800
+#define I2C_INT_NACK           0x00000400
+#define I2C_INT_DAT_REQ                0x00000200
+#define I2C_INT_CMD_COMP       0x00000100
+#define I2C_INT_STOP_ERR       0x00000080
+#define I2C_INT_BUSY           0x00000040
+#define I2C_INT_IDLE           0x00000020
+
+#define I2C_INT_ENABLE         0x0000ff80
+#define I2C_INT_ERR            0x0000fcc0
+
+/* status register */
+#define I2C_STAT_INV_CMD       0x80000000
+#define I2C_STAT_PARITY                0x40000000
+#define I2C_STAT_BE_OVERRUN    0x20000000
+#define I2C_STAT_BE_ACCESS     0x10000000
+#define I2C_STAT_LOST_ARB      0x08000000
+#define I2C_STAT_NACK          0x04000000
+#define I2C_STAT_DAT_REQ       0x02000000
+#define I2C_STAT_CMD_COMP      0x01000000
+#define I2C_STAT_STOP_ERR      0x00800000
+#define I2C_STAT_MAX_PORT      0x000f0000
+#define I2C_STAT_ANY_INT       0x00008000
+#define I2C_STAT_SCL_IN                0x00000800
+#define I2C_STAT_SDA_IN                0x00000400
+#define I2C_STAT_PORT_BUSY     0x00000200
+#define I2C_STAT_SELF_BUSY     0x00000100
+#define I2C_STAT_FIFO_COUNT    0x000000ff
+
+#define I2C_STAT_ERR           0xfc800000
+#define I2C_STAT_ANY_RESP      0xff800000
+
+/* extended status register */
+#define I2C_ESTAT_FIFO_SZ      0xff000000
+#define I2C_ESTAT_SCL_IN_SY    0x00008000
+#define I2C_ESTAT_SDA_IN_SY    0x00004000
+#define I2C_ESTAT_S_SCL                0x00002000
+#define I2C_ESTAT_S_SDA                0x00001000
+#define I2C_ESTAT_M_SCL                0x00000800
+#define I2C_ESTAT_M_SDA                0x00000400
+#define I2C_ESTAT_HI_WATER     0x00000200
+#define I2C_ESTAT_LO_WATER     0x00000100
+#define I2C_ESTAT_PORT_BUSY    0x00000080
+#define I2C_ESTAT_SELF_BUSY    0x00000040
+#define I2C_ESTAT_VERSION      0x0000001f
+
+struct fsi_i2c_master {
+       struct fsi_device       *fsi;
+       u8                      fifo_size;
+       unsigned long           clk;
Quoted 8 lines

drivers/i2c/busses/i2c-fsi.c line 143

Quoted 7 lines
drivers/i2c/busses/i2c-fsi.c new file +7 -0
+};
+
+static int fsi_i2c_read_reg(struct fsi_device *fsi, unsigned int reg,
+                           u32 *data)
+{
+       int rc;
+       u32 _data;

Not a fan of the underscores. Perhaps raw_data?

Sure.

Quoted 11 lines
Quoted 11 lines
drivers/i2c/busses/i2c-fsi.c new file +10 -0
+
+       rc = fsi_device_read(fsi, reg, &_data, sizeof(_data));
+       if (rc)
+               return rc;
+
+       *data = be32_to_cpu(_data);
+
+       return 0;
+}
+
Quoted 8 lines

drivers/i2c/busses/i2c-fsi.c line 159

Quoted 6 lines
drivers/i2c/busses/i2c-fsi.c new file +6 -0
+static int fsi_i2c_write_reg(struct fsi_device *fsi, unsigned int reg,
+                            u32 *data)
+{
+       u32 _data = cpu_to_be32(*data);
+
+       return fsi_device_write(fsi, reg, &_data, sizeof(_data));

It would be nice if the FSI api checked that you were passing it data in the correct endiannes.

Hmm, I disagree. Mainly because the API accepts data of any size, so byte-swapping words doesn't make sense. (although in reality only 1, 2, or 4 byte access is accepted). When this driver reads from the FIFO register, it should not swap the endian so that it returns the data as-is off the device. However we do need to swap the endian for the other registers, so that we're reading/setting the right bits. So it's really usage-dependent.

Quoted 10 lines

drivers/i2c/busses/i2c-fsi.c line 167

Quoted 9 lines
drivers/i2c/busses/i2c-fsi.c new file +8 -0
+}
+
+static unsigned long fsi_i2c_get_clk_div(struct fsi_i2c_master *i2c)
+{
+       unsigned long clk_div = I2C_DEFAULT_CLK_DIV;
+
+       return clk_div;
+}

A bit of overkill. You could just use the #define where you need it.

I did this so that we could actually calculate clock div based on fsi and i2c clocks in the future... I'll just use the #define for now then.

Quoted 98 lines
Quoted 98 lines
drivers/i2c/busses/i2c-fsi.c new file +97 -0
+
+static int fsi_i2c_dev_init(struct fsi_i2c_master *i2c)
+{
+       int rc;
+       unsigned long clk_div;
+       u32 mode = I2C_MODE_ENHANCED, extended_status, watermark = 0;
+       u32 interrupt = 0;
+
+       /* since we use polling, disable interrupts */
+       rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_INT_MASK, &interrupt);
+       if (rc)
+               return rc;
+
+       clk_div = fsi_i2c_get_clk_div(i2c);
+       mode = SETFIELD(I2C_MODE_CLKDIV, mode, clk_div);
+       rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_MODE, &mode);
+       if (rc)
+               return rc;
+
+       rc = fsi_i2c_read_reg(i2c->fsi, I2C_FSI_ESTAT, &extended_status);
+       if (rc)
+               return rc;
+
+       i2c->fifo_size = GETFIELD(I2C_ESTAT_FIFO_SZ, extended_status);
+       watermark = SETFIELD(I2C_WATERMARK_HI, watermark,
+                            i2c->fifo_size - I2C_FIFO_HI_LVL);
+       watermark = SETFIELD(I2C_WATERMARK_LO, watermark,
+                            I2C_FIFO_LO_LVL);
+
+       rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_WATER_MARK, &watermark);
+
+       return rc;
+}
+
+static int fsi_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
+                       int num)
+{
+       return -ENOSYS;
+}
+
+static u32 fsi_i2c_functionality(struct i2c_adapter *adap)
+{
+       return I2C_FUNC_I2C | I2C_FUNC_PROTOCOL_MANGLING | I2C_FUNC_10BIT_ADDR;
+}
+
+static const struct i2c_algorithm fsi_i2c_algorithm = {
+       .master_xfer = fsi_i2c_xfer,
+       .functionality = fsi_i2c_functionality,
+};
+
+static int fsi_i2c_probe(struct device *dev)
+{
+       struct fsi_i2c_master *i2c;
+       int rc;
+       u32 clk;
+
+       i2c = devm_kzalloc(dev, sizeof(*i2c), GFP_KERNEL);
+       if (!i2c)
+               return -ENOMEM;
+
+       i2c->fsi = to_fsi_dev(dev);
+       i2c->clk = I2C_DEFAULT_CLK;
+
+       if (dev->of_node) {
+               if (!of_property_read_u32(dev->of_node, "clock-frequency",
+                                         &clk))
+                       i2c->clk = clk;
+       }
+
+       rc = fsi_i2c_dev_init(i2c);
+       if (rc)
+               return rc;
+
+       dev_set_drvdata(dev, i2c);
+
+       return 0;
+}
+
+static const struct fsi_device_id fsi_i2c_ids[] = {
+       { FSI_ENGID_I2C_FSI, FSI_VERSION_ANY },
+       { 0 }
+};
+
+static struct fsi_driver fsi_i2c_driver = {
+       .id_table = fsi_i2c_ids,
+       .drv = {
+               .name = "i2c_master_fsi",
+               .bus = &fsi_bus_type,
+               .probe = fsi_i2c_probe,
+       },
+};
+
+static int fsi_i2c_init(void)
+{
+       return fsi_driver_register(&fsi_i2c_driver);
+}
+
Quoted 8 lines

drivers/i2c/busses/i2c-fsi.c line 271

Quoted 7 lines
drivers/i2c/busses/i2c-fsi.c new file +7 -0
+static void fsi_i2c_exit(void)
+{
+       fsi_driver_unregister(&fsi_i2c_driver);
+}
+
+module_init(fsi_i2c_init);
+module_exit(fsi_i2c_exit);

Please use module_fsi_driver() instead.

Sure.

Thanks for the review, Joel!
Eddie

Quoted 9 lines
Quoted 9 lines
drivers/i2c/busses/i2c-fsi.c new file +4 -1
+
+MODULE_AUTHOR("Eddie James <[email protected]>");
+MODULE_DESCRIPTION("FSI attached I2C master");
+MODULE_LICENSE("GPL");
--

1.8.3.1

#4 lore raw

PATCH 2/6 drivers: i2c: Add port structure to FSI algorithm

In reply to Eddie James

From: "Edward A. James" <[email protected]>

Add and initialize I2C adapters for each port on the FSI-attached I2C master. Ports are defined in the devicetree.

---
 drivers/i2c/busses/i2c-fsi.c | 112 ++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 110 insertions(+), 2 deletions(-)
drivers/i2c/busses/i2c-fsi.c +110 -2
index bb12fa9..c81e255 100644
@@ -30,6 +30,7 @@
 #define SETFIELD(m, v, val)	\
 	(((v) & ~(m)) | ((((typeof(v))(val)) << MASK_TO_LSH(m)) & (m)))
 
+#define I2C_MASTER_NR_OFFSET	100
 #define I2C_DEFAULT_CLK_DIV	6
 #define I2C_DEFAULT_CLK		333333
 
@@ -132,10 +133,22 @@
 
 struct fsi_i2c_master {
 	struct fsi_device	*fsi;
+	int			idx;
 	u8			fifo_size;
 	unsigned long		clk;
+	struct list_head	ports;
+	struct ida		ida;
 };
 
+struct fsi_i2c_port {
+	struct list_head	list;
+	struct i2c_adapter	adapter;
+	struct fsi_i2c_master	*master;
+	u16			port;
+};
+
+static DEFINE_IDA(fsi_i2c_ida);
+
 static int fsi_i2c_read_reg(struct fsi_device *fsi, unsigned int reg,
 			    u32 *data)
 {
@@ -199,9 +212,44 @@ static int fsi_i2c_dev_init(struct fsi_i2c_master *i2c)
 	return rc;
 }
 
+static int fsi_i2c_set_port(struct fsi_i2c_port *port)
+{
+	int rc;
+	struct fsi_device *fsi = port->master->fsi;
+	u32 mode, dummy = 0;
+	u16 old_port;
+
+	rc = fsi_i2c_read_reg(fsi, I2C_FSI_MODE, &mode);
+	if (rc)
+		return rc;
+
+	old_port = GETFIELD(I2C_MODE_PORT, mode);
+
+	if (old_port != port->port) {
+		mode = SETFIELD(I2C_MODE_PORT, mode, port->port);
+		rc = fsi_i2c_write_reg(fsi, I2C_FSI_MODE, &mode);
+		if (rc)
+			return rc;
+
+		/* reset engine when port is changed */
+		rc = fsi_i2c_write_reg(fsi, I2C_FSI_RESET_ERR, &dummy);
+		if (rc)
+			return rc;
+	}
+
+	return rc;
+}
+
 static int fsi_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
 			int num)
 {
+	int rc;
+	struct fsi_i2c_port *port = adap->algo_data;
+
+	rc = fsi_i2c_set_port(port);
+	if (rc)
+		return rc;
+
 	return -ENOSYS;
 }
 
@@ -218,20 +266,63 @@ static u32 fsi_i2c_functionality(struct i2c_adapter *adap)
 static int fsi_i2c_probe(struct device *dev)
 {
 	struct fsi_i2c_master *i2c;
-	int rc;
-	u32 clk;
+	struct fsi_i2c_port *port;
+	struct device_node *np;
+	int rc, idx;
+	u32 clk, port_no;
 
 	i2c = devm_kzalloc(dev, sizeof(*i2c), GFP_KERNEL);
 	if (!i2c)
 		return -ENOMEM;
 
 	i2c->fsi = to_fsi_dev(dev);
+	i2c->idx = ida_simple_get(&fsi_i2c_ida, 1, INT_MAX, GFP_KERNEL);
 	i2c->clk = I2C_DEFAULT_CLK;
+	ida_init(&i2c->ida);
+	INIT_LIST_HEAD(&i2c->ports);
 
 	if (dev->of_node) {
 		if (!of_property_read_u32(dev->of_node, "clock-frequency",
 					  &clk))
 			i2c->clk = clk;
+
+		/* add adapter for each i2c port of the master */
+		for_each_child_of_node(dev->of_node, np) {
+			rc = of_property_read_u32(np, "port", &port_no);
+			if (rc || port_no > 0xFFFF)
+				continue;
+
+			/* make sure we don't overlap index with a buggy dts */
+			idx = ida_simple_get(&i2c->ida, port_no,
+					     port_no + 1, GFP_KERNEL);
+			if (idx < 0)
+				continue;
+
+			port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
+			if (!port)
+				return -ENOMEM;
+
+			port->master = i2c;
+			port->port = (u16)port_no;
+
+			port->adapter.owner = THIS_MODULE;
+			port->adapter.dev.parent = dev;
+			port->adapter.algo = &fsi_i2c_algorithm;
+			port->adapter.algo_data = port;
+			/* number ports uniquely */
+			port->adapter.nr = (i2c->idx * I2C_MASTER_NR_OFFSET) +
+				port_no;
+
+			snprintf(port->adapter.name,
+				 sizeof(port->adapter.name),
+				 "fsi_i2c-%u", port_no);
+
+			rc = i2c_add_numbered_adapter(&port->adapter);
+			if (rc < 0)
+				return rc;
+
+			list_add(&port->list, &i2c->ports);
+		}
 	}
 
 	rc = fsi_i2c_dev_init(i2c);
@@ -243,6 +334,22 @@ static int fsi_i2c_probe(struct device *dev)
 	return 0;
 }
 
+static int fsi_i2c_remove(struct device *dev)
+{
+	struct fsi_i2c_master *i2c = dev_get_drvdata(dev);
+	struct fsi_i2c_port *port;
+
+	list_for_each_entry(port, &i2c->ports, list) {
+		i2c_del_adapter(&port->adapter);
+	}
+
+	ida_destroy(&i2c->ida);
+
+	ida_simple_remove(&fsi_i2c_ida, i2c->idx);
+
+	return 0;
+}
+
 static const struct fsi_device_id fsi_i2c_ids[] = {
 	{ FSI_ENGID_I2C_FSI, FSI_VERSION_ANY },
 	{ 0 }
@@ -254,6 +361,7 @@ static int fsi_i2c_probe(struct device *dev)
 		.name = "i2c_master_fsi",
 		.bus = &fsi_bus_type,
 		.probe = fsi_i2c_probe,
+		.remove = fsi_i2c_remove,
 	},
 };
 
Signature · 1 line
1.8.3.1
#5 lore raw

PATCH 3/6 drivers: i2c: Add transfer implementation for FSI algorithm

In reply to Eddie James

From: "Edward A. James" <[email protected]>

Execute I2C transfers from the FSI-attached I2C master. Use polling instead of interrupts as we have no hardware IRQ over FSI.

---
 drivers/i2c/busses/i2c-fsi.c | 193 ++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 191 insertions(+), 2 deletions(-)
drivers/i2c/busses/i2c-fsi.c +191 -2
index c81e255..fa77a2b 100644
@@ -145,6 +145,7 @@ struct fsi_i2c_port {
 	struct i2c_adapter	adapter;
 	struct fsi_i2c_master	*master;
 	u16			port;
+	u16			xfrd;
 };
 
 static DEFINE_IDA(fsi_i2c_ida);
@@ -240,17 +241,205 @@ static int fsi_i2c_set_port(struct fsi_i2c_port *port)
 	return rc;
 }
 
+static int fsi_i2c_start(struct fsi_i2c_port *port, struct i2c_msg *msg,
+			 bool stop)
+{
+	int rc;
+	struct fsi_i2c_master *i2c = port->master;
+	u32 cmd = I2C_CMD_WITH_START | I2C_CMD_WITH_ADDR;
+
+	port->xfrd = 0;
+
+	if (msg->flags & I2C_M_RD)
+		cmd |= I2C_CMD_READ;
+
+	if (stop || msg->flags & I2C_M_STOP)
+		cmd |= I2C_CMD_WITH_STOP;
+
+	cmd = SETFIELD(I2C_CMD_ADDR, cmd, msg->addr >> 1);
+	cmd = SETFIELD(I2C_CMD_LEN, cmd, msg->len);
+
+	rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_CMD, &cmd);
+
+	return rc;
+}
+
+static int fsi_i2c_write_fifo(struct fsi_i2c_port *port, struct i2c_msg *msg,
+			      u8 fifo_count)
+{
+	int write;
+	int rc = 0;
+	struct fsi_i2c_master *i2c = port->master;
+	int bytes_to_write = i2c->fifo_size - fifo_count;
+	int bytes_remaining = msg->len - port->xfrd;
+
+	if (bytes_to_write > bytes_remaining)
+		bytes_to_write = bytes_remaining;
+
+	while (bytes_to_write > 0) {
+		write = bytes_to_write;
+		/* fsi limited to max 4 byte aligned ops */
+		if (bytes_to_write > 4)
+			write = 4;
+		else if (write == 3)
+			write = 2;
+
+		rc = fsi_device_write(i2c->fsi, I2C_FSI_FIFO,
+				      &msg->buf[port->xfrd], write);
+		if (rc)
+			return rc;
+
+		port->xfrd += write;
+		bytes_to_write -= write;
+	}
+
+	return rc;
+}
+
+static int fsi_i2c_read_fifo(struct fsi_i2c_port *port, struct i2c_msg *msg,
+			     u8 fifo_count)
+{
+	int read;
+	int rc = 0;
+	struct fsi_i2c_master *i2c = port->master;
+	int xfr_remaining = msg->len - port->xfrd;
+	u32 dummy;
+
+	while (fifo_count) {
+		read = fifo_count;
+		/* fsi limited to max 4 byte aligned ops */
+		if (fifo_count > 4)
+			read = 4;
+		else if (read == 3)
+			read = 2;
+
+		if (xfr_remaining) {
+			if (xfr_remaining < read)
+				read = xfr_remaining;
+
+			rc = fsi_device_read(i2c->fsi, I2C_FSI_FIFO,
+					     &msg->buf[port->xfrd], read);
+			if (rc)
+				return rc;
+
+			port->xfrd += read;
+			xfr_remaining -= read;
+		} else {
+			/* no more buffer but data in fifo, need to clear it */
+			rc = fsi_device_read(i2c->fsi, I2C_FSI_FIFO, &dummy,
+					     read);
+			if (rc)
+				return rc;
+		}
+
+		fifo_count -= read;
+	}
+
+	return rc;
+}
+
+static int fsi_i2c_handle_status(struct fsi_i2c_port *port,
+				 struct i2c_msg *msg, u32 status)
+{
+	int rc;
+	u8 fifo_count;
+	struct fsi_i2c_master *i2c = port->master;
+	u32 dummy = 0;
+
+	if (status & I2C_STAT_ERR) {
+		rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_RESET_ERR, &dummy);
+		if (rc)
+			return rc;
+
+		if (status & I2C_STAT_NACK)
+			return -EFAULT;
+
+		return -EIO;
+	}
+
+	if (status & I2C_STAT_DAT_REQ) {
+		fifo_count = GETFIELD(I2C_STAT_FIFO_COUNT, status);
+
+		if (msg->flags & I2C_M_RD)
+			rc = fsi_i2c_read_fifo(port, msg, fifo_count);
+		else
+			rc = fsi_i2c_write_fifo(port, msg, fifo_count);
+
+		return rc;
+	}
+
+	if (status & I2C_STAT_CMD_COMP) {
+		if (port->xfrd < msg->len)
+			rc = -ENODATA;
+		else
+			rc = msg->len;
+	}
+
+	return rc;
+}
+
+static int fsi_i2c_wait(struct fsi_i2c_port *port, struct i2c_msg *msg,
+			unsigned long timeout)
+{
+	const unsigned long local_timeout = 2; /* jiffies */
+	u32 status = 0;
+	int rc;
+
+	do {
+		rc = fsi_i2c_read_reg(port->master->fsi, I2C_FSI_STAT,
+				      &status);
+		if (rc)
+			return rc;
+
+		if (status & I2C_STAT_ANY_RESP) {
+			rc = fsi_i2c_handle_status(port, msg, status);
+			if (rc < 0)
+				return rc;
+
+			/* cmd complete and all data xfrd */
+			if (rc == msg->len)
+				return 0;
+
+			/* need to xfr more data, but maybe don't need wait */
+			continue;
+		}
+
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout(local_timeout);
+		timeout = (timeout < local_timeout) ? 0 :
+			timeout - local_timeout;
+	} while (timeout);
+
+	return -ETIME;
+}
+
 static int fsi_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
 			int num)
 {
-	int rc;
+	int i, rc;
+	unsigned long start_time;
 	struct fsi_i2c_port *port = adap->algo_data;
+	struct i2c_msg *msg;
 
 	rc = fsi_i2c_set_port(port);
 	if (rc)
 		return rc;
 
-	return -ENOSYS;
+	for (i = 0; i < num; ++i) {
+		msg = msgs + i;
+		start_time = jiffies;
+
+		rc = fsi_i2c_start(port, msg, i == num - 1);
+		if (rc)
+			return rc;
+
+		rc = fsi_i2c_wait(port, msg,
+				  adap->timeout - (jiffies - start_time));
+		if (rc)
+			return rc;
+	}
+
+	return 0;
 }
 
 static u32 fsi_i2c_functionality(struct i2c_adapter *adap)
Signature · 1 line
1.8.3.1
#6 lore raw

PATCH 4/6 drivers: i2c: Add I2C master locking to FSI algorithm

In reply to Eddie James

From: "Edward A. James" <[email protected]>

Since there are many ports per master, each with it's own adapter and chardev, we need some locking to prevent xfers from changingg the master state while other xfers are in progress.

---
 drivers/i2c/busses/i2c-fsi.c | 41 +++++++++++++++++++++++++++++++++++++----
 1 file changed, 37 insertions(+), 4 deletions(-)
drivers/i2c/busses/i2c-fsi.c +37 -4
index fa77a2b..3bc9bbe 100644
@@ -138,6 +138,8 @@ struct fsi_i2c_master {
 	unsigned long		clk;
 	struct list_head	ports;
 	struct ida		ida;
+	wait_queue_head_t	wait;
+	struct semaphore	lock;
 };
 
 struct fsi_i2c_port {
@@ -173,6 +175,29 @@ static int fsi_i2c_write_reg(struct fsi_device *fsi, unsigned int reg,
 	return fsi_device_write(fsi, reg, &_data, sizeof(_data));
 }
 
+static int fsi_i2c_lock_master(struct fsi_i2c_master *i2c, int timeout)
+{
+	int rc;
+
+	rc = down_trylock(&i2c->lock);
+	if (!rc)
+		return 0;
+
+	rc = wait_event_interruptible_timeout(i2c->wait,
+					      !down_trylock(&i2c->lock),
+					      timeout);
+	if (rc > 0)
+		return 0;
+
+	return -EBUSY;
+}
+
+static void fsi_i2c_unlock_master(struct fsi_i2c_master *i2c)
+{
+	up(&i2c->lock);
+	wake_up(&i2c->wait);
+}
+
 static unsigned long fsi_i2c_get_clk_div(struct fsi_i2c_master *i2c)
 {
 	unsigned long clk_div = I2C_DEFAULT_CLK_DIV;
@@ -421,25 +446,31 @@ static int fsi_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
 	struct fsi_i2c_port *port = adap->algo_data;
 	struct i2c_msg *msg;
 
-	rc = fsi_i2c_set_port(port);
+	rc = fsi_i2c_lock_master(port->master, adap->timeout);
 	if (rc)
 		return rc;
 
+	rc = fsi_i2c_set_port(port);
+	if (rc)
+		goto unlock;
+
 	for (i = 0; i < num; ++i) {
 		msg = msgs + i;
 		start_time = jiffies;
 
 		rc = fsi_i2c_start(port, msg, i == num - 1);
 		if (rc)
-			return rc;
+			goto unlock;
 
 		rc = fsi_i2c_wait(port, msg,
 				  adap->timeout - (jiffies - start_time));
 		if (rc)
-			return rc;
+			goto unlock;
 	}
 
-	return 0;
+unlock:
+	fsi_i2c_unlock_master(port->master);
+	return rc;
 }
 
 static u32 fsi_i2c_functionality(struct i2c_adapter *adap)
@@ -464,6 +495,8 @@ static int fsi_i2c_probe(struct device *dev)
 	if (!i2c)
 		return -ENOMEM;
 
+	init_waitqueue_head(&i2c->wait);
+	sema_init(&i2c->lock, 1);
 	i2c->fsi = to_fsi_dev(dev);
 	i2c->idx = ida_simple_get(&fsi_i2c_ida, 1, INT_MAX, GFP_KERNEL);
 	i2c->clk = I2C_DEFAULT_CLK;
Signature · 1 line
1.8.3.1
#7 lore raw

PATCH 5/6 drivers: i2c: Add bus recovery for FSI algorithm

In reply to Eddie James

From: "Edward A. James" <[email protected]>

Bus recovery should reset the engine and force block the bus 9 times to recover most situations.

---
 drivers/i2c/busses/i2c-fsi.c | 76 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 76 insertions(+)
drivers/i2c/busses/i2c-fsi.c +76 -0
index 3bc9bbe..8e5762a 100644
@@ -478,6 +478,80 @@ static u32 fsi_i2c_functionality(struct i2c_adapter *adap)
 	return I2C_FUNC_I2C | I2C_FUNC_PROTOCOL_MANGLING | I2C_FUNC_10BIT_ADDR;
 }
 
+static int fsi_i2c_low_level_recover_bus(struct fsi_i2c_master *i2c)
+{
+	int i, rc;
+	u32 mode, dummy = 0;
+
+	rc = fsi_i2c_read_reg(i2c->fsi, I2C_FSI_MODE, &mode);
+	if (rc)
+		return rc;
+
+	mode |= I2C_MODE_DIAG;
+	rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_MODE, &mode);
+	if (rc)
+		return rc;
+
+	for (i = 0; i < 9; ++i) {
+		rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_RESET_SCL, &dummy);
+		if (rc)
+			return rc;
+
+		rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_SET_SCL, &dummy);
+		if (rc)
+			return rc;
+	}
+
+	rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_RESET_SCL, &dummy);
+	if (rc)
+		return rc;
+
+	rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_RESET_SDA, &dummy);
+	if (rc)
+		return rc;
+
+	rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_SET_SCL, &dummy);
+	if (rc)
+		return rc;
+
+	rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_SET_SDA, &dummy);
+	if (rc)
+		return rc;
+
+	mode &= ~I2C_MODE_DIAG;
+	rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_MODE, &mode);
+
+	return rc;
+}
+
+static int fsi_i2c_recover_bus(struct i2c_adapter *adap)
+{
+	int rc;
+	u32 dummy = 0;
+	struct fsi_i2c_port *port = adap->algo_data;
+	struct fsi_i2c_master *i2c = port->master;
+
+	rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_RESET_I2C, &dummy);
+	if (rc)
+		return rc;
+
+	rc = fsi_i2c_dev_init(i2c);
+	if (rc)
+		return rc;
+
+	rc = fsi_i2c_low_level_recover_bus(i2c);
+	if (rc)
+		return rc;
+
+	rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_RESET_ERR, &dummy);
+
+	return rc;
+}
+
+static struct i2c_bus_recovery_info fsi_i2c_bus_recovery_info = {
+	.recover_bus = fsi_i2c_recover_bus,
+};
+
 static const struct i2c_algorithm fsi_i2c_algorithm = {
 	.master_xfer = fsi_i2c_xfer,
 	.functionality = fsi_i2c_functionality,
@@ -530,6 +604,8 @@ static int fsi_i2c_probe(struct device *dev)
 			port->adapter.owner = THIS_MODULE;
 			port->adapter.dev.parent = dev;
 			port->adapter.algo = &fsi_i2c_algorithm;
+			port->adapter.bus_recovery_info =
+				&fsi_i2c_bus_recovery_info;
 			port->adapter.algo_data = port;
 			/* number ports uniquely */
 			port->adapter.nr = (i2c->idx * I2C_MASTER_NR_OFFSET) +
Signature · 1 line
1.8.3.1
#8 lore raw

PATCH 6/6 dts: aspeed: witherspoon: Add I2C master under FSI masters

In reply to Eddie James

From: "Edward A. James" <[email protected]>

Include all the ports that are present off I2C engine on P9.

---
 arch/arm/boot/dts/aspeed-bmc-opp-witherspoon.dts | 156 +++++++++++++++++++++++
 1 file changed, 156 insertions(+)
arch/arm/boot/dts/aspeed-bmc-opp-witherspoon.dts +156 -0
index f20aaf4..ac903c8 100644
@@ -65,6 +65,8 @@
 
 	gpio-fsi {
 		compatible = "fsi-master-gpio", "fsi-master";
+		#address-cells = <2>;
+		#size-cells = <0>;
 
 		status = "okay";
 
@@ -73,6 +75,160 @@
 		mux-gpios = <&gpio ASPEED_GPIO(A, 6) GPIO_ACTIVE_HIGH>;
 		enable-gpios = <&gpio ASPEED_GPIO(D, 0) GPIO_ACTIVE_HIGH>;
 		trans-gpios = <&gpio ASPEED_GPIO(R, 2) GPIO_ACTIVE_HIGH>;
+
+		cfam@0,0 {
+			reg = <0 0>;
+			#address-cells = <1>;
+			#size-cells = <1>;
+
+			i2c-master@1800 {
+				compatible = "ibm,fsi-i2c-master";
+				reg = <0x1800 0x400>;
+				#address-cells = <1>;
+				#size-cells = <0>;
+
+				port@0 {
+					port = <0>;
+				};
+
+				port@1 {
+					port = <1>;
+				};
+
+				port@2 {
+					port = <2>;
+				};
+
+				port@3 {
+					port = <3>;
+				};
+
+				port@4 {
+					port = <4>;
+				};
+
+				port@5 {
+					port = <5>;
+				};
+
+				port@6 {
+					port = <6>;
+				};
+
+				port@7 {
+					port = <7>;
+				};
+
+				port@8 {
+					port = <8>;
+				};
+
+				port@9 {
+					port = <9>;
+				};
+
+				port@10 {
+					port = <10>;
+				};
+
+				port@11 {
+					port = <11>;
+				};
+
+				port@12 {
+					port = <12>;
+				};
+
+				port@13 {
+					port = <13>;
+				};
+
+				port@14 {
+					port = <14>;
+				};
+			};
+
+			hub@3400 {
+				compatible = "fsi-master-hub";
+				reg = <0x3400 0x400>;
+				#address-cells = <2>;
+				#size-cells = <0>;
+
+				cfam@1,0 {
+					reg = <1 0>;
+					#address-cells = <1>;
+					#size-cells = <1>;
+
+					i2c-master@1800 {
+						compatible = "ibm,fsi-i2c-master";
+						reg = <0x1800 0x400>;
+						#address-cells = <1>;
+						#size-cells = <0>;
+
+						port@0 {
+							port = <0>;
+						};
+
+						port@1 {
+							port = <1>;
+						};
+
+						port@2 {
+							port = <2>;
+						};
+
+						port@3 {
+							port = <3>;
+						};
+
+						port@4 {
+							port = <4>;
+						};
+
+						port@5 {
+							port = <5>;
+						};
+
+						port@6 {
+							port = <6>;
+						};
+
+						port@7 {
+							port = <7>;
+						};
+
+						port@8 {
+							port = <8>;
+						};
+
+						port@9 {
+							port = <9>;
+						};
+
+						port@10 {
+							port = <10>;
+						};
+
+						port@11 {
+							port = <11>;
+						};
+
+						port@12 {
+							port = <12>;
+						};
+
+						port@13 {
+							port = <13>;
+						};
+
+						port@14 {
+							port = <14>;
+						};
+					};
+
+				};
+			};
+		};
 	};
 
 	iio-hwmon {
Signature · 1 line
1.8.3.1
#9 lore raw
In reply to Eddie James
Quoted 23 lines · Eddie James

From: "Edward A. James" <[email protected]>

This patch series adds an I2C bus algorithm to drive the I2C master located on POWER CPUs. The master is accessed over FSI bus from the service processor.

The driver creates one chardev entry for each port off an I2C master by registering one I2C adapter for port. This implementation requires locking of the master in the driver to prevent transfers running into each other. The master registers are shared between ports.

This patchset also adds the latest in FSI dt bindings from Jeremy Kerr. This describes the entire FSI tree structure (master -> slaves -> engines -> engine data (such as i2c master ports)). These bindings are required for the I2C driver in order to enumerate the ports off each I2C master.

Edward A. James (6):
drivers: i2c: Add FSI-attached I2C master algorithm
drivers: i2c: Add port structure to FSI algorithm
drivers: i2c: Add transfer implementation for FSI algorithm
drivers: i2c: Add I2C master locking to FSI algorithm
drivers: i2c: Add bus recovery for FSI algorithm
dts: aspeed: witherspoon: Add I2C master under FSI masters

Quoted 8 lines
  arch/arm/boot/dts/aspeed-bmc-opp-witherspoon.dts | 156 ++++++
  drivers/Makefile                                 |   2 +-
  drivers/i2c/busses/Kconfig                       |  11 +
  drivers/i2c/busses/Makefile                      |   1 +
  drivers/i2c/busses/i2c-fsi.c                     | 681 +++++++++++++++++++++++
  5 files changed, 850 insertions(+), 1 deletion(-)

create mode 100644 drivers/i2c/busses/i2c-fsi.c

I also have a new debug tool for this driver. The old iicmaster doesn't work, and i2cset/i2cget don't work either, mainly because they don't support 10 bit addresses.

https://github.com/eddiejames/iicmaster

Eddie