Skip to contentSkip to conversation

drivers: fsi: Add OCC driver

PATCH Applied Eddie James 2 participants 4 messages Jun 2, 2017 – Jun 5, 2017 FSI

lore
Share & tools
lore
#0 lore raw

v4 cover drivers: fsi: Add OCC driver

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

This patch series adds a driver to communicate with the POWER processor On-Chip Controller (OCC), from a service processor. Communication is performed via the SBEFIFO driver, which required a new in-kernel API.

An application to test the OCC driver can be found at
https://github.com/eddiejames/occtest

Changes since v3:
* Add include file for occ driver.

Changes since v2:
* Switch to use getSRAM/putSRAM over the SBE. This is significantly more
efficient and also circumvents the need to set up OCB channels manually.
* Include the in-kernel API (for hwmon driver).
* xfr struct embedded in client
* add sequence number and checksum to command

Edward A. James (2):
drivers: fsi: sbefifo: Add in-kernel API
drivers: fsi: sbefifo: Add OCC driver

 drivers/fsi/Kconfig         |   6 +
 drivers/fsi/Makefile        |   1 +
 drivers/fsi/fsi-sbefifo.c   | 161 +++++++--
 drivers/fsi/occ.c           | 783 ++++++++++++++++++++++++++++++++++++++++++++
 include/linux/fsi-sbefifo.h |  30 ++
 include/linux/occ.h         |  27 ++
 6 files changed, 978 insertions(+), 30 deletions(-)
 create mode 100644 drivers/fsi/occ.c
 create mode 100644 include/linux/fsi-sbefifo.h
 create mode 100644 include/linux/occ.h
Signature · 1 line
1.8.3.1
#1 lore raw

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

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

Refactor user interface of sbefifo driver to allow for an in-kernel API as well. Add exported functions for other drivers to call, and add an include file with those functions. Also parse the device tree for child nodes and create child platform devices accordingly.

---
 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
drivers/fsi/fsi-sbefifo.c +131 -30
index b49aec2..fbc8dc5 100644
@@ -18,6 +18,8 @@
 #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 +84,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 +509,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 +534,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 +564,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 +593,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 +626,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 +666,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 +701,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 +733,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);
@@ -755,6 +840,19 @@ static int sbefifo_probe(struct device *dev)
 			(unsigned long)sbefifo);
 
 	list_add(&sbefifo->link, &sbefifo_fifos);
+
+	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");
+		}
+	}
+	
 	return misc_register(&sbefifo->mdev);
 }
 
@@ -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);
include/linux/fsi-sbefifo.h new file +30 -0
new file mode 100644
index 0000000..1b46c63
@@ -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__ */
Signature · 1 line
1.8.3.1
#2 lore raw

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

In reply to Eddie James

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

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 SBEFIFO 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.

---
 drivers/fsi/Kconfig  |   6 +
 drivers/fsi/Makefile |   1 +
 drivers/fsi/occ.c    | 783 +++++++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/occ.h  |  27 ++
 4 files changed, 817 insertions(+)
 create mode 100644 drivers/fsi/occ.c
 create mode 100644 include/linux/occ.h
drivers/fsi/Kconfig +6 -0
index 39527fa..d56b582 100644
@@ -36,6 +36,12 @@ config FSI_SBEFIFO
 	---help---
 	This option enables an FSI based SBEFIFO device driver.
 
+config OCCFIFO
+	tristate "OCC SBEFIFO client device driver"
+	depends on FSI_SBEFIFO
+	---help---
+	This option enables an SBEFIFO based OCC device driver.
+
 endif
 
 endmenu
drivers/fsi/Makefile +1 -0
index 851182e..7f5ca61 100644
@@ -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
drivers/fsi/occ.c new file +783 -0

783 lines not on this page · raw message

include/linux/occ.h new file +27 -0
new file mode 100644
index 0000000..d78332c
@@ -0,0 +1,27 @@
+/*
+ * 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 __OCC_H__
+#define __OCC_H__
+
+struct device;
+struct occ_client;
+
+extern struct occ_client *occ_drv_open(struct device *dev,
+				       unsigned long flags);
+extern int occ_drv_read(struct occ_client *client, char *buf, size_t len);
+extern int occ_drv_write(struct occ_client *client, const char *buf,
+			 size_t len);
+extern void occ_drv_release(struct occ_client *client);
+
+#endif /* __OCC_H__ */
Signature · 1 line
1.8.3.1
Applied
#3 lore raw

Re: [PATCH linux dev-4.10 v4 2/2] drivers: fsi: sbefifo: Add OCC driver

Quoted 7 lines · Eddie James

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

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 SBEFIFO driver to get hardware access to the OCC SRAM.

Quoted 7 lines

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.

I applied this to dev-4.10.

Note that there are a number of outstanding issues we must address before it is considered production quality. I propose we do that review when you submit it to the upstream lists.

drivers/fsi/occ.c line 735

Quoted 6 lines
drivers/fsi/occ.c new file +6 -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;

This created some warnings:

drivers/fsi/occ.c: In function ‘occ_remove’:
drivers/fsi/occ.c:735:21: warning: unused variable ‘client’ [-Wunused-variable]

  struct occ_client *client;
                     ^~~~~~
drivers/fsi/occ.c:733:24: warning: unused variable ‘tmp’ [-Wunused-variable]
  struct occ_xfr *xfr, *tmp;
                        ^~~
drivers/fsi/occ.c:733:18: warning: unused variable ‘xfr’ [-Wunused-variable]
  struct occ_xfr *xfr, *tmp;
                  ^~~

I fixed them for you. Please make sure you fix them in your tree before submitting upstream.

Cheers,

Joel

Quoted 84 lines
drivers/fsi/occ.c new file +48 -0
+
+       flush_work(&occ->work);
+
+       misc_deregister(&occ->mdev);
+
+       device_for_each_child(&pdev->dev, NULL, occ_unregister_child);
+
+       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 <[email protected]>");
+MODULE_DESCRIPTION("BMC P9 OCC driver");
+MODULE_LICENSE("GPL");
include/linux/occ.h new file +27 -1
new file mode 100644
index 0000000..d78332c
@@ -0,0 +1,27 @@
+/*
+ * 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 __OCC_H__
+#define __OCC_H__
+
+struct device;
+struct occ_client;
+
+extern struct occ_client *occ_drv_open(struct device *dev,
+                                      unsigned long flags);
+extern int occ_drv_read(struct occ_client *client, char *buf, size_t len);
+extern int occ_drv_write(struct occ_client *client, const char *buf,
+                        size_t len);
+extern void occ_drv_release(struct occ_client *client);
+
+#endif /* __OCC_H__ */
--

1.8.3.1