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/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", &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) +