kernel: Patch in i2c, sbefifo, and occ drivers
Change-Id: I845752ca9f64f42f1fc46e14cac90a48f7195ca3
Signed-off-by: Edward A. James <eajames@us.ibm.com>
diff --git a/common/recipes-kernel/linux/linux-obmc.inc b/common/recipes-kernel/linux/linux-obmc.inc
index 78da469..92aaef2 100644
--- a/common/recipes-kernel/linux/linux-obmc.inc
+++ b/common/recipes-kernel/linux/linux-obmc.inc
@@ -10,6 +10,16 @@
SRC_URI = "${KSRC}"
SRC_URI += "file://phosphor-gpio-keys.scc"
SRC_URI += "file://phosphor-gpio-keys.cfg"
+SRC_URI += "file://0001-fsi-Match-fsi-slaves-engines-to-available-device-tre.patch"
+SRC_URI += "file://linux-dev-4.10-v2-1-6-drivers-i2c-Add-FSI-attached-I2C-master-algorithm.patch"
+SRC_URI += "file://linux-dev-4.10-v2-2-6-drivers-i2c-Add-port-structure-to-FSI-algorithm.patch"
+SRC_URI += "file://linux-dev-4.10-v2-3-6-drivers-i2c-Add-transfer-implementation-for-FSI-algorithm.patch"
+SRC_URI += "file://linux-dev-4.10-v2-4-6-drivers-i2c-Add-I2C-master-locking-to-FSI-algorithm.patch"
+SRC_URI += "file://linux-dev-4.10-v2-5-6-drivers-i2c-Add-bus-recovery-for-FSI-algorithm.patch"
+SRC_URI += "file://linux-dev-4.10-drivers-fsi-Add-FSI-SBEFIFO-driver.patch"
+SRC_URI += "file://linux-dev-4.10-1-3-drivers-fsi-sbefifo-Add-in-kernel-API.patch"
+SRC_URI += "file://linux-dev-4.10-2-3-drivers-fsi-sbefifo-Add-OCC-driver.patch"
+SRC_URI += "file://0001-arm-dts-aspeed-Add-FSI-devices.patch"
LINUX_VERSION_EXTENSION ?= "-${SRCREV}"
diff --git a/common/recipes-kernel/linux/linux-obmc/0001-arm-dts-aspeed-Add-FSI-devices.patch b/common/recipes-kernel/linux/linux-obmc/0001-arm-dts-aspeed-Add-FSI-devices.patch
new file mode 100644
index 0000000..3b49685
--- /dev/null
+++ b/common/recipes-kernel/linux/linux-obmc/0001-arm-dts-aspeed-Add-FSI-devices.patch
@@ -0,0 +1,507 @@
+From e45cd4677a36eb759537f59c5f5bcf46b91728b3 Mon Sep 17 00:00:00 2001
+From: "Edward A. James" <eajames@us.ibm.com>
+Date: Mon, 15 May 2017 11:02:48 -0500
+Subject: [PATCH linux dev-4.10] arm: dts: aspeed: Add FSI devices
+
+using Jeremy's new bindings
+
+Signed-off-by: Edward A. James <eajames@us.ibm.com>
+---
+ arch/arm/boot/dts/aspeed-bmc-opp-palmetto.dts | 75 ++++++++++
+ arch/arm/boot/dts/aspeed-bmc-opp-witherspoon.dts | 181 +++++++++++++++++++++++
+ arch/arm/boot/dts/aspeed-bmc-opp-zaius.dts | 181 +++++++++++++++++++++++
+ 3 files changed, 437 insertions(+)
+
+diff --git a/arch/arm/boot/dts/aspeed-bmc-opp-palmetto.dts b/arch/arm/boot/dts/aspeed-bmc-opp-palmetto.dts
+index b4faa1d..6f23ce3 100644
+--- a/arch/arm/boot/dts/aspeed-bmc-opp-palmetto.dts
++++ b/arch/arm/boot/dts/aspeed-bmc-opp-palmetto.dts
+@@ -85,12 +85,87 @@
+
+ gpio-fsi {
+ compatible = "fsi-master-gpio", "fsi-master";
++ #address-cells = <2>;
++ #size-cells = <0>;
+
+ clock-gpios = <&gpio ASPEED_GPIO(A, 4) GPIO_ACTIVE_HIGH>;
+ data-gpios = <&gpio ASPEED_GPIO(A, 5) GPIO_ACTIVE_HIGH>;
+ 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(H, 6) 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>;
++ };
++ };
++ };
+ };
+ };
+
+diff --git a/arch/arm/boot/dts/aspeed-bmc-opp-witherspoon.dts b/arch/arm/boot/dts/aspeed-bmc-opp-witherspoon.dts
+index f20aaf4..5d83a76 100644
+--- a/arch/arm/boot/dts/aspeed-bmc-opp-witherspoon.dts
++++ b/arch/arm/boot/dts/aspeed-bmc-opp-witherspoon.dts
+@@ -65,6 +65,8 @@
+
+ gpio-fsi {
+ compatible = "fsi-master-gpio", "fsi-master";
++ #address-cells = <2>;
++ #size-cells = <0>;
+
+ status = "okay";
+
+@@ -73,6 +75,185 @@
+ 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>;
++ };
++ };
++
++ sbefifo@2400 {
++ compatible = "ibm,p9-sbefifo";
++ reg = <0x2400 0x400>;
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ occ@1 {
++ compatible = "ibm,p9-occ";
++ reg = <1>;
++ };
++ };
++
++ 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>;
++ };
++ };
++
++ sbefifo@2400 {
++ compatible = "ibm,p9-sbefifo";
++ reg = <0x2400 0x400>;
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ occ@2 {
++ compatible =
++ "ibm,p9-occ";
++ reg = <2>;
++ };
++ };
++ };
++ };
++ };
+ };
+
+ iio-hwmon {
+diff --git a/arch/arm/boot/dts/aspeed-bmc-opp-zaius.dts b/arch/arm/boot/dts/aspeed-bmc-opp-zaius.dts
+index b5c4c0f..6f36e56 100644
+--- a/arch/arm/boot/dts/aspeed-bmc-opp-zaius.dts
++++ b/arch/arm/boot/dts/aspeed-bmc-opp-zaius.dts
+@@ -77,6 +77,8 @@
+
+ gpio-fsi {
+ compatible = "fsi-master-gpio", "fsi-master";
++ #address-cells = <2>;
++ #size-cells = <0>;
+
+ status = "okay";
+
+@@ -85,6 +87,185 @@
+ trans-gpios = <&gpio ASPEED_GPIO(O, 6) GPIO_ACTIVE_HIGH>;
+ enable-gpios = <&gpio ASPEED_GPIO(D, 0) GPIO_ACTIVE_HIGH>;
+ mux-gpios = <&gpio ASPEED_GPIO(P, 6) 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>;
++ };
++ };
++
++ sbefifo@2400 {
++ compatible = "ibm,p9-sbefifo";
++ reg = <0x2400 0x400>;
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ occ@1 {
++ compatible = "ibm,p9-occ";
++ reg = <1>;
++ };
++ };
++
++ 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>;
++ };
++ };
++
++ sbefifo@2400 {
++ compatible = "ibm,p9-sbefifo";
++ reg = <0x2400 0x400>;
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ occ@2 {
++ compatible =
++ "ibm,p9-occ";
++ reg = <2>;
++ };
++ };
++ };
++ };
++ };
+ };
+
+ iio-hwmon {
+--
+1.8.3.1
+
diff --git a/common/recipes-kernel/linux/linux-obmc/0001-fsi-Match-fsi-slaves-engines-to-available-device-tre.patch b/common/recipes-kernel/linux/linux-obmc/0001-fsi-Match-fsi-slaves-engines-to-available-device-tre.patch
new file mode 100644
index 0000000..6fdfe93
--- /dev/null
+++ b/common/recipes-kernel/linux/linux-obmc/0001-fsi-Match-fsi-slaves-engines-to-available-device-tre.patch
@@ -0,0 +1,236 @@
+From 8f10daa0cc99ce286b9a2f249f1fa0a245d4fc03 Mon Sep 17 00:00:00 2001
+From: "Edward A. James" <eajames@us.ibm.com>
+Date: Wed, 17 May 2017 09:46:19 -0500
+Subject: [PATCH] fsi: Match fsi slaves & engines to available device tree
+ nodes
+
+This change populates device tree nodes for scanned FSI slaves and
+engines. If the master populates ->of_node of the FSI master device,
+we'll look for matching slaves, and under those slaves we'll look for
+matching engines.
+
+This means that FSI drivers will have their ->of_node pointer populated
+if there's a corresponding DT node, which they can use for further
+device discover.
+
+Presence of device tree nodes is optional, and only required for
+fsi device drivers that need extra properties, or subordinate devices,
+to be enumerated.
+
+Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
+Signed-off-by: Edward A. James <eajames@us.ibm.com>
+---
+ drivers/fsi/fsi-core.c | 99 +++++++++++++++++++++++++++++++++++++++++++
+ drivers/fsi/fsi-master-gpio.c | 4 ++
+ drivers/fsi/fsi-master-hub.c | 4 ++
+ 3 files changed, 107 insertions(+)
+
+diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c
+index 7ca5b74..71e3af9 100644
+--- a/drivers/fsi/fsi-core.c
++++ b/drivers/fsi/fsi-core.c
+@@ -17,6 +17,7 @@
+ #include <linux/fsi.h>
+ #include <linux/idr.h>
+ #include <linux/module.h>
++#include <linux/of.h>
+ #include <linux/slab.h>
+
+ #include "fsi-master.h"
+@@ -125,6 +126,7 @@ static void fsi_device_release(struct device *_device)
+ {
+ struct fsi_device *device = to_fsi_dev(_device);
+
++ of_node_put(device->dev.of_node);
+ kfree(device);
+ }
+
+@@ -337,6 +339,57 @@ extern void fsi_slave_release_range(struct fsi_slave *slave,
+ {
+ }
+
++static bool fsi_device_node_matches(struct device *dev, struct device_node *np,
++ uint32_t addr, uint32_t size)
++{
++ unsigned int len, na, ns;
++ const __be32 *prop;
++ uint32_t psize;
++
++ na = of_n_addr_cells(np);
++ ns = of_n_size_cells(np);
++
++ if (na != 1 || ns != 1)
++ return false;
++
++ prop = of_get_property(np, "reg", &len);
++ if (!prop || len != 8)
++ return false;
++
++ if (of_read_number(prop, 1) != addr)
++ return false;
++
++ psize = of_read_number(prop + 1, 1);
++ if (psize != size) {
++ dev_warn(dev,
++ "node %s matches probed address, but not size (got 0x%x, expected 0x%x)",
++ of_node_full_name(np), psize, size);
++ }
++
++ return true;
++}
++
++/* Find a matching node for the slave engine at @address, using @size bytes
++ * of space. Returns NULL if not found, or a matching node with refcount
++ * already incremented.
++ */
++static struct device_node *fsi_device_find_of_node(struct fsi_device *dev)
++{
++ struct device_node *parent, *np;
++
++ parent = dev_of_node(&dev->slave->dev);
++ if (!parent)
++ return NULL;
++
++ for_each_child_of_node(parent, np) {
++ if (fsi_device_node_matches(&dev->dev, np,
++ dev->addr, dev->size))
++ return np;
++ }
++
++ return NULL;
++}
++
+ static int fsi_slave_scan(struct fsi_slave *slave)
+ {
+ uint32_t engine_addr;
+@@ -405,6 +458,7 @@ static int fsi_slave_scan(struct fsi_slave *slave)
+ dev_set_name(&dev->dev, "%02x:%02x:%02x:%02x",
+ slave->master->idx, slave->link,
+ slave->id, i - 2);
++ dev->dev.of_node = fsi_device_find_of_node(dev);
+
+ rc = device_register(&dev->dev);
+ if (rc) {
+@@ -561,9 +615,53 @@ static void fsi_slave_release(struct device *dev)
+ {
+ struct fsi_slave *slave = to_fsi_slave(dev);
+
++ of_node_put(dev->of_node);
+ kfree(slave);
+ }
+
++static bool fsi_slave_node_matches(struct device_node *np,
++ int link, uint8_t id)
++{
++ unsigned int len, na, ns;
++ const __be32 *prop;
++
++ na = of_n_addr_cells(np);
++ ns = of_n_size_cells(np);
++
++ /* Ensure we have the correct format for addresses and sizes in
++ * reg properties
++ */
++ if (na != 2 || ns != 0)
++ return false;
++
++ prop = of_get_property(np, "reg", &len);
++ if (!prop || len != 8)
++ return false;
++
++ return (of_read_number(prop, 1) == link) &&
++ (of_read_number(prop + 1, 1) == id);
++}
++
++/* Find a matching node for the slave at (link, id). Returns NULL if none
++ * found, or a matching node with refcount already incremented.
++ */
++static struct device_node *fsi_slave_find_of_node(struct fsi_master *master,
++ int link, uint8_t id)
++{
++ struct device_node *parent, *np;
++
++ parent = dev_of_node(&master->dev);
++ if (!parent)
++ return NULL;
++
++ for_each_child_of_node(parent, np) {
++ if (fsi_slave_node_matches(np, link, id))
++ return np;
++ }
++
++ return NULL;
++}
++
+ static int fsi_slave_init(struct fsi_master *master, int link, uint8_t id)
+ {
+ uint32_t chip_id, llmode;
+@@ -626,6 +724,7 @@ static int fsi_slave_init(struct fsi_master *master, int link, uint8_t id)
+
+ slave->master = master;
+ slave->dev.parent = &master->dev;
++ slave->dev.of_node = fsi_slave_find_of_node(master, link, id);
+ slave->dev.release = fsi_slave_release;
+ slave->link = link;
+ slave->id = id;
+diff --git a/drivers/fsi/fsi-master-gpio.c b/drivers/fsi/fsi-master-gpio.c
+index ef209ef..2a6a812 100644
+--- a/drivers/fsi/fsi-master-gpio.c
++++ b/drivers/fsi/fsi-master-gpio.c
+@@ -5,6 +5,7 @@
+ #include <linux/platform_device.h>
+ #include <linux/gpio/consumer.h>
+ #include <linux/module.h>
++#include <linux/of.h>
+ #include <linux/delay.h>
+ #include <linux/fsi.h>
+ #include <linux/device.h>
+@@ -529,6 +530,7 @@ static int fsi_master_gpio_probe(struct platform_device *pdev)
+
+ master->dev = &pdev->dev;
+ master->master.dev.parent = master->dev;
++ master->master.dev.of_node = of_node_get(dev_of_node(master->dev));
+ master->master.dev.release = fsi_master_gpio_release;
+
+ gpio = devm_gpiod_get(&pdev->dev, "clock", 0);
+@@ -598,6 +600,8 @@ static int fsi_master_gpio_remove(struct platform_device *pdev)
+ devm_gpiod_put(&pdev->dev, master->gpio_mux);
+ fsi_master_unregister(&master->master);
+
++ of_node_put(master->master.dev.of_node);
++
+ return 0;
+ }
+
+diff --git a/drivers/fsi/fsi-master-hub.c b/drivers/fsi/fsi-master-hub.c
+index 133b9bf..3223a67 100644
+--- a/drivers/fsi/fsi-master-hub.c
++++ b/drivers/fsi/fsi-master-hub.c
+@@ -16,6 +16,7 @@
+ #include <linux/delay.h>
+ #include <linux/fsi.h>
+ #include <linux/module.h>
++#include <linux/of.h>
+ #include <linux/slab.h>
+
+ #include "fsi-master.h"
+@@ -274,6 +275,7 @@ static int hub_master_probe(struct device *dev)
+
+ hub->master.dev.parent = dev;
+ hub->master.dev.release = hub_master_release;
++ hub->master.dev.of_node = of_node_get(dev_of_node(dev));
+
+ hub->master.n_links = links;
+ hub->master.read = hub_master_read;
+@@ -302,6 +304,8 @@ static int hub_master_remove(struct device *dev)
+
+ fsi_master_unregister(&hub->master);
+ fsi_slave_release_range(hub->upstream->slave, hub->addr, hub->size);
++ of_node_put(hub->master.dev.of_node);
++
+ return 0;
+ }
+
+--
+1.8.3.1
+
diff --git a/common/recipes-kernel/linux/linux-obmc/linux-dev-4.10-1-3-drivers-fsi-sbefifo-Add-in-kernel-API.patch b/common/recipes-kernel/linux/linux-obmc/linux-dev-4.10-1-3-drivers-fsi-sbefifo-Add-in-kernel-API.patch
new file mode 100644
index 0000000..70a11f2
--- /dev/null
+++ b/common/recipes-kernel/linux/linux-obmc/linux-dev-4.10-1-3-drivers-fsi-sbefifo-Add-in-kernel-API.patch
@@ -0,0 +1,338 @@
+From patchwork Fri May 12 19:38:18 2017
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: [linux,dev-4.10,1/3] drivers: fsi: sbefifo: Add in-kernel API
+From: eajames@linux.vnet.ibm.com
+X-Patchwork-Id: 761836
+Message-Id: <1494617900-32369-2-git-send-email-eajames@linux.vnet.ibm.com>
+To: openbmc@lists.ozlabs.org
+Cc: "Edward A. James" <eajames@us.ibm.com>, bradleyb@fuzziesquirrel.com,
+ cbostic@linux.vnet.ibm.com
+Date: Fri, 12 May 2017 14:38:18 -0500
+
+From: "Edward A. James" <eajames@us.ibm.com>
+
+Add exported functions to the SBEFIFO driver to open/write/read/close
+from within the kernel.
+
+Signed-off-by: Edward A. James <eajames@us.ibm.com>
+---
+ drivers/fsi/fsi-sbefifo.c | 161 +++++++++++++++++++++++++++++++++++---------
+ include/linux/fsi-sbefifo.h | 30 +++++++++
+ 2 files changed, 161 insertions(+), 30 deletions(-)
+ create mode 100644 include/linux/fsi-sbefifo.h
+
+diff --git a/drivers/fsi/fsi-sbefifo.c b/drivers/fsi/fsi-sbefifo.c
+index b49aec2..56e6331 100644
+--- a/drivers/fsi/fsi-sbefifo.c
++++ b/drivers/fsi/fsi-sbefifo.c
+@@ -15,9 +15,12 @@
+ #include <linux/errno.h>
+ #include <linux/idr.h>
+ #include <linux/fsi.h>
++#include <linux/fsi-sbefifo.h>
+ #include <linux/list.h>
+ #include <linux/miscdevice.h>
+ #include <linux/module.h>
++#include <linux/of.h>
++#include <linux/of_platform.h>
+ #include <linux/poll.h>
+ #include <linux/sched.h>
+ #include <linux/slab.h>
+@@ -82,6 +85,7 @@ struct sbefifo_client {
+ struct list_head xfrs;
+ struct sbefifo *dev;
+ struct kref kref;
++ unsigned long f_flags;
+ };
+
+ static struct list_head sbefifo_fifos;
+@@ -506,6 +510,7 @@ static int sbefifo_open(struct inode *inode, struct file *file)
+ return -ENOMEM;
+
+ file->private_data = client;
++ client->f_flags = file->f_flags;
+
+ return 0;
+ }
+@@ -530,24 +535,18 @@ static unsigned int sbefifo_poll(struct file *file, poll_table *wait)
+ return mask;
+ }
+
+-static ssize_t sbefifo_read(struct file *file, char __user *buf,
+- size_t len, loff_t *offset)
++static ssize_t sbefifo_read_common(struct sbefifo_client *client,
++ char __user *ubuf, char *kbuf, size_t len)
+ {
+- struct sbefifo_client *client = file->private_data;
+ struct sbefifo *sbefifo = client->dev;
+ struct sbefifo_xfr *xfr;
+- ssize_t ret = 0;
+ size_t n;
+-
+- WARN_ON(*offset);
+-
+- if (!access_ok(VERIFY_WRITE, buf, len))
+- return -EFAULT;
++ ssize_t ret = 0;
+
+ if ((len >> 2) << 2 != len)
+ return -EINVAL;
+
+- if ((file->f_flags & O_NONBLOCK) && !sbefifo_xfr_rsp_pending(client))
++ if ((client->f_flags & O_NONBLOCK) && !sbefifo_xfr_rsp_pending(client))
+ return -EAGAIN;
+
+ sbefifo_get_client(client);
+@@ -566,10 +565,13 @@ static ssize_t sbefifo_read(struct file *file, char __user *buf,
+
+ n = min_t(size_t, n, len);
+
+- if (copy_to_user(buf, READ_ONCE(client->rbuf.rpos), n)) {
+- sbefifo_put_client(client);
+- return -EFAULT;
+- }
++ if (ubuf) {
++ if (copy_to_user(ubuf, READ_ONCE(client->rbuf.rpos), n)) {
++ sbefifo_put_client(client);
++ return -EFAULT;
++ }
++ } else
++ memcpy(kbuf, READ_ONCE(client->rbuf.rpos), n);
+
+ if (sbefifo_buf_readnb(&client->rbuf, n)) {
+ xfr = sbefifo_client_next_xfr(client);
+@@ -592,20 +594,28 @@ static ssize_t sbefifo_read(struct file *file, char __user *buf,
+ return n;
+ }
+
+-static ssize_t sbefifo_write(struct file *file, const char __user *buf,
++static ssize_t sbefifo_read(struct file *file, char __user *buf,
+ size_t len, loff_t *offset)
+ {
+ struct sbefifo_client *client = file->private_data;
+- struct sbefifo *sbefifo = client->dev;
+- struct sbefifo_xfr *xfr;
+- ssize_t ret = 0;
+- size_t n;
+
+ WARN_ON(*offset);
+
+- if (!access_ok(VERIFY_READ, buf, len))
++ if (!access_ok(VERIFY_WRITE, buf, len))
+ return -EFAULT;
+
++ return sbefifo_read_common(client, buf, NULL, len);
++}
++
++static ssize_t sbefifo_write_common(struct sbefifo_client *client,
++ const char __user *ubuf, const char *kbuf,
++ size_t len)
++{
++ struct sbefifo *sbefifo = client->dev;
++ struct sbefifo_xfr *xfr;
++ ssize_t ret = 0;
++ size_t n;
++
+ if ((len >> 2) << 2 != len)
+ return -EINVAL;
+
+@@ -617,7 +627,7 @@ static ssize_t sbefifo_write(struct file *file, const char __user *buf,
+ spin_lock_irq(&sbefifo->lock);
+ xfr = sbefifo_next_xfr(sbefifo);
+
+- if ((file->f_flags & O_NONBLOCK) && xfr && n < len) {
++ if ((client->f_flags & O_NONBLOCK) && xfr && n < len) {
+ spin_unlock_irq(&sbefifo->lock);
+ return -EAGAIN;
+ }
+@@ -657,18 +667,25 @@ static ssize_t sbefifo_write(struct file *file, const char __user *buf,
+
+ n = min_t(size_t, n, len);
+
+- if (copy_from_user(READ_ONCE(client->wbuf.wpos), buf, n)) {
+- set_bit(SBEFIFO_XFR_CANCEL, &xfr->flags);
+- sbefifo_get(sbefifo);
+- if (mod_timer(&sbefifo->poll_timer, jiffies))
+- sbefifo_put(sbefifo);
+- sbefifo_put_client(client);
+- return -EFAULT;
++ if (ubuf) {
++ if (copy_from_user(READ_ONCE(client->wbuf.wpos), ubuf,
++ n)) {
++ set_bit(SBEFIFO_XFR_CANCEL, &xfr->flags);
++ sbefifo_get(sbefifo);
++ if (mod_timer(&sbefifo->poll_timer, jiffies))
++ sbefifo_put(sbefifo);
++ sbefifo_put_client(client);
++ return -EFAULT;
++ }
++
++ ubuf += n;
++ } else {
++ memcpy(READ_ONCE(client->wbuf.wpos), kbuf, n);
++ kbuf += n;
+ }
+
+ sbefifo_buf_wrotenb(&client->wbuf, n);
+ len -= n;
+- buf += n;
+ ret += n;
+
+ /*
+@@ -685,6 +702,19 @@ static ssize_t sbefifo_write(struct file *file, const char __user *buf,
+ return ret;
+ }
+
++static ssize_t sbefifo_write(struct file *file, const char __user *buf,
++ size_t len, loff_t *offset)
++{
++ struct sbefifo_client *client = file->private_data;
++
++ WARN_ON(*offset);
++
++ if (!access_ok(VERIFY_READ, buf, len))
++ return -EFAULT;
++
++ return sbefifo_write_common(client, buf, NULL, len);
++}
++
+ static int sbefifo_release(struct inode *inode, struct file *file)
+ {
+ struct sbefifo_client *client = file->private_data;
+@@ -704,12 +734,68 @@ static int sbefifo_release(struct inode *inode, struct file *file)
+ .release = sbefifo_release,
+ };
+
++struct sbefifo_client *sbefifo_drv_open(struct device *dev,
++ unsigned long flags)
++{
++ struct sbefifo_client *client = NULL;
++ struct sbefifo *sbefifo;
++ struct fsi_device *fsi_dev = to_fsi_dev(dev);
++
++ list_for_each_entry(sbefifo, &sbefifo_fifos, link) {
++ if (sbefifo->fsi_dev != fsi_dev)
++ continue;
++
++ client = sbefifo_new_client(sbefifo);
++ if (client)
++ client->f_flags = flags;
++ }
++
++ return client;
++}
++EXPORT_SYMBOL_GPL(sbefifo_drv_open);
++
++int sbefifo_drv_read(struct sbefifo_client *client, char *buf, size_t len)
++{
++ return sbefifo_read_common(client, NULL, buf, len);
++}
++EXPORT_SYMBOL_GPL(sbefifo_drv_read);
++
++int sbefifo_drv_write(struct sbefifo_client *client, const char *buf,
++ size_t len)
++{
++ return sbefifo_write_common(client, NULL, buf, len);
++}
++EXPORT_SYMBOL_GPL(sbefifo_drv_write);
++
++void sbefifo_drv_release(struct sbefifo_client *client)
++{
++ if (!client)
++ return;
++
++ sbefifo_put_client(client);
++}
++EXPORT_SYMBOL_GPL(sbefifo_drv_release);
++
++static int sbefifo_unregister_child(struct device *dev, void *data)
++{
++ struct platform_device *child = to_platform_device(dev);
++
++ of_device_unregister(child);
++ if (dev->of_node)
++ of_node_clear_flag(dev->of_node, OF_POPULATED);
++
++ return 0;
++}
++
+ static int sbefifo_probe(struct device *dev)
+ {
+ struct fsi_device *fsi_dev = to_fsi_dev(dev);
+ struct sbefifo *sbefifo;
++ struct device_node *np;
++ struct platform_device *child;
++ char child_name[32];
+ u32 sts;
+- int ret;
++ int ret, child_idx = 0;
+
+ dev_info(dev, "Found sbefifo device\n");
+ sbefifo = kzalloc(sizeof(*sbefifo), GFP_KERNEL);
+@@ -750,6 +836,18 @@ static int sbefifo_probe(struct device *dev)
+ init_waitqueue_head(&sbefifo->wait);
+ INIT_LIST_HEAD(&sbefifo->xfrs);
+
++ if (dev->of_node) {
++ /* create platform devs for dts child nodes (occ, etc) */
++ for_each_child_of_node(dev->of_node, np) {
++ snprintf(child_name, sizeof(child_name), "%s-dev%d",
++ sbefifo->name, child_idx++);
++ child = of_platform_device_create(np, child_name, dev);
++ if (!child)
++ dev_warn(&sbefifo->fsi_dev->dev,
++ "failed to create child node dev\n");
++ }
++ }
++
+ /* This bit of silicon doesn't offer any interrupts... */
+ setup_timer(&sbefifo->poll_timer, sbefifo_poll_timer,
+ (unsigned long)sbefifo);
+@@ -767,6 +865,9 @@ static int sbefifo_remove(struct device *dev)
+ list_for_each_entry_safe(sbefifo, sbefifo_tmp, &sbefifo_fifos, link) {
+ if (sbefifo->fsi_dev != fsi_dev)
+ continue;
++
++ device_for_each_child(dev, NULL, sbefifo_unregister_child);
++
+ misc_deregister(&sbefifo->mdev);
+ list_del(&sbefifo->link);
+ ida_simple_remove(&sbefifo_ida, sbefifo->idx);
+diff --git a/include/linux/fsi-sbefifo.h b/include/linux/fsi-sbefifo.h
+new file mode 100644
+index 0000000..1b46c63
+--- /dev/null
++++ b/include/linux/fsi-sbefifo.h
+@@ -0,0 +1,30 @@
++/*
++ * SBEFIFO FSI Client device driver
++ *
++ * Copyright (C) IBM Corporation 2017
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERGCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ */
++
++#ifndef __FSI_SBEFIFO_H__
++#define __FSI_SBEFIFO_H__
++
++struct device;
++struct sbefifo_client;
++
++extern struct sbefifo_client *sbefifo_drv_open(struct device *dev,
++ unsigned long flags);
++extern int sbefifo_drv_read(struct sbefifo_client *client, char *buf,
++ size_t len);
++extern int sbefifo_drv_write(struct sbefifo_client *client, const char *buf,
++ size_t len);
++extern void sbefifo_drv_release(struct sbefifo_client *client);
++
++#endif /* __FSI_SBEFIFO_H__ */
diff --git a/common/recipes-kernel/linux/linux-obmc/linux-dev-4.10-2-3-drivers-fsi-sbefifo-Add-OCC-driver.patch b/common/recipes-kernel/linux/linux-obmc/linux-dev-4.10-2-3-drivers-fsi-sbefifo-Add-OCC-driver.patch
new file mode 100644
index 0000000..5c53dad
--- /dev/null
+++ b/common/recipes-kernel/linux/linux-obmc/linux-dev-4.10-2-3-drivers-fsi-sbefifo-Add-OCC-driver.patch
@@ -0,0 +1,694 @@
+From patchwork Fri May 12 19:38:19 2017
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: [linux,dev-4.10,2/3] drivers: fsi: sbefifo: Add OCC driver
+From: eajames@linux.vnet.ibm.com
+X-Patchwork-Id: 761838
+Message-Id: <1494617900-32369-3-git-send-email-eajames@linux.vnet.ibm.com>
+To: openbmc@lists.ozlabs.org
+Cc: "Edward A. James" <eajames@us.ibm.com>, bradleyb@fuzziesquirrel.com,
+ cbostic@linux.vnet.ibm.com
+Date: Fri, 12 May 2017 14:38:19 -0500
+
+From: "Edward A. James" <eajames@us.ibm.com>
+
+This driver provides an atomic communications channel between the OCC on
+the POWER9 processor and a service processor (a BMC). The driver is
+dependent on the FSI SBEIFO driver to get hardware access to the OCC
+SRAM.
+
+The format of the communication is a command followed by a response.
+Since the command and response must be performed atomically, the driver
+will perform this operations asynchronously. In this way, a write
+operation starts the command, and a read will gather the response data
+once it is complete.
+
+Signed-off-by: Edward A. James <eajames@us.ibm.com>
+---
+ drivers/fsi/Kconfig | 9 +
+ drivers/fsi/Makefile | 1 +
+ drivers/fsi/occ.c | 625 +++++++++++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 635 insertions(+)
+ create mode 100644 drivers/fsi/occ.c
+
+diff --git a/drivers/fsi/Kconfig b/drivers/fsi/Kconfig
+index 39527fa..f3d8593 100644
+--- a/drivers/fsi/Kconfig
++++ b/drivers/fsi/Kconfig
+@@ -36,6 +36,15 @@ config FSI_SBEFIFO
+ ---help---
+ This option enables an FSI based SBEFIFO device driver.
+
++if FSI_SBEFIFO
++
++config OCCFIFO
++ tristate "OCC SBEFIFO client device driver"
++ ---help---
++ This option enables an SBEFIFO based OCC device driver.
++
++endif
++
+ endif
+
+ endmenu
+diff --git a/drivers/fsi/Makefile b/drivers/fsi/Makefile
+index 851182e..336d9d5 100644
+--- a/drivers/fsi/Makefile
++++ b/drivers/fsi/Makefile
+@@ -4,3 +4,4 @@ obj-$(CONFIG_FSI_MASTER_HUB) += fsi-master-hub.o
+ obj-$(CONFIG_FSI_MASTER_GPIO) += fsi-master-gpio.o
+ obj-$(CONFIG_FSI_SCOM) += fsi-scom.o
+ obj-$(CONFIG_FSI_SBEFIFO) += fsi-sbefifo.o
++obj-$(CONFIG_OCCFIFO) += occ.o
+diff --git a/drivers/fsi/occ.c b/drivers/fsi/occ.c
+new file mode 100644
+index 0000000..74272c8
+--- /dev/null
++++ b/drivers/fsi/occ.c
+@@ -0,0 +1,625 @@
++/*
++ * Copyright 2017 IBM Corp.
++ *
++ * 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 <asm/unaligned.h>
++#include <linux/device.h>
++#include <linux/err.h>
++#include <linux/fsi-sbefifo.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/miscdevice.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/platform_device.h>
++#include <linux/slab.h>
++#include <linux/uaccess.h>
++#include <linux/wait.h>
++#include <linux/workqueue.h>
++
++#define OCC_SRAM_BYTES 4096
++#define OCC_CMD_DATA_BYTES 4090
++#define OCC_RESP_DATA_BYTES 4089
++
++struct occ {
++ struct device *sbefifo;
++ char name[32];
++ int idx;
++ struct miscdevice mdev;
++ struct list_head xfrs;
++ spinlock_t list_lock;
++ spinlock_t occ_lock;
++ struct work_struct work;
++};
++
++#define to_occ(x) container_of((x), struct occ, mdev)
++
++struct occ_command {
++ u8 seq_no;
++ u8 cmd_type;
++ u16 data_length;
++ u8 data[OCC_CMD_DATA_BYTES];
++ u16 checksum;
++};
++
++struct occ_response {
++ u8 seq_no;
++ u8 cmd_type;
++ u8 return_status;
++ u16 data_length;
++ u8 data[OCC_RESP_DATA_BYTES];
++ u16 checksum;
++};
++
++struct occ_xfr;
++
++enum {
++ CLIENT_NONBLOCKING,
++};
++
++struct occ_client {
++ struct occ *occ;
++ struct occ_xfr *xfr;
++ spinlock_t lock;
++ wait_queue_head_t wait;
++ size_t read_offset;
++ unsigned long flags;
++};
++
++enum {
++ XFR_IN_PROGRESS,
++ XFR_COMPLETE,
++ XFR_CANCELED,
++ XFR_WAITING,
++};
++
++struct occ_xfr {
++ struct list_head link;
++ struct occ_client *client;
++ int rc;
++ u8 buf[OCC_SRAM_BYTES];
++ size_t cmd_data_length;
++ size_t resp_data_length;
++ unsigned long flags;
++};
++
++static struct workqueue_struct *occ_wq;
++
++static DEFINE_IDA(occ_ida);
++
++static void occ_enqueue_xfr(struct occ_xfr *xfr)
++{
++ int empty;
++ struct occ *occ = xfr->client->occ;
++
++ spin_lock_irq(&occ->list_lock);
++ empty = list_empty(&occ->xfrs);
++ list_add_tail(&xfr->link, &occ->xfrs);
++ spin_unlock(&occ->list_lock);
++
++ if (empty)
++ queue_work(occ_wq, &occ->work);
++}
++
++static int occ_open(struct inode *inode, struct file *file)
++{
++ struct occ_client *client;
++ struct miscdevice *mdev = file->private_data;
++ struct occ *occ = to_occ(mdev);
++
++ client = kzalloc(sizeof(*client), GFP_KERNEL);
++ if (!client)
++ return -ENOMEM;
++
++ client->occ = occ;
++ spin_lock_init(&client->lock);
++ init_waitqueue_head(&client->wait);
++
++ if (file->f_flags & O_NONBLOCK)
++ set_bit(CLIENT_NONBLOCKING, &client->flags);
++
++ file->private_data = client;
++
++ return 0;
++}
++
++static ssize_t occ_read(struct file *file, char __user *buf, size_t len,
++ loff_t *offset)
++{
++ int rc;
++ size_t bytes;
++ struct occ_xfr *xfr;
++ struct occ_client *client = file->private_data;
++
++ if (!access_ok(VERIFY_WRITE, buf, len))
++ return -EFAULT;
++
++ if (len > OCC_SRAM_BYTES)
++ return -EINVAL;
++
++ spin_lock_irq(&client->lock);
++ if (!client->xfr) {
++ /* we just finished reading all data, return 0 */
++ if (client->read_offset) {
++ rc = 0;
++ client->read_offset = 0;
++ } else
++ rc = -ENOMSG;
++
++ goto done;
++ }
++
++ xfr = client->xfr;
++
++ if (!test_bit(XFR_COMPLETE, &xfr->flags)) {
++ if (client->flags & CLIENT_NONBLOCKING) {
++ rc = -ERESTARTSYS;
++ goto done;
++ }
++
++ set_bit(XFR_WAITING, &xfr->flags);
++ spin_unlock(&client->lock);
++
++ rc = wait_event_interruptible(client->wait,
++ test_bit(XFR_COMPLETE, &xfr->flags) ||
++ test_bit(XFR_CANCELED, &xfr->flags));
++
++ spin_lock_irq(&client->lock);
++ if (test_bit(XFR_CANCELED, &xfr->flags)) {
++ kfree(xfr);
++ spin_unlock(&client->lock);
++ kfree(client);
++ return -EBADFD;
++ }
++
++ clear_bit(XFR_WAITING, &xfr->flags);
++ if (!test_bit(XFR_COMPLETE, &xfr->flags)) {
++ rc = -EINTR;
++ goto done;
++ }
++ }
++
++ if (xfr->rc) {
++ rc = xfr->rc;
++ goto done;
++ }
++
++ bytes = min(len, xfr->resp_data_length - client->read_offset);
++ if (copy_to_user(buf, &xfr->buf[client->read_offset], bytes)) {
++ rc = -EFAULT;
++ goto done;
++ }
++
++ client->read_offset += bytes;
++
++ /* xfr done */
++ if (client->read_offset == xfr->resp_data_length) {
++ kfree(xfr);
++ client->xfr = NULL;
++ }
++
++ rc = bytes;
++
++done:
++ spin_unlock(&client->lock);
++ return rc;
++}
++
++static ssize_t occ_write(struct file *file, const char __user *buf,
++ size_t len, loff_t *offset)
++{
++ int rc;
++ struct occ_xfr *xfr;
++ struct occ_client *client = file->private_data;
++
++ if (!access_ok(VERIFY_READ, buf, len))
++ return -EFAULT;
++
++ if (len > OCC_SRAM_BYTES)
++ return -EINVAL;
++
++ spin_lock_irq(&client->lock);
++ if (client->xfr) {
++ rc = -EDEADLK;
++ goto done;
++ }
++
++ xfr = kzalloc(sizeof(*xfr), GFP_KERNEL);
++ if (!xfr) {
++ rc = -ENOMEM;
++ goto done;
++ }
++
++ if (copy_from_user(xfr->buf, buf, len)) {
++ kfree(xfr);
++ rc = -EFAULT;
++ goto done;
++ }
++
++ xfr->client = client;
++ xfr->cmd_data_length = len;
++ client->xfr = xfr;
++ client->read_offset = 0;
++
++ occ_enqueue_xfr(xfr);
++
++ rc = len;
++
++done:
++ spin_unlock(&client->lock);
++ return rc;
++}
++
++static int occ_release(struct inode *inode, struct file *file)
++{
++ struct occ_xfr *xfr;
++ struct occ_client *client = file->private_data;
++ struct occ *occ = client->occ;
++
++ spin_lock_irq(&client->lock);
++ xfr = client->xfr;
++ if (!xfr) {
++ spin_unlock(&client->lock);
++ kfree(client);
++ return 0;
++ }
++
++ spin_lock_irq(&occ->list_lock);
++ set_bit(XFR_CANCELED, &xfr->flags);
++ if (!test_bit(XFR_IN_PROGRESS, &xfr->flags)) {
++ /* already deleted from list if complete */
++ if (!test_bit(XFR_COMPLETE, &xfr->flags))
++ list_del(&xfr->link);
++
++ spin_unlock(&occ->list_lock);
++
++ if (test_bit(XFR_WAITING, &xfr->flags)) {
++ /* blocking read; let reader clean up */
++ wake_up_interruptible(&client->wait);
++ spin_unlock(&client->lock);
++ return 0;
++ }
++
++ kfree(xfr);
++ spin_unlock(&client->lock);
++ kfree(client);
++ return 0;
++ }
++
++ /* operation is in progress; let worker clean up*/
++ spin_unlock(&occ->list_lock);
++ spin_unlock(&client->lock);
++ return 0;
++}
++
++static const struct file_operations occ_fops = {
++ .owner = THIS_MODULE,
++ .open = occ_open,
++ .read = occ_read,
++ .write = occ_write,
++ .release = occ_release,
++};
++
++static int occ_getscom(struct device *sbefifo, u32 address, u8 *data)
++{
++ int rc;
++ u32 buf[4];
++ struct sbefifo_client *client;
++ const size_t len = sizeof(buf);
++
++ buf[0] = cpu_to_be32(0x4);
++ buf[1] = cpu_to_be32(0xa201);
++ buf[2] = 0;
++ buf[3] = cpu_to_be32(address);
++
++ client = sbefifo_drv_open(sbefifo, 0);
++ if (!client)
++ return -ENODEV;
++
++ rc = sbefifo_drv_write(client, (const char *)buf, len);
++ if (rc < 0)
++ goto done;
++ else if (rc != len) {
++ rc = -EMSGSIZE;
++ goto done;
++ }
++
++ rc = sbefifo_drv_read(client, (char *)buf, len);
++ if (rc < 0)
++ goto done;
++ else if (rc != len) {
++ rc = -EMSGSIZE;
++ goto done;
++ }
++
++ /* check for good response */
++ if ((be32_to_cpu(buf[2]) >> 16) != 0xC0DE) {
++ rc = -EFAULT;
++ goto done;
++ }
++
++ rc = 0;
++
++ memcpy(data, buf, sizeof(u64));
++
++done:
++ sbefifo_drv_release(client);
++ return rc;
++}
++
++static int occ_putscom(struct device *sbefifo, u32 address, u8 *data)
++{
++ int rc;
++ u32 buf[6];
++ struct sbefifo_client *client;
++ const size_t len = sizeof(buf);
++
++ buf[0] = cpu_to_be32(0x6);
++ buf[1] = cpu_to_be32(0xa202);
++ buf[2] = 0;
++ buf[3] = cpu_to_be32(address);
++ memcpy(&buf[4], data, sizeof(u64));
++
++ client = sbefifo_drv_open(sbefifo, 0);
++ if (!client)
++ return -ENODEV;
++
++ rc = sbefifo_drv_write(client, (const char *)buf, len);
++ if (rc < 0)
++ goto done;
++ else if (rc != len) {
++ rc = -EMSGSIZE;
++ goto done;
++ }
++
++ rc = 0;
++
++ /* ignore response */
++ sbefifo_drv_read(client, (char *)buf, len);
++
++done:
++ sbefifo_drv_release(client);
++ return rc;
++}
++
++static int occ_putscom_u32(struct device *sbefifo, u32 address, u32 data0,
++ u32 data1)
++{
++ u8 buf[8];
++ u32 raw_data0 = cpu_to_be32(data0), raw_data1 = cpu_to_be32(data1);
++
++ memcpy(buf, &raw_data0, 4);
++ memcpy(buf + 4, &raw_data1, 4);
++
++ return occ_putscom(sbefifo, address, buf);
++}
++
++static void occ_worker(struct work_struct *work)
++{
++ int i, empty, canceled, waiting, rc;
++ u16 resp_data_length;
++ struct occ *occ = container_of(work, struct occ, work);
++ struct device *sbefifo = occ->sbefifo;
++ struct occ_client *client;
++ struct occ_xfr *xfr;
++ struct occ_response *resp;
++
++again:
++ spin_lock_irq(&occ->list_lock);
++ xfr = list_first_entry(&occ->xfrs, struct occ_xfr, link);
++ if (!xfr) {
++ spin_unlock(&occ->list_lock);
++ return;
++ }
++
++ set_bit(XFR_IN_PROGRESS, &xfr->flags);
++ spin_unlock(&occ->list_lock);
++
++ resp = (struct occ_response *)xfr->buf;
++
++ spin_lock_irq(&occ->occ_lock);
++
++ /* set address reg to occ sram command buffer */
++ rc = occ_putscom_u32(sbefifo, 0x6D050, 0xFFFBE000, 0);
++ if (rc)
++ goto done;
++
++ /* write cmd data */
++ for (i = 0; i < xfr->cmd_data_length; i += 8) {
++ rc = occ_putscom(sbefifo, 0x6D055, &xfr->buf[i]);
++ if (rc)
++ goto done;
++ }
++
++ /* trigger attention */
++ rc = occ_putscom_u32(sbefifo, 0x6D035, 0x20010000, 0);
++ if (rc)
++ goto done;
++
++ /* set address reg to occ sram response buffer */
++ rc = occ_putscom_u32(sbefifo, 0x6D050, 0xFFFBF000, 0);
++ if (rc)
++ goto done;
++
++ rc = occ_getscom(sbefifo, 0x6D055, xfr->buf);
++ if (rc)
++ goto done;
++
++ xfr->resp_data_length += 8;
++
++ resp_data_length = be16_to_cpu(get_unaligned(&resp->data_length));
++ if (resp_data_length > OCC_RESP_DATA_BYTES) {
++ rc = -EFAULT;
++ goto done;
++ }
++
++ /* already read 3 bytes of resp data, but also need 2 bytes chksum */
++ for (i = 8; i < resp_data_length + 7; i += 8) {
++ rc = occ_getscom(sbefifo, 0x6D055, &xfr->buf[i]);
++ if (rc)
++ goto done;
++
++ xfr->resp_data_length += 8;
++ }
++
++ /* no errors, got all data */
++ xfr->resp_data_length = resp_data_length + 7;
++
++done:
++ spin_unlock(&occ->occ_lock);
++
++ xfr->rc = rc;
++ client = xfr->client;
++
++ /* lock client to prevent race with read() */
++ spin_lock_irq(&client->lock);
++ set_bit(XFR_COMPLETE, &xfr->flags);
++ waiting = test_bit(XFR_WAITING, &xfr->flags);
++ spin_unlock(&client->lock);
++
++ spin_lock_irq(&occ->list_lock);
++ clear_bit(XFR_IN_PROGRESS, &xfr->flags);
++ list_del(&xfr->link);
++ empty = list_empty(&occ->xfrs);
++ canceled = test_bit(XFR_CANCELED, &xfr->flags);
++ spin_unlock(&occ->list_lock);
++
++ if (waiting)
++ wake_up_interruptible(&client->wait);
++ else if (canceled) {
++ kfree(xfr);
++ kfree(xfr->client);
++ }
++
++ if (!empty)
++ goto again;
++}
++
++static int occ_probe(struct platform_device *pdev)
++{
++ int rc;
++ u32 reg;
++ struct occ *occ;
++ struct device *dev = &pdev->dev;
++
++ dev_info(dev, "Found occ device\n");
++ occ = devm_kzalloc(dev, sizeof(*occ), GFP_KERNEL);
++ if (!occ)
++ return -ENOMEM;
++
++ occ->sbefifo = dev->parent;
++ INIT_LIST_HEAD(&occ->xfrs);
++ spin_lock_init(&occ->list_lock);
++ spin_lock_init(&occ->occ_lock);
++ INIT_WORK(&occ->work, occ_worker);
++
++ if (dev->of_node) {
++ rc = of_property_read_u32(dev->of_node, "reg", ®);
++ if (!rc) {
++ /* make sure we don't have a duplicate from dts */
++ occ->idx = ida_simple_get(&occ_ida, reg, reg + 1,
++ GFP_KERNEL);
++ if (occ->idx < 0)
++ occ->idx = ida_simple_get(&occ_ida, 1, INT_MAX,
++ GFP_KERNEL);
++ } else
++ occ->idx = ida_simple_get(&occ_ida, 1, INT_MAX,
++ GFP_KERNEL);
++ } else
++ occ->idx = ida_simple_get(&occ_ida, 1, INT_MAX, GFP_KERNEL);
++
++ snprintf(occ->name, sizeof(occ->name), "occ%d", occ->idx);
++ occ->mdev.fops = &occ_fops;
++ occ->mdev.minor = MISC_DYNAMIC_MINOR;
++ occ->mdev.name = occ->name;
++ occ->mdev.parent = dev;
++
++ rc = misc_register(&occ->mdev);
++ if (rc) {
++ dev_err(dev, "failed to register miscdevice\n");
++ return rc;
++ }
++
++ platform_set_drvdata(pdev, occ);
++
++ return 0;
++}
++
++static int occ_remove(struct platform_device *pdev)
++{
++ struct occ_xfr *xfr, *tmp;
++ struct occ *occ = platform_get_drvdata(pdev);
++ struct occ_client *client;
++
++ misc_deregister(&occ->mdev);
++
++ spin_lock_irq(&occ->list_lock);
++ list_for_each_entry_safe(xfr, tmp, &occ->xfrs, link) {
++ client = xfr->client;
++ set_bit(XFR_CANCELED, &xfr->flags);
++
++ if (!test_bit(XFR_IN_PROGRESS, &xfr->flags)) {
++ list_del(&xfr->link);
++
++ spin_lock_irq(&client->lock);
++ if (test_bit(XFR_WAITING, &xfr->flags)) {
++ wake_up_interruptible(&client->wait);
++ spin_unlock(&client->lock);
++ } else {
++ kfree(xfr);
++ spin_unlock(&client->lock);
++ kfree(client);
++ }
++ }
++ }
++ spin_unlock(&occ->list_lock);
++
++ flush_work(&occ->work);
++
++ ida_simple_remove(&occ_ida, occ->idx);
++
++ return 0;
++}
++
++static const struct of_device_id occ_match[] = {
++ { .compatible = "ibm,p9-occ" },
++ { },
++};
++
++static struct platform_driver occ_driver = {
++ .driver = {
++ .name = "occ",
++ .of_match_table = occ_match,
++ },
++ .probe = occ_probe,
++ .remove = occ_remove,
++};
++
++static int occ_init(void)
++{
++ occ_wq = create_singlethread_workqueue("occ");
++ if (!occ_wq)
++ return -ENOMEM;
++
++ return platform_driver_register(&occ_driver);
++}
++
++static void occ_exit(void)
++{
++ destroy_workqueue(occ_wq);
++
++ platform_driver_unregister(&occ_driver);
++}
++
++module_init(occ_init);
++module_exit(occ_exit);
++
++MODULE_AUTHOR("Eddie James <eajames@us.ibm.com>");
++MODULE_DESCRIPTION("BMC P9 OCC driver");
++MODULE_LICENSE("GPL");
++
diff --git a/common/recipes-kernel/linux/linux-obmc/linux-dev-4.10-drivers-fsi-Add-FSI-SBEFIFO-driver.patch b/common/recipes-kernel/linux/linux-obmc/linux-dev-4.10-drivers-fsi-Add-FSI-SBEFIFO-driver.patch
new file mode 100644
index 0000000..b29e0e9
--- /dev/null
+++ b/common/recipes-kernel/linux/linux-obmc/linux-dev-4.10-drivers-fsi-Add-FSI-SBEFIFO-driver.patch
@@ -0,0 +1,899 @@
+From patchwork Thu May 11 02:52:53 2017
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: [linux,dev-4.10] drivers: fsi: Add FSI SBEFIFO driver
+From: eajames@linux.vnet.ibm.com
+X-Patchwork-Id: 760920
+Message-Id: <1494471173-6077-1-git-send-email-eajames@linux.vnet.ibm.com>
+To: openbmc@lists.ozlabs.org
+Cc: "Edward A. James" <eajames@us.ibm.com>, bradleyb@fuzziesquirrel.com
+Date: Wed, 10 May 2017 21:52:53 -0500
+
+From: "Edward A. James" <eajames@us.ibm.com>
+
+IBM POWER9 processors contain some embedded hardware and software bits
+collectively referred to as the self boot engine (SBE). One role of
+the SBE is to act as a proxy that provides access to the registers of
+the POWER chip from other (embedded) systems.
+
+The POWER9 chip contains a hardware frontend for communicating with
+the SBE from remote systems called the SBEFIFO. The SBEFIFO logic
+is contained within an FSI CFAM (see Documentation/fsi) and as such
+the driver implements an FSI bus device.
+
+The SBE expects to communicate using a defined wire protocol; however,
+the driver knows nothing of the protocol and only provides raw access
+to the fifo device to userspace applications wishing to communicate with
+the SBE using the wire protocol.
+
+The SBEFIFO consists of two hardware fifos. The upstream fifo is used
+by the driver to transfer data to the SBE on the POWER chip, from the
+system hosting the driver. The downstream fifo is used by the driver to
+transfer data from the SBE on the power chip to the system hosting the
+driver.
+
+Signed-off-by: Edward A. James <eajames@us.ibm.com>
+Signed-off-by: Brad Bishop <bradleyb@fuzziesquirrel.com>
+---
+ drivers/fsi/Kconfig | 5 +
+ drivers/fsi/Makefile | 1 +
+ drivers/fsi/fsi-sbefifo.c | 824 ++++++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 830 insertions(+)
+ create mode 100644 drivers/fsi/fsi-sbefifo.c
+
+diff --git a/drivers/fsi/Kconfig b/drivers/fsi/Kconfig
+index fc031ac..39527fa 100644
+--- a/drivers/fsi/Kconfig
++++ b/drivers/fsi/Kconfig
+@@ -31,6 +31,11 @@ config FSI_SCOM
+ ---help---
+ This option enables an FSI based SCOM device driver.
+
++config FSI_SBEFIFO
++ tristate "SBEFIFO FSI client device driver"
++ ---help---
++ This option enables an FSI based SBEFIFO device driver.
++
+ endif
+
+ endmenu
+diff --git a/drivers/fsi/Makefile b/drivers/fsi/Makefile
+index 65eb99d..851182e 100644
+--- a/drivers/fsi/Makefile
++++ b/drivers/fsi/Makefile
+@@ -3,3 +3,4 @@ obj-$(CONFIG_FSI) += fsi-core.o
+ obj-$(CONFIG_FSI_MASTER_HUB) += fsi-master-hub.o
+ obj-$(CONFIG_FSI_MASTER_GPIO) += fsi-master-gpio.o
+ obj-$(CONFIG_FSI_SCOM) += fsi-scom.o
++obj-$(CONFIG_FSI_SBEFIFO) += fsi-sbefifo.o
+diff --git a/drivers/fsi/fsi-sbefifo.c b/drivers/fsi/fsi-sbefifo.c
+new file mode 100644
+index 0000000..b49aec2
+--- /dev/null
++++ b/drivers/fsi/fsi-sbefifo.c
+@@ -0,0 +1,824 @@
++/*
++ * Copyright (C) IBM Corporation 2017
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERGCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ */
++
++#include <linux/delay.h>
++#include <linux/errno.h>
++#include <linux/idr.h>
++#include <linux/fsi.h>
++#include <linux/list.h>
++#include <linux/miscdevice.h>
++#include <linux/module.h>
++#include <linux/poll.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/timer.h>
++#include <linux/uaccess.h>
++
++/*
++ * The SBEFIFO is a pipe-like FSI device for communicating with
++ * the self boot engine on POWER processors.
++ */
++
++#define DEVICE_NAME "sbefifo"
++#define FSI_ENGID_SBE 0x22
++#define SBEFIFO_BUF_CNT 32
++
++#define SBEFIFO_UP 0x00 /* Up register offset */
++#define SBEFIFO_DWN 0x40 /* Down register offset */
++
++#define SBEFIFO_STS 0x04
++#define SBEFIFO_EMPTY BIT(20)
++#define SBEFIFO_EOT_RAISE 0x08
++#define SBEFIFO_EOT_MAGIC 0xffffffff
++#define SBEFIFO_EOT_ACK 0x14
++
++struct sbefifo {
++ struct timer_list poll_timer;
++ struct fsi_device *fsi_dev;
++ struct miscdevice mdev;
++ wait_queue_head_t wait;
++ struct list_head link;
++ struct list_head xfrs;
++ struct kref kref;
++ spinlock_t lock;
++ char name[32];
++ int idx;
++ int rc;
++};
++
++struct sbefifo_buf {
++ u32 buf[SBEFIFO_BUF_CNT];
++ unsigned long flags;
++#define SBEFIFO_BUF_FULL 1
++ u32 *rpos;
++ u32 *wpos;
++};
++
++struct sbefifo_xfr {
++ struct sbefifo_buf *rbuf;
++ struct sbefifo_buf *wbuf;
++ struct list_head client;
++ struct list_head xfrs;
++ unsigned long flags;
++#define SBEFIFO_XFR_WRITE_DONE 1
++#define SBEFIFO_XFR_RESP_PENDING 2
++#define SBEFIFO_XFR_COMPLETE 3
++#define SBEFIFO_XFR_CANCEL 4
++};
++
++struct sbefifo_client {
++ struct sbefifo_buf rbuf;
++ struct sbefifo_buf wbuf;
++ struct list_head xfrs;
++ struct sbefifo *dev;
++ struct kref kref;
++};
++
++static struct list_head sbefifo_fifos;
++
++static DEFINE_IDA(sbefifo_ida);
++
++static int sbefifo_inw(struct sbefifo *sbefifo, int reg, u32 *word)
++{
++ int rc;
++ u32 raw_word;
++
++ rc = fsi_device_read(sbefifo->fsi_dev, reg, &raw_word,
++ sizeof(raw_word));
++ if (rc)
++ return rc;
++
++ *word = be32_to_cpu(raw_word);
++ return 0;
++}
++
++static int sbefifo_outw(struct sbefifo *sbefifo, int reg, u32 word)
++{
++ u32 raw_word = cpu_to_be32(word);
++
++ return fsi_device_write(sbefifo->fsi_dev, reg, &raw_word,
++ sizeof(raw_word));
++}
++
++static int sbefifo_readw(struct sbefifo *sbefifo, u32 *word)
++{
++ return fsi_device_read(sbefifo->fsi_dev, SBEFIFO_DWN, word,
++ sizeof(*word));
++}
++
++static int sbefifo_writew(struct sbefifo *sbefifo, u32 word)
++{
++ return fsi_device_write(sbefifo->fsi_dev, SBEFIFO_UP, &word,
++ sizeof(word));
++}
++
++static int sbefifo_ack_eot(struct sbefifo *sbefifo)
++{
++ u32 discard;
++ int ret;
++
++ /* Discard the EOT word. */
++ ret = sbefifo_readw(sbefifo, &discard);
++ if (ret)
++ return ret;
++
++ return sbefifo_outw(sbefifo, SBEFIFO_DWN | SBEFIFO_EOT_ACK,
++ SBEFIFO_EOT_MAGIC);
++}
++
++static size_t sbefifo_dev_nwreadable(u32 sts)
++{
++ static const u32 FIFO_NTRY_CNT_MSK = 0x000f0000;
++ static const unsigned int FIFO_NTRY_CNT_SHIFT = 16;
++
++ return (sts & FIFO_NTRY_CNT_MSK) >> FIFO_NTRY_CNT_SHIFT;
++}
++
++static size_t sbefifo_dev_nwwriteable(u32 sts)
++{
++ static const size_t FIFO_DEPTH = 8;
++
++ return FIFO_DEPTH - sbefifo_dev_nwreadable(sts);
++}
++
++static void sbefifo_buf_init(struct sbefifo_buf *buf)
++{
++ WRITE_ONCE(buf->rpos, buf->buf);
++ WRITE_ONCE(buf->wpos, buf->buf);
++}
++
++static size_t sbefifo_buf_nbreadable(struct sbefifo_buf *buf)
++{
++ size_t n;
++ u32 *rpos = READ_ONCE(buf->rpos);
++ u32 *wpos = READ_ONCE(buf->wpos);
++
++ if (test_bit(SBEFIFO_BUF_FULL, &buf->flags))
++ n = SBEFIFO_BUF_CNT;
++ else if (rpos <= wpos)
++ n = wpos - rpos;
++ else
++ n = (buf->buf + SBEFIFO_BUF_CNT) - rpos;
++
++ return n << 2;
++}
++
++static size_t sbefifo_buf_nbwriteable(struct sbefifo_buf *buf)
++{
++ size_t n;
++ u32 *rpos = READ_ONCE(buf->rpos);
++ u32 *wpos = READ_ONCE(buf->wpos);
++
++ if (test_bit(SBEFIFO_BUF_FULL, &buf->flags))
++ n = 0;
++ else if (wpos < rpos)
++ n = rpos - wpos;
++ else
++ n = (buf->buf + SBEFIFO_BUF_CNT) - wpos;
++
++ return n << 2;
++}
++
++/*
++ * Update pointers and flags after doing a buffer read. Return true if the
++ * buffer is now empty;
++ */
++static bool sbefifo_buf_readnb(struct sbefifo_buf *buf, size_t n)
++{
++ u32 *rpos = READ_ONCE(buf->rpos);
++ u32 *wpos = READ_ONCE(buf->wpos);
++
++ if (n)
++ clear_bit(SBEFIFO_BUF_FULL, &buf->flags);
++
++ rpos += (n >> 2);
++ if (rpos == buf->buf + SBEFIFO_BUF_CNT)
++ rpos = buf->buf;
++
++ WRITE_ONCE(buf->rpos, rpos);
++ return rpos == wpos;
++}
++
++/*
++ * Update pointers and flags after doing a buffer write. Return true if the
++ * buffer is now full.
++ */
++static bool sbefifo_buf_wrotenb(struct sbefifo_buf *buf, size_t n)
++{
++ u32 *rpos = READ_ONCE(buf->rpos);
++ u32 *wpos = READ_ONCE(buf->wpos);
++
++ wpos += (n >> 2);
++ if (wpos == buf->buf + SBEFIFO_BUF_CNT)
++ wpos = buf->buf;
++ if (wpos == rpos)
++ set_bit(SBEFIFO_BUF_FULL, &buf->flags);
++
++ WRITE_ONCE(buf->wpos, wpos);
++ return rpos == wpos;
++}
++
++static void sbefifo_free(struct kref *kref)
++{
++ struct sbefifo *sbefifo;
++
++ sbefifo = container_of(kref, struct sbefifo, kref);
++ kfree(sbefifo);
++}
++
++static void sbefifo_get(struct sbefifo *sbefifo)
++{
++ kref_get(&sbefifo->kref);
++}
++
++static void sbefifo_put(struct sbefifo *sbefifo)
++{
++ kref_put(&sbefifo->kref, sbefifo_free);
++}
++
++static struct sbefifo_xfr *sbefifo_enq_xfr(struct sbefifo_client *client)
++{
++ struct sbefifo *sbefifo = client->dev;
++ struct sbefifo_xfr *xfr;
++
++ xfr = kzalloc(sizeof(*xfr), GFP_KERNEL);
++ if (!xfr)
++ return NULL;
++
++ xfr->rbuf = &client->rbuf;
++ xfr->wbuf = &client->wbuf;
++ list_add_tail(&xfr->xfrs, &sbefifo->xfrs);
++ list_add_tail(&xfr->client, &client->xfrs);
++
++ return xfr;
++}
++
++static struct sbefifo_xfr *sbefifo_client_next_xfr(
++ struct sbefifo_client *client)
++{
++ if (list_empty(&client->xfrs))
++ return NULL;
++
++ return container_of(client->xfrs.next, struct sbefifo_xfr,
++ client);
++}
++
++static bool sbefifo_xfr_rsp_pending(struct sbefifo_client *client)
++{
++ struct sbefifo_xfr *xfr;
++
++ xfr = sbefifo_client_next_xfr(client);
++ if (xfr && test_bit(SBEFIFO_XFR_RESP_PENDING, &xfr->flags))
++ return true;
++
++ return false;
++}
++
++static struct sbefifo_client *sbefifo_new_client(struct sbefifo *sbefifo)
++{
++ struct sbefifo_client *client;
++
++ client = kzalloc(sizeof(*client), GFP_KERNEL);
++ if (!client)
++ return NULL;
++
++ kref_init(&client->kref);
++ client->dev = sbefifo;
++ sbefifo_buf_init(&client->rbuf);
++ sbefifo_buf_init(&client->wbuf);
++ INIT_LIST_HEAD(&client->xfrs);
++
++ sbefifo_get(sbefifo);
++
++ return client;
++}
++
++static void sbefifo_client_release(struct kref *kref)
++{
++ struct sbefifo_client *client;
++ struct sbefifo_xfr *xfr;
++
++ client = container_of(kref, struct sbefifo_client, kref);
++ list_for_each_entry(xfr, &client->xfrs, client) {
++ /*
++ * The client left with pending or running xfrs.
++ * Cancel them.
++ */
++ set_bit(SBEFIFO_XFR_CANCEL, &xfr->flags);
++ sbefifo_get(client->dev);
++ if (mod_timer(&client->dev->poll_timer, jiffies))
++ sbefifo_put(client->dev);
++ }
++
++ sbefifo_put(client->dev);
++ kfree(client);
++}
++
++static void sbefifo_get_client(struct sbefifo_client *client)
++{
++ kref_get(&client->kref);
++}
++
++static void sbefifo_put_client(struct sbefifo_client *client)
++{
++ kref_put(&client->kref, sbefifo_client_release);
++}
++
++static struct sbefifo_xfr *sbefifo_next_xfr(struct sbefifo *sbefifo)
++{
++ struct sbefifo_xfr *xfr, *tmp;
++
++ list_for_each_entry_safe(xfr, tmp, &sbefifo->xfrs, xfrs) {
++ if (unlikely(test_bit(SBEFIFO_XFR_CANCEL, &xfr->flags))) {
++ /* Discard cancelled transfers. */
++ list_del(&xfr->xfrs);
++ kfree(xfr);
++ continue;
++ }
++ return xfr;
++ }
++
++ return NULL;
++}
++
++static void sbefifo_poll_timer(unsigned long data)
++{
++ static const unsigned long EOT_MASK = 0x000000ff;
++ struct sbefifo *sbefifo = (void *)data;
++ struct sbefifo_buf *rbuf, *wbuf;
++ struct sbefifo_xfr *xfr = NULL;
++ struct sbefifo_buf drain;
++ size_t devn, bufn;
++ int eot = 0;
++ int ret = 0;
++ u32 sts;
++ int i;
++
++ spin_lock(&sbefifo->lock);
++ xfr = list_first_entry_or_null(&sbefifo->xfrs, struct sbefifo_xfr,
++ xfrs);
++ if (!xfr)
++ goto out_unlock;
++
++ rbuf = xfr->rbuf;
++ wbuf = xfr->wbuf;
++
++ if (unlikely(test_bit(SBEFIFO_XFR_CANCEL, &xfr->flags))) {
++ /* The client left. */
++ rbuf = &drain;
++ wbuf = &drain;
++ sbefifo_buf_init(&drain);
++ if (!test_bit(SBEFIFO_XFR_RESP_PENDING, &xfr->flags))
++ set_bit(SBEFIFO_XFR_WRITE_DONE, &xfr->flags);
++ }
++
++ /* Drain the write buffer. */
++ while ((bufn = sbefifo_buf_nbreadable(wbuf))) {
++ ret = sbefifo_inw(sbefifo, SBEFIFO_UP | SBEFIFO_STS,
++ &sts);
++ if (ret)
++ goto out;
++
++ devn = sbefifo_dev_nwwriteable(sts);
++ if (devn == 0) {
++ /* No open slot for write. Reschedule. */
++ sbefifo->poll_timer.expires = jiffies +
++ msecs_to_jiffies(500);
++ add_timer(&sbefifo->poll_timer);
++ goto out_unlock;
++ }
++
++ devn = min_t(size_t, devn, bufn >> 2);
++ for (i = 0; i < devn; i++) {
++ ret = sbefifo_writew(sbefifo, *wbuf->rpos);
++ if (ret)
++ goto out;
++
++ sbefifo_buf_readnb(wbuf, 1 << 2);
++ }
++ }
++
++ /* Send EOT if the writer is finished. */
++ if (test_and_clear_bit(SBEFIFO_XFR_WRITE_DONE, &xfr->flags)) {
++ ret = sbefifo_outw(sbefifo,
++ SBEFIFO_UP | SBEFIFO_EOT_RAISE,
++ SBEFIFO_EOT_MAGIC);
++ if (ret)
++ goto out;
++
++ /* Inform reschedules that the writer is finished. */
++ set_bit(SBEFIFO_XFR_RESP_PENDING, &xfr->flags);
++ }
++
++ /* Nothing left to do if the writer is not finished. */
++ if (!test_bit(SBEFIFO_XFR_RESP_PENDING, &xfr->flags))
++ goto out;
++
++ /* Fill the read buffer. */
++ while ((bufn = sbefifo_buf_nbwriteable(rbuf))) {
++ ret = sbefifo_inw(sbefifo, SBEFIFO_DWN | SBEFIFO_STS, &sts);
++ if (ret)
++ goto out;
++
++ devn = sbefifo_dev_nwreadable(sts);
++ if (devn == 0) {
++ /* No data yet. Reschedule. */
++ sbefifo->poll_timer.expires = jiffies +
++ msecs_to_jiffies(500);
++ add_timer(&sbefifo->poll_timer);
++ goto out_unlock;
++ }
++
++ /* Fill. The EOT word is discarded. */
++ devn = min_t(size_t, devn, bufn >> 2);
++ eot = (sts & EOT_MASK) != 0;
++ if (eot)
++ devn--;
++
++ for (i = 0; i < devn; i++) {
++ ret = sbefifo_readw(sbefifo, rbuf->wpos);
++ if (ret)
++ goto out;
++
++ if (likely(!test_bit(SBEFIFO_XFR_CANCEL, &xfr->flags)))
++ sbefifo_buf_wrotenb(rbuf, 1 << 2);
++ }
++
++ if (eot) {
++ ret = sbefifo_ack_eot(sbefifo);
++ if (ret)
++ goto out;
++
++ set_bit(SBEFIFO_XFR_COMPLETE, &xfr->flags);
++ list_del(&xfr->xfrs);
++ if (unlikely(test_bit(SBEFIFO_XFR_CANCEL,
++ &xfr->flags)))
++ kfree(xfr);
++ break;
++ }
++ }
++
++out:
++ if (unlikely(ret)) {
++ sbefifo->rc = ret;
++ dev_err(&sbefifo->fsi_dev->dev,
++ "Fatal bus access failure: %d\n", ret);
++ list_for_each_entry(xfr, &sbefifo->xfrs, xfrs)
++ kfree(xfr);
++ INIT_LIST_HEAD(&sbefifo->xfrs);
++
++ } else if (eot && sbefifo_next_xfr(sbefifo)) {
++ sbefifo_get(sbefifo);
++ sbefifo->poll_timer.expires = jiffies;
++ add_timer(&sbefifo->poll_timer);
++ }
++
++ sbefifo_put(sbefifo);
++ wake_up(&sbefifo->wait);
++
++out_unlock:
++ spin_unlock(&sbefifo->lock);
++}
++
++static int sbefifo_open(struct inode *inode, struct file *file)
++{
++ struct sbefifo *sbefifo = container_of(file->private_data,
++ struct sbefifo, mdev);
++ struct sbefifo_client *client;
++ int ret;
++
++ ret = READ_ONCE(sbefifo->rc);
++ if (ret)
++ return ret;
++
++ client = sbefifo_new_client(sbefifo);
++ if (!client)
++ return -ENOMEM;
++
++ file->private_data = client;
++
++ return 0;
++}
++
++static unsigned int sbefifo_poll(struct file *file, poll_table *wait)
++{
++ struct sbefifo_client *client = file->private_data;
++ struct sbefifo *sbefifo = client->dev;
++ unsigned int mask = 0;
++
++ poll_wait(file, &sbefifo->wait, wait);
++
++ if (READ_ONCE(sbefifo->rc))
++ mask |= POLLERR;
++
++ if (sbefifo_buf_nbreadable(&client->rbuf))
++ mask |= POLLIN;
++
++ if (sbefifo_buf_nbwriteable(&client->wbuf))
++ mask |= POLLOUT;
++
++ return mask;
++}
++
++static ssize_t sbefifo_read(struct file *file, char __user *buf,
++ size_t len, loff_t *offset)
++{
++ struct sbefifo_client *client = file->private_data;
++ struct sbefifo *sbefifo = client->dev;
++ struct sbefifo_xfr *xfr;
++ ssize_t ret = 0;
++ size_t n;
++
++ WARN_ON(*offset);
++
++ if (!access_ok(VERIFY_WRITE, buf, len))
++ return -EFAULT;
++
++ if ((len >> 2) << 2 != len)
++ return -EINVAL;
++
++ if ((file->f_flags & O_NONBLOCK) && !sbefifo_xfr_rsp_pending(client))
++ return -EAGAIN;
++
++ sbefifo_get_client(client);
++ if (wait_event_interruptible(sbefifo->wait,
++ (ret = READ_ONCE(sbefifo->rc)) ||
++ (n = sbefifo_buf_nbreadable(
++ &client->rbuf)))) {
++ sbefifo_put_client(client);
++ return -ERESTARTSYS;
++ }
++ if (ret) {
++ INIT_LIST_HEAD(&client->xfrs);
++ sbefifo_put_client(client);
++ return ret;
++ }
++
++ n = min_t(size_t, n, len);
++
++ if (copy_to_user(buf, READ_ONCE(client->rbuf.rpos), n)) {
++ sbefifo_put_client(client);
++ return -EFAULT;
++ }
++
++ if (sbefifo_buf_readnb(&client->rbuf, n)) {
++ xfr = sbefifo_client_next_xfr(client);
++ if (!test_bit(SBEFIFO_XFR_COMPLETE, &xfr->flags)) {
++ /*
++ * Fill the read buffer back up.
++ */
++ sbefifo_get(sbefifo);
++ if (mod_timer(&client->dev->poll_timer, jiffies))
++ sbefifo_put(sbefifo);
++ } else {
++ kfree(xfr);
++ list_del(&xfr->client);
++ wake_up(&sbefifo->wait);
++ }
++ }
++
++ sbefifo_put_client(client);
++
++ return n;
++}
++
++static ssize_t sbefifo_write(struct file *file, const char __user *buf,
++ size_t len, loff_t *offset)
++{
++ struct sbefifo_client *client = file->private_data;
++ struct sbefifo *sbefifo = client->dev;
++ struct sbefifo_xfr *xfr;
++ ssize_t ret = 0;
++ size_t n;
++
++ WARN_ON(*offset);
++
++ if (!access_ok(VERIFY_READ, buf, len))
++ return -EFAULT;
++
++ if ((len >> 2) << 2 != len)
++ return -EINVAL;
++
++ if (!len)
++ return 0;
++
++ n = sbefifo_buf_nbwriteable(&client->wbuf);
++
++ spin_lock_irq(&sbefifo->lock);
++ xfr = sbefifo_next_xfr(sbefifo);
++
++ if ((file->f_flags & O_NONBLOCK) && xfr && n < len) {
++ spin_unlock_irq(&sbefifo->lock);
++ return -EAGAIN;
++ }
++
++ xfr = sbefifo_enq_xfr(client);
++ if (!xfr) {
++ spin_unlock_irq(&sbefifo->lock);
++ return -ENOMEM;
++ }
++ spin_unlock_irq(&sbefifo->lock);
++
++ sbefifo_get_client(client);
++
++ /*
++ * Partial writes are not really allowed in that EOT is sent exactly
++ * once per write.
++ */
++ while (len) {
++ if (wait_event_interruptible(sbefifo->wait,
++ READ_ONCE(sbefifo->rc) ||
++ (sbefifo_client_next_xfr(client) == xfr &&
++ (n = sbefifo_buf_nbwriteable(
++ &client->wbuf))))) {
++ set_bit(SBEFIFO_XFR_CANCEL, &xfr->flags);
++ sbefifo_get(sbefifo);
++ if (mod_timer(&sbefifo->poll_timer, jiffies))
++ sbefifo_put(sbefifo);
++
++ sbefifo_put_client(client);
++ return -ERESTARTSYS;
++ }
++ if (sbefifo->rc) {
++ INIT_LIST_HEAD(&client->xfrs);
++ sbefifo_put_client(client);
++ return sbefifo->rc;
++ }
++
++ n = min_t(size_t, n, len);
++
++ if (copy_from_user(READ_ONCE(client->wbuf.wpos), buf, n)) {
++ set_bit(SBEFIFO_XFR_CANCEL, &xfr->flags);
++ sbefifo_get(sbefifo);
++ if (mod_timer(&sbefifo->poll_timer, jiffies))
++ sbefifo_put(sbefifo);
++ sbefifo_put_client(client);
++ return -EFAULT;
++ }
++
++ sbefifo_buf_wrotenb(&client->wbuf, n);
++ len -= n;
++ buf += n;
++ ret += n;
++
++ /*
++ * Drain the write buffer.
++ */
++ sbefifo_get(sbefifo);
++ if (mod_timer(&client->dev->poll_timer, jiffies))
++ sbefifo_put(sbefifo);
++ }
++
++ set_bit(SBEFIFO_XFR_WRITE_DONE, &xfr->flags);
++ sbefifo_put_client(client);
++
++ return ret;
++}
++
++static int sbefifo_release(struct inode *inode, struct file *file)
++{
++ struct sbefifo_client *client = file->private_data;
++ struct sbefifo *sbefifo = client->dev;
++
++ sbefifo_put_client(client);
++
++ return READ_ONCE(sbefifo->rc);
++}
++
++static const struct file_operations sbefifo_fops = {
++ .owner = THIS_MODULE,
++ .open = sbefifo_open,
++ .read = sbefifo_read,
++ .write = sbefifo_write,
++ .poll = sbefifo_poll,
++ .release = sbefifo_release,
++};
++
++static int sbefifo_probe(struct device *dev)
++{
++ struct fsi_device *fsi_dev = to_fsi_dev(dev);
++ struct sbefifo *sbefifo;
++ u32 sts;
++ int ret;
++
++ dev_info(dev, "Found sbefifo device\n");
++ sbefifo = kzalloc(sizeof(*sbefifo), GFP_KERNEL);
++ if (!sbefifo)
++ return -ENOMEM;
++
++ sbefifo->fsi_dev = fsi_dev;
++
++ ret = sbefifo_inw(sbefifo,
++ SBEFIFO_UP | SBEFIFO_STS, &sts);
++ if (ret)
++ return ret;
++ if (!(sts & SBEFIFO_EMPTY)) {
++ dev_err(&sbefifo->fsi_dev->dev,
++ "Found data in upstream fifo\n");
++ return -EIO;
++ }
++
++ ret = sbefifo_inw(sbefifo, SBEFIFO_DWN | SBEFIFO_STS, &sts);
++ if (ret)
++ return ret;
++ if (!(sts & SBEFIFO_EMPTY)) {
++ dev_err(&sbefifo->fsi_dev->dev,
++ "Found data in downstream fifo\n");
++ return -EIO;
++ }
++
++ sbefifo->mdev.minor = MISC_DYNAMIC_MINOR;
++ sbefifo->mdev.fops = &sbefifo_fops;
++ sbefifo->mdev.name = sbefifo->name;
++ sbefifo->mdev.parent = dev;
++ spin_lock_init(&sbefifo->lock);
++ kref_init(&sbefifo->kref);
++
++ sbefifo->idx = ida_simple_get(&sbefifo_ida, 1, INT_MAX, GFP_KERNEL);
++ snprintf(sbefifo->name, sizeof(sbefifo->name), "sbefifo%d",
++ sbefifo->idx);
++ init_waitqueue_head(&sbefifo->wait);
++ INIT_LIST_HEAD(&sbefifo->xfrs);
++
++ /* This bit of silicon doesn't offer any interrupts... */
++ setup_timer(&sbefifo->poll_timer, sbefifo_poll_timer,
++ (unsigned long)sbefifo);
++
++ list_add(&sbefifo->link, &sbefifo_fifos);
++ return misc_register(&sbefifo->mdev);
++}
++
++static int sbefifo_remove(struct device *dev)
++{
++ struct fsi_device *fsi_dev = to_fsi_dev(dev);
++ struct sbefifo *sbefifo, *sbefifo_tmp;
++ struct sbefifo_xfr *xfr;
++
++ list_for_each_entry_safe(sbefifo, sbefifo_tmp, &sbefifo_fifos, link) {
++ if (sbefifo->fsi_dev != fsi_dev)
++ continue;
++ misc_deregister(&sbefifo->mdev);
++ list_del(&sbefifo->link);
++ ida_simple_remove(&sbefifo_ida, sbefifo->idx);
++
++ if (del_timer_sync(&sbefifo->poll_timer))
++ sbefifo_put(sbefifo);
++
++ spin_lock(&sbefifo->lock);
++ list_for_each_entry(xfr, &sbefifo->xfrs, xfrs)
++ kfree(xfr);
++ spin_unlock(&sbefifo->lock);
++
++ WRITE_ONCE(sbefifo->rc, -ENODEV);
++
++ wake_up(&sbefifo->wait);
++ sbefifo_put(sbefifo);
++ }
++
++ return 0;
++}
++
++static struct fsi_device_id sbefifo_ids[] = {
++ {
++ .engine_type = FSI_ENGID_SBE,
++ .version = FSI_VERSION_ANY,
++ },
++ { 0 }
++};
++
++static struct fsi_driver sbefifo_drv = {
++ .id_table = sbefifo_ids,
++ .drv = {
++ .name = DEVICE_NAME,
++ .bus = &fsi_bus_type,
++ .probe = sbefifo_probe,
++ .remove = sbefifo_remove,
++ }
++};
++
++static int sbefifo_init(void)
++{
++ INIT_LIST_HEAD(&sbefifo_fifos);
++ return fsi_driver_register(&sbefifo_drv);
++}
++
++static void sbefifo_exit(void)
++{
++ fsi_driver_unregister(&sbefifo_drv);
++}
++
++module_init(sbefifo_init);
++module_exit(sbefifo_exit);
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Brad Bishop <bradleyb@fuzziesquirrel.com>");
++MODULE_DESCRIPTION("Linux device interface to the POWER self boot engine");
diff --git a/common/recipes-kernel/linux/linux-obmc/linux-dev-4.10-v2-1-6-drivers-i2c-Add-FSI-attached-I2C-master-algorithm.patch b/common/recipes-kernel/linux/linux-obmc/linux-dev-4.10-v2-1-6-drivers-i2c-Add-FSI-attached-I2C-master-algorithm.patch
new file mode 100644
index 0000000..f38a6a4
--- /dev/null
+++ b/common/recipes-kernel/linux/linux-obmc/linux-dev-4.10-v2-1-6-drivers-i2c-Add-FSI-attached-I2C-master-algorithm.patch
@@ -0,0 +1,326 @@
+From patchwork Wed May 10 15:52:37 2017
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: [linux, dev-4.10, v2,
+ 1/6] drivers: i2c: Add FSI-attached I2C master algorithm
+From: eajames@linux.vnet.ibm.com
+X-Patchwork-Id: 760697
+Message-Id: <1494431562-25101-2-git-send-email-eajames@linux.vnet.ibm.com>
+To: openbmc@lists.ozlabs.org
+Cc: "Edward A. James" <eajames@us.ibm.com>, cbostic@linux.vnet.ibm.com
+Date: Wed, 10 May 2017 10:52:37 -0500
+
+From: "Edward A. James" <eajames@us.ibm.com>
+
+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 <eajames@us.ibm.com>
+---
+ 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
+
+diff --git a/drivers/Makefile b/drivers/Makefile
+index 67ce51d..278f109 100644
+--- a/drivers/Makefile
++++ b/drivers/Makefile
+@@ -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/
+diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
+index 7e34901d..0c714e0 100644
+--- a/drivers/i2c/busses/Kconfig
++++ b/drivers/i2c/busses/Kconfig
+@@ -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
+diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
+index 5798b4e..547773b 100644
+--- a/drivers/i2c/busses/Makefile
++++ b/drivers/i2c/busses/Makefile
+@@ -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
+diff --git a/drivers/i2c/busses/i2c-fsi.c b/drivers/i2c/busses/i2c-fsi.c
+new file mode 100644
+index 0000000..3c1087d
+--- /dev/null
++++ b/drivers/i2c/busses/i2c-fsi.c
+@@ -0,0 +1,245 @@
++/*
++ * Copyright 2017 IBM Corporation
++ *
++ * Eddie James <eajames@us.ibm.com>
++ *
++ * 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>
++#include <linux/of.h>
++#include <linux/sched.h>
++#include <linux/semaphore.h>
++#include <linux/wait.h>
++
++#define FSI_ENGID_I2C_FSI 0x7
++
++/* 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)))
++
++#define I2C_DEFAULT_CLK_DIV 6
++
++/* 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;
++};
++
++static int fsi_i2c_read_reg(struct fsi_device *fsi, unsigned int reg,
++ u32 *data)
++{
++ int rc;
++ u32 raw_data;
++
++ rc = fsi_device_read(fsi, reg, &raw_data, sizeof(raw_data));
++ if (rc)
++ return rc;
++
++ *data = be32_to_cpu(raw_data);
++
++ return 0;
++}
++
++static int fsi_i2c_write_reg(struct fsi_device *fsi, unsigned int reg,
++ u32 *data)
++{
++ u32 raw_data = cpu_to_be32(*data);
++
++ return fsi_device_write(fsi, reg, &raw_data, sizeof(raw_data));
++}
++
++static int fsi_i2c_dev_init(struct fsi_i2c_master *i2c)
++{
++ int rc;
++ 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;
++
++ mode = SETFIELD(I2C_MODE_CLKDIV, mode, I2C_DEFAULT_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;
++
++ i2c = devm_kzalloc(dev, sizeof(*i2c), GFP_KERNEL);
++ if (!i2c)
++ return -ENOMEM;
++
++ i2c->fsi = to_fsi_dev(dev);
++
++ 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,
++ },
++};
++
++module_fsi_driver(fsi_i2c_driver);
++
++MODULE_AUTHOR("Eddie James <eajames@us.ibm.com>");
++MODULE_DESCRIPTION("FSI attached I2C master");
++MODULE_LICENSE("GPL");
diff --git a/common/recipes-kernel/linux/linux-obmc/linux-dev-4.10-v2-2-6-drivers-i2c-Add-port-structure-to-FSI-algorithm.patch b/common/recipes-kernel/linux/linux-obmc/linux-dev-4.10-v2-2-6-drivers-i2c-Add-port-structure-to-FSI-algorithm.patch
new file mode 100644
index 0000000..20f66bf
--- /dev/null
+++ b/common/recipes-kernel/linux/linux-obmc/linux-dev-4.10-v2-2-6-drivers-i2c-Add-port-structure-to-FSI-algorithm.patch
@@ -0,0 +1,194 @@
+From patchwork Wed May 10 15:52:38 2017
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: [linux, dev-4.10, v2,
+ 2/6] drivers: i2c: Add port structure to FSI algorithm
+From: eajames@linux.vnet.ibm.com
+X-Patchwork-Id: 760696
+Message-Id: <1494431562-25101-3-git-send-email-eajames@linux.vnet.ibm.com>
+To: openbmc@lists.ozlabs.org
+Cc: "Edward A. James" <eajames@us.ibm.com>, cbostic@linux.vnet.ibm.com
+Date: Wed, 10 May 2017 10:52:38 -0500
+
+From: "Edward A. James" <eajames@us.ibm.com>
+
+Add and initialize I2C adapters for each port on the FSI-attached I2C
+master. Ports are defined in the devicetree.
+
+Signed-off-by: Edward A. James <eajames@us.ibm.com>
+---
+ drivers/i2c/busses/i2c-fsi.c | 113 ++++++++++++++++++++++++++++++++++++++++++-
+ 1 file changed, 112 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/i2c/busses/i2c-fsi.c b/drivers/i2c/busses/i2c-fsi.c
+index 3c1087d..cdebc99 100644
+--- a/drivers/i2c/busses/i2c-fsi.c
++++ b/drivers/i2c/busses/i2c-fsi.c
+@@ -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,
+ },
+ };
+
diff --git a/common/recipes-kernel/linux/linux-obmc/linux-dev-4.10-v2-3-6-drivers-i2c-Add-transfer-implementation-for-FSI-algorithm.patch b/common/recipes-kernel/linux/linux-obmc/linux-dev-4.10-v2-3-6-drivers-i2c-Add-transfer-implementation-for-FSI-algorithm.patch
new file mode 100644
index 0000000..50410ff
--- /dev/null
+++ b/common/recipes-kernel/linux/linux-obmc/linux-dev-4.10-v2-3-6-drivers-i2c-Add-transfer-implementation-for-FSI-algorithm.patch
@@ -0,0 +1,243 @@
+From patchwork Wed May 10 15:52:39 2017
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: [linux, dev-4.10, v2,
+ 3/6] drivers: i2c: Add transfer implementation for FSI algorithm
+From: eajames@linux.vnet.ibm.com
+X-Patchwork-Id: 760702
+Message-Id: <1494431562-25101-4-git-send-email-eajames@linux.vnet.ibm.com>
+To: openbmc@lists.ozlabs.org
+Cc: "Edward A. James" <eajames@us.ibm.com>, cbostic@linux.vnet.ibm.com
+Date: Wed, 10 May 2017 10:52:39 -0500
+
+From: "Edward A. James" <eajames@us.ibm.com>
+
+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 <eajames@us.ibm.com>
+---
+ drivers/i2c/busses/i2c-fsi.c | 193 ++++++++++++++++++++++++++++++++++++++++++-
+ 1 file changed, 191 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/i2c/busses/i2c-fsi.c b/drivers/i2c/busses/i2c-fsi.c
+index cdebc99..f690b16 100644
+--- a/drivers/i2c/busses/i2c-fsi.c
++++ b/drivers/i2c/busses/i2c-fsi.c
+@@ -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)
diff --git a/common/recipes-kernel/linux/linux-obmc/linux-dev-4.10-v2-4-6-drivers-i2c-Add-I2C-master-locking-to-FSI-algorithm.patch b/common/recipes-kernel/linux/linux-obmc/linux-dev-4.10-v2-4-6-drivers-i2c-Add-I2C-master-locking-to-FSI-algorithm.patch
new file mode 100644
index 0000000..e649d62
--- /dev/null
+++ b/common/recipes-kernel/linux/linux-obmc/linux-dev-4.10-v2-4-6-drivers-i2c-Add-I2C-master-locking-to-FSI-algorithm.patch
@@ -0,0 +1,112 @@
+From patchwork Wed May 10 15:52:40 2017
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: [linux, dev-4.10, v2,
+ 4/6] drivers: i2c: Add I2C master locking to FSI algorithm
+From: eajames@linux.vnet.ibm.com
+X-Patchwork-Id: 760699
+Message-Id: <1494431562-25101-5-git-send-email-eajames@linux.vnet.ibm.com>
+To: openbmc@lists.ozlabs.org
+Cc: "Edward A. James" <eajames@us.ibm.com>, cbostic@linux.vnet.ibm.com
+Date: Wed, 10 May 2017 10:52:40 -0500
+
+From: "Edward A. James" <eajames@us.ibm.com>
+
+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.
+
+Signed-off-by: Edward A. James <eajames@us.ibm.com>
+---
+ drivers/i2c/busses/i2c-fsi.c | 41 +++++++++++++++++++++++++++++++++++++----
+ 1 file changed, 37 insertions(+), 4 deletions(-)
+
+diff --git a/drivers/i2c/busses/i2c-fsi.c b/drivers/i2c/busses/i2c-fsi.c
+index f690b16..d757aee 100644
+--- a/drivers/i2c/busses/i2c-fsi.c
++++ b/drivers/i2c/busses/i2c-fsi.c
+@@ -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);
diff --git a/common/recipes-kernel/linux/linux-obmc/linux-dev-4.10-v2-5-6-drivers-i2c-Add-bus-recovery-for-FSI-algorithm.patch b/common/recipes-kernel/linux/linux-obmc/linux-dev-4.10-v2-5-6-drivers-i2c-Add-bus-recovery-for-FSI-algorithm.patch
new file mode 100644
index 0000000..f9fc75d
--- /dev/null
+++ b/common/recipes-kernel/linux/linux-obmc/linux-dev-4.10-v2-5-6-drivers-i2c-Add-bus-recovery-for-FSI-algorithm.patch
@@ -0,0 +1,117 @@
+From patchwork Wed May 10 15:52:41 2017
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: [linux, dev-4.10, v2,
+ 5/6] drivers: i2c: Add bus recovery for FSI algorithm
+From: eajames@linux.vnet.ibm.com
+X-Patchwork-Id: 760701
+Message-Id: <1494431562-25101-6-git-send-email-eajames@linux.vnet.ibm.com>
+To: openbmc@lists.ozlabs.org
+Cc: "Edward A. James" <eajames@us.ibm.com>, cbostic@linux.vnet.ibm.com
+Date: Wed, 10 May 2017 10:52:41 -0500
+
+From: "Edward A. James" <eajames@us.ibm.com>
+
+Bus recovery should reset the engine and force block the bus 9 times
+to recover most situations.
+
+Signed-off-by: Edward A. James <eajames@us.ibm.com>
+---
+ drivers/i2c/busses/i2c-fsi.c | 76 ++++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 76 insertions(+)
+
+diff --git a/drivers/i2c/busses/i2c-fsi.c b/drivers/i2c/busses/i2c-fsi.c
+index d757aee..4689479 100644
+--- a/drivers/i2c/busses/i2c-fsi.c
++++ b/drivers/i2c/busses/i2c-fsi.c
+@@ -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) +