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/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__ */