drivers: i2c: FSI-attached I2C master algorithm
Subsystems (4)
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
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
@@ -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
@@ -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
@@ -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
PATCH v2 2/6 drivers: i2c: Add port structure to FSI algorithm
In reply to Eddie JamesFrom: "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
@@ -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
PATCH v2 3/6 drivers: i2c: Add transfer implementation for FSI algorithm
In reply to Eddie JamesFrom: "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
@@ -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
Joel Stanley
maintainerRe: [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.
Signed-off-by Edward A. James <[email protected]>--- drivers/i2c/busses/i2c-fsi.c | 193 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 191 insertions(+), 2 deletions(-)drivers/i2c/busses/i2c-fsi.c +103
-0@@ -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
PATCH v2 4/6 drivers: i2c: Add I2C master locking to FSI algorithm
In reply to Eddie JamesFrom: "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
@@ -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
PATCH v2 5/6 drivers: i2c: Add bus recovery for FSI algorithm
In reply to Eddie JamesFrom: "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
@@ -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
PATCH v2 6/6 dts: aspeed: witherspoon: Add I2C master under FSI masters
In reply to Eddie JamesFrom: "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
@@ -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
Joel Stanley
maintainerQuoted 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 mastersarch/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