diff options
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.patch | 694 |
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", ®); -+ 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"); -+ |