summaryrefslogtreecommitdiffstats
path: root/meta-phosphor/common/recipes-kernel/linux/linux-obmc/linux-dev-4.10-2-3-drivers-fsi-sbefifo-Add-OCC-driver.patch
diff options
context:
space:
mode:
Diffstat (limited to 'meta-phosphor/common/recipes-kernel/linux/linux-obmc/linux-dev-4.10-2-3-drivers-fsi-sbefifo-Add-OCC-driver.patch')
-rw-r--r--meta-phosphor/common/recipes-kernel/linux/linux-obmc/linux-dev-4.10-2-3-drivers-fsi-sbefifo-Add-OCC-driver.patch694
1 files changed, 0 insertions, 694 deletions
diff --git a/meta-phosphor/common/recipes-kernel/linux/linux-obmc/linux-dev-4.10-2-3-drivers-fsi-sbefifo-Add-OCC-driver.patch b/meta-phosphor/common/recipes-kernel/linux/linux-obmc/linux-dev-4.10-2-3-drivers-fsi-sbefifo-Add-OCC-driver.patch
deleted file mode 100644
index 5c53dadc4..000000000
--- a/meta-phosphor/common/recipes-kernel/linux/linux-obmc/linux-dev-4.10-2-3-drivers-fsi-sbefifo-Add-OCC-driver.patch
+++ /dev/null
@@ -1,694 +0,0 @@
-From patchwork Fri May 12 19:38:19 2017
-Content-Type: text/plain; charset="utf-8"
-MIME-Version: 1.0
-Content-Transfer-Encoding: 7bit
-Subject: [linux,dev-4.10,2/3] drivers: fsi: sbefifo: Add OCC driver
-From: eajames@linux.vnet.ibm.com
-X-Patchwork-Id: 761838
-Message-Id: <1494617900-32369-3-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:19 -0500
-
-From: "Edward A. James" <eajames@us.ibm.com>
-
-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 SBEIFO 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.
-
-Signed-off-by: Edward A. James <eajames@us.ibm.com>
----
- drivers/fsi/Kconfig | 9 +
- drivers/fsi/Makefile | 1 +
- drivers/fsi/occ.c | 625 +++++++++++++++++++++++++++++++++++++++++++++++++++
- 3 files changed, 635 insertions(+)
- create mode 100644 drivers/fsi/occ.c
-
-diff --git a/drivers/fsi/Kconfig b/drivers/fsi/Kconfig
-index 39527fa..f3d8593 100644
---- a/drivers/fsi/Kconfig
-+++ b/drivers/fsi/Kconfig
-@@ -36,6 +36,15 @@ config FSI_SBEFIFO
- ---help---
- This option enables an FSI based SBEFIFO device driver.
-
-+if FSI_SBEFIFO
-+
-+config OCCFIFO
-+ tristate "OCC SBEFIFO client device driver"
-+ ---help---
-+ This option enables an SBEFIFO based OCC device driver.
-+
-+endif
-+
- endif
-
- endmenu
-diff --git a/drivers/fsi/Makefile b/drivers/fsi/Makefile
-index 851182e..336d9d5 100644
---- a/drivers/fsi/Makefile
-+++ b/drivers/fsi/Makefile
-@@ -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
-diff --git a/drivers/fsi/occ.c b/drivers/fsi/occ.c
-new file mode 100644
-index 0000000..74272c8
---- /dev/null
-+++ b/drivers/fsi/occ.c
-@@ -0,0 +1,625 @@
-+/*
-+ * Copyright 2017 IBM Corp.
-+ *
-+ * This program is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ */
-+
-+#include <asm/unaligned.h>
-+#include <linux/device.h>
-+#include <linux/err.h>
-+#include <linux/fsi-sbefifo.h>
-+#include <linux/init.h>
-+#include <linux/kernel.h>
-+#include <linux/miscdevice.h>
-+#include <linux/module.h>
-+#include <linux/of.h>
-+#include <linux/platform_device.h>
-+#include <linux/slab.h>
-+#include <linux/uaccess.h>
-+#include <linux/wait.h>
-+#include <linux/workqueue.h>
-+
-+#define OCC_SRAM_BYTES 4096
-+#define OCC_CMD_DATA_BYTES 4090
-+#define OCC_RESP_DATA_BYTES 4089
-+
-+struct occ {
-+ struct device *sbefifo;
-+ char name[32];
-+ int idx;
-+ struct miscdevice mdev;
-+ struct list_head xfrs;
-+ spinlock_t list_lock;
-+ spinlock_t occ_lock;
-+ struct work_struct work;
-+};
-+
-+#define to_occ(x) container_of((x), struct occ, mdev)
-+
-+struct occ_command {
-+ u8 seq_no;
-+ u8 cmd_type;
-+ u16 data_length;
-+ u8 data[OCC_CMD_DATA_BYTES];
-+ u16 checksum;
-+};
-+
-+struct occ_response {
-+ u8 seq_no;
-+ u8 cmd_type;
-+ u8 return_status;
-+ u16 data_length;
-+ u8 data[OCC_RESP_DATA_BYTES];
-+ u16 checksum;
-+};
-+
-+struct occ_xfr;
-+
-+enum {
-+ CLIENT_NONBLOCKING,
-+};
-+
-+struct occ_client {
-+ struct occ *occ;
-+ struct occ_xfr *xfr;
-+ spinlock_t lock;
-+ wait_queue_head_t wait;
-+ size_t read_offset;
-+ unsigned long flags;
-+};
-+
-+enum {
-+ XFR_IN_PROGRESS,
-+ XFR_COMPLETE,
-+ XFR_CANCELED,
-+ XFR_WAITING,
-+};
-+
-+struct occ_xfr {
-+ struct list_head link;
-+ struct occ_client *client;
-+ int rc;
-+ u8 buf[OCC_SRAM_BYTES];
-+ size_t cmd_data_length;
-+ size_t resp_data_length;
-+ unsigned long flags;
-+};
-+
-+static struct workqueue_struct *occ_wq;
-+
-+static DEFINE_IDA(occ_ida);
-+
-+static void occ_enqueue_xfr(struct occ_xfr *xfr)
-+{
-+ int empty;
-+ struct occ *occ = xfr->client->occ;
-+
-+ spin_lock_irq(&occ->list_lock);
-+ empty = list_empty(&occ->xfrs);
-+ list_add_tail(&xfr->link, &occ->xfrs);
-+ spin_unlock(&occ->list_lock);
-+
-+ if (empty)
-+ queue_work(occ_wq, &occ->work);
-+}
-+
-+static int occ_open(struct inode *inode, struct file *file)
-+{
-+ struct occ_client *client;
-+ struct miscdevice *mdev = file->private_data;
-+ struct occ *occ = to_occ(mdev);
-+
-+ client = kzalloc(sizeof(*client), GFP_KERNEL);
-+ if (!client)
-+ return -ENOMEM;
-+
-+ client->occ = occ;
-+ spin_lock_init(&client->lock);
-+ init_waitqueue_head(&client->wait);
-+
-+ if (file->f_flags & O_NONBLOCK)
-+ set_bit(CLIENT_NONBLOCKING, &client->flags);
-+
-+ file->private_data = client;
-+
-+ return 0;
-+}
-+
-+static ssize_t occ_read(struct file *file, char __user *buf, size_t len,
-+ loff_t *offset)
-+{
-+ int rc;
-+ size_t bytes;
-+ struct occ_xfr *xfr;
-+ struct occ_client *client = file->private_data;
-+
-+ if (!access_ok(VERIFY_WRITE, buf, len))
-+ return -EFAULT;
-+
-+ if (len > OCC_SRAM_BYTES)
-+ return -EINVAL;
-+
-+ spin_lock_irq(&client->lock);
-+ if (!client->xfr) {
-+ /* we just finished reading all data, return 0 */
-+ if (client->read_offset) {
-+ rc = 0;
-+ client->read_offset = 0;
-+ } else
-+ rc = -ENOMSG;
-+
-+ goto done;
-+ }
-+
-+ xfr = client->xfr;
-+
-+ if (!test_bit(XFR_COMPLETE, &xfr->flags)) {
-+ if (client->flags & CLIENT_NONBLOCKING) {
-+ rc = -ERESTARTSYS;
-+ goto done;
-+ }
-+
-+ set_bit(XFR_WAITING, &xfr->flags);
-+ spin_unlock(&client->lock);
-+
-+ rc = wait_event_interruptible(client->wait,
-+ test_bit(XFR_COMPLETE, &xfr->flags) ||
-+ test_bit(XFR_CANCELED, &xfr->flags));
-+
-+ spin_lock_irq(&client->lock);
-+ if (test_bit(XFR_CANCELED, &xfr->flags)) {
-+ kfree(xfr);
-+ spin_unlock(&client->lock);
-+ kfree(client);
-+ return -EBADFD;
-+ }
-+
-+ clear_bit(XFR_WAITING, &xfr->flags);
-+ if (!test_bit(XFR_COMPLETE, &xfr->flags)) {
-+ rc = -EINTR;
-+ goto done;
-+ }
-+ }
-+
-+ if (xfr->rc) {
-+ rc = xfr->rc;
-+ goto done;
-+ }
-+
-+ bytes = min(len, xfr->resp_data_length - client->read_offset);
-+ if (copy_to_user(buf, &xfr->buf[client->read_offset], bytes)) {
-+ rc = -EFAULT;
-+ goto done;
-+ }
-+
-+ client->read_offset += bytes;
-+
-+ /* xfr done */
-+ if (client->read_offset == xfr->resp_data_length) {
-+ kfree(xfr);
-+ client->xfr = NULL;
-+ }
-+
-+ rc = bytes;
-+
-+done:
-+ spin_unlock(&client->lock);
-+ return rc;
-+}
-+
-+static ssize_t occ_write(struct file *file, const char __user *buf,
-+ size_t len, loff_t *offset)
-+{
-+ int rc;
-+ struct occ_xfr *xfr;
-+ struct occ_client *client = file->private_data;
-+
-+ if (!access_ok(VERIFY_READ, buf, len))
-+ return -EFAULT;
-+
-+ if (len > OCC_SRAM_BYTES)
-+ return -EINVAL;
-+
-+ spin_lock_irq(&client->lock);
-+ if (client->xfr) {
-+ rc = -EDEADLK;
-+ goto done;
-+ }
-+
-+ xfr = kzalloc(sizeof(*xfr), GFP_KERNEL);
-+ if (!xfr) {
-+ rc = -ENOMEM;
-+ goto done;
-+ }
-+
-+ if (copy_from_user(xfr->buf, buf, len)) {
-+ kfree(xfr);
-+ rc = -EFAULT;
-+ goto done;
-+ }
-+
-+ xfr->client = client;
-+ xfr->cmd_data_length = len;
-+ client->xfr = xfr;
-+ client->read_offset = 0;
-+
-+ occ_enqueue_xfr(xfr);
-+
-+ rc = len;
-+
-+done:
-+ spin_unlock(&client->lock);
-+ return rc;
-+}
-+
-+static int occ_release(struct inode *inode, struct file *file)
-+{
-+ struct occ_xfr *xfr;
-+ struct occ_client *client = file->private_data;
-+ struct occ *occ = client->occ;
-+
-+ spin_lock_irq(&client->lock);
-+ xfr = client->xfr;
-+ if (!xfr) {
-+ spin_unlock(&client->lock);
-+ kfree(client);
-+ return 0;
-+ }
-+
-+ spin_lock_irq(&occ->list_lock);
-+ set_bit(XFR_CANCELED, &xfr->flags);
-+ if (!test_bit(XFR_IN_PROGRESS, &xfr->flags)) {
-+ /* already deleted from list if complete */
-+ if (!test_bit(XFR_COMPLETE, &xfr->flags))
-+ list_del(&xfr->link);
-+
-+ spin_unlock(&occ->list_lock);
-+
-+ if (test_bit(XFR_WAITING, &xfr->flags)) {
-+ /* blocking read; let reader clean up */
-+ wake_up_interruptible(&client->wait);
-+ spin_unlock(&client->lock);
-+ return 0;
-+ }
-+
-+ kfree(xfr);
-+ spin_unlock(&client->lock);
-+ kfree(client);
-+ return 0;
-+ }
-+
-+ /* operation is in progress; let worker clean up*/
-+ spin_unlock(&occ->list_lock);
-+ spin_unlock(&client->lock);
-+ return 0;
-+}
-+
-+static const struct file_operations occ_fops = {
-+ .owner = THIS_MODULE,
-+ .open = occ_open,
-+ .read = occ_read,
-+ .write = occ_write,
-+ .release = occ_release,
-+};
-+
-+static int occ_getscom(struct device *sbefifo, u32 address, u8 *data)
-+{
-+ int rc;
-+ u32 buf[4];
-+ struct sbefifo_client *client;
-+ const size_t len = sizeof(buf);
-+
-+ buf[0] = cpu_to_be32(0x4);
-+ buf[1] = cpu_to_be32(0xa201);
-+ buf[2] = 0;
-+ buf[3] = cpu_to_be32(address);
-+
-+ client = sbefifo_drv_open(sbefifo, 0);
-+ if (!client)
-+ return -ENODEV;
-+
-+ rc = sbefifo_drv_write(client, (const char *)buf, len);
-+ if (rc < 0)
-+ goto done;
-+ else if (rc != len) {
-+ rc = -EMSGSIZE;
-+ goto done;
-+ }
-+
-+ rc = sbefifo_drv_read(client, (char *)buf, len);
-+ if (rc < 0)
-+ goto done;
-+ else if (rc != len) {
-+ rc = -EMSGSIZE;
-+ goto done;
-+ }
-+
-+ /* check for good response */
-+ if ((be32_to_cpu(buf[2]) >> 16) != 0xC0DE) {
-+ rc = -EFAULT;
-+ goto done;
-+ }
-+
-+ rc = 0;
-+
-+ memcpy(data, buf, sizeof(u64));
-+
-+done:
-+ sbefifo_drv_release(client);
-+ return rc;
-+}
-+
-+static int occ_putscom(struct device *sbefifo, u32 address, u8 *data)
-+{
-+ int rc;
-+ u32 buf[6];
-+ struct sbefifo_client *client;
-+ const size_t len = sizeof(buf);
-+
-+ buf[0] = cpu_to_be32(0x6);
-+ buf[1] = cpu_to_be32(0xa202);
-+ buf[2] = 0;
-+ buf[3] = cpu_to_be32(address);
-+ memcpy(&buf[4], data, sizeof(u64));
-+
-+ client = sbefifo_drv_open(sbefifo, 0);
-+ if (!client)
-+ return -ENODEV;
-+
-+ rc = sbefifo_drv_write(client, (const char *)buf, len);
-+ if (rc < 0)
-+ goto done;
-+ else if (rc != len) {
-+ rc = -EMSGSIZE;
-+ goto done;
-+ }
-+
-+ rc = 0;
-+
-+ /* ignore response */
-+ sbefifo_drv_read(client, (char *)buf, len);
-+
-+done:
-+ sbefifo_drv_release(client);
-+ return rc;
-+}
-+
-+static int occ_putscom_u32(struct device *sbefifo, u32 address, u32 data0,
-+ u32 data1)
-+{
-+ u8 buf[8];
-+ u32 raw_data0 = cpu_to_be32(data0), raw_data1 = cpu_to_be32(data1);
-+
-+ memcpy(buf, &raw_data0, 4);
-+ memcpy(buf + 4, &raw_data1, 4);
-+
-+ return occ_putscom(sbefifo, address, buf);
-+}
-+
-+static void occ_worker(struct work_struct *work)
-+{
-+ int i, empty, canceled, waiting, rc;
-+ u16 resp_data_length;
-+ struct occ *occ = container_of(work, struct occ, work);
-+ struct device *sbefifo = occ->sbefifo;
-+ struct occ_client *client;
-+ struct occ_xfr *xfr;
-+ struct occ_response *resp;
-+
-+again:
-+ spin_lock_irq(&occ->list_lock);
-+ xfr = list_first_entry(&occ->xfrs, struct occ_xfr, link);
-+ if (!xfr) {
-+ spin_unlock(&occ->list_lock);
-+ return;
-+ }
-+
-+ set_bit(XFR_IN_PROGRESS, &xfr->flags);
-+ spin_unlock(&occ->list_lock);
-+
-+ resp = (struct occ_response *)xfr->buf;
-+
-+ spin_lock_irq(&occ->occ_lock);
-+
-+ /* set address reg to occ sram command buffer */
-+ rc = occ_putscom_u32(sbefifo, 0x6D050, 0xFFFBE000, 0);
-+ if (rc)
-+ goto done;
-+
-+ /* write cmd data */
-+ for (i = 0; i < xfr->cmd_data_length; i += 8) {
-+ rc = occ_putscom(sbefifo, 0x6D055, &xfr->buf[i]);
-+ if (rc)
-+ goto done;
-+ }
-+
-+ /* trigger attention */
-+ rc = occ_putscom_u32(sbefifo, 0x6D035, 0x20010000, 0);
-+ if (rc)
-+ goto done;
-+
-+ /* set address reg to occ sram response buffer */
-+ rc = occ_putscom_u32(sbefifo, 0x6D050, 0xFFFBF000, 0);
-+ if (rc)
-+ goto done;
-+
-+ rc = occ_getscom(sbefifo, 0x6D055, xfr->buf);
-+ if (rc)
-+ goto done;
-+
-+ xfr->resp_data_length += 8;
-+
-+ resp_data_length = be16_to_cpu(get_unaligned(&resp->data_length));
-+ if (resp_data_length > OCC_RESP_DATA_BYTES) {
-+ rc = -EFAULT;
-+ goto done;
-+ }
-+
-+ /* already read 3 bytes of resp data, but also need 2 bytes chksum */
-+ for (i = 8; i < resp_data_length + 7; i += 8) {
-+ rc = occ_getscom(sbefifo, 0x6D055, &xfr->buf[i]);
-+ if (rc)
-+ goto done;
-+
-+ xfr->resp_data_length += 8;
-+ }
-+
-+ /* no errors, got all data */
-+ xfr->resp_data_length = resp_data_length + 7;
-+
-+done:
-+ spin_unlock(&occ->occ_lock);
-+
-+ xfr->rc = rc;
-+ client = xfr->client;
-+
-+ /* lock client to prevent race with read() */
-+ spin_lock_irq(&client->lock);
-+ set_bit(XFR_COMPLETE, &xfr->flags);
-+ waiting = test_bit(XFR_WAITING, &xfr->flags);
-+ spin_unlock(&client->lock);
-+
-+ spin_lock_irq(&occ->list_lock);
-+ clear_bit(XFR_IN_PROGRESS, &xfr->flags);
-+ list_del(&xfr->link);
-+ empty = list_empty(&occ->xfrs);
-+ canceled = test_bit(XFR_CANCELED, &xfr->flags);
-+ spin_unlock(&occ->list_lock);
-+
-+ if (waiting)
-+ wake_up_interruptible(&client->wait);
-+ else if (canceled) {
-+ kfree(xfr);
-+ kfree(xfr->client);
-+ }
-+
-+ if (!empty)
-+ goto again;
-+}
-+
-+static int occ_probe(struct platform_device *pdev)
-+{
-+ int rc;
-+ u32 reg;
-+ struct occ *occ;
-+ struct device *dev = &pdev->dev;
-+
-+ dev_info(dev, "Found occ device\n");
-+ occ = devm_kzalloc(dev, sizeof(*occ), GFP_KERNEL);
-+ if (!occ)
-+ return -ENOMEM;
-+
-+ occ->sbefifo = dev->parent;
-+ INIT_LIST_HEAD(&occ->xfrs);
-+ spin_lock_init(&occ->list_lock);
-+ spin_lock_init(&occ->occ_lock);
-+ INIT_WORK(&occ->work, occ_worker);
-+
-+ if (dev->of_node) {
-+ rc = of_property_read_u32(dev->of_node, "reg", &reg);
-+ if (!rc) {
-+ /* make sure we don't have a duplicate from dts */
-+ occ->idx = ida_simple_get(&occ_ida, reg, reg + 1,
-+ GFP_KERNEL);
-+ if (occ->idx < 0)
-+ occ->idx = ida_simple_get(&occ_ida, 1, INT_MAX,
-+ GFP_KERNEL);
-+ } else
-+ occ->idx = ida_simple_get(&occ_ida, 1, INT_MAX,
-+ GFP_KERNEL);
-+ } else
-+ occ->idx = ida_simple_get(&occ_ida, 1, INT_MAX, GFP_KERNEL);
-+
-+ snprintf(occ->name, sizeof(occ->name), "occ%d", occ->idx);
-+ occ->mdev.fops = &occ_fops;
-+ occ->mdev.minor = MISC_DYNAMIC_MINOR;
-+ occ->mdev.name = occ->name;
-+ occ->mdev.parent = dev;
-+
-+ rc = misc_register(&occ->mdev);
-+ if (rc) {
-+ dev_err(dev, "failed to register miscdevice\n");
-+ return rc;
-+ }
-+
-+ platform_set_drvdata(pdev, occ);
-+
-+ return 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;
-+
-+ misc_deregister(&occ->mdev);
-+
-+ spin_lock_irq(&occ->list_lock);
-+ list_for_each_entry_safe(xfr, tmp, &occ->xfrs, link) {
-+ client = xfr->client;
-+ set_bit(XFR_CANCELED, &xfr->flags);
-+
-+ if (!test_bit(XFR_IN_PROGRESS, &xfr->flags)) {
-+ list_del(&xfr->link);
-+
-+ spin_lock_irq(&client->lock);
-+ if (test_bit(XFR_WAITING, &xfr->flags)) {
-+ wake_up_interruptible(&client->wait);
-+ spin_unlock(&client->lock);
-+ } else {
-+ kfree(xfr);
-+ spin_unlock(&client->lock);
-+ kfree(client);
-+ }
-+ }
-+ }
-+ spin_unlock(&occ->list_lock);
-+
-+ flush_work(&occ->work);
-+
-+ 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 <eajames@us.ibm.com>");
-+MODULE_DESCRIPTION("BMC P9 OCC driver");
-+MODULE_LICENSE("GPL");
-+
OpenPOWER on IntegriCloud