blob: 70a11f2650fa9244f0cf71cf589cd985d495e3c9 [file] [log] [blame]
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__ */