diff options
Diffstat (limited to 'meta-phosphor/common/recipes-kernel/linux/linux-obmc/linux-dev-4.10-1-3-drivers-fsi-sbefifo-Add-in-kernel-API.patch')
-rw-r--r-- | meta-phosphor/common/recipes-kernel/linux/linux-obmc/linux-dev-4.10-1-3-drivers-fsi-sbefifo-Add-in-kernel-API.patch | 338 |
1 files changed, 338 insertions, 0 deletions
diff --git a/meta-phosphor/common/recipes-kernel/linux/linux-obmc/linux-dev-4.10-1-3-drivers-fsi-sbefifo-Add-in-kernel-API.patch b/meta-phosphor/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 000000000..70a11f265 --- /dev/null +++ b/meta-phosphor/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__ */ |