summaryrefslogtreecommitdiffstats
path: root/meta-phosphor/common/recipes-kernel/linux/linux-obmc/linux-dev-4.10-drivers-fsi-Add-FSI-SBEFIFO-driver.patch
diff options
context:
space:
mode:
Diffstat (limited to 'meta-phosphor/common/recipes-kernel/linux/linux-obmc/linux-dev-4.10-drivers-fsi-Add-FSI-SBEFIFO-driver.patch')
-rw-r--r--meta-phosphor/common/recipes-kernel/linux/linux-obmc/linux-dev-4.10-drivers-fsi-Add-FSI-SBEFIFO-driver.patch899
1 files changed, 0 insertions, 899 deletions
diff --git a/meta-phosphor/common/recipes-kernel/linux/linux-obmc/linux-dev-4.10-drivers-fsi-Add-FSI-SBEFIFO-driver.patch b/meta-phosphor/common/recipes-kernel/linux/linux-obmc/linux-dev-4.10-drivers-fsi-Add-FSI-SBEFIFO-driver.patch
deleted file mode 100644
index b29e0e933..000000000
--- a/meta-phosphor/common/recipes-kernel/linux/linux-obmc/linux-dev-4.10-drivers-fsi-Add-FSI-SBEFIFO-driver.patch
+++ /dev/null
@@ -1,899 +0,0 @@
-From patchwork Thu May 11 02:52:53 2017
-Content-Type: text/plain; charset="utf-8"
-MIME-Version: 1.0
-Content-Transfer-Encoding: 7bit
-Subject: [linux,dev-4.10] drivers: fsi: Add FSI SBEFIFO driver
-From: eajames@linux.vnet.ibm.com
-X-Patchwork-Id: 760920
-Message-Id: <1494471173-6077-1-git-send-email-eajames@linux.vnet.ibm.com>
-To: openbmc@lists.ozlabs.org
-Cc: "Edward A. James" <eajames@us.ibm.com>, bradleyb@fuzziesquirrel.com
-Date: Wed, 10 May 2017 21:52:53 -0500
-
-From: "Edward A. James" <eajames@us.ibm.com>
-
-IBM POWER9 processors contain some embedded hardware and software bits
-collectively referred to as the self boot engine (SBE). One role of
-the SBE is to act as a proxy that provides access to the registers of
-the POWER chip from other (embedded) systems.
-
-The POWER9 chip contains a hardware frontend for communicating with
-the SBE from remote systems called the SBEFIFO. The SBEFIFO logic
-is contained within an FSI CFAM (see Documentation/fsi) and as such
-the driver implements an FSI bus device.
-
-The SBE expects to communicate using a defined wire protocol; however,
-the driver knows nothing of the protocol and only provides raw access
-to the fifo device to userspace applications wishing to communicate with
-the SBE using the wire protocol.
-
-The SBEFIFO consists of two hardware fifos. The upstream fifo is used
-by the driver to transfer data to the SBE on the POWER chip, from the
-system hosting the driver. The downstream fifo is used by the driver to
-transfer data from the SBE on the power chip to the system hosting the
-driver.
-
-Signed-off-by: Edward A. James <eajames@us.ibm.com>
-Signed-off-by: Brad Bishop <bradleyb@fuzziesquirrel.com>
----
- drivers/fsi/Kconfig | 5 +
- drivers/fsi/Makefile | 1 +
- drivers/fsi/fsi-sbefifo.c | 824 ++++++++++++++++++++++++++++++++++++++++++++++
- 3 files changed, 830 insertions(+)
- create mode 100644 drivers/fsi/fsi-sbefifo.c
-
-diff --git a/drivers/fsi/Kconfig b/drivers/fsi/Kconfig
-index fc031ac..39527fa 100644
---- a/drivers/fsi/Kconfig
-+++ b/drivers/fsi/Kconfig
-@@ -31,6 +31,11 @@ config FSI_SCOM
- ---help---
- This option enables an FSI based SCOM device driver.
-
-+config FSI_SBEFIFO
-+ tristate "SBEFIFO FSI client device driver"
-+ ---help---
-+ This option enables an FSI based SBEFIFO device driver.
-+
- endif
-
- endmenu
-diff --git a/drivers/fsi/Makefile b/drivers/fsi/Makefile
-index 65eb99d..851182e 100644
---- a/drivers/fsi/Makefile
-+++ b/drivers/fsi/Makefile
-@@ -3,3 +3,4 @@ obj-$(CONFIG_FSI) += fsi-core.o
- 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
-diff --git a/drivers/fsi/fsi-sbefifo.c b/drivers/fsi/fsi-sbefifo.c
-new file mode 100644
-index 0000000..b49aec2
---- /dev/null
-+++ b/drivers/fsi/fsi-sbefifo.c
-@@ -0,0 +1,824 @@
-+/*
-+ * 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.
-+ */
-+
-+#include <linux/delay.h>
-+#include <linux/errno.h>
-+#include <linux/idr.h>
-+#include <linux/fsi.h>
-+#include <linux/list.h>
-+#include <linux/miscdevice.h>
-+#include <linux/module.h>
-+#include <linux/poll.h>
-+#include <linux/sched.h>
-+#include <linux/slab.h>
-+#include <linux/timer.h>
-+#include <linux/uaccess.h>
-+
-+/*
-+ * The SBEFIFO is a pipe-like FSI device for communicating with
-+ * the self boot engine on POWER processors.
-+ */
-+
-+#define DEVICE_NAME "sbefifo"
-+#define FSI_ENGID_SBE 0x22
-+#define SBEFIFO_BUF_CNT 32
-+
-+#define SBEFIFO_UP 0x00 /* Up register offset */
-+#define SBEFIFO_DWN 0x40 /* Down register offset */
-+
-+#define SBEFIFO_STS 0x04
-+#define SBEFIFO_EMPTY BIT(20)
-+#define SBEFIFO_EOT_RAISE 0x08
-+#define SBEFIFO_EOT_MAGIC 0xffffffff
-+#define SBEFIFO_EOT_ACK 0x14
-+
-+struct sbefifo {
-+ struct timer_list poll_timer;
-+ struct fsi_device *fsi_dev;
-+ struct miscdevice mdev;
-+ wait_queue_head_t wait;
-+ struct list_head link;
-+ struct list_head xfrs;
-+ struct kref kref;
-+ spinlock_t lock;
-+ char name[32];
-+ int idx;
-+ int rc;
-+};
-+
-+struct sbefifo_buf {
-+ u32 buf[SBEFIFO_BUF_CNT];
-+ unsigned long flags;
-+#define SBEFIFO_BUF_FULL 1
-+ u32 *rpos;
-+ u32 *wpos;
-+};
-+
-+struct sbefifo_xfr {
-+ struct sbefifo_buf *rbuf;
-+ struct sbefifo_buf *wbuf;
-+ struct list_head client;
-+ struct list_head xfrs;
-+ unsigned long flags;
-+#define SBEFIFO_XFR_WRITE_DONE 1
-+#define SBEFIFO_XFR_RESP_PENDING 2
-+#define SBEFIFO_XFR_COMPLETE 3
-+#define SBEFIFO_XFR_CANCEL 4
-+};
-+
-+struct sbefifo_client {
-+ struct sbefifo_buf rbuf;
-+ struct sbefifo_buf wbuf;
-+ struct list_head xfrs;
-+ struct sbefifo *dev;
-+ struct kref kref;
-+};
-+
-+static struct list_head sbefifo_fifos;
-+
-+static DEFINE_IDA(sbefifo_ida);
-+
-+static int sbefifo_inw(struct sbefifo *sbefifo, int reg, u32 *word)
-+{
-+ int rc;
-+ u32 raw_word;
-+
-+ rc = fsi_device_read(sbefifo->fsi_dev, reg, &raw_word,
-+ sizeof(raw_word));
-+ if (rc)
-+ return rc;
-+
-+ *word = be32_to_cpu(raw_word);
-+ return 0;
-+}
-+
-+static int sbefifo_outw(struct sbefifo *sbefifo, int reg, u32 word)
-+{
-+ u32 raw_word = cpu_to_be32(word);
-+
-+ return fsi_device_write(sbefifo->fsi_dev, reg, &raw_word,
-+ sizeof(raw_word));
-+}
-+
-+static int sbefifo_readw(struct sbefifo *sbefifo, u32 *word)
-+{
-+ return fsi_device_read(sbefifo->fsi_dev, SBEFIFO_DWN, word,
-+ sizeof(*word));
-+}
-+
-+static int sbefifo_writew(struct sbefifo *sbefifo, u32 word)
-+{
-+ return fsi_device_write(sbefifo->fsi_dev, SBEFIFO_UP, &word,
-+ sizeof(word));
-+}
-+
-+static int sbefifo_ack_eot(struct sbefifo *sbefifo)
-+{
-+ u32 discard;
-+ int ret;
-+
-+ /* Discard the EOT word. */
-+ ret = sbefifo_readw(sbefifo, &discard);
-+ if (ret)
-+ return ret;
-+
-+ return sbefifo_outw(sbefifo, SBEFIFO_DWN | SBEFIFO_EOT_ACK,
-+ SBEFIFO_EOT_MAGIC);
-+}
-+
-+static size_t sbefifo_dev_nwreadable(u32 sts)
-+{
-+ static const u32 FIFO_NTRY_CNT_MSK = 0x000f0000;
-+ static const unsigned int FIFO_NTRY_CNT_SHIFT = 16;
-+
-+ return (sts & FIFO_NTRY_CNT_MSK) >> FIFO_NTRY_CNT_SHIFT;
-+}
-+
-+static size_t sbefifo_dev_nwwriteable(u32 sts)
-+{
-+ static const size_t FIFO_DEPTH = 8;
-+
-+ return FIFO_DEPTH - sbefifo_dev_nwreadable(sts);
-+}
-+
-+static void sbefifo_buf_init(struct sbefifo_buf *buf)
-+{
-+ WRITE_ONCE(buf->rpos, buf->buf);
-+ WRITE_ONCE(buf->wpos, buf->buf);
-+}
-+
-+static size_t sbefifo_buf_nbreadable(struct sbefifo_buf *buf)
-+{
-+ size_t n;
-+ u32 *rpos = READ_ONCE(buf->rpos);
-+ u32 *wpos = READ_ONCE(buf->wpos);
-+
-+ if (test_bit(SBEFIFO_BUF_FULL, &buf->flags))
-+ n = SBEFIFO_BUF_CNT;
-+ else if (rpos <= wpos)
-+ n = wpos - rpos;
-+ else
-+ n = (buf->buf + SBEFIFO_BUF_CNT) - rpos;
-+
-+ return n << 2;
-+}
-+
-+static size_t sbefifo_buf_nbwriteable(struct sbefifo_buf *buf)
-+{
-+ size_t n;
-+ u32 *rpos = READ_ONCE(buf->rpos);
-+ u32 *wpos = READ_ONCE(buf->wpos);
-+
-+ if (test_bit(SBEFIFO_BUF_FULL, &buf->flags))
-+ n = 0;
-+ else if (wpos < rpos)
-+ n = rpos - wpos;
-+ else
-+ n = (buf->buf + SBEFIFO_BUF_CNT) - wpos;
-+
-+ return n << 2;
-+}
-+
-+/*
-+ * Update pointers and flags after doing a buffer read. Return true if the
-+ * buffer is now empty;
-+ */
-+static bool sbefifo_buf_readnb(struct sbefifo_buf *buf, size_t n)
-+{
-+ u32 *rpos = READ_ONCE(buf->rpos);
-+ u32 *wpos = READ_ONCE(buf->wpos);
-+
-+ if (n)
-+ clear_bit(SBEFIFO_BUF_FULL, &buf->flags);
-+
-+ rpos += (n >> 2);
-+ if (rpos == buf->buf + SBEFIFO_BUF_CNT)
-+ rpos = buf->buf;
-+
-+ WRITE_ONCE(buf->rpos, rpos);
-+ return rpos == wpos;
-+}
-+
-+/*
-+ * Update pointers and flags after doing a buffer write. Return true if the
-+ * buffer is now full.
-+ */
-+static bool sbefifo_buf_wrotenb(struct sbefifo_buf *buf, size_t n)
-+{
-+ u32 *rpos = READ_ONCE(buf->rpos);
-+ u32 *wpos = READ_ONCE(buf->wpos);
-+
-+ wpos += (n >> 2);
-+ if (wpos == buf->buf + SBEFIFO_BUF_CNT)
-+ wpos = buf->buf;
-+ if (wpos == rpos)
-+ set_bit(SBEFIFO_BUF_FULL, &buf->flags);
-+
-+ WRITE_ONCE(buf->wpos, wpos);
-+ return rpos == wpos;
-+}
-+
-+static void sbefifo_free(struct kref *kref)
-+{
-+ struct sbefifo *sbefifo;
-+
-+ sbefifo = container_of(kref, struct sbefifo, kref);
-+ kfree(sbefifo);
-+}
-+
-+static void sbefifo_get(struct sbefifo *sbefifo)
-+{
-+ kref_get(&sbefifo->kref);
-+}
-+
-+static void sbefifo_put(struct sbefifo *sbefifo)
-+{
-+ kref_put(&sbefifo->kref, sbefifo_free);
-+}
-+
-+static struct sbefifo_xfr *sbefifo_enq_xfr(struct sbefifo_client *client)
-+{
-+ struct sbefifo *sbefifo = client->dev;
-+ struct sbefifo_xfr *xfr;
-+
-+ xfr = kzalloc(sizeof(*xfr), GFP_KERNEL);
-+ if (!xfr)
-+ return NULL;
-+
-+ xfr->rbuf = &client->rbuf;
-+ xfr->wbuf = &client->wbuf;
-+ list_add_tail(&xfr->xfrs, &sbefifo->xfrs);
-+ list_add_tail(&xfr->client, &client->xfrs);
-+
-+ return xfr;
-+}
-+
-+static struct sbefifo_xfr *sbefifo_client_next_xfr(
-+ struct sbefifo_client *client)
-+{
-+ if (list_empty(&client->xfrs))
-+ return NULL;
-+
-+ return container_of(client->xfrs.next, struct sbefifo_xfr,
-+ client);
-+}
-+
-+static bool sbefifo_xfr_rsp_pending(struct sbefifo_client *client)
-+{
-+ struct sbefifo_xfr *xfr;
-+
-+ xfr = sbefifo_client_next_xfr(client);
-+ if (xfr && test_bit(SBEFIFO_XFR_RESP_PENDING, &xfr->flags))
-+ return true;
-+
-+ return false;
-+}
-+
-+static struct sbefifo_client *sbefifo_new_client(struct sbefifo *sbefifo)
-+{
-+ struct sbefifo_client *client;
-+
-+ client = kzalloc(sizeof(*client), GFP_KERNEL);
-+ if (!client)
-+ return NULL;
-+
-+ kref_init(&client->kref);
-+ client->dev = sbefifo;
-+ sbefifo_buf_init(&client->rbuf);
-+ sbefifo_buf_init(&client->wbuf);
-+ INIT_LIST_HEAD(&client->xfrs);
-+
-+ sbefifo_get(sbefifo);
-+
-+ return client;
-+}
-+
-+static void sbefifo_client_release(struct kref *kref)
-+{
-+ struct sbefifo_client *client;
-+ struct sbefifo_xfr *xfr;
-+
-+ client = container_of(kref, struct sbefifo_client, kref);
-+ list_for_each_entry(xfr, &client->xfrs, client) {
-+ /*
-+ * The client left with pending or running xfrs.
-+ * Cancel them.
-+ */
-+ set_bit(SBEFIFO_XFR_CANCEL, &xfr->flags);
-+ sbefifo_get(client->dev);
-+ if (mod_timer(&client->dev->poll_timer, jiffies))
-+ sbefifo_put(client->dev);
-+ }
-+
-+ sbefifo_put(client->dev);
-+ kfree(client);
-+}
-+
-+static void sbefifo_get_client(struct sbefifo_client *client)
-+{
-+ kref_get(&client->kref);
-+}
-+
-+static void sbefifo_put_client(struct sbefifo_client *client)
-+{
-+ kref_put(&client->kref, sbefifo_client_release);
-+}
-+
-+static struct sbefifo_xfr *sbefifo_next_xfr(struct sbefifo *sbefifo)
-+{
-+ struct sbefifo_xfr *xfr, *tmp;
-+
-+ list_for_each_entry_safe(xfr, tmp, &sbefifo->xfrs, xfrs) {
-+ if (unlikely(test_bit(SBEFIFO_XFR_CANCEL, &xfr->flags))) {
-+ /* Discard cancelled transfers. */
-+ list_del(&xfr->xfrs);
-+ kfree(xfr);
-+ continue;
-+ }
-+ return xfr;
-+ }
-+
-+ return NULL;
-+}
-+
-+static void sbefifo_poll_timer(unsigned long data)
-+{
-+ static const unsigned long EOT_MASK = 0x000000ff;
-+ struct sbefifo *sbefifo = (void *)data;
-+ struct sbefifo_buf *rbuf, *wbuf;
-+ struct sbefifo_xfr *xfr = NULL;
-+ struct sbefifo_buf drain;
-+ size_t devn, bufn;
-+ int eot = 0;
-+ int ret = 0;
-+ u32 sts;
-+ int i;
-+
-+ spin_lock(&sbefifo->lock);
-+ xfr = list_first_entry_or_null(&sbefifo->xfrs, struct sbefifo_xfr,
-+ xfrs);
-+ if (!xfr)
-+ goto out_unlock;
-+
-+ rbuf = xfr->rbuf;
-+ wbuf = xfr->wbuf;
-+
-+ if (unlikely(test_bit(SBEFIFO_XFR_CANCEL, &xfr->flags))) {
-+ /* The client left. */
-+ rbuf = &drain;
-+ wbuf = &drain;
-+ sbefifo_buf_init(&drain);
-+ if (!test_bit(SBEFIFO_XFR_RESP_PENDING, &xfr->flags))
-+ set_bit(SBEFIFO_XFR_WRITE_DONE, &xfr->flags);
-+ }
-+
-+ /* Drain the write buffer. */
-+ while ((bufn = sbefifo_buf_nbreadable(wbuf))) {
-+ ret = sbefifo_inw(sbefifo, SBEFIFO_UP | SBEFIFO_STS,
-+ &sts);
-+ if (ret)
-+ goto out;
-+
-+ devn = sbefifo_dev_nwwriteable(sts);
-+ if (devn == 0) {
-+ /* No open slot for write. Reschedule. */
-+ sbefifo->poll_timer.expires = jiffies +
-+ msecs_to_jiffies(500);
-+ add_timer(&sbefifo->poll_timer);
-+ goto out_unlock;
-+ }
-+
-+ devn = min_t(size_t, devn, bufn >> 2);
-+ for (i = 0; i < devn; i++) {
-+ ret = sbefifo_writew(sbefifo, *wbuf->rpos);
-+ if (ret)
-+ goto out;
-+
-+ sbefifo_buf_readnb(wbuf, 1 << 2);
-+ }
-+ }
-+
-+ /* Send EOT if the writer is finished. */
-+ if (test_and_clear_bit(SBEFIFO_XFR_WRITE_DONE, &xfr->flags)) {
-+ ret = sbefifo_outw(sbefifo,
-+ SBEFIFO_UP | SBEFIFO_EOT_RAISE,
-+ SBEFIFO_EOT_MAGIC);
-+ if (ret)
-+ goto out;
-+
-+ /* Inform reschedules that the writer is finished. */
-+ set_bit(SBEFIFO_XFR_RESP_PENDING, &xfr->flags);
-+ }
-+
-+ /* Nothing left to do if the writer is not finished. */
-+ if (!test_bit(SBEFIFO_XFR_RESP_PENDING, &xfr->flags))
-+ goto out;
-+
-+ /* Fill the read buffer. */
-+ while ((bufn = sbefifo_buf_nbwriteable(rbuf))) {
-+ ret = sbefifo_inw(sbefifo, SBEFIFO_DWN | SBEFIFO_STS, &sts);
-+ if (ret)
-+ goto out;
-+
-+ devn = sbefifo_dev_nwreadable(sts);
-+ if (devn == 0) {
-+ /* No data yet. Reschedule. */
-+ sbefifo->poll_timer.expires = jiffies +
-+ msecs_to_jiffies(500);
-+ add_timer(&sbefifo->poll_timer);
-+ goto out_unlock;
-+ }
-+
-+ /* Fill. The EOT word is discarded. */
-+ devn = min_t(size_t, devn, bufn >> 2);
-+ eot = (sts & EOT_MASK) != 0;
-+ if (eot)
-+ devn--;
-+
-+ for (i = 0; i < devn; i++) {
-+ ret = sbefifo_readw(sbefifo, rbuf->wpos);
-+ if (ret)
-+ goto out;
-+
-+ if (likely(!test_bit(SBEFIFO_XFR_CANCEL, &xfr->flags)))
-+ sbefifo_buf_wrotenb(rbuf, 1 << 2);
-+ }
-+
-+ if (eot) {
-+ ret = sbefifo_ack_eot(sbefifo);
-+ if (ret)
-+ goto out;
-+
-+ set_bit(SBEFIFO_XFR_COMPLETE, &xfr->flags);
-+ list_del(&xfr->xfrs);
-+ if (unlikely(test_bit(SBEFIFO_XFR_CANCEL,
-+ &xfr->flags)))
-+ kfree(xfr);
-+ break;
-+ }
-+ }
-+
-+out:
-+ if (unlikely(ret)) {
-+ sbefifo->rc = ret;
-+ dev_err(&sbefifo->fsi_dev->dev,
-+ "Fatal bus access failure: %d\n", ret);
-+ list_for_each_entry(xfr, &sbefifo->xfrs, xfrs)
-+ kfree(xfr);
-+ INIT_LIST_HEAD(&sbefifo->xfrs);
-+
-+ } else if (eot && sbefifo_next_xfr(sbefifo)) {
-+ sbefifo_get(sbefifo);
-+ sbefifo->poll_timer.expires = jiffies;
-+ add_timer(&sbefifo->poll_timer);
-+ }
-+
-+ sbefifo_put(sbefifo);
-+ wake_up(&sbefifo->wait);
-+
-+out_unlock:
-+ spin_unlock(&sbefifo->lock);
-+}
-+
-+static int sbefifo_open(struct inode *inode, struct file *file)
-+{
-+ struct sbefifo *sbefifo = container_of(file->private_data,
-+ struct sbefifo, mdev);
-+ struct sbefifo_client *client;
-+ int ret;
-+
-+ ret = READ_ONCE(sbefifo->rc);
-+ if (ret)
-+ return ret;
-+
-+ client = sbefifo_new_client(sbefifo);
-+ if (!client)
-+ return -ENOMEM;
-+
-+ file->private_data = client;
-+
-+ return 0;
-+}
-+
-+static unsigned int sbefifo_poll(struct file *file, poll_table *wait)
-+{
-+ struct sbefifo_client *client = file->private_data;
-+ struct sbefifo *sbefifo = client->dev;
-+ unsigned int mask = 0;
-+
-+ poll_wait(file, &sbefifo->wait, wait);
-+
-+ if (READ_ONCE(sbefifo->rc))
-+ mask |= POLLERR;
-+
-+ if (sbefifo_buf_nbreadable(&client->rbuf))
-+ mask |= POLLIN;
-+
-+ if (sbefifo_buf_nbwriteable(&client->wbuf))
-+ mask |= POLLOUT;
-+
-+ return mask;
-+}
-+
-+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_WRITE, buf, len))
-+ return -EFAULT;
-+
-+ if ((len >> 2) << 2 != len)
-+ return -EINVAL;
-+
-+ if ((file->f_flags & O_NONBLOCK) && !sbefifo_xfr_rsp_pending(client))
-+ return -EAGAIN;
-+
-+ sbefifo_get_client(client);
-+ if (wait_event_interruptible(sbefifo->wait,
-+ (ret = READ_ONCE(sbefifo->rc)) ||
-+ (n = sbefifo_buf_nbreadable(
-+ &client->rbuf)))) {
-+ sbefifo_put_client(client);
-+ return -ERESTARTSYS;
-+ }
-+ if (ret) {
-+ INIT_LIST_HEAD(&client->xfrs);
-+ sbefifo_put_client(client);
-+ return ret;
-+ }
-+
-+ 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 (sbefifo_buf_readnb(&client->rbuf, n)) {
-+ xfr = sbefifo_client_next_xfr(client);
-+ if (!test_bit(SBEFIFO_XFR_COMPLETE, &xfr->flags)) {
-+ /*
-+ * Fill the read buffer back up.
-+ */
-+ sbefifo_get(sbefifo);
-+ if (mod_timer(&client->dev->poll_timer, jiffies))
-+ sbefifo_put(sbefifo);
-+ } else {
-+ kfree(xfr);
-+ list_del(&xfr->client);
-+ wake_up(&sbefifo->wait);
-+ }
-+ }
-+
-+ sbefifo_put_client(client);
-+
-+ return n;
-+}
-+
-+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;
-+ 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))
-+ return -EFAULT;
-+
-+ if ((len >> 2) << 2 != len)
-+ return -EINVAL;
-+
-+ if (!len)
-+ return 0;
-+
-+ n = sbefifo_buf_nbwriteable(&client->wbuf);
-+
-+ spin_lock_irq(&sbefifo->lock);
-+ xfr = sbefifo_next_xfr(sbefifo);
-+
-+ if ((file->f_flags & O_NONBLOCK) && xfr && n < len) {
-+ spin_unlock_irq(&sbefifo->lock);
-+ return -EAGAIN;
-+ }
-+
-+ xfr = sbefifo_enq_xfr(client);
-+ if (!xfr) {
-+ spin_unlock_irq(&sbefifo->lock);
-+ return -ENOMEM;
-+ }
-+ spin_unlock_irq(&sbefifo->lock);
-+
-+ sbefifo_get_client(client);
-+
-+ /*
-+ * Partial writes are not really allowed in that EOT is sent exactly
-+ * once per write.
-+ */
-+ while (len) {
-+ if (wait_event_interruptible(sbefifo->wait,
-+ READ_ONCE(sbefifo->rc) ||
-+ (sbefifo_client_next_xfr(client) == xfr &&
-+ (n = sbefifo_buf_nbwriteable(
-+ &client->wbuf))))) {
-+ 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 -ERESTARTSYS;
-+ }
-+ if (sbefifo->rc) {
-+ INIT_LIST_HEAD(&client->xfrs);
-+ sbefifo_put_client(client);
-+ return sbefifo->rc;
-+ }
-+
-+ 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;
-+ }
-+
-+ sbefifo_buf_wrotenb(&client->wbuf, n);
-+ len -= n;
-+ buf += n;
-+ ret += n;
-+
-+ /*
-+ * Drain the write buffer.
-+ */
-+ sbefifo_get(sbefifo);
-+ if (mod_timer(&client->dev->poll_timer, jiffies))
-+ sbefifo_put(sbefifo);
-+ }
-+
-+ set_bit(SBEFIFO_XFR_WRITE_DONE, &xfr->flags);
-+ sbefifo_put_client(client);
-+
-+ return ret;
-+}
-+
-+static int sbefifo_release(struct inode *inode, struct file *file)
-+{
-+ struct sbefifo_client *client = file->private_data;
-+ struct sbefifo *sbefifo = client->dev;
-+
-+ sbefifo_put_client(client);
-+
-+ return READ_ONCE(sbefifo->rc);
-+}
-+
-+static const struct file_operations sbefifo_fops = {
-+ .owner = THIS_MODULE,
-+ .open = sbefifo_open,
-+ .read = sbefifo_read,
-+ .write = sbefifo_write,
-+ .poll = sbefifo_poll,
-+ .release = sbefifo_release,
-+};
-+
-+static int sbefifo_probe(struct device *dev)
-+{
-+ struct fsi_device *fsi_dev = to_fsi_dev(dev);
-+ struct sbefifo *sbefifo;
-+ u32 sts;
-+ int ret;
-+
-+ dev_info(dev, "Found sbefifo device\n");
-+ sbefifo = kzalloc(sizeof(*sbefifo), GFP_KERNEL);
-+ if (!sbefifo)
-+ return -ENOMEM;
-+
-+ sbefifo->fsi_dev = fsi_dev;
-+
-+ ret = sbefifo_inw(sbefifo,
-+ SBEFIFO_UP | SBEFIFO_STS, &sts);
-+ if (ret)
-+ return ret;
-+ if (!(sts & SBEFIFO_EMPTY)) {
-+ dev_err(&sbefifo->fsi_dev->dev,
-+ "Found data in upstream fifo\n");
-+ return -EIO;
-+ }
-+
-+ ret = sbefifo_inw(sbefifo, SBEFIFO_DWN | SBEFIFO_STS, &sts);
-+ if (ret)
-+ return ret;
-+ if (!(sts & SBEFIFO_EMPTY)) {
-+ dev_err(&sbefifo->fsi_dev->dev,
-+ "Found data in downstream fifo\n");
-+ return -EIO;
-+ }
-+
-+ sbefifo->mdev.minor = MISC_DYNAMIC_MINOR;
-+ sbefifo->mdev.fops = &sbefifo_fops;
-+ sbefifo->mdev.name = sbefifo->name;
-+ sbefifo->mdev.parent = dev;
-+ spin_lock_init(&sbefifo->lock);
-+ kref_init(&sbefifo->kref);
-+
-+ sbefifo->idx = ida_simple_get(&sbefifo_ida, 1, INT_MAX, GFP_KERNEL);
-+ snprintf(sbefifo->name, sizeof(sbefifo->name), "sbefifo%d",
-+ sbefifo->idx);
-+ init_waitqueue_head(&sbefifo->wait);
-+ INIT_LIST_HEAD(&sbefifo->xfrs);
-+
-+ /* This bit of silicon doesn't offer any interrupts... */
-+ setup_timer(&sbefifo->poll_timer, sbefifo_poll_timer,
-+ (unsigned long)sbefifo);
-+
-+ list_add(&sbefifo->link, &sbefifo_fifos);
-+ return misc_register(&sbefifo->mdev);
-+}
-+
-+static int sbefifo_remove(struct device *dev)
-+{
-+ struct fsi_device *fsi_dev = to_fsi_dev(dev);
-+ struct sbefifo *sbefifo, *sbefifo_tmp;
-+ struct sbefifo_xfr *xfr;
-+
-+ list_for_each_entry_safe(sbefifo, sbefifo_tmp, &sbefifo_fifos, link) {
-+ if (sbefifo->fsi_dev != fsi_dev)
-+ continue;
-+ misc_deregister(&sbefifo->mdev);
-+ list_del(&sbefifo->link);
-+ ida_simple_remove(&sbefifo_ida, sbefifo->idx);
-+
-+ if (del_timer_sync(&sbefifo->poll_timer))
-+ sbefifo_put(sbefifo);
-+
-+ spin_lock(&sbefifo->lock);
-+ list_for_each_entry(xfr, &sbefifo->xfrs, xfrs)
-+ kfree(xfr);
-+ spin_unlock(&sbefifo->lock);
-+
-+ WRITE_ONCE(sbefifo->rc, -ENODEV);
-+
-+ wake_up(&sbefifo->wait);
-+ sbefifo_put(sbefifo);
-+ }
-+
-+ return 0;
-+}
-+
-+static struct fsi_device_id sbefifo_ids[] = {
-+ {
-+ .engine_type = FSI_ENGID_SBE,
-+ .version = FSI_VERSION_ANY,
-+ },
-+ { 0 }
-+};
-+
-+static struct fsi_driver sbefifo_drv = {
-+ .id_table = sbefifo_ids,
-+ .drv = {
-+ .name = DEVICE_NAME,
-+ .bus = &fsi_bus_type,
-+ .probe = sbefifo_probe,
-+ .remove = sbefifo_remove,
-+ }
-+};
-+
-+static int sbefifo_init(void)
-+{
-+ INIT_LIST_HEAD(&sbefifo_fifos);
-+ return fsi_driver_register(&sbefifo_drv);
-+}
-+
-+static void sbefifo_exit(void)
-+{
-+ fsi_driver_unregister(&sbefifo_drv);
-+}
-+
-+module_init(sbefifo_init);
-+module_exit(sbefifo_exit);
-+MODULE_LICENSE("GPL");
-+MODULE_AUTHOR("Brad Bishop <bradleyb@fuzziesquirrel.com>");
-+MODULE_DESCRIPTION("Linux device interface to the POWER self boot engine");
OpenPOWER on IntegriCloud