drivers: i2c: FSI-attached I2C master algorithm
Subsystems (4)
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
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
@@ -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 +275 -0
275 lines not on this page · raw message
Signature · 1 line
1.8.3.1
Joel Stanley
maintainerRe: [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.
Signed-off-by Edward A. James <[email protected]>--- 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.cdrivers/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. + endmenudrivers/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) := -DDEBUGdrivers/i2c/busses/i2c-fsi.c new file +11
-0@@ -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
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.
Signed-off-by Edward A. James <[email protected]>--- 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.cdrivers/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. + endmenudrivers/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) := -DDEBUGdrivers/i2c/busses/i2c-fsi.c new file +15
-0@@ -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 0x7Is 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
PATCH 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 | 112 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 110 insertions(+), 2 deletions(-)
drivers/i2c/busses/i2c-fsi.c +110 -2
@@ -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
PATCH 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
@@ -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
PATCH 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 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
@@ -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
PATCH 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
@@ -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
PATCH 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
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