Skip to contentSkip to conversation

drivers: fsi: sbefifo: Add in-kernel API

PATCH In review Eddie James 2 participants 5 messages Apr 18, 2017 – Apr 24, 2017

Subsystems (3)
lore
Share & tools
lore
#0 lore raw

cover drivers: fsi: sbefifo: Add in-kernel API

From: "Edward A. James" <[email protected]>

This patchset provides an in-kernel API for the SBEFIFO driver and the first user, a driver to access the P9 OCC hardware. There are also some potential devicetree bindings for the SBEFIFO and OCC devices.

The OCC provides a read/write interface, but only performs a single, atomic, OCC "command and response" operation per write operation. The driver cannot perform multiple writes in a row without first reading back to the user the response generated for each command. I can't see a use case for doing multiple commands without fetching the response.

Any feedback, particularly on how to improve the dt bindings or how to inform the OCC client that the SBEFIFO device has gone away (via unbinding FSI, or other means), would be helpful.

Edward A. James (3):
drivers: fsi: sbefifo: Add in-kernel API
drivers: fsi: sbefifo: Add OCC driver
ARM: dts: aspeed: witherspoon: Add example sbefifo and occfifo devices

 arch/arm/boot/dts/aspeed-bmc-opp-witherspoon.dts |  16 +
 drivers/fsi/Kconfig                              |  10 +
 drivers/fsi/Makefile                             |   1 +
 drivers/fsi/fsi-sbefifo.c                        | 173 +++++-
 drivers/fsi/occfifo.c                            | 649 +++++++++++++++++++++++
 include/linux/fsi-sbefifo.h                      |  41 ++
 6 files changed, 861 insertions(+), 29 deletions(-)
 create mode 100644 drivers/fsi/occfifo.c
 create mode 100644 include/linux/fsi-sbefifo.h
Signature · 1 line
1.8.3.1
#1 lore raw

PATCH 1/3 drivers: fsi: sbefifo: Add in-kernel API

From: "Edward A. James" <[email protected]>

Add exported functions to the SBEFIFO driver to open/write/read/close from within the kernel.

---
 drivers/fsi/fsi-sbefifo.c   | 173 ++++++++++++++++++++++++++++++++++++--------
 include/linux/fsi-sbefifo.h |  41 +++++++++++
 2 files changed, 185 insertions(+), 29 deletions(-)
 create mode 100644 include/linux/fsi-sbefifo.h
drivers/fsi/fsi-sbefifo.c +144 -29
index 9a13ce3..4b95b00 100644
@@ -15,9 +15,11 @@
 #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/poll.h>
 #include <linux/sched.h>
 #include <linux/slab.h>
@@ -49,7 +51,9 @@ struct sbefifo {
 	wait_queue_head_t wait;
 	struct list_head link;
 	struct list_head xfrs;
+	struct list_head drv_refs;
 	struct kref kref;
+	struct device_node *node;
 	spinlock_t lock;
 	char name[32];
 	int idx;
@@ -82,6 +86,7 @@ struct sbefifo_client {
 	struct list_head xfrs;
 	struct sbefifo *dev;
 	struct kref kref;
+	unsigned long f_flags;
 };
 
 static struct list_head sbefifo_fifos;
@@ -493,6 +498,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;
 }
@@ -517,24 +523,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);
@@ -553,10 +553,14 @@ 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);
@@ -579,20 +583,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;
 
@@ -604,7 +616,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;
 	}
@@ -644,18 +656,26 @@ 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;
 
 		/*
@@ -672,6 +692,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;
@@ -691,11 +724,72 @@ static const struct file_operations sbefifo_fops = {
 	.release	= sbefifo_release,
 };
 
+struct sbefifo *sbefifo_drv_reference(struct device_node *node,
+				      struct sbefifo_drv_ref *ref)
+{
+	struct sbefifo *sbefifo;
+
+	list_for_each_entry(sbefifo, &sbefifo_fifos, link) {
+		if (node == sbefifo->node) {
+			if (ref)
+				list_add(&ref->link, &sbefifo->drv_refs);
+			return sbefifo;
+		}
+	}
+
+	return NULL;
+}
+EXPORT_SYMBOL_GPL(sbefifo_drv_reference);
+
+struct sbefifo_client *sbefifo_drv_open(struct sbefifo *sbefifo,
+					unsigned long flags)
+{
+	struct sbefifo_client *client;
+
+	if (!sbefifo)
+		return NULL;
+
+	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);
+
+int sbefifo_drv_get_idx(struct sbefifo *sbefifo)
+{
+	return sbefifo->idx;
+}
+
 static int sbefifo_probe(struct device *dev)
 {
 	struct fsi_device *fsi_dev = to_fsi_dev(dev);
 	struct sbefifo *sbefifo;
-	u32 sts;
+	struct device_node *np;
+	u32 sts, addr;
 	int ret;
 
 	dev_info(dev, "Found sbefifo device\n");
@@ -736,6 +830,19 @@ static int sbefifo_probe(struct device *dev)
 			sbefifo->idx);
 	init_waitqueue_head(&sbefifo->wait);
 	INIT_LIST_HEAD(&sbefifo->xfrs);
+	INIT_LIST_HEAD(&sbefifo->drv_refs);
+
+	for_each_compatible_node(np, NULL, "ibm,power9-sbefifo") {
+		ret = of_property_read_u32(np, "reg", &addr);
+		if (ret)
+			continue;
+
+		/* TODO: get real address, not cfam offset */
+		if (addr == fsi_dev->addr) {
+			sbefifo->node = np;
+			break;
+		}
+	}
 
 	/* This bit of silicon doesn't offer any interrupts... */
 	setup_timer(&sbefifo->poll_timer, sbefifo_poll_timer,
@@ -749,11 +856,19 @@ static int sbefifo_remove(struct device *dev)
 {
 	struct fsi_device *fsi_dev = to_fsi_dev(dev);
 	struct sbefifo *sbefifo, *sbefifo_tmp;
+	struct sbefifo_drv_ref *ref, *ref_tmp;
 	struct sbefifo_xfr *xfr;
 
 	list_for_each_entry_safe(sbefifo, sbefifo_tmp, &sbefifo_fifos, link) {
 		if (sbefifo->fsi_dev != fsi_dev)
 			continue;
+
+		list_for_each_entry_safe(ref, ref_tmp, &sbefifo->drv_refs,
+					 link) {
+			ref->notify(ref);
+			list_del(&ref->link);
+		}
+
 		misc_deregister(&sbefifo->mdev);
 		list_del(&sbefifo->link);
 		ida_simple_remove(&sbefifo_ida, sbefifo->idx);
include/linux/fsi-sbefifo.h new file +41 -0

41 lines not on this page · raw message

Signature · 1 line
1.8.3.1
#2 lore raw

PATCH 2/3 drivers: fsi: sbefifo: Add OCC driver

In reply to Eddie James

From: "Edward A. James" <[email protected]>

Add a driver to control communications with the OCC on the POWER9. The driver communicates with OCC SRAM using the FSI SBEFIFO driver.

The format of the communication is a command followed by response. The sequence must be done atomically. The driver performs these operations asynchronously, such that a write operation starts the command, and a read will gather the response data.

---
 drivers/fsi/Kconfig   |  10 +
 drivers/fsi/Makefile  |   1 +
 drivers/fsi/occfifo.c | 649 ++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 660 insertions(+)
 create mode 100644 drivers/fsi/occfifo.c
drivers/fsi/Kconfig +10 -0
index 087f239..8dbe5d9 100644
@@ -24,6 +24,16 @@ config FSI_SBEFIFO
 	---help---
 	This option enables an FSI based SBEFIFO device driver.
 
+if FSI_SBEFIFO
+
+config OCCFIFO
+	tristate "OCC SBEFIFO client device driver"
+	depends on FSI_SBEFIFO
+	---help---
+	This option enables an FSI based OCC driver using the SBEFIFO.
+
+endif
+
 config FSI_SCOM
 	tristate "SCOM FSI client device driver"
 	depends on FSI
drivers/fsi/Makefile +1 -0
index edd84b2..eddaf79 100644
@@ -2,5 +2,6 @@
 obj-$(CONFIG_FSI) += fsi-core.o
 obj-$(CONFIG_FSI_MASTER_GPIO) += fsi-master-gpio.o
 obj-$(CONFIG_FSI_SBEFIFO) += fsi-sbefifo.o
+obj-$(CONFIG_OCCFIFO) += occfifo.o
 obj-$(CONFIG_FSI_SCOM) += fsi-scom.o
 obj-$(CONFIG_FSI_I2C) += i2c/
drivers/fsi/occfifo.c new file +649 -0

649 lines not on this page · raw message

Signature · 1 line
1.8.3.1
#3 lore raw

PATCH 3/3 ARM: dts: aspeed: witherspoon: Add example sbefifo and occfifo devices

In reply to Eddie James

From: "Edward A. James" <[email protected]>

A possible implementation of the bindings for SBEFIFO device and OCC "FIFO" device. Use a phandle to reference the appropriate SBE device from the OCC entry.

---
 arch/arm/boot/dts/aspeed-bmc-opp-witherspoon.dts | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)
arch/arm/boot/dts/aspeed-bmc-opp-witherspoon.dts +16 -0
index e3a7b77..57ed260 100644
@@ -57,6 +57,22 @@
 		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>;
+
+		sbefifo1: sbefifo@2400 {
+			compatible = "ibm,power9-sbefifo";
+
+			status = "okay";
+
+			reg = <0x2400>;
+		};
+	};
+
+	occfifo@1 {
+		compatible = "ibm,occfifo";
+
+		status = "okay";
+
+		bus = <&sbefifo1>;
 	};
 
 	iio-hwmon {
Signature · 1 line
1.8.3.1
#4 lore raw
In reply to Eddie James

Thanks for the patch Eddie.

Quoted 14 lines · Eddie James

From: "Edward A. James" <[email protected]>

This patchset provides an in-kernel API for the SBEFIFO driver and the first user, a driver to access the P9 OCC hardware. There are also some potential devicetree bindings for the SBEFIFO and OCC devices.

The OCC provides a read/write interface, but only performs a single, atomic, OCC "command and response" operation per write operation. The driver cannot perform multiple writes in a row without first reading back to the user the response generated for each command. I can't see a use case for doing multiple commands without fetching the response.

Quoted 5 lines

Any feedback, particularly on how to improve the dt bindings or how to inform the OCC client that the SBEFIFO device has gone away (via unbinding FSI, or other means), would be helpful.

Edward A. James (3):

Since un-merged patches are needed for this to apply I think the normal thing to do would have been for you and I to co-ordinate and one of us sent the entire patchset (sbefifo + these three patches).

Lets do that when we re-submit for 4.10.

Anyone else, please don’t let this discourage you from providing cursory thoughts especially on the DT bindings.

Quoted 16 lines

drivers: fsi: sbefifo: Add in-kernel API
drivers: fsi: sbefifo: Add OCC driver
ARM: dts: aspeed: witherspoon: Add example sbefifo and occfifo devices

arch/arm/boot/dts/aspeed-bmc-opp-witherspoon.dts |  16 +
drivers/fsi/Kconfig                              |  10 +
drivers/fsi/Makefile                             |   1 +
drivers/fsi/fsi-sbefifo.c                        | 173 +++++-
drivers/fsi/occfifo.c                            | 649 +++++++++++++++++++++++
include/linux/fsi-sbefifo.h                      |  41 ++
6 files changed, 861 insertions(+), 29 deletions(-)

create mode 100644 drivers/fsi/occfifo.c
create mode 100644 include/linux/fsi-sbefifo.h

Signature · 1 line
1.8.3.1