Skip to contentSkip to conversation

drivers: i2c: FSI-attached I2C master algorithm

PATCH In review Eddie James 2 participants 9 messages May 10, 2017 – Jun 1, 2017

Subsystems (4)
lore
Share & tools
lore
#0 lore raw

v2 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                     | 654 +++++++++++++++++++++++
 5 files changed, 823 insertions(+), 1 deletion(-)
 create mode 100644 drivers/i2c/busses/i2c-fsi.c
Signature · 1 line
1.8.3.1
#1 lore raw

PATCH v2 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 | 245 +++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 258 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 +245 -0

245 lines not on this page · raw message

Signature · 1 line
1.8.3.1
#2 lore raw

PATCH v2 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 | 113 ++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 112 insertions(+), 1 deletion(-)
drivers/i2c/busses/i2c-fsi.c +112 -1
index 3c1087d..cdebc99 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
 
 /* i2c registers */
@@ -131,9 +132,21 @@
 
 struct fsi_i2c_master {
 	struct fsi_device	*fsi;
+	int			idx;
 	u8			fifo_size;
+	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)
 {
@@ -188,9 +201,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;
 }
 
@@ -207,13 +255,59 @@ static u32 fsi_i2c_functionality(struct i2c_adapter *adap)
 static int fsi_i2c_probe(struct device *dev)
 {
 	struct fsi_i2c_master *i2c;
-	int rc;
+	struct fsi_i2c_port *port;
+	struct device_node *np;
+	int rc, idx;
+	u32 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);
+	ida_init(&i2c->ida);
+	INIT_LIST_HEAD(&i2c->ports);
+
+	if (dev->of_node) {
+		/* 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);
 	if (rc)
@@ -224,6 +318,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 }
@@ -235,6 +345,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
#3 lore raw

PATCH v2 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 cdebc99..f690b16 100644
@@ -143,6 +143,7 @@ struct fsi_i2c_port {
 	struct i2c_adapter	adapter;
 	struct fsi_i2c_master	*master;
 	u16			port;
+	u16			xfrd;
 };
 
 static DEFINE_IDA(fsi_i2c_ida);
@@ -229,17 +230,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
maintainer
#4 lore raw

Re: [PATCH linux dev-4.10 v2 3/6] drivers: i2c: Add transfer implementation for FSI algorithm

Quoted 128 lines · 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 +103 -0
index cdebc99..f690b16 100644
@@ -143,6 +143,7 @@ struct fsi_i2c_port {
        struct i2c_adapter      adapter;
        struct fsi_i2c_master   *master;
        u16                     port;
+       u16                     xfrd;
 };
 
 static DEFINE_IDA(fsi_i2c_ida);
@@ -229,17 +230,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;

drivers/i2c/busses/i2c-fsi.c static int fsi_i2c_set_port(struct fsi_i2c_port *port) · line 344

Quoted 10 lines
drivers/i2c/busses/i2c-fsi.c +10 -0
+       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;

Why EFAULT?

Quoted 13 lines
drivers/i2c/busses/i2c-fsi.c +13 -0
+
+               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;

drivers/i2c/busses/i2c-fsi.c static int fsi_i2c_set_port(struct fsi_i2c_port *port) · line 367

Quoted 10 lines
drivers/i2c/busses/i2c-fsi.c +10 -0
+       }
+
+       if (status & I2C_STAT_CMD_COMP) {
+               if (port->xfrd < msg->len)
+                       rc = -ENODATA;
+               else
+                       rc = msg->len;
+       }
+
+       return rc;

rc is returned uninitialised here when all of the if () tests are false.

Quoted 20 lines
drivers/i2c/busses/i2c-fsi.c +20 -0
+}
+
+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;
+

drivers/i2c/busses/i2c-fsi.c static int fsi_i2c_set_port(struct fsi_i2c_port *port) · line 397

Quoted 10 lines
drivers/i2c/busses/i2c-fsi.c +10 -0
+                       /* 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);

This is an open coded msleep(2). Just use msleep.

drivers/i2c/busses/i2c-fsi.c static int fsi_i2c_set_port(struct fsi_i2c_port *port) · line 399

Quoted 2 lines
drivers/i2c/busses/i2c-fsi.c +2 -0
+               timeout = (timeout < local_timeout) ? 0 :
+                       timeout - local_timeout;

And then this could be (untested):

end = timeout + jiffies;

do {

} while (time_is_before_jiffies(end));

Quoted 40 lines
drivers/i2c/busses/i2c-fsi.c +23 -3
+       } 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)
--

1.8.3.1

#5 lore raw

PATCH v2 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 changing 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 f690b16..d757aee 100644
@@ -136,6 +136,8 @@ struct fsi_i2c_master {
 	u8			fifo_size;
 	struct list_head	ports;
 	struct ida		ida;
+	wait_queue_head_t	wait;
+	struct semaphore	lock;
 };
 
 struct fsi_i2c_port {
@@ -171,6 +173,29 @@ static int fsi_i2c_write_reg(struct fsi_device *fsi, unsigned int reg,
 	return fsi_device_write(fsi, reg, &raw_data, sizeof(raw_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 int fsi_i2c_dev_init(struct fsi_i2c_master *i2c)
 {
 	int rc;
@@ -410,25 +435,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)
@@ -453,6 +484,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);
 	ida_init(&i2c->ida);
Signature · 1 line
1.8.3.1
#6 lore raw

PATCH v2 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 d757aee..4689479 100644
@@ -467,6 +467,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,
@@ -514,6 +588,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
#7 lore raw

PATCH v2 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
maintainer
#8 lore raw
In reply to Eddie James
Quoted 10 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.

Quoted 5 lines

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.

I have added this series to dev-4.10. Note that under our kernel rules, this means you must be in the process of submitting them upstream :)

I think you can send them to the i2c maintainer with a mention in your cover letter that it depends on the unmerged FSI series and that you'd like to kick off the review process for your driver.

Cheers,

Joel

Quoted 19 lines

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                     | 654 +++++++++++++++++++++++
 5 files changed, 823 insertions(+), 1 deletion(-)
 create mode 100644 drivers/i2c/busses/i2c-fsi.c

--
1.8.3.1