All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 0/5] fsi: Add regmap and refactor sbefifo
@ 2022-11-02 20:51 Eddie James
  2022-11-02 20:51 ` [PATCH v2 1/5] regmap: Add FSI bus support Eddie James
                   ` (6 more replies)
  0 siblings, 7 replies; 10+ messages in thread
From: Eddie James @ 2022-11-02 20:51 UTC (permalink / raw)
  To: linux-fsi
  Cc: linux-kernel, broonie, gregkh, rafael, jk, joel, alistair, eajames

The SBEFIFO hardware can now be attached over a new I2C endpoint interface
called the I2C Responder (I2CR). In order to use the existing SBEFIFO
driver, add a regmap driver for the FSI bus and an endpoint driver for the
I2CR. Then, refactor the SBEFIFO and OCC drivers to clean up and use the
new regmap driver or the I2CR interface.

Changes since v1:
 - Instead of a regmap driver for the I2CR, just have a private interface
   driver for FSI, since SBEFIFO is likely the only user.

Eddie James (5):
  regmap: Add FSI bus support
  drivers: fsi: Add I2C Responder driver
  drivers: fsi: Rename sbefifo and occ sources
  drivers: fsi: separate char device code for occ and sbefifo
  drivers: fsi: occ and sbefifo refactor

 drivers/base/regmap/Kconfig      |    6 +-
 drivers/base/regmap/Makefile     |    1 +
 drivers/base/regmap/regmap-fsi.c |  231 ++++++
 drivers/fsi/Kconfig              |   32 +-
 drivers/fsi/Makefile             |    9 +-
 drivers/fsi/fsi-occ.c            |  766 --------------------
 drivers/fsi/fsi-sbefifo.c        | 1144 ------------------------------
 drivers/fsi/i2cr.c               |  116 +++
 drivers/fsi/i2cr.h               |   19 +
 drivers/fsi/occ-cdev.c           |  157 ++++
 drivers/fsi/occ.c                |  536 ++++++++++++++
 drivers/fsi/occ.h                |   57 ++
 drivers/fsi/sbefifo-cdev.c       |  218 ++++++
 drivers/fsi/sbefifo-fsi.c        |   68 ++
 drivers/fsi/sbefifo-i2c.c        |   73 ++
 drivers/fsi/sbefifo.c            |  797 +++++++++++++++++++++
 drivers/fsi/sbefifo.h            |   50 ++
 include/linux/regmap.h           |   37 +
 18 files changed, 2398 insertions(+), 1919 deletions(-)
 create mode 100644 drivers/base/regmap/regmap-fsi.c
 delete mode 100644 drivers/fsi/fsi-occ.c
 delete mode 100644 drivers/fsi/fsi-sbefifo.c
 create mode 100644 drivers/fsi/i2cr.c
 create mode 100644 drivers/fsi/i2cr.h
 create mode 100644 drivers/fsi/occ-cdev.c
 create mode 100644 drivers/fsi/occ.c
 create mode 100644 drivers/fsi/occ.h
 create mode 100644 drivers/fsi/sbefifo-cdev.c
 create mode 100644 drivers/fsi/sbefifo-fsi.c
 create mode 100644 drivers/fsi/sbefifo-i2c.c
 create mode 100644 drivers/fsi/sbefifo.c
 create mode 100644 drivers/fsi/sbefifo.h

-- 
2.31.1


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH v2 1/5] regmap: Add FSI bus support
  2022-11-02 20:51 [PATCH v2 0/5] fsi: Add regmap and refactor sbefifo Eddie James
@ 2022-11-02 20:51 ` Eddie James
  2022-11-03 14:54   ` Mark Brown
  2022-11-02 20:51 ` [PATCH v2 2/5] drivers: fsi: Add I2C Responder driver Eddie James
                   ` (5 subsequent siblings)
  6 siblings, 1 reply; 10+ messages in thread
From: Eddie James @ 2022-11-02 20:51 UTC (permalink / raw)
  To: linux-fsi
  Cc: linux-kernel, broonie, gregkh, rafael, jk, joel, alistair, eajames

Add regmap support for the FSI bus.

Signed-off-by: Eddie James <eajames@linux.ibm.com>
---
 drivers/base/regmap/Kconfig      |   6 +-
 drivers/base/regmap/Makefile     |   1 +
 drivers/base/regmap/regmap-fsi.c | 231 +++++++++++++++++++++++++++++++
 include/linux/regmap.h           |  37 +++++
 4 files changed, 274 insertions(+), 1 deletion(-)
 create mode 100644 drivers/base/regmap/regmap-fsi.c

diff --git a/drivers/base/regmap/Kconfig b/drivers/base/regmap/Kconfig
index 159bac6c5046..cd4bb642b9de 100644
--- a/drivers/base/regmap/Kconfig
+++ b/drivers/base/regmap/Kconfig
@@ -4,7 +4,7 @@
 # subsystems should select the appropriate symbols.
 
 config REGMAP
-	default y if (REGMAP_I2C || REGMAP_SPI || REGMAP_SPMI || REGMAP_W1 || REGMAP_AC97 || REGMAP_MMIO || REGMAP_IRQ || REGMAP_SOUNDWIRE || REGMAP_SOUNDWIRE_MBQ || REGMAP_SCCB || REGMAP_I3C || REGMAP_SPI_AVMM || REGMAP_MDIO)
+	default y if (REGMAP_I2C || REGMAP_SPI || REGMAP_SPMI || REGMAP_W1 || REGMAP_AC97 || REGMAP_MMIO || REGMAP_IRQ || REGMAP_SOUNDWIRE || REGMAP_SOUNDWIRE_MBQ || REGMAP_SCCB || REGMAP_I3C || REGMAP_SPI_AVMM || REGMAP_MDIO || REGMAP_FSI)
 	select IRQ_DOMAIN if REGMAP_IRQ
 	select MDIO_BUS if REGMAP_MDIO
 	bool
@@ -65,3 +65,7 @@ config REGMAP_I3C
 config REGMAP_SPI_AVMM
 	tristate
 	depends on SPI
+
+config REGMAP_FSI
+	tristate
+	depends on FSI
diff --git a/drivers/base/regmap/Makefile b/drivers/base/regmap/Makefile
index 11facb32a027..6990de7ca9a9 100644
--- a/drivers/base/regmap/Makefile
+++ b/drivers/base/regmap/Makefile
@@ -20,3 +20,4 @@ obj-$(CONFIG_REGMAP_SCCB) += regmap-sccb.o
 obj-$(CONFIG_REGMAP_I3C) += regmap-i3c.o
 obj-$(CONFIG_REGMAP_SPI_AVMM) += regmap-spi-avmm.o
 obj-$(CONFIG_REGMAP_MDIO) += regmap-mdio.o
+obj-$(CONFIG_REGMAP_FSI) += regmap-fsi.o
diff --git a/drivers/base/regmap/regmap-fsi.c b/drivers/base/regmap/regmap-fsi.c
new file mode 100644
index 000000000000..3d2f3cb31d5e
--- /dev/null
+++ b/drivers/base/regmap/regmap-fsi.c
@@ -0,0 +1,231 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Register map access API - FSI support
+//
+// Copyright 2022 IBM Corp
+//
+// Author: Eddie James <eajames@linux.ibm.com>
+
+#include <linux/fsi.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+
+#include "internal.h"
+
+static int regmap_fsi32_reg_read(void *context, unsigned int reg, unsigned int *val)
+{
+	u32 v;
+	int ret;
+
+	ret = fsi_slave_read(context, reg, &v, sizeof(v));
+	if (ret)
+		return ret;
+
+	*val = v;
+	return 0;
+}
+
+static int regmap_fsi32_reg_write(void *context, unsigned int reg, unsigned int val)
+{
+	u32 v = val;
+
+	return fsi_slave_write(context, reg, &v, sizeof(v));
+}
+
+static const struct regmap_bus regmap_fsi32 = {
+	.reg_write = regmap_fsi32_reg_write,
+	.reg_read = regmap_fsi32_reg_read,
+};
+
+static int regmap_fsi32le_reg_read(void *context, unsigned int reg, unsigned int *val)
+{
+	__be32 v;
+	int ret;
+
+	ret = fsi_slave_read(context, reg, &v, sizeof(v));
+	if (ret)
+		return ret;
+
+	*val = be32_to_cpu(v);
+	return 0;
+}
+
+static int regmap_fsi32le_reg_write(void *context, unsigned int reg, unsigned int val)
+{
+	__be32 v = cpu_to_be32(val);
+
+	return fsi_slave_write(context, reg, &v, sizeof(v));
+}
+
+static const struct regmap_bus regmap_fsi32le = {
+	.reg_write = regmap_fsi32le_reg_write,
+	.reg_read = regmap_fsi32le_reg_read,
+};
+
+static int regmap_fsi16_reg_read(void *context, unsigned int reg, unsigned int *val)
+{
+	u16 v;
+	int ret;
+
+	ret = fsi_slave_read(context, reg, &v, sizeof(v));
+	if (ret)
+		return ret;
+
+	*val = v;
+	return 0;
+}
+
+static int regmap_fsi16_reg_write(void *context, unsigned int reg, unsigned int val)
+{
+	u16 v;
+
+	if (val > 0xffff)
+		return -EINVAL;
+
+	v = val;
+	return fsi_slave_write(context, reg, &v, sizeof(v));
+}
+
+static const struct regmap_bus regmap_fsi16 = {
+	.reg_write = regmap_fsi16_reg_write,
+	.reg_read = regmap_fsi16_reg_read,
+};
+
+static int regmap_fsi16le_reg_read(void *context, unsigned int reg, unsigned int *val)
+{
+	__be16 v;
+	int ret;
+
+	ret = fsi_slave_read(context, reg, &v, sizeof(v));
+	if (ret)
+		return ret;
+
+	*val = be16_to_cpu(v);
+	return 0;
+}
+
+static int regmap_fsi16le_reg_write(void *context, unsigned int reg, unsigned int val)
+{
+	__be16 v;
+
+	if (val > 0xffff)
+		return -EINVAL;
+
+	v = cpu_to_be16(val);
+	return fsi_slave_write(context, reg, &v, sizeof(v));
+}
+
+static const struct regmap_bus regmap_fsi16le = {
+	.reg_write = regmap_fsi16le_reg_write,
+	.reg_read = regmap_fsi16le_reg_read,
+};
+
+static int regmap_fsi8_reg_read(void *context, unsigned int reg, unsigned int *val)
+{
+	u8 v;
+	int ret;
+
+	ret = fsi_slave_read(context, reg, &v, sizeof(v));
+	if (ret)
+		return ret;
+
+	*val = v;
+	return 0;
+}
+
+static int regmap_fsi8_reg_write(void *context, unsigned int reg, unsigned int val)
+{
+	u8 v;
+
+	if (val > 0xff)
+		return -EINVAL;
+
+	v = val;
+	return fsi_slave_write(context, reg, &v, sizeof(v));
+}
+
+static const struct regmap_bus regmap_fsi8 = {
+	.reg_write = regmap_fsi8_reg_write,
+	.reg_read = regmap_fsi8_reg_read,
+};
+
+static const struct regmap_bus *regmap_get_fsi_bus(struct fsi_device *fsi_dev,
+						   const struct regmap_config *config)
+{
+	const struct regmap_bus *bus = NULL;
+
+	if (config->reg_bits == 8 || config->reg_bits == 16 || config->reg_bits == 32) {
+		switch (config->val_bits) {
+		case 8:
+			bus = &regmap_fsi8;
+			break;
+		case 16:
+			switch (regmap_get_val_endian(&fsi_dev->dev, NULL, config)) {
+			case REGMAP_ENDIAN_LITTLE:
+#ifdef __LITTLE_ENDIAN
+			case REGMAP_ENDIAN_NATIVE:
+#endif
+				bus = &regmap_fsi16le;
+				break;
+			case REGMAP_ENDIAN_DEFAULT:
+			case REGMAP_ENDIAN_BIG:
+#ifdef __BIG_ENDIAN
+			case REGMAP_ENDIAN_NATIVE:
+#endif
+				bus = &regmap_fsi16;
+				break;
+			default:
+				break;
+			}
+			break;
+		case 32:
+			switch (regmap_get_val_endian(&fsi_dev->dev, NULL, config)) {
+			case REGMAP_ENDIAN_LITTLE:
+#ifdef __LITTLE_ENDIAN
+			case REGMAP_ENDIAN_NATIVE:
+#endif
+				bus = &regmap_fsi32le;
+				break;
+			case REGMAP_ENDIAN_DEFAULT:
+			case REGMAP_ENDIAN_BIG:
+#ifdef __BIG_ENDIAN
+			case REGMAP_ENDIAN_NATIVE:
+#endif
+				bus = &regmap_fsi32;
+				break;
+			default:
+				break;
+			}
+			break;
+		}
+	}
+
+	return bus ?: ERR_PTR(-EOPNOTSUPP);
+}
+
+struct regmap *__regmap_init_fsi(struct fsi_device *fsi_dev, const struct regmap_config *config,
+				 struct lock_class_key *lock_key, const char *lock_name)
+{
+	const struct regmap_bus *bus = regmap_get_fsi_bus(fsi_dev, config);
+
+	if (IS_ERR(bus))
+		return ERR_CAST(bus);
+
+	return __regmap_init(&fsi_dev->dev, bus, fsi_dev->slave, config, lock_key, lock_name);
+}
+EXPORT_SYMBOL_GPL(__regmap_init_fsi);
+
+struct regmap *__devm_regmap_init_fsi(struct fsi_device *fsi_dev,
+				      const struct regmap_config *config,
+				      struct lock_class_key *lock_key, const char *lock_name)
+{
+	const struct regmap_bus *bus = regmap_get_fsi_bus(fsi_dev, config);
+
+	if (IS_ERR(bus))
+		return ERR_CAST(bus);
+
+	return __devm_regmap_init(&fsi_dev->dev, bus, fsi_dev->slave, config, lock_key, lock_name);
+}
+EXPORT_SYMBOL_GPL(__devm_regmap_init_fsi);
+
+MODULE_LICENSE("GPL");
diff --git a/include/linux/regmap.h b/include/linux/regmap.h
index ca3434dca3a0..e477112fb1c7 100644
--- a/include/linux/regmap.h
+++ b/include/linux/regmap.h
@@ -24,6 +24,7 @@ struct module;
 struct clk;
 struct device;
 struct device_node;
+struct fsi_device;
 struct i2c_client;
 struct i3c_device;
 struct irq_domain;
@@ -628,6 +629,10 @@ struct regmap *__regmap_init_spi_avmm(struct spi_device *spi,
 				      const struct regmap_config *config,
 				      struct lock_class_key *lock_key,
 				      const char *lock_name);
+struct regmap *__regmap_init_fsi(struct fsi_device *fsi_dev,
+				 const struct regmap_config *config,
+				 struct lock_class_key *lock_key,
+				 const char *lock_name);
 
 struct regmap *__devm_regmap_init(struct device *dev,
 				  const struct regmap_bus *bus,
@@ -693,6 +698,11 @@ struct regmap *__devm_regmap_init_spi_avmm(struct spi_device *spi,
 					   const struct regmap_config *config,
 					   struct lock_class_key *lock_key,
 					   const char *lock_name);
+struct regmap *__devm_regmap_init_fsi(struct fsi_device *fsi_dev,
+				      const struct regmap_config *config,
+				      struct lock_class_key *lock_key,
+				      const char *lock_name);
+
 /*
  * Wrapper for regmap_init macros to include a unique lockdep key and name
  * for each call. No-op if CONFIG_LOCKDEP is not set.
@@ -919,6 +929,19 @@ bool regmap_ac97_default_volatile(struct device *dev, unsigned int reg);
 	__regmap_lockdep_wrapper(__regmap_init_spi_avmm, #config,		\
 				 spi, config)
 
+/**
+ * regmap_init_fsi() - Initialise register map
+ *
+ * @fsi_dev: Device that will be interacted with
+ * @config: Configuration for register map
+ *
+ * The return value will be an ERR_PTR() on error or a valid pointer to
+ * a struct regmap.
+ */
+#define regmap_init_fsi(fsi_dev, config)				\
+	__regmap_lockdep_wrapper(__regmap_init_fsi, #config, fsi_dev,	\
+				 config)
+
 /**
  * devm_regmap_init() - Initialise managed register map
  *
@@ -1148,6 +1171,20 @@ bool regmap_ac97_default_volatile(struct device *dev, unsigned int reg);
 	__regmap_lockdep_wrapper(__devm_regmap_init_spi_avmm, #config,	\
 				 spi, config)
 
+/**
+ * devm_regmap_init_fsi() - Initialise managed register map
+ *
+ * @fsi_dev: Device that will be interacted with
+ * @config: Configuration for register map
+ *
+ * The return value will be an ERR_PTR() on error or a valid pointer
+ * to a struct regmap.  The regmap will be automatically freed by the
+ * device management code.
+ */
+#define devm_regmap_init_fsi(fsi_dev, config)				\
+	__regmap_lockdep_wrapper(__devm_regmap_init_fsi, #config,	\
+				 fsi_dev, config)
+
 int regmap_mmio_attach_clk(struct regmap *map, struct clk *clk);
 void regmap_mmio_detach_clk(struct regmap *map);
 void regmap_exit(struct regmap *map);
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH v2 2/5] drivers: fsi: Add I2C Responder driver
  2022-11-02 20:51 [PATCH v2 0/5] fsi: Add regmap and refactor sbefifo Eddie James
  2022-11-02 20:51 ` [PATCH v2 1/5] regmap: Add FSI bus support Eddie James
@ 2022-11-02 20:51 ` Eddie James
  2022-11-02 20:51 ` [PATCH v2 3/5] drivers: fsi: Rename sbefifo and occ sources Eddie James
                   ` (4 subsequent siblings)
  6 siblings, 0 replies; 10+ messages in thread
From: Eddie James @ 2022-11-02 20:51 UTC (permalink / raw)
  To: linux-fsi
  Cc: linux-kernel, broonie, gregkh, rafael, jk, joel, alistair, eajames

Add a regmap driver for the I2C Responder (I2CR) which provides
access to an FSI CFAM through an I2C endpoint device. Since no
userspace access is needed, simply provide a regmap bus for the
CFAM address space through the I2CR.

Signed-off-by: Eddie James <eajames@linux.ibm.com>
---
 drivers/fsi/Kconfig  |   6 +++
 drivers/fsi/Makefile |   1 +
 drivers/fsi/i2cr.c   | 116 +++++++++++++++++++++++++++++++++++++++++++
 drivers/fsi/i2cr.h   |  19 +++++++
 4 files changed, 142 insertions(+)
 create mode 100644 drivers/fsi/i2cr.c
 create mode 100644 drivers/fsi/i2cr.h

diff --git a/drivers/fsi/Kconfig b/drivers/fsi/Kconfig
index e6668a869913..c6049a7bc0dd 100644
--- a/drivers/fsi/Kconfig
+++ b/drivers/fsi/Kconfig
@@ -67,6 +67,12 @@ config FSI_SCOM
 	help
 	This option enables an FSI based SCOM device driver.
 
+config FSI_I2CR
+	tristate "I2C Responder device driver"
+	help
+	  This option enables the I2C client driver for the IBM POWER I2C
+	  responder which gives access to CFAM address space.
+
 config FSI_SBEFIFO
 	tristate "SBEFIFO FSI client device driver"
 	depends on OF_ADDRESS
diff --git a/drivers/fsi/Makefile b/drivers/fsi/Makefile
index da218a1ad8e1..c49d7e65ee5b 100644
--- a/drivers/fsi/Makefile
+++ b/drivers/fsi/Makefile
@@ -6,5 +6,6 @@ obj-$(CONFIG_FSI_MASTER_ASPEED) += fsi-master-aspeed.o
 obj-$(CONFIG_FSI_MASTER_GPIO) += fsi-master-gpio.o
 obj-$(CONFIG_FSI_MASTER_AST_CF) += fsi-master-ast-cf.o
 obj-$(CONFIG_FSI_SCOM) += fsi-scom.o
+obj-$(CONFIG_FSI_I2CR) += i2cr.o
 obj-$(CONFIG_FSI_SBEFIFO) += fsi-sbefifo.o
 obj-$(CONFIG_FSI_OCC) += fsi-occ.o
diff --git a/drivers/fsi/i2cr.c b/drivers/fsi/i2cr.c
new file mode 100644
index 000000000000..4d3c90fc198f
--- /dev/null
+++ b/drivers/fsi/i2cr.c
@@ -0,0 +1,116 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+
+#define I2CR_STATUS		0x30001
+#define  I2CR_STATUS_ERR	 BIT(29)
+
+static bool i2cr_check_parity(u32 v, bool parity)
+{
+	u32 i;
+
+	for (i = 0; i < 32; ++i) {
+		if (v & (1 << i))
+			parity = !parity;
+	}
+
+	return parity;
+}
+
+static __be32 i2cr_get_command(u32 address, bool parity)
+{
+	address <<= 1;
+
+	if (i2cr_check_parity(address, parity))
+		address |= 1;
+
+	return cpu_to_be32(address);
+}
+
+static int i2cr_transfer(struct i2c_client *client, u32 address, u32 *data)
+{
+	struct i2c_msg msgs[2];
+	__be32 response[2];
+	__be32 command;
+	int ret;
+
+	command = i2cr_get_command(address, false);
+	msgs[0].addr = client->addr;
+	msgs[0].flags = 0;
+	msgs[0].len = sizeof(command);
+	msgs[0].buf = (__u8 *)&command;
+	msgs[1].addr = client->addr;
+	msgs[1].flags = I2C_M_RD;
+	msgs[1].len = sizeof(response);
+	msgs[1].buf = (__u8 *)response;
+
+	ret = i2c_transfer(client->adapter, msgs, 2);
+	if (ret == 2) {
+		*data = be32_to_cpu(response[0]);
+		return 0;
+	}
+
+	if (ret < 0)
+		return ret;
+
+	return -EIO;
+}
+
+static int i2cr_check_status(struct i2c_client *client)
+{
+	u32 status;
+	int ret;
+
+	ret = i2cr_transfer(client, I2CR_STATUS, &status);
+	if (ret)
+		return ret;
+
+	if (status & I2CR_STATUS_ERR)
+		return -EREMOTEIO;
+
+	return 0;
+}
+
+static int i2cr_reg_read(void *context, unsigned int reg, unsigned int *val)
+{
+	struct i2c_client *client = context;
+	int ret;
+	u32 v;
+
+	ret = i2cr_transfer(client, (u32)reg, &v);
+	if (ret)
+		return ret;
+
+	*val = v;
+	return i2cr_check_status(client);
+}
+
+static int i2cr_reg_write(void *context, unsigned int reg, unsigned int val)
+{
+	struct i2c_client *client = context;
+	__be32 data[3];
+	int ret;
+
+	data[0] = i2cr_get_command((u32)reg, i2cr_check_parity((u32)val, false));
+	data[1] = cpu_to_be32((u32)val);
+
+	ret = i2c_master_send(client, (const char *)data, sizeof(data));
+	if (ret == sizeof(data))
+		return i2cr_check_status(client);
+
+	if (ret < 0)
+		return ret;
+
+	return -EIO;
+}
+
+const struct regmap_bus regmap_bus_i2cr = {
+	.reg_write = i2cr_reg_write,
+	.reg_read = i2cr_reg_read,
+};
+EXPORT_SYMBOL_GPL(regmap_bus_i2cr);
+
+MODULE_AUTHOR("Eddie James <eajames@linux.ibm.com>");
+MODULE_DESCRIPTION("IBM I2C Responder driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/fsi/i2cr.h b/drivers/fsi/i2cr.h
new file mode 100644
index 000000000000..681945713843
--- /dev/null
+++ b/drivers/fsi/i2cr.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright (C) IBM Corporation 2022 */
+
+#ifndef DRIVERS_I2CR_H
+#define DRIVERS_I2CR_H
+
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+
+extern const struct regmap_bus regmap_bus_i2cr;
+
+#define init_i2cr(client, config)	\
+	__regmap_lockdep_wrapper(__regmap_init, #config, &(client)->dev, &regmap_bus_i2cr, \
+				 client, config)
+#define devm_init_i2cr(client, config)	\
+	__regmap_lockdep_wrapper(__devm_regmap_init, #config, &(client)->dev, &regmap_bus_i2cr, \
+				 client, config)
+
+#endif /* DRIVERS_I2CR_H */
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH v2 3/5] drivers: fsi: Rename sbefifo and occ sources
  2022-11-02 20:51 [PATCH v2 0/5] fsi: Add regmap and refactor sbefifo Eddie James
  2022-11-02 20:51 ` [PATCH v2 1/5] regmap: Add FSI bus support Eddie James
  2022-11-02 20:51 ` [PATCH v2 2/5] drivers: fsi: Add I2C Responder driver Eddie James
@ 2022-11-02 20:51 ` Eddie James
  2022-11-02 20:51 ` [PATCH v2 4/5] drivers: fsi: separate char device code for occ and sbefifo Eddie James
                   ` (3 subsequent siblings)
  6 siblings, 0 replies; 10+ messages in thread
From: Eddie James @ 2022-11-02 20:51 UTC (permalink / raw)
  To: linux-fsi
  Cc: linux-kernel, broonie, gregkh, rafael, jk, joel, alistair, eajames

In preparation for some refactoring and adding multiple underlying
bus support for the SBEFIFO, rename the sources.

Signed-off-by: Eddie James <eajames@linux.ibm.com>
---
 drivers/fsi/Makefile                     | 4 ++--
 drivers/fsi/{fsi-occ.c => occ.c}         | 0
 drivers/fsi/{fsi-sbefifo.c => sbefifo.c} | 0
 3 files changed, 2 insertions(+), 2 deletions(-)
 rename drivers/fsi/{fsi-occ.c => occ.c} (100%)
 rename drivers/fsi/{fsi-sbefifo.c => sbefifo.c} (100%)

diff --git a/drivers/fsi/Makefile b/drivers/fsi/Makefile
index c49d7e65ee5b..850825556e9c 100644
--- a/drivers/fsi/Makefile
+++ b/drivers/fsi/Makefile
@@ -7,5 +7,5 @@ obj-$(CONFIG_FSI_MASTER_GPIO) += fsi-master-gpio.o
 obj-$(CONFIG_FSI_MASTER_AST_CF) += fsi-master-ast-cf.o
 obj-$(CONFIG_FSI_SCOM) += fsi-scom.o
 obj-$(CONFIG_FSI_I2CR) += i2cr.o
-obj-$(CONFIG_FSI_SBEFIFO) += fsi-sbefifo.o
-obj-$(CONFIG_FSI_OCC) += fsi-occ.o
+obj-$(CONFIG_FSI_SBEFIFO) += sbefifo.o
+obj-$(CONFIG_FSI_OCC) += occ.o
diff --git a/drivers/fsi/fsi-occ.c b/drivers/fsi/occ.c
similarity index 100%
rename from drivers/fsi/fsi-occ.c
rename to drivers/fsi/occ.c
diff --git a/drivers/fsi/fsi-sbefifo.c b/drivers/fsi/sbefifo.c
similarity index 100%
rename from drivers/fsi/fsi-sbefifo.c
rename to drivers/fsi/sbefifo.c
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH v2 4/5] drivers: fsi: separate char device code for occ and sbefifo
  2022-11-02 20:51 [PATCH v2 0/5] fsi: Add regmap and refactor sbefifo Eddie James
                   ` (2 preceding siblings ...)
  2022-11-02 20:51 ` [PATCH v2 3/5] drivers: fsi: Rename sbefifo and occ sources Eddie James
@ 2022-11-02 20:51 ` Eddie James
  2022-11-02 20:51 ` [PATCH v2 5/5] drivers: fsi: occ and sbefifo refactor Eddie James
                   ` (2 subsequent siblings)
  6 siblings, 0 replies; 10+ messages in thread
From: Eddie James @ 2022-11-02 20:51 UTC (permalink / raw)
  To: linux-fsi
  Cc: linux-kernel, broonie, gregkh, rafael, jk, joel, alistair, eajames

Signed-off-by: Eddie James <eajames@linux.ibm.com>
---
 drivers/fsi/Makefile       |   4 +-
 drivers/fsi/occ-cdev.c     | 139 ++++++++++++++++++++++++
 drivers/fsi/occ.c          | 137 -----------------------
 drivers/fsi/sbefifo-cdev.c | 217 +++++++++++++++++++++++++++++++++++++
 drivers/fsi/sbefifo.c      | 215 ------------------------------------
 5 files changed, 358 insertions(+), 354 deletions(-)
 create mode 100644 drivers/fsi/occ-cdev.c
 create mode 100644 drivers/fsi/sbefifo-cdev.c

diff --git a/drivers/fsi/Makefile b/drivers/fsi/Makefile
index 850825556e9c..1a52fc6c5258 100644
--- a/drivers/fsi/Makefile
+++ b/drivers/fsi/Makefile
@@ -7,5 +7,5 @@ obj-$(CONFIG_FSI_MASTER_GPIO) += fsi-master-gpio.o
 obj-$(CONFIG_FSI_MASTER_AST_CF) += fsi-master-ast-cf.o
 obj-$(CONFIG_FSI_SCOM) += fsi-scom.o
 obj-$(CONFIG_FSI_I2CR) += i2cr.o
-obj-$(CONFIG_FSI_SBEFIFO) += sbefifo.o
-obj-$(CONFIG_FSI_OCC) += occ.o
+obj-$(CONFIG_FSI_SBEFIFO) += sbefifo.o sbefifo-cdev.o
+obj-$(CONFIG_FSI_OCC) += occ.o occ-cdev.o
diff --git a/drivers/fsi/occ-cdev.c b/drivers/fsi/occ-cdev.c
new file mode 100644
index 000000000000..b3be880b5fe8
--- /dev/null
+++ b/drivers/fsi/occ-cdev.c
@@ -0,0 +1,139 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) IBM Corporation 2022 */
+
+static int occ_open(struct inode *inode, struct file *file)
+{
+	struct occ_client *client = kzalloc(sizeof(*client), GFP_KERNEL);
+	struct miscdevice *mdev = file->private_data;
+	struct occ *occ = to_occ(mdev);
+
+	if (!client)
+		return -ENOMEM;
+
+	client->buffer = (u8 *)__get_free_page(GFP_KERNEL);
+	if (!client->buffer) {
+		kfree(client);
+		return -ENOMEM;
+	}
+
+	client->occ = occ;
+	mutex_init(&client->lock);
+	file->private_data = client;
+	get_device(occ->dev);
+
+	/* We allocate a 1-page buffer, make sure it all fits */
+	BUILD_BUG_ON((OCC_CMD_DATA_BYTES + 3) > PAGE_SIZE);
+	BUILD_BUG_ON((OCC_RESP_DATA_BYTES + 7) > PAGE_SIZE);
+
+	return 0;
+}
+
+static ssize_t occ_read(struct file *file, char __user *buf, size_t len,
+			loff_t *offset)
+{
+	struct occ_client *client = file->private_data;
+	ssize_t rc = 0;
+
+	if (!client)
+		return -ENODEV;
+
+	if (len > OCC_SRAM_BYTES)
+		return -EINVAL;
+
+	mutex_lock(&client->lock);
+
+	/* This should not be possible ... */
+	if (WARN_ON_ONCE(client->read_offset > client->data_size)) {
+		rc = -EIO;
+		goto done;
+	}
+
+	/* Grab how much data we have to read */
+	rc = min(len, client->data_size - client->read_offset);
+	if (copy_to_user(buf, client->buffer + client->read_offset, rc))
+		rc = -EFAULT;
+	else
+		client->read_offset += rc;
+
+ done:
+	mutex_unlock(&client->lock);
+
+	return rc;
+}
+
+static ssize_t occ_write(struct file *file, const char __user *buf,
+			 size_t len, loff_t *offset)
+{
+	struct occ_client *client = file->private_data;
+	size_t rlen, data_length;
+	ssize_t rc;
+	u8 *cmd;
+
+	if (!client)
+		return -ENODEV;
+
+	if (len > (OCC_CMD_DATA_BYTES + 3) || len < 3)
+		return -EINVAL;
+
+	mutex_lock(&client->lock);
+
+	/* Construct the command */
+	cmd = client->buffer;
+
+	/*
+	 * Copy the user command (assume user data follows the occ command
+	 * format)
+	 * byte 0: command type
+	 * bytes 1-2: data length (msb first)
+	 * bytes 3-n: data
+	 */
+	if (copy_from_user(&cmd[1], buf, len)) {
+		rc = -EFAULT;
+		goto done;
+	}
+
+	/* Extract data length */
+	data_length = (cmd[2] << 8) + cmd[3];
+	if (data_length > OCC_CMD_DATA_BYTES) {
+		rc = -EINVAL;
+		goto done;
+	}
+
+	/* Submit command; 4 bytes before the data and 2 bytes after */
+	rlen = PAGE_SIZE;
+	rc = fsi_occ_submit(client->occ->dev, cmd, data_length + 6, cmd,
+			    &rlen);
+	if (rc)
+		goto done;
+
+	/* Set read tracking data */
+	client->data_size = rlen;
+	client->read_offset = 0;
+
+	/* Done */
+	rc = len;
+
+ done:
+	mutex_unlock(&client->lock);
+
+	return rc;
+}
+
+static int occ_release(struct inode *inode, struct file *file)
+{
+	struct occ_client *client = file->private_data;
+
+	put_device(client->occ->dev);
+	free_page((unsigned long)client->buffer);
+	kfree(client);
+
+	return 0;
+}
+
+static const struct file_operations occ_fops = {
+	.owner = THIS_MODULE,
+	.open = occ_open,
+	.read = occ_read,
+	.write = occ_write,
+	.release = occ_release,
+};
\ No newline at end of file
diff --git a/drivers/fsi/occ.c b/drivers/fsi/occ.c
index abdd37d5507f..83429b0289e9 100644
--- a/drivers/fsi/occ.c
+++ b/drivers/fsi/occ.c
@@ -77,143 +77,6 @@ struct occ_client {
 
 static DEFINE_IDA(occ_ida);
 
-static int occ_open(struct inode *inode, struct file *file)
-{
-	struct occ_client *client = kzalloc(sizeof(*client), GFP_KERNEL);
-	struct miscdevice *mdev = file->private_data;
-	struct occ *occ = to_occ(mdev);
-
-	if (!client)
-		return -ENOMEM;
-
-	client->buffer = (u8 *)__get_free_page(GFP_KERNEL);
-	if (!client->buffer) {
-		kfree(client);
-		return -ENOMEM;
-	}
-
-	client->occ = occ;
-	mutex_init(&client->lock);
-	file->private_data = client;
-	get_device(occ->dev);
-
-	/* We allocate a 1-page buffer, make sure it all fits */
-	BUILD_BUG_ON((OCC_CMD_DATA_BYTES + 3) > PAGE_SIZE);
-	BUILD_BUG_ON((OCC_RESP_DATA_BYTES + 7) > PAGE_SIZE);
-
-	return 0;
-}
-
-static ssize_t occ_read(struct file *file, char __user *buf, size_t len,
-			loff_t *offset)
-{
-	struct occ_client *client = file->private_data;
-	ssize_t rc = 0;
-
-	if (!client)
-		return -ENODEV;
-
-	if (len > OCC_SRAM_BYTES)
-		return -EINVAL;
-
-	mutex_lock(&client->lock);
-
-	/* This should not be possible ... */
-	if (WARN_ON_ONCE(client->read_offset > client->data_size)) {
-		rc = -EIO;
-		goto done;
-	}
-
-	/* Grab how much data we have to read */
-	rc = min(len, client->data_size - client->read_offset);
-	if (copy_to_user(buf, client->buffer + client->read_offset, rc))
-		rc = -EFAULT;
-	else
-		client->read_offset += rc;
-
- done:
-	mutex_unlock(&client->lock);
-
-	return rc;
-}
-
-static ssize_t occ_write(struct file *file, const char __user *buf,
-			 size_t len, loff_t *offset)
-{
-	struct occ_client *client = file->private_data;
-	size_t rlen, data_length;
-	ssize_t rc;
-	u8 *cmd;
-
-	if (!client)
-		return -ENODEV;
-
-	if (len > (OCC_CMD_DATA_BYTES + 3) || len < 3)
-		return -EINVAL;
-
-	mutex_lock(&client->lock);
-
-	/* Construct the command */
-	cmd = client->buffer;
-
-	/*
-	 * Copy the user command (assume user data follows the occ command
-	 * format)
-	 * byte 0: command type
-	 * bytes 1-2: data length (msb first)
-	 * bytes 3-n: data
-	 */
-	if (copy_from_user(&cmd[1], buf, len)) {
-		rc = -EFAULT;
-		goto done;
-	}
-
-	/* Extract data length */
-	data_length = (cmd[2] << 8) + cmd[3];
-	if (data_length > OCC_CMD_DATA_BYTES) {
-		rc = -EINVAL;
-		goto done;
-	}
-
-	/* Submit command; 4 bytes before the data and 2 bytes after */
-	rlen = PAGE_SIZE;
-	rc = fsi_occ_submit(client->occ->dev, cmd, data_length + 6, cmd,
-			    &rlen);
-	if (rc)
-		goto done;
-
-	/* Set read tracking data */
-	client->data_size = rlen;
-	client->read_offset = 0;
-
-	/* Done */
-	rc = len;
-
- done:
-	mutex_unlock(&client->lock);
-
-	return rc;
-}
-
-static int occ_release(struct inode *inode, struct file *file)
-{
-	struct occ_client *client = file->private_data;
-
-	put_device(client->occ->dev);
-	free_page((unsigned long)client->buffer);
-	kfree(client);
-
-	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 void occ_save_ffdc(struct occ *occ, __be32 *resp, size_t parsed_len,
 			  size_t resp_len)
 {
diff --git a/drivers/fsi/sbefifo-cdev.c b/drivers/fsi/sbefifo-cdev.c
new file mode 100644
index 000000000000..75765e8c307a
--- /dev/null
+++ b/drivers/fsi/sbefifo-cdev.c
@@ -0,0 +1,217 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) IBM Corporation 2022 */
+
+/*
+ * Char device interface
+ */
+
+static void sbefifo_release_command(struct sbefifo_user *user)
+{
+	if (is_vmalloc_addr(user->pending_cmd))
+		vfree(user->pending_cmd);
+	user->pending_cmd = NULL;
+	user->pending_len = 0;
+}
+
+static int sbefifo_user_open(struct inode *inode, struct file *file)
+{
+	struct sbefifo *sbefifo = container_of(inode->i_cdev, struct sbefifo, cdev);
+	struct sbefifo_user *user;
+
+	user = kzalloc(sizeof(struct sbefifo_user), GFP_KERNEL);
+	if (!user)
+		return -ENOMEM;
+
+	file->private_data = user;
+	user->sbefifo = sbefifo;
+	user->cmd_page = (void *)__get_free_page(GFP_KERNEL);
+	if (!user->cmd_page) {
+		kfree(user);
+		return -ENOMEM;
+	}
+	mutex_init(&user->file_lock);
+	user->read_timeout_ms = SBEFIFO_TIMEOUT_START_RSP;
+
+	return 0;
+}
+
+static ssize_t sbefifo_user_read(struct file *file, char __user *buf,
+				 size_t len, loff_t *offset)
+{
+	struct sbefifo_user *user = file->private_data;
+	struct sbefifo *sbefifo;
+	struct iov_iter resp_iter;
+        struct iovec resp_iov;
+	size_t cmd_len;
+	int rc;
+
+	if (!user)
+		return -EINVAL;
+	sbefifo = user->sbefifo;
+	if (len & 3)
+		return -EINVAL;
+
+	mutex_lock(&user->file_lock);
+
+	/* Cronus relies on -EAGAIN after a short read */
+	if (user->pending_len == 0) {
+		rc = -EAGAIN;
+		goto bail;
+	}
+	if (user->pending_len < 8) {
+		rc = -EINVAL;
+		goto bail;
+	}
+	cmd_len = user->pending_len >> 2;
+
+	/* Prepare iov iterator */
+	resp_iov.iov_base = buf;
+	resp_iov.iov_len = len;
+	iov_iter_init(&resp_iter, WRITE, &resp_iov, 1, len);
+
+	/* Perform the command */
+	rc = mutex_lock_interruptible(&sbefifo->lock);
+	if (rc)
+		goto bail;
+	sbefifo->timeout_start_rsp_ms = user->read_timeout_ms;
+	rc = __sbefifo_submit(sbefifo, user->pending_cmd, cmd_len, &resp_iter);
+	sbefifo->timeout_start_rsp_ms = SBEFIFO_TIMEOUT_START_RSP;
+	mutex_unlock(&sbefifo->lock);
+	if (rc < 0)
+		goto bail;
+
+	/* Extract the response length */
+	rc = len - iov_iter_count(&resp_iter);
+ bail:
+	sbefifo_release_command(user);
+	mutex_unlock(&user->file_lock);
+	return rc;
+}
+
+static ssize_t sbefifo_user_write(struct file *file, const char __user *buf,
+				  size_t len, loff_t *offset)
+{
+	struct sbefifo_user *user = file->private_data;
+	struct sbefifo *sbefifo;
+	int rc = len;
+
+	if (!user)
+		return -EINVAL;
+	sbefifo = user->sbefifo;
+	if (len > SBEFIFO_MAX_USER_CMD_LEN)
+		return -EINVAL;
+	if (len & 3)
+		return -EINVAL;
+
+	mutex_lock(&user->file_lock);
+
+	/* Can we use the pre-allocate buffer ? If not, allocate */
+	if (len <= PAGE_SIZE)
+		user->pending_cmd = user->cmd_page;
+	else
+		user->pending_cmd = vmalloc(len);
+	if (!user->pending_cmd) {
+		rc = -ENOMEM;
+		goto bail;
+	}
+
+	/* Copy the command into the staging buffer */
+	if (copy_from_user(user->pending_cmd, buf, len)) {
+		rc = -EFAULT;
+		goto bail;
+	}
+
+	/* Check for the magic reset command */
+	if (len == 4 && be32_to_cpu(*(__be32 *)user->pending_cmd) ==
+	    SBEFIFO_RESET_MAGIC)  {
+
+		/* Clear out any pending command */
+		user->pending_len = 0;
+
+		/* Trigger reset request */
+		rc = mutex_lock_interruptible(&sbefifo->lock);
+		if (rc)
+			goto bail;
+		rc = sbefifo_request_reset(user->sbefifo);
+		mutex_unlock(&sbefifo->lock);
+		if (rc == 0)
+			rc = 4;
+		goto bail;
+	}
+
+	/* Update the staging buffer size */
+	user->pending_len = len;
+ bail:
+	if (!user->pending_len)
+		sbefifo_release_command(user);
+
+	mutex_unlock(&user->file_lock);
+
+	/* And that's it, we'll issue the command on a read */
+	return rc;
+}
+
+static int sbefifo_user_release(struct inode *inode, struct file *file)
+{
+	struct sbefifo_user *user = file->private_data;
+
+	if (!user)
+		return -EINVAL;
+
+	sbefifo_release_command(user);
+	free_page((unsigned long)user->cmd_page);
+	kfree(user);
+
+	return 0;
+}
+
+static int sbefifo_read_timeout(struct sbefifo_user *user, void __user *argp)
+{
+	struct device *dev = &user->sbefifo->dev;
+	u32 timeout;
+
+	if (get_user(timeout, (__u32 __user *)argp))
+		return -EFAULT;
+
+	if (timeout == 0) {
+		user->read_timeout_ms = SBEFIFO_TIMEOUT_START_RSP;
+		dev_dbg(dev, "Timeout reset to %d\n", user->read_timeout_ms);
+		return 0;
+	}
+
+	if (timeout < 10 || timeout > 120)
+		return -EINVAL;
+
+	user->read_timeout_ms = timeout * 1000; /* user timeout is in sec */
+
+	dev_dbg(dev, "Timeout set to %d\n", user->read_timeout_ms);
+
+	return 0;
+}
+
+static long sbefifo_user_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct sbefifo_user *user = file->private_data;
+	int rc = -ENOTTY;
+
+	if (!user)
+		return -EINVAL;
+
+	mutex_lock(&user->file_lock);
+	switch (cmd) {
+	case FSI_SBEFIFO_READ_TIMEOUT_SECONDS:
+		rc = sbefifo_read_timeout(user, (void __user *)arg);
+		break;
+	}
+	mutex_unlock(&user->file_lock);
+	return rc;
+}
+
+static const struct file_operations sbefifo_fops = {
+	.owner		= THIS_MODULE,
+	.open		= sbefifo_user_open,
+	.read		= sbefifo_user_read,
+	.write		= sbefifo_user_write,
+	.release	= sbefifo_user_release,
+	.unlocked_ioctl = sbefifo_user_ioctl,
+};
diff --git a/drivers/fsi/sbefifo.c b/drivers/fsi/sbefifo.c
index 5f93a53846aa..634405b7aaf4 100644
--- a/drivers/fsi/sbefifo.c
+++ b/drivers/fsi/sbefifo.c
@@ -773,221 +773,6 @@ int sbefifo_submit(struct device *dev, const __be32 *command, size_t cmd_len,
 }
 EXPORT_SYMBOL_GPL(sbefifo_submit);
 
-/*
- * Char device interface
- */
-
-static void sbefifo_release_command(struct sbefifo_user *user)
-{
-	if (is_vmalloc_addr(user->pending_cmd))
-		vfree(user->pending_cmd);
-	user->pending_cmd = NULL;
-	user->pending_len = 0;
-}
-
-static int sbefifo_user_open(struct inode *inode, struct file *file)
-{
-	struct sbefifo *sbefifo = container_of(inode->i_cdev, struct sbefifo, cdev);
-	struct sbefifo_user *user;
-
-	user = kzalloc(sizeof(struct sbefifo_user), GFP_KERNEL);
-	if (!user)
-		return -ENOMEM;
-
-	file->private_data = user;
-	user->sbefifo = sbefifo;
-	user->cmd_page = (void *)__get_free_page(GFP_KERNEL);
-	if (!user->cmd_page) {
-		kfree(user);
-		return -ENOMEM;
-	}
-	mutex_init(&user->file_lock);
-	user->read_timeout_ms = SBEFIFO_TIMEOUT_START_RSP;
-
-	return 0;
-}
-
-static ssize_t sbefifo_user_read(struct file *file, char __user *buf,
-				 size_t len, loff_t *offset)
-{
-	struct sbefifo_user *user = file->private_data;
-	struct sbefifo *sbefifo;
-	struct iov_iter resp_iter;
-        struct iovec resp_iov;
-	size_t cmd_len;
-	int rc;
-
-	if (!user)
-		return -EINVAL;
-	sbefifo = user->sbefifo;
-	if (len & 3)
-		return -EINVAL;
-
-	mutex_lock(&user->file_lock);
-
-	/* Cronus relies on -EAGAIN after a short read */
-	if (user->pending_len == 0) {
-		rc = -EAGAIN;
-		goto bail;
-	}
-	if (user->pending_len < 8) {
-		rc = -EINVAL;
-		goto bail;
-	}
-	cmd_len = user->pending_len >> 2;
-
-	/* Prepare iov iterator */
-	resp_iov.iov_base = buf;
-	resp_iov.iov_len = len;
-	iov_iter_init(&resp_iter, WRITE, &resp_iov, 1, len);
-
-	/* Perform the command */
-	rc = mutex_lock_interruptible(&sbefifo->lock);
-	if (rc)
-		goto bail;
-	sbefifo->timeout_start_rsp_ms = user->read_timeout_ms;
-	rc = __sbefifo_submit(sbefifo, user->pending_cmd, cmd_len, &resp_iter);
-	sbefifo->timeout_start_rsp_ms = SBEFIFO_TIMEOUT_START_RSP;
-	mutex_unlock(&sbefifo->lock);
-	if (rc < 0)
-		goto bail;
-
-	/* Extract the response length */
-	rc = len - iov_iter_count(&resp_iter);
- bail:
-	sbefifo_release_command(user);
-	mutex_unlock(&user->file_lock);
-	return rc;
-}
-
-static ssize_t sbefifo_user_write(struct file *file, const char __user *buf,
-				  size_t len, loff_t *offset)
-{
-	struct sbefifo_user *user = file->private_data;
-	struct sbefifo *sbefifo;
-	int rc = len;
-
-	if (!user)
-		return -EINVAL;
-	sbefifo = user->sbefifo;
-	if (len > SBEFIFO_MAX_USER_CMD_LEN)
-		return -EINVAL;
-	if (len & 3)
-		return -EINVAL;
-
-	mutex_lock(&user->file_lock);
-
-	/* Can we use the pre-allocate buffer ? If not, allocate */
-	if (len <= PAGE_SIZE)
-		user->pending_cmd = user->cmd_page;
-	else
-		user->pending_cmd = vmalloc(len);
-	if (!user->pending_cmd) {
-		rc = -ENOMEM;
-		goto bail;
-	}
-
-	/* Copy the command into the staging buffer */
-	if (copy_from_user(user->pending_cmd, buf, len)) {
-		rc = -EFAULT;
-		goto bail;
-	}
-
-	/* Check for the magic reset command */
-	if (len == 4 && be32_to_cpu(*(__be32 *)user->pending_cmd) ==
-	    SBEFIFO_RESET_MAGIC)  {
-
-		/* Clear out any pending command */
-		user->pending_len = 0;
-
-		/* Trigger reset request */
-		rc = mutex_lock_interruptible(&sbefifo->lock);
-		if (rc)
-			goto bail;
-		rc = sbefifo_request_reset(user->sbefifo);
-		mutex_unlock(&sbefifo->lock);
-		if (rc == 0)
-			rc = 4;
-		goto bail;
-	}
-
-	/* Update the staging buffer size */
-	user->pending_len = len;
- bail:
-	if (!user->pending_len)
-		sbefifo_release_command(user);
-
-	mutex_unlock(&user->file_lock);
-
-	/* And that's it, we'll issue the command on a read */
-	return rc;
-}
-
-static int sbefifo_user_release(struct inode *inode, struct file *file)
-{
-	struct sbefifo_user *user = file->private_data;
-
-	if (!user)
-		return -EINVAL;
-
-	sbefifo_release_command(user);
-	free_page((unsigned long)user->cmd_page);
-	kfree(user);
-
-	return 0;
-}
-
-static int sbefifo_read_timeout(struct sbefifo_user *user, void __user *argp)
-{
-	struct device *dev = &user->sbefifo->dev;
-	u32 timeout;
-
-	if (get_user(timeout, (__u32 __user *)argp))
-		return -EFAULT;
-
-	if (timeout == 0) {
-		user->read_timeout_ms = SBEFIFO_TIMEOUT_START_RSP;
-		dev_dbg(dev, "Timeout reset to %d\n", user->read_timeout_ms);
-		return 0;
-	}
-
-	if (timeout < 10 || timeout > 120)
-		return -EINVAL;
-
-	user->read_timeout_ms = timeout * 1000; /* user timeout is in sec */
-
-	dev_dbg(dev, "Timeout set to %d\n", user->read_timeout_ms);
-
-	return 0;
-}
-
-static long sbefifo_user_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
-{
-	struct sbefifo_user *user = file->private_data;
-	int rc = -ENOTTY;
-
-	if (!user)
-		return -EINVAL;
-
-	mutex_lock(&user->file_lock);
-	switch (cmd) {
-	case FSI_SBEFIFO_READ_TIMEOUT_SECONDS:
-		rc = sbefifo_read_timeout(user, (void __user *)arg);
-		break;
-	}
-	mutex_unlock(&user->file_lock);
-	return rc;
-}
-
-static const struct file_operations sbefifo_fops = {
-	.owner		= THIS_MODULE,
-	.open		= sbefifo_user_open,
-	.read		= sbefifo_user_read,
-	.write		= sbefifo_user_write,
-	.release	= sbefifo_user_release,
-	.unlocked_ioctl = sbefifo_user_ioctl,
-};
-
 static void sbefifo_free(struct device *dev)
 {
 	struct sbefifo *sbefifo = container_of(dev, struct sbefifo, dev);
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH v2 5/5] drivers: fsi: occ and sbefifo refactor
  2022-11-02 20:51 [PATCH v2 0/5] fsi: Add regmap and refactor sbefifo Eddie James
                   ` (3 preceding siblings ...)
  2022-11-02 20:51 ` [PATCH v2 4/5] drivers: fsi: separate char device code for occ and sbefifo Eddie James
@ 2022-11-02 20:51 ` Eddie James
  2022-11-25 21:28 ` (subset) [PATCH v2 0/5] fsi: Add regmap and refactor sbefifo Mark Brown
  2023-01-19 17:48 ` Eddie James
  6 siblings, 0 replies; 10+ messages in thread
From: Eddie James @ 2022-11-02 20:51 UTC (permalink / raw)
  To: linux-fsi
  Cc: linux-kernel, broonie, gregkh, rafael, jk, joel, alistair, eajames

Rework the drivers to allow for SBEFIFO connected over FSI or I2C.

Signed-off-by: Eddie James <eajames@linux.ibm.com>
---
 drivers/fsi/Kconfig        |  26 +-
 drivers/fsi/Makefile       |   6 +-
 drivers/fsi/occ-cdev.c     | 178 ++++++-----
 drivers/fsi/occ.c          | 495 +++++++++++++------------------
 drivers/fsi/occ.h          |  57 ++++
 drivers/fsi/sbefifo-cdev.c | 239 +++++++--------
 drivers/fsi/sbefifo-fsi.c  |  68 +++++
 drivers/fsi/sbefifo-i2c.c  |  73 +++++
 drivers/fsi/sbefifo.c      | 588 ++++++++++++++-----------------------
 drivers/fsi/sbefifo.h      |  50 ++++
 10 files changed, 920 insertions(+), 860 deletions(-)
 create mode 100644 drivers/fsi/occ.h
 create mode 100644 drivers/fsi/sbefifo-fsi.c
 create mode 100644 drivers/fsi/sbefifo-i2c.c
 create mode 100644 drivers/fsi/sbefifo.h

diff --git a/drivers/fsi/Kconfig b/drivers/fsi/Kconfig
index c6049a7bc0dd..ab98c4b718fb 100644
--- a/drivers/fsi/Kconfig
+++ b/drivers/fsi/Kconfig
@@ -75,15 +75,29 @@ config FSI_I2CR
 
 config FSI_SBEFIFO
 	tristate "SBEFIFO FSI client device driver"
-	depends on OF_ADDRESS
+	select REGMAP_FSI
+	select SBEFIFO_CORE
 	help
-	This option enables an FSI based SBEFIFO device driver. The SBEFIFO is
-	a pipe-like FSI device for communicating with the self boot engine
-	(SBE) on POWER processors.
+	  This option enables an FSI based SBEFIFO device driver. The SBEFIFO is
+	  a pipe-like device for communicating with the self boot engine
+	  (SBE) on POWER processors.
+
+config I2C_SBEFIFO
+	tristate "SBEFIFO I2C client device driver"
+	select FSI_I2CR
+	select SBEFIFO_CORE
+	help
+	  This option enables an I2C based SBEFIFO device driver. The SBEFIFO is
+	  a pipe-like device for communicating with the self boot engine
+	  (SBE) on POWER processors.
+
+config SBEFIFO_CORE
+	tristate
+	select FSI_OCC
 
 config FSI_OCC
-	tristate "OCC SBEFIFO client device driver"
-	depends on FSI_SBEFIFO
+	tristate "OCC communications driver"
+	depends on SBEFIFO_CORE
 	help
 	This option enables an SBEFIFO based On-Chip Controller (OCC) device
 	driver. The OCC is a device embedded on a POWER processor that collects
diff --git a/drivers/fsi/Makefile b/drivers/fsi/Makefile
index 1a52fc6c5258..c1d0e020c845 100644
--- a/drivers/fsi/Makefile
+++ b/drivers/fsi/Makefile
@@ -7,5 +7,9 @@ obj-$(CONFIG_FSI_MASTER_GPIO) += fsi-master-gpio.o
 obj-$(CONFIG_FSI_MASTER_AST_CF) += fsi-master-ast-cf.o
 obj-$(CONFIG_FSI_SCOM) += fsi-scom.o
 obj-$(CONFIG_FSI_I2CR) += i2cr.o
-obj-$(CONFIG_FSI_SBEFIFO) += sbefifo.o sbefifo-cdev.o
 obj-$(CONFIG_FSI_OCC) += occ.o occ-cdev.o
+
+sbefifo-core-objs := sbefifo.o sbefifo-cdev.o
+
+obj-$(CONFIG_FSI_SBEFIFO) += sbefifo-core.o sbefifo-fsi.o
+obj-$(CONFIG_I2C_SBEFIFO) += sbefifo-core.o sbefifo-i2c.o
diff --git a/drivers/fsi/occ-cdev.c b/drivers/fsi/occ-cdev.c
index b3be880b5fe8..ff9cfd7d8350 100644
--- a/drivers/fsi/occ-cdev.c
+++ b/drivers/fsi/occ-cdev.c
@@ -1,132 +1,138 @@
 // SPDX-License-Identifier: GPL-2.0
 /* Copyright (C) IBM Corporation 2022 */
 
-static int occ_open(struct inode *inode, struct file *file)
+#include <linux/container_of.h>
+#include <linux/errno.h>
+#include <linux/export.h>
+#include <linux/fs.h>
+#include <linux/minmax.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+
+#include "occ.h"
+
+static void occ_release_command(struct occ_user *user)
 {
-	struct occ_client *client = kzalloc(sizeof(*client), GFP_KERNEL);
-	struct miscdevice *mdev = file->private_data;
-	struct occ *occ = to_occ(mdev);
+	if (user->pending.allocated)
+		kfree(user->pending.buffer);
 
-	if (!client)
-		return -ENOMEM;
+	user->pending.buffer = user->buffer;
+	user->pending.buffer_size = sizeof(user->buffer);
+	user->pending.data_size = 0;
+	user->pending.allocated = 0;
+}
 
-	client->buffer = (u8 *)__get_free_page(GFP_KERNEL);
-	if (!client->buffer) {
-		kfree(client);
-		return -ENOMEM;
-	}
+static int occ_open(struct inode *inode, struct file *file)
+{
+	struct occ_user *user;
 
-	client->occ = occ;
-	mutex_init(&client->lock);
-	file->private_data = client;
-	get_device(occ->dev);
+	user = kmalloc(sizeof(*user), GFP_KERNEL);
+	if (!user)
+		return -ENOMEM;
 
-	/* We allocate a 1-page buffer, make sure it all fits */
-	BUILD_BUG_ON((OCC_CMD_DATA_BYTES + 3) > PAGE_SIZE);
-	BUILD_BUG_ON((OCC_RESP_DATA_BYTES + 7) > PAGE_SIZE);
+	file->private_data = user;
+	mutex_init(&user->lock);
+	user->occ = container_of(inode->i_cdev, struct occ, occ);
+	user->pending.buffer = user->buffer;
+	user->pending.buffer_size = sizeof(user->buffer);
+	user->pending.data_size = 0;
+	user->pending.allocated = 0;
+	user->pending.pending = 0;
+	user->pending.resizable = 1;
 
 	return 0;
 }
 
-static ssize_t occ_read(struct file *file, char __user *buf, size_t len,
-			loff_t *offset)
+static ssize_t occ_read(struct file *file, char __user *buf, size_t len, loff_t *offset)
 {
-	struct occ_client *client = file->private_data;
-	ssize_t rc = 0;
-
-	if (!client)
-		return -ENODEV;
+	struct occ_user *user;
+	size_t offs = *offset;
+	int rc;
 
-	if (len > OCC_SRAM_BYTES)
+	if (len + offs > OCC_SRAM_BYTES)
 		return -EINVAL;
 
-	mutex_lock(&client->lock);
+	user = file->private_data;
+	mutex_lock(&user->lock);
 
-	/* This should not be possible ... */
-	if (WARN_ON_ONCE(client->read_offset > client->data_size)) {
-		rc = -EIO;
-		goto done;
+	if (!offs) {
+		if (!user->pending.pending) {
+			rc = -EAGAIN;
+			goto done;
+		}
+
+		user->pending.pending = 0;
+
+		/* 1 byte sequence, 1 byte command type, 2 bytes data size, 2 bytes checksum */
+		rc = occ_submit(user->occ, user->pending.buffer, user->pending.data_size + 6,
+				&user->pending);
+		if (rc)
+			goto done;
 	}
 
-	/* Grab how much data we have to read */
-	rc = min(len, client->data_size - client->read_offset);
-	if (copy_to_user(buf, client->buffer + client->read_offset, rc))
+	rc = min(len, user->pending.data_size - offs);
+	if (copy_to_user(buf, user->pending.buffer + offs, rc))
 		rc = -EFAULT;
 	else
-		client->read_offset += rc;
-
- done:
-	mutex_unlock(&client->lock);
+		*offset += rc;
 
+done:
+	mutex_unlock(&user->lock);
 	return rc;
 }
 
-static ssize_t occ_write(struct file *file, const char __user *buf,
-			 size_t len, loff_t *offset)
+static ssize_t occ_write(struct file *file, const char __user *buf, size_t len, loff_t *offset)
 {
-	struct occ_client *client = file->private_data;
-	size_t rlen, data_length;
-	ssize_t rc;
-	u8 *cmd;
-
-	if (!client)
-		return -ENODEV;
+	struct occ_user *user;
+	int rc;
 
 	if (len > (OCC_CMD_DATA_BYTES + 3) || len < 3)
 		return -EINVAL;
 
-	mutex_lock(&client->lock);
+	user = file->private_data;
+	mutex_lock(&user->lock);
 
-	/* Construct the command */
-	cmd = client->buffer;
+	if (len > user->pending.buffer_size) {
+		occ_pending_alloc(&user->pending, len + 3);
+		if (!user->pending.buffer) {
+			rc = -ENOMEM;
+			goto done;
+		}
+	}
 
 	/*
-	 * Copy the user command (assume user data follows the occ command
-	 * format)
-	 * byte 0: command type
-	 * bytes 1-2: data length (msb first)
-	 * bytes 3-n: data
+	 * Copy the user command (assume user data follows the occ command format)
+	 *  byte 0: command type
+	 *  bytes 1-2: data length (msb first)
+	 *  bytes 3-n: data
 	 */
-	if (copy_from_user(&cmd[1], buf, len)) {
+	if (copy_from_user(&user->pending.buffer[1], buf, len)) {
 		rc = -EFAULT;
+		occ_release_command(user);
 		goto done;
 	}
 
-	/* Extract data length */
-	data_length = (cmd[2] << 8) + cmd[3];
-	if (data_length > OCC_CMD_DATA_BYTES) {
+	user->pending.data_size = (user->pending.buffer[2] << 8) + user->pending.buffer[3];
+	if (user->pending.data_size > OCC_CMD_DATA_BYTES) {
 		rc = -EINVAL;
+		occ_release_command(user);
 		goto done;
 	}
 
-	/* Submit command; 4 bytes before the data and 2 bytes after */
-	rlen = PAGE_SIZE;
-	rc = fsi_occ_submit(client->occ->dev, cmd, data_length + 6, cmd,
-			    &rlen);
-	if (rc)
-		goto done;
-
-	/* Set read tracking data */
-	client->data_size = rlen;
-	client->read_offset = 0;
-
-	/* Done */
+	user->pending.pending = 1;
 	rc = len;
 
- done:
-	mutex_unlock(&client->lock);
-
+done:
+	mutex_unlock(&user->lock);
 	return rc;
 }
 
 static int occ_release(struct inode *inode, struct file *file)
 {
-	struct occ_client *client = file->private_data;
-
-	put_device(client->occ->dev);
-	free_page((unsigned long)client->buffer);
-	kfree(client);
+	struct occ_user *user = file->private_data;
 
+	occ_release_command(user);
+	kfree(user);
 	return 0;
 }
 
@@ -136,4 +142,16 @@ static const struct file_operations occ_fops = {
 	.read = occ_read,
 	.write = occ_write,
 	.release = occ_release,
-};
\ No newline at end of file
+};
+
+void occ_cdev_init(struct occ *occ)
+{
+	int rc;
+
+	cdev_init(&occ->occ, &occ_fops);
+	rc = cdev_device_add(&occ->occ, &occ->dev);
+	if (rc) {
+		occ->no_cdev = 1;
+		dev_err(&occ->dev, "Failed to create char device: %d.\n", rc);
+	}
+}
diff --git a/drivers/fsi/occ.c b/drivers/fsi/occ.c
index 83429b0289e9..37834219f64d 100644
--- a/drivers/fsi/occ.c
+++ b/drivers/fsi/occ.c
@@ -1,30 +1,18 @@
 // SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) IBM Corporation 2022 */
 
-#include <linux/device.h>
-#include <linux/err.h>
-#include <linux/errno.h>
-#include <linux/fs.h>
+#include <linux/fsi.h>
+#include <linux/fsi-occ.h>
 #include <linux/fsi-sbefifo.h>
-#include <linux/gfp.h>
-#include <linux/idr.h>
-#include <linux/kernel.h>
-#include <linux/list.h>
-#include <linux/miscdevice.h>
+#include <linux/jiffies.h>
 #include <linux/mm.h>
-#include <linux/module.h>
-#include <linux/mutex.h>
-#include <linux/fsi-occ.h>
-#include <linux/of.h>
-#include <linux/of_device.h>
 #include <linux/platform_device.h>
-#include <linux/sched.h>
+#include <linux/property.h>
 #include <linux/slab.h>
-#include <linux/uaccess.h>
 #include <asm/unaligned.h>
 
-#define OCC_SRAM_BYTES		4096
-#define OCC_CMD_DATA_BYTES	4090
-#define OCC_RESP_DATA_BYTES	4089
+#include "occ.h"
+#include "sbefifo.h"
 
 #define OCC_P9_SRAM_CMD_ADDR	0xFFFBE000
 #define OCC_P9_SRAM_RSP_ADDR	0xFFFBF000
@@ -37,64 +25,61 @@
 #define OCC_TIMEOUT_MS		1000
 #define OCC_CMD_IN_PRG_WAIT_MS	50
 
-enum versions { occ_p9, occ_p10 };
-
-struct occ {
-	struct device *dev;
-	struct device *sbefifo;
-	char name[32];
-	int idx;
-	bool platform_hwmon;
-	u8 sequence_number;
-	void *buffer;
-	void *client_buffer;
-	size_t client_buffer_size;
-	size_t client_response_size;
-	enum versions version;
-	struct miscdevice mdev;
-	struct mutex occ_lock;
-};
-
-#define to_occ(x)	container_of((x), struct occ, mdev)
-
 struct occ_response {
 	u8 seq_no;
 	u8 cmd_type;
 	u8 return_status;
 	__be16 data_length;
-	u8 data[OCC_RESP_DATA_BYTES + 2];	/* two bytes checksum */
+	u8 data[OCC_RESP_DATA_BYTES];
+	u16 checksum;
 } __packed;
 
-struct occ_client {
-	struct occ *occ;
-	struct mutex lock;
-	size_t data_size;
-	size_t read_offset;
-	u8 *buffer;
-};
+void occ_pending_alloc(struct occ_pending *pending, size_t size)
+{
+	if (pending->allocated) {
+		void *b = krealloc(pending->buffer, size, GFP_KERNEL);
 
-#define to_client(x)	container_of((x), struct occ_client, xfr)
+		if (!b)
+			kfree(pending->buffer);
 
-static DEFINE_IDA(occ_ida);
+		pending->buffer = b;
+	} else {
+		pending->buffer = kmalloc(size, GFP_KERNEL);
+		pending->allocated = 1;
+	}
 
-static void occ_save_ffdc(struct occ *occ, __be32 *resp, size_t parsed_len,
-			  size_t resp_len)
+	pending->buffer_size = size;
+}
+
+static int occ_store(const void *data, size_t size, struct occ_pending *pending)
+{
+	if (size > pending->buffer_size) {
+		if (!pending->resizable)
+			return -ENOBUFS;
+
+		occ_pending_alloc(pending, size);
+		if (!pending->buffer)
+			return -ENOMEM;
+	}
+
+	memcpy(pending->buffer, data, size);
+	pending->data_size = size;
+	return 0;
+}
+
+static void occ_save_ffdc(struct occ *occ, __be32 *resp, size_t parsed_len, size_t resp_len,
+			  struct occ_pending *pending)
 {
 	if (resp_len > parsed_len) {
 		size_t dh = resp_len - parsed_len;
 		size_t ffdc_len = (dh - 1) * 4; /* SBE words are four bytes */
 		__be32 *ffdc = &resp[parsed_len];
 
-		if (ffdc_len > occ->client_buffer_size)
-			ffdc_len = occ->client_buffer_size;
-
-		memcpy(occ->client_buffer, ffdc, ffdc_len);
-		occ->client_response_size = ffdc_len;
+		occ_store(ffdc, ffdc_len, pending);
 	}
 }
 
-static int occ_verify_checksum(struct occ *occ, struct occ_response *resp,
-			       u16 data_length)
+static int occ_verify_checksum(struct occ *occ, struct occ_response *resp, u16 data_length)
 {
 	/* Fetch the two bytes after the data for the checksum. */
 	u16 checksum_resp = get_unaligned_be16(&resp->data[data_length]);
@@ -110,22 +95,24 @@ static int occ_verify_checksum(struct occ *occ, struct occ_response *resp,
 		checksum += resp->data[i];
 
 	if (checksum != checksum_resp) {
-		dev_err(occ->dev, "Bad checksum: %04x!=%04x\n", checksum,
-			checksum_resp);
+		dev_err(&occ->dev, "Bad checksum: %04x!=%04x\n", checksum, checksum_resp);
 		return -EBADE;
 	}
 
 	return 0;
 }
 
-static int occ_getsram(struct occ *occ, u32 offset, void *data, ssize_t len)
+static int occ_getsram(struct occ *occ, u32 offset, ssize_t len, struct occ_pending *pending)
 {
-	u32 data_len = ((len + 7) / 8) * 8;	/* must be multiples of 8 B */
-	size_t cmd_len, parsed_len, resp_data_len;
 	size_t resp_len = OCC_MAX_RESP_WORDS;
-	__be32 *resp = occ->buffer;
+	u32 data_len = ((len + 7) / 8) * 8;	/* must be multiples of 8 B */
+	__be32 *resp = (__be32 *)occ->buffer;
+	size_t resp_data_len;
+	size_t parsed_len;
+	size_t cmd_len;
 	__be32 cmd[6];
-	int idx = 0, rc;
+	int idx = 0;
+	int rc;
 
 	/*
 	 * Magic sequence to do SBE getsram command. SBE will fetch data from
@@ -151,16 +138,15 @@ static int occ_getsram(struct occ *occ, u32 offset, void *data, ssize_t len)
 	cmd[1] = cpu_to_be32(SBEFIFO_CMD_GET_OCC_SRAM);
 	cmd[4 + idx] = cpu_to_be32(data_len);
 
-	rc = sbefifo_submit(occ->sbefifo, cmd, cmd_len, resp, &resp_len);
+	rc = sbefifo_submit(&occ->sbefifo->dev, cmd, cmd_len, resp, &resp_len);
 	if (rc)
 		return rc;
 
-	rc = sbefifo_parse_status(occ->sbefifo, SBEFIFO_CMD_GET_OCC_SRAM,
-				  resp, resp_len, &parsed_len);
+	rc = sbefifo_parse_status(&occ->sbefifo->dev, SBEFIFO_CMD_GET_OCC_SRAM, resp, resp_len,
+				  &parsed_len);
 	if (rc > 0) {
-		dev_err(occ->dev, "SRAM read returned failure status: %08x\n",
-			rc);
-		occ_save_ffdc(occ, resp, parsed_len, resp_len);
+		dev_err(&occ->dev, "SRAM read returned failure status: %08x\n", rc);
+		occ_save_ffdc(occ, resp, parsed_len, resp_len, pending);
 		return -ECOMM;
 	} else if (rc) {
 		return rc;
@@ -168,25 +154,26 @@ static int occ_getsram(struct occ *occ, u32 offset, void *data, ssize_t len)
 
 	resp_data_len = be32_to_cpu(resp[parsed_len - 1]);
 	if (resp_data_len != data_len) {
-		dev_err(occ->dev, "SRAM read expected %d bytes got %zd\n",
-			data_len, resp_data_len);
+		dev_err(&occ->dev, "SRAM read expected %d bytes got %zd\n", data_len,
+			resp_data_len);
 		rc = -EBADMSG;
-	} else {
-		memcpy(data, resp, len);
 	}
 
 	return rc;
 }
 
-static int occ_putsram(struct occ *occ, const void *data, ssize_t len,
-		       u8 seq_no, u16 checksum)
+static int occ_putsram(struct occ *occ, const void *data, ssize_t len, u8 seq_no, u16 checksum,
+		       struct occ_pending *pending)
 {
-	u32 data_len = ((len + 7) / 8) * 8;	/* must be multiples of 8 B */
-	size_t cmd_len, parsed_len, resp_data_len;
 	size_t resp_len = OCC_MAX_RESP_WORDS;
-	__be32 *buf = occ->buffer;
+	u32 data_len = ((len + 7) / 8) * 8;	/* must be multiples of 8 B */
+	__be32 *buf = (__be32 *)occ->buffer;
+	size_t resp_data_len;
+	size_t parsed_len;
+	size_t cmd_len;
 	u8 *byte_buf;
-	int idx = 0, rc;
+	int idx = 0;
+	int rc;
 
 	cmd_len = (occ->version == occ_p10) ? 6 : 5;
 	cmd_len += data_len >> 2;
@@ -224,31 +211,28 @@ static int occ_putsram(struct occ *occ, const void *data, ssize_t len,
 	byte_buf[len - 2] = checksum >> 8;
 	byte_buf[len - 1] = checksum & 0xff;
 
-	rc = sbefifo_submit(occ->sbefifo, buf, cmd_len, buf, &resp_len);
+	rc = sbefifo_submit(&occ->sbefifo->dev, buf, cmd_len, buf, &resp_len);
 	if (rc)
 		return rc;
 
-	rc = sbefifo_parse_status(occ->sbefifo, SBEFIFO_CMD_PUT_OCC_SRAM,
-				  buf, resp_len, &parsed_len);
+	rc = sbefifo_parse_status(&occ->sbefifo->dev, SBEFIFO_CMD_PUT_OCC_SRAM, buf, resp_len,
+				  &parsed_len);
 	if (rc > 0) {
-		dev_err(occ->dev, "SRAM write returned failure status: %08x\n",
-			rc);
-		occ_save_ffdc(occ, buf, parsed_len, resp_len);
+		dev_err(&occ->dev, "SRAM write returned failure status: %08x\n", rc);
+		occ_save_ffdc(occ, buf, parsed_len, resp_len, pending);
 		return -ECOMM;
 	} else if (rc) {
 		return rc;
 	}
 
 	if (parsed_len != 1) {
-		dev_err(occ->dev, "SRAM write response length invalid: %zd\n",
-			parsed_len);
+		dev_err(&occ->dev, "SRAM write response length invalid: %zd\n", parsed_len);
 		rc = -EBADMSG;
 	} else {
 		resp_data_len = be32_to_cpu(buf[0]);
 		if (resp_data_len != data_len) {
-			dev_err(occ->dev,
-				"SRAM write expected %d bytes got %zd\n",
-				data_len, resp_data_len);
+			dev_err(&occ->dev, "SRAM write expected %d bytes got %zd\n", data_len,
+				resp_data_len);
 			rc = -EBADMSG;
 		}
 	}
@@ -256,12 +240,15 @@ static int occ_putsram(struct occ *occ, const void *data, ssize_t len,
 	return rc;
 }
 
-static int occ_trigger_attn(struct occ *occ)
+static int occ_trigger_attn(struct occ *occ, struct occ_pending *pending)
 {
-	__be32 *buf = occ->buffer;
-	size_t cmd_len, parsed_len, resp_data_len;
 	size_t resp_len = OCC_MAX_RESP_WORDS;
-	int idx = 0, rc;
+	__be32 *buf = (__be32 *)occ->buffer;
+	size_t resp_data_len;
+	size_t parsed_len;
+	size_t cmd_len;
+	int idx = 0;
+	int rc;
 
 	switch (occ->version) {
 	default:
@@ -285,31 +272,27 @@ static int occ_trigger_attn(struct occ *occ)
 	buf[5 + idx] = cpu_to_be32(0x20010000);	/* Trigger OCC attention */
 	buf[6 + idx] = 0;
 
-	rc = sbefifo_submit(occ->sbefifo, buf, cmd_len, buf, &resp_len);
+	rc = sbefifo_submit(&occ->sbefifo->dev, buf, cmd_len, buf, &resp_len);
 	if (rc)
 		return rc;
 
-	rc = sbefifo_parse_status(occ->sbefifo, SBEFIFO_CMD_PUT_OCC_SRAM,
-				  buf, resp_len, &parsed_len);
+	rc = sbefifo_parse_status(&occ->sbefifo->dev, SBEFIFO_CMD_PUT_OCC_SRAM, buf, resp_len,
+				  &parsed_len);
 	if (rc > 0) {
-		dev_err(occ->dev, "SRAM attn returned failure status: %08x\n",
-			rc);
-		occ_save_ffdc(occ, buf, parsed_len, resp_len);
+		dev_err(&occ->dev, "SRAM attn returned failure status: %08x\n", rc);
+		occ_save_ffdc(occ, buf, parsed_len, resp_len, pending);
 		return -ECOMM;
 	} else if (rc) {
 		return rc;
 	}
 
 	if (parsed_len != 1) {
-		dev_err(occ->dev, "SRAM attn response length invalid: %zd\n",
-			parsed_len);
+		dev_err(&occ->dev, "SRAM attn response length invalid: %zd\n", parsed_len);
 		rc = -EBADMSG;
 	} else {
 		resp_data_len = be32_to_cpu(buf[0]);
 		if (resp_data_len != 8) {
-			dev_err(occ->dev,
-				"SRAM attn expected 8 bytes got %zd\n",
-				resp_data_len);
+			dev_err(&occ->dev, "SRAM attn expected 8 bytes got %zd\n", resp_data_len);
 			rc = -EBADMSG;
 		}
 	}
@@ -317,61 +300,37 @@ static int occ_trigger_attn(struct occ *occ)
 	return rc;
 }
 
-static bool fsi_occ_response_not_ready(struct occ_response *resp, u8 seq_no,
-				       u8 cmd_type)
+static bool occ_response_not_ready(struct occ_response *resp, u8 seq_no, u8 cmd_type)
 {
 	return resp->return_status == OCC_RESP_CMD_IN_PRG ||
 		resp->return_status == OCC_RESP_CRIT_INIT ||
 		resp->seq_no != seq_no || resp->cmd_type != cmd_type;
 }
 
-int fsi_occ_submit(struct device *dev, const void *request, size_t req_len,
-		   void *response, size_t *resp_len)
+int occ_submit(struct occ *occ, const u8 *request, size_t request_size,
+	       struct occ_pending *pending)
 {
+	const unsigned long wait_time = msecs_to_jiffies(OCC_CMD_IN_PRG_WAIT_MS);
 	const unsigned long timeout = msecs_to_jiffies(OCC_TIMEOUT_MS);
-	const unsigned long wait_time =
-		msecs_to_jiffies(OCC_CMD_IN_PRG_WAIT_MS);
-	struct occ *occ = dev_get_drvdata(dev);
-	struct occ_response *resp = response;
-	size_t user_resp_len = *resp_len;
-	u8 seq_no;
-	u8 cmd_type;
-	u16 checksum = 0;
-	u16 resp_data_length;
-	const u8 *byte_request = (const u8 *)request;
+	struct occ_response *resp = (struct occ_response *)occ->buffer;
+	u8 cmd_type = request[1];
+	size_t resp_data_length;
 	unsigned long end;
+	u16 checksum = 0;
+	u8 seq_no;
 	int rc;
-	size_t i;
-
-	*resp_len = 0;
+	int i;
 
-	if (!occ)
+	if (occ->dead)
 		return -ENODEV;
 
-	if (user_resp_len < 7) {
-		dev_dbg(dev, "Bad resplen %zd\n", user_resp_len);
-		return -EINVAL;
-	}
-
-	cmd_type = byte_request[1];
-
-	/* Checksum the request, ignoring first byte (sequence number). */
-	for (i = 1; i < req_len - 2; ++i)
-		checksum += byte_request[i];
+	for (i = 1; i < request_size - 2; ++i)
+		checksum += request[i];
 
-	rc = mutex_lock_interruptible(&occ->occ_lock);
+	rc = mutex_lock_interruptible(&occ->lock);
 	if (rc)
 		return rc;
 
-	occ->client_buffer = response;
-	occ->client_buffer_size = user_resp_len;
-	occ->client_response_size = 0;
-
-	if (!occ->buffer) {
-		rc = -ENOENT;
-		goto done;
-	}
-
 	/*
 	 * Get a sequence number and update the counter. Avoid a sequence
 	 * number of 0 which would pass the response check below even if the
@@ -385,27 +344,27 @@ int fsi_occ_submit(struct device *dev, const void *request, size_t req_len,
 		occ->sequence_number = 1;
 	checksum += seq_no;
 
-	rc = occ_putsram(occ, request, req_len, seq_no, checksum);
+	rc = occ_putsram(occ, request, request_size, seq_no, checksum, pending);
 	if (rc)
 		goto done;
 
-	rc = occ_trigger_attn(occ);
+	rc = occ_trigger_attn(occ, pending);
 	if (rc)
 		goto done;
 
 	end = jiffies + timeout;
 	while (true) {
 		/* Read occ response header */
-		rc = occ_getsram(occ, 0, resp, 8);
+		rc = occ_getsram(occ, 0, 8, pending);
 		if (rc)
 			goto done;
 
-		if (fsi_occ_response_not_ready(resp, seq_no, cmd_type)) {
+		if (occ_response_not_ready(resp, seq_no, cmd_type)) {
 			if (time_after(jiffies, end)) {
-				dev_err(occ->dev,
-					"resp timeout status=%02x seq=%d cmd=%d, our seq=%d cmd=%d\n",
-					resp->return_status, resp->seq_no,
-					resp->cmd_type, seq_no, cmd_type);
+				dev_err(&occ->dev,
+					"resp timeout sts=%02x seq=%d cmd=%d, our seq=%d cmd=%d\n",
+					resp->return_status, resp->seq_no, resp->cmd_type, seq_no,
+					cmd_type);
 				rc = -ETIMEDOUT;
 				goto done;
 			}
@@ -414,14 +373,13 @@ int fsi_occ_submit(struct device *dev, const void *request, size_t req_len,
 			schedule_timeout(wait_time);
 		} else {
 			/* Extract size of response data */
-			resp_data_length =
-				get_unaligned_be16(&resp->data_length);
+			resp_data_length = get_unaligned_be16(&resp->data_length);
 
 			/*
 			 * Message size is data length + 5 bytes header + 2
 			 * bytes checksum
 			 */
-			if ((resp_data_length + 7) > user_resp_len) {
+			if ((resp_data_length + 7) > sizeof(occ->buffer)) {
 				rc = -EMSGSIZE;
 				goto done;
 			}
@@ -431,13 +389,11 @@ int fsi_occ_submit(struct device *dev, const void *request, size_t req_len,
 			 * in case it changed
 			 */
 			if (resp_data_length > 1) {
-				rc = occ_getsram(occ, 0, resp,
-						 resp_data_length + 7);
+				rc = occ_getsram(occ, 0, resp_data_length + 7, pending);
 				if (rc)
 					goto done;
 
-				if (!fsi_occ_response_not_ready(resp, seq_no,
-								cmd_type))
+				if (!occ_response_not_ready(resp, seq_no, cmd_type))
 					break;
 			} else {
 				break;
@@ -445,185 +401,136 @@ int fsi_occ_submit(struct device *dev, const void *request, size_t req_len,
 		}
 	}
 
-	dev_dbg(dev, "resp_status=%02x resp_data_len=%d\n",
-		resp->return_status, resp_data_length);
-
 	rc = occ_verify_checksum(occ, resp, resp_data_length);
 	if (rc)
 		goto done;
 
-	occ->client_response_size = resp_data_length + 7;
-
- done:
-	*resp_len = occ->client_response_size;
-	mutex_unlock(&occ->occ_lock);
+	rc = occ_store(resp, resp_data_length + 7, pending);
 
+done:
+	mutex_unlock(&occ->lock);
 	return rc;
 }
-EXPORT_SYMBOL_GPL(fsi_occ_submit);
 
-static int occ_unregister_platform_child(struct device *dev, void *data)
+static void occ_free(struct device *dev)
 {
-	struct platform_device *hwmon_dev = to_platform_device(dev);
+	struct occ *occ = container_of(dev, struct occ, dev);
 
-	platform_device_unregister(hwmon_dev);
+	put_device(dev->parent);
+	kvfree(occ);
+}
 
-	return 0;
+static const struct property_entry hwmon_properties[] = {
+	PROPERTY_ENTRY_BOOL("ibm,no-poll-on-init"),
+	{ }
+};
+
+static void occ_register_hwmon(struct occ *occ, int idx)
+{
+	struct platform_device_info hwmon_dev_info = {
+		.parent = &occ->dev,
+		.name = "occ-hwmon",
+		.id = idx,
+		.properties = hwmon_properties,
+	};
+	struct platform_device *hwmon_dev;
+
+	hwmon_dev = platform_device_register_full(&hwmon_dev_info);
+	if (IS_ERR(hwmon_dev))
+		dev_warn(&occ->dev, "Failed to create hwmon device: %ld.\n", PTR_ERR(hwmon_dev));
 }
 
-static int occ_unregister_of_child(struct device *dev, void *data)
+static int occ_unregister_child(struct device *dev, void *data)
 {
 	struct platform_device *hwmon_dev = to_platform_device(dev);
 
-	of_device_unregister(hwmon_dev);
-	if (dev->of_node)
-		of_node_clear_flag(dev->of_node, OF_POPULATED);
+	platform_device_unregister(hwmon_dev);
 
 	return 0;
 }
 
-static int occ_probe(struct platform_device *pdev)
+struct occ *occ_create(struct sbefifo *sbefifo,
+		       int (*get_devt)(struct device *, int, dev_t *, int *), enum versions v)
 {
-	int rc;
-	u32 reg;
-	char child_name[32];
 	struct occ *occ;
-	struct platform_device *hwmon_dev = NULL;
-	struct device_node *hwmon_node;
-	struct device *dev = &pdev->dev;
-	struct platform_device_info hwmon_dev_info = {
-		.parent = dev,
-		.name = "occ-hwmon",
-	};
+	int idx;
+	int rc;
+
+	if (!get_device(&sbefifo->dev))
+		return NULL;
 
-	occ = devm_kzalloc(dev, sizeof(*occ), GFP_KERNEL);
-	if (!occ)
-		return -ENOMEM;
+	occ = kvzalloc(sizeof(*occ), GFP_KERNEL);
+	if (!occ) {
+		put_device(&sbefifo->dev);
+		return NULL;
+	}
+
+	rc = get_devt(sbefifo->dev.parent, (int)fsi_dev_occ, &occ->dev.devt, &idx);
+	if (rc) {
+		kvfree(occ);
+		put_device(&sbefifo->dev);
+		return NULL;
+	}
+
+	occ->dev.type = &fsi_cdev_type;
+	occ->dev.parent = &sbefifo->dev;
+	occ->dev.release = occ_free;
+	device_initialize(&occ->dev);
+	dev_set_name(&occ->dev, "occ%d", idx);
 
-	/* SBE words are always four bytes */
-	occ->buffer = kvmalloc(OCC_MAX_RESP_WORDS * 4, GFP_KERNEL);
-	if (!occ->buffer)
-		return -ENOMEM;
+	occ_cdev_init(occ);
 
-	occ->version = (uintptr_t)of_device_get_match_data(dev);
-	occ->dev = dev;
-	occ->sbefifo = dev->parent;
+	mutex_init(&occ->lock);
+	occ->sbefifo = sbefifo;
+	occ->version = v;
 	/*
 	 * Quickly derive a pseudo-random number from jiffies so that
 	 * re-probing the driver doesn't accidentally overlap sequence numbers.
 	 */
 	occ->sequence_number = (u8)((jiffies % 0xff) + 1);
-	mutex_init(&occ->occ_lock);
-
-	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);
-	}
-
-	platform_set_drvdata(pdev, occ);
-
-	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: %d\n", rc);
-		ida_simple_remove(&occ_ida, occ->idx);
-		kvfree(occ->buffer);
-		return rc;
-	}
 
-	hwmon_node = of_get_child_by_name(dev->of_node, hwmon_dev_info.name);
-	if (hwmon_node) {
-		snprintf(child_name, sizeof(child_name), "%s.%d", hwmon_dev_info.name, occ->idx);
-		hwmon_dev = of_platform_device_create(hwmon_node, child_name, dev);
-		of_node_put(hwmon_node);
-	}
-
-	if (!hwmon_dev) {
-		occ->platform_hwmon = true;
-		hwmon_dev_info.id = occ->idx;
-		hwmon_dev = platform_device_register_full(&hwmon_dev_info);
-		if (IS_ERR(hwmon_dev))
-			dev_warn(dev, "failed to create hwmon device\n");
-	}
+	occ_register_hwmon(occ, idx);
 
-	return 0;
+	return occ;
 }
+EXPORT_SYMBOL_GPL(occ_create);
 
-static int occ_remove(struct platform_device *pdev)
+void occ_destroy(struct occ *occ, void(*free_devt)(dev_t))
 {
-	struct occ *occ = platform_get_drvdata(pdev);
-
-	misc_deregister(&occ->mdev);
+	occ->dead = 1;
 
-	mutex_lock(&occ->occ_lock);
-	kvfree(occ->buffer);
-	occ->buffer = NULL;
-	mutex_unlock(&occ->occ_lock);
+	device_for_each_child(&occ->dev, NULL, occ_unregister_child);
 
-	if (occ->platform_hwmon)
-		device_for_each_child(&pdev->dev, NULL, occ_unregister_platform_child);
-	else
-		device_for_each_child(&pdev->dev, NULL, occ_unregister_of_child);
+	if (free_devt)
+		free_devt(occ->dev.devt);
 
-	ida_simple_remove(&occ_ida, occ->idx);
+	if (!occ->no_cdev)
+		cdev_device_del(&occ->occ, &occ->dev);
 
-	return 0;
+	put_device(&occ->dev);
 }
+EXPORT_SYMBOL_GPL(occ_destroy);
 
-static const struct of_device_id occ_match[] = {
-	{
-		.compatible = "ibm,p9-occ",
-		.data = (void *)occ_p9
-	},
-	{
-		.compatible = "ibm,p10-occ",
-		.data = (void *)occ_p10
-	},
-	{ },
-};
-MODULE_DEVICE_TABLE(of, occ_match);
-
-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)
+int fsi_occ_submit(struct device *dev, const void *request, size_t req_len, void *response,
+		   size_t *resp_len)
 {
-	return platform_driver_register(&occ_driver);
-}
+	struct occ *occ = container_of(dev, struct occ, dev);
+	struct occ_pending pending;
+	int rc;
 
-static void occ_exit(void)
-{
-	platform_driver_unregister(&occ_driver);
+	pending.buffer = response;
+	pending.buffer_size = *resp_len;
+	pending.data_size = 0;
+	pending.allocated = 0;
+	pending.resizable = 0;
+	pending.pending = 0;
 
-	ida_destroy(&occ_ida);
+	rc = occ_submit(occ, request, req_len, &pending);
+	*resp_len = pending.data_size;
+	return rc;
 }
-
-module_init(occ_init);
-module_exit(occ_exit);
+EXPORT_SYMBOL_GPL(fsi_occ_submit);
 
 MODULE_AUTHOR("Eddie James <eajames@linux.ibm.com>");
-MODULE_DESCRIPTION("BMC P9 OCC driver");
+MODULE_DESCRIPTION("OCC communications driver");
 MODULE_LICENSE("GPL");
diff --git a/drivers/fsi/occ.h b/drivers/fsi/occ.h
new file mode 100644
index 000000000000..bbb1b3aecdeb
--- /dev/null
+++ b/drivers/fsi/occ.h
@@ -0,0 +1,57 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright (C) IBM Corporation 2022 */
+
+#ifndef DRIVERS_OCC_H
+#define DRIVERS_OCC_H
+
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/fsi-occ.h>
+#include <linux/mutex.h>
+#include <linux/types.h>
+
+#define OCC_SRAM_BYTES		4096
+#define OCC_CMD_DATA_BYTES	4090
+#define OCC_RESP_DATA_BYTES	4089
+
+struct sbefifo;
+
+enum versions { occ_p9, occ_p10, };
+
+struct occ {
+	struct device dev;
+	struct cdev occ;
+	struct mutex lock;	// device lock
+	struct sbefifo *sbefifo;
+	enum versions version;
+	u8 sequence_number;
+	u8 dead : 1;
+	u8 no_cdev : 1;
+	u8 buffer[OCC_MAX_RESP_WORDS * 4];
+};
+
+struct occ_pending {
+	u8 *buffer;
+	size_t buffer_size;
+	size_t data_size;
+	u8 allocated : 1;
+	u8 pending : 1;
+	u8 resizable : 1;
+};
+
+struct occ_user {
+	struct mutex lock;	// file lock
+	struct occ *occ;
+	struct occ_pending pending;
+	u8 buffer[128];
+};
+
+void occ_cdev_init(struct occ *occ);
+struct occ *occ_create(struct sbefifo *sbefifo,
+		       int (*get_devt)(struct device *, int, dev_t *, int *), enum versions v);
+void occ_destroy(struct occ *occ, void(*free_devt)(dev_t));
+void occ_pending_alloc(struct occ_pending *pending, size_t size);
+int occ_submit(struct occ *occ, const u8 *request, size_t request_size,
+	       struct occ_pending *pending);
+
+#endif /* DRIVERS_OCC_H */
diff --git a/drivers/fsi/sbefifo-cdev.c b/drivers/fsi/sbefifo-cdev.c
index 75765e8c307a..ba34b1436557 100644
--- a/drivers/fsi/sbefifo-cdev.c
+++ b/drivers/fsi/sbefifo-cdev.c
@@ -1,217 +1,218 @@
 // SPDX-License-Identifier: GPL-2.0
 /* Copyright (C) IBM Corporation 2022 */
 
-/*
- * Char device interface
- */
+#include <linux/container_of.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/uio.h>
+
+#include <uapi/linux/fsi.h>
+
+#include "sbefifo.h"
+
+struct sbefifo_user {
+	struct mutex lock;	// file lock
+	struct sbefifo *sbefifo;
+	struct {
+		void *buffer;
+		size_t size;
+	} pending;
+	unsigned int timeout;
+	u8 buffer[128];
+};
 
 static void sbefifo_release_command(struct sbefifo_user *user)
 {
-	if (is_vmalloc_addr(user->pending_cmd))
-		vfree(user->pending_cmd);
-	user->pending_cmd = NULL;
-	user->pending_len = 0;
+	if (user->pending.buffer != user->buffer)
+		kvfree(user->pending.buffer);
+
+	user->pending.buffer = NULL;
+	user->pending.size = 0;
 }
 
-static int sbefifo_user_open(struct inode *inode, struct file *file)
+static int sbefifo_open(struct inode *inode, struct file *file)
 {
-	struct sbefifo *sbefifo = container_of(inode->i_cdev, struct sbefifo, cdev);
 	struct sbefifo_user *user;
 
-	user = kzalloc(sizeof(struct sbefifo_user), GFP_KERNEL);
+	user = kmalloc(sizeof(*user), GFP_KERNEL);
 	if (!user)
 		return -ENOMEM;
 
 	file->private_data = user;
-	user->sbefifo = sbefifo;
-	user->cmd_page = (void *)__get_free_page(GFP_KERNEL);
-	if (!user->cmd_page) {
-		kfree(user);
-		return -ENOMEM;
-	}
-	mutex_init(&user->file_lock);
-	user->read_timeout_ms = SBEFIFO_TIMEOUT_START_RSP;
+	mutex_init(&user->lock);
+	user->sbefifo = container_of(inode->i_cdev, struct sbefifo, sbe);
+	user->pending.buffer = NULL;
+	user->pending.size = 0;
+	user->timeout = SBEFIFO_TIMEOUT_START_RSP;
 
 	return 0;
 }
 
-static ssize_t sbefifo_user_read(struct file *file, char __user *buf,
-				 size_t len, loff_t *offset)
+static ssize_t sbefifo_read(struct file *file, char __user *buf, size_t len, loff_t *offset)
 {
-	struct sbefifo_user *user = file->private_data;
-	struct sbefifo *sbefifo;
+	struct sbefifo_user *user;
 	struct iov_iter resp_iter;
-        struct iovec resp_iov;
-	size_t cmd_len;
+	struct iovec resp_iov;
 	int rc;
 
-	if (!user)
-		return -EINVAL;
-	sbefifo = user->sbefifo;
 	if (len & 3)
 		return -EINVAL;
 
-	mutex_lock(&user->file_lock);
+	user = file->private_data;
+	mutex_lock(&user->lock);
 
-	/* Cronus relies on -EAGAIN after a short read */
-	if (user->pending_len == 0) {
+	if (!user->pending.size) {
 		rc = -EAGAIN;
-		goto bail;
+		goto done;
 	}
-	if (user->pending_len < 8) {
+
+	if (user->pending.size < 8) {
 		rc = -EINVAL;
-		goto bail;
+		goto done;
 	}
-	cmd_len = user->pending_len >> 2;
 
-	/* Prepare iov iterator */
 	resp_iov.iov_base = buf;
 	resp_iov.iov_len = len;
 	iov_iter_init(&resp_iter, WRITE, &resp_iov, 1, len);
 
-	/* Perform the command */
-	rc = mutex_lock_interruptible(&sbefifo->lock);
+	rc = mutex_lock_interruptible(&user->sbefifo->lock);
+	if (rc)
+		goto done;
+	user->sbefifo->timeout = user->timeout;
+	rc = sbefifo_submit_internal(user->sbefifo, user->pending.buffer, user->pending.size >> 2,
+				     &resp_iter);
+	user->sbefifo->timeout = SBEFIFO_TIMEOUT_START_RSP;
+	mutex_unlock(&user->sbefifo->lock);
 	if (rc)
-		goto bail;
-	sbefifo->timeout_start_rsp_ms = user->read_timeout_ms;
-	rc = __sbefifo_submit(sbefifo, user->pending_cmd, cmd_len, &resp_iter);
-	sbefifo->timeout_start_rsp_ms = SBEFIFO_TIMEOUT_START_RSP;
-	mutex_unlock(&sbefifo->lock);
-	if (rc < 0)
-		goto bail;
-
-	/* Extract the response length */
+		goto done;
+
 	rc = len - iov_iter_count(&resp_iter);
- bail:
+
+done:
 	sbefifo_release_command(user);
-	mutex_unlock(&user->file_lock);
+	mutex_unlock(&user->lock);
 	return rc;
 }
 
-static ssize_t sbefifo_user_write(struct file *file, const char __user *buf,
-				  size_t len, loff_t *offset)
+static ssize_t sbefifo_write(struct file *file, const char __user *buf, size_t len, loff_t *offset)
 {
-	struct sbefifo_user *user = file->private_data;
-	struct sbefifo *sbefifo;
-	int rc = len;
+	struct sbefifo_user *user;
+	int rc;
 
-	if (!user)
-		return -EINVAL;
-	sbefifo = user->sbefifo;
-	if (len > SBEFIFO_MAX_USER_CMD_LEN)
-		return -EINVAL;
-	if (len & 3)
+	if (len > SBEFIFO_MAX_USER_CMD_LEN || len & 3)
 		return -EINVAL;
 
-	mutex_lock(&user->file_lock);
-
-	/* Can we use the pre-allocate buffer ? If not, allocate */
-	if (len <= PAGE_SIZE)
-		user->pending_cmd = user->cmd_page;
-	else
-		user->pending_cmd = vmalloc(len);
-	if (!user->pending_cmd) {
-		rc = -ENOMEM;
-		goto bail;
+	user = file->private_data;
+	mutex_lock(&user->lock);
+
+	if (len <= sizeof(user->buffer)) {
+		user->pending.buffer = user->buffer;
+	} else {
+		user->pending.buffer = kvmalloc(len, GFP_KERNEL);
+		if (!user->pending.buffer) {
+			user->pending.size = 0;
+			rc = -ENOMEM;
+			goto done;
+		}
 	}
 
-	/* Copy the command into the staging buffer */
-	if (copy_from_user(user->pending_cmd, buf, len)) {
+	if (copy_from_user(user->pending.buffer, buf, len)) {
 		rc = -EFAULT;
-		goto bail;
+		sbefifo_release_command(user);
+		goto done;
 	}
 
 	/* Check for the magic reset command */
-	if (len == 4 && be32_to_cpu(*(__be32 *)user->pending_cmd) ==
-	    SBEFIFO_RESET_MAGIC)  {
+	if (len == 4 && be32_to_cpu(*(__be32 *)user->pending.buffer) == SBEFIFO_RESET_MAGIC) {
+		user->pending.buffer = NULL;
+		user->pending.size = 0;
 
-		/* Clear out any pending command */
-		user->pending_len = 0;
-
-		/* Trigger reset request */
-		rc = mutex_lock_interruptible(&sbefifo->lock);
+		rc = mutex_lock_interruptible(&user->sbefifo->lock);
 		if (rc)
-			goto bail;
+			goto done;
 		rc = sbefifo_request_reset(user->sbefifo);
-		mutex_unlock(&sbefifo->lock);
-		if (rc == 0)
+		mutex_unlock(&user->sbefifo->lock);
+		if (!rc)
 			rc = 4;
-		goto bail;
+	} else {
+		user->pending.size = len;
+		rc = len;
 	}
 
-	/* Update the staging buffer size */
-	user->pending_len = len;
- bail:
-	if (!user->pending_len)
-		sbefifo_release_command(user);
-
-	mutex_unlock(&user->file_lock);
-
-	/* And that's it, we'll issue the command on a read */
+done:
+	mutex_unlock(&user->lock);
 	return rc;
 }
 
-static int sbefifo_user_release(struct inode *inode, struct file *file)
+static int sbefifo_release(struct inode *inode, struct file *file)
 {
 	struct sbefifo_user *user = file->private_data;
 
-	if (!user)
-		return -EINVAL;
-
 	sbefifo_release_command(user);
-	free_page((unsigned long)user->cmd_page);
 	kfree(user);
-
 	return 0;
 }
 
 static int sbefifo_read_timeout(struct sbefifo_user *user, void __user *argp)
 {
-	struct device *dev = &user->sbefifo->dev;
 	u32 timeout;
 
 	if (get_user(timeout, (__u32 __user *)argp))
 		return -EFAULT;
 
-	if (timeout == 0) {
-		user->read_timeout_ms = SBEFIFO_TIMEOUT_START_RSP;
-		dev_dbg(dev, "Timeout reset to %d\n", user->read_timeout_ms);
+	if (!timeout) {
+		user->timeout = SBEFIFO_TIMEOUT_START_RSP;
 		return 0;
 	}
 
 	if (timeout < 10 || timeout > 120)
 		return -EINVAL;
 
-	user->read_timeout_ms = timeout * 1000; /* user timeout is in sec */
-
-	dev_dbg(dev, "Timeout set to %d\n", user->read_timeout_ms);
-
+	user->timeout = timeout * 1000;
 	return 0;
 }
 
-static long sbefifo_user_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+static long sbefifo_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 {
-	struct sbefifo_user *user = file->private_data;
-	int rc = -ENOTTY;
+	struct sbefifo_user *user;
+	int rc;
 
-	if (!user)
-		return -EINVAL;
+	user = file->private_data;
+	mutex_lock(&user->lock);
 
-	mutex_lock(&user->file_lock);
 	switch (cmd) {
 	case FSI_SBEFIFO_READ_TIMEOUT_SECONDS:
 		rc = sbefifo_read_timeout(user, (void __user *)arg);
 		break;
+	default:
+		rc = -ENOTTY;
+		break;
 	}
-	mutex_unlock(&user->file_lock);
+
+	mutex_unlock(&user->lock);
 	return rc;
 }
 
 static const struct file_operations sbefifo_fops = {
-	.owner		= THIS_MODULE,
-	.open		= sbefifo_user_open,
-	.read		= sbefifo_user_read,
-	.write		= sbefifo_user_write,
-	.release	= sbefifo_user_release,
-	.unlocked_ioctl = sbefifo_user_ioctl,
+	.owner = THIS_MODULE,
+	.open = sbefifo_open,
+	.read = sbefifo_read,
+	.write = sbefifo_write,
+	.release = sbefifo_release,
+	.unlocked_ioctl = sbefifo_ioctl,
 };
+
+void sbefifo_cdev_init(struct sbefifo *sbefifo)
+{
+	int rc;
+
+	cdev_init(&sbefifo->sbe, &sbefifo_fops);
+	rc = cdev_device_add(&sbefifo->sbe, &sbefifo->dev);
+	if (rc) {
+		sbefifo->no_cdev = 1;
+		dev_err(&sbefifo->dev, "Failed to create char device: %d.\n", rc);
+	}
+}
diff --git a/drivers/fsi/sbefifo-fsi.c b/drivers/fsi/sbefifo-fsi.c
new file mode 100644
index 000000000000..9b425cf71ca8
--- /dev/null
+++ b/drivers/fsi/sbefifo-fsi.c
@@ -0,0 +1,68 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) IBM Corporation 2022 */
+
+#include <linux/fsi.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/regmap.h>
+
+#include "sbefifo.h"
+
+static int sbefifo_fsi_get_devt(struct device *dev, int type, dev_t *devt, int *idx)
+{
+	return fsi_get_new_minor(to_fsi_dev(dev), (enum fsi_dev_type)type, devt, idx);
+}
+
+static int sbefifo_fsi_probe(struct device *dev)
+{
+	struct regmap_config cfg = {
+		.reg_bits = 32,
+		.reg_stride = 4,
+		.val_bits = 32,
+		.disable_locking = true,
+		.cache_type = REGCACHE_NONE,
+		.val_format_endian = REGMAP_ENDIAN_BIG,
+	};
+	struct fsi_device *fsi_dev;
+	struct regmap *regmap;
+	struct sbefifo *sbefifo;
+
+	fsi_dev = to_fsi_dev(dev);
+	regmap = regmap_init_fsi(fsi_dev, &cfg);
+	if (IS_ERR(regmap))
+		return PTR_ERR(regmap);
+
+	sbefifo = sbefifo_create(dev, regmap, sbefifo_fsi_get_devt);
+	if (IS_ERR(sbefifo)) {
+		regmap_exit(regmap);
+		return PTR_ERR(sbefifo);
+	}
+
+	sbefifo->fsi_dev = fsi_dev;
+	dev_set_drvdata(dev, sbefifo);
+
+	return 0;
+}
+
+static int sbefifo_fsi_remove(struct device *dev)
+{
+	sbefifo_destroy(dev_get_drvdata(dev), fsi_free_minor);
+	return 0;
+}
+
+static const struct fsi_device_id sbefifo_fsi_ids[] = {
+	{ .engine_type = 0x22, .version = FSI_VERSION_ANY, },
+	{ }
+};
+
+static struct fsi_driver sbefifo_fsi_driver = {
+	.drv = {
+		.name = SBEFIFO_DEVICE_NAME,
+		.bus = &fsi_bus_type,
+		.probe = sbefifo_fsi_probe,
+		.remove = sbefifo_fsi_remove,
+	},
+	.id_table = sbefifo_fsi_ids,
+};
+
+module_fsi_driver(sbefifo_fsi_driver);
diff --git a/drivers/fsi/sbefifo-i2c.c b/drivers/fsi/sbefifo-i2c.c
new file mode 100644
index 000000000000..efc526a5947c
--- /dev/null
+++ b/drivers/fsi/sbefifo-i2c.c
@@ -0,0 +1,73 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) IBM Corporation 2022 */
+
+#include <linux/i2c.h>
+#include <linux/i2c-dev.h>
+#include <linux/kdev_t.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/regmap.h>
+
+#include "i2cr.h"
+#include "sbefifo.h"
+
+static int sbefifo_i2c_get_devt(struct device *dev, int type, dev_t *devt, int *idx)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+
+	*idx = client->adapter->nr;
+	*devt = MKDEV(I2C_MAJOR, (*idx << 7) | client->addr | type);
+
+	return 0;
+}
+
+static int sbefifo_i2c_probe(struct i2c_client *client)
+{
+	struct regmap_config cfg = {
+		.reg_bits = 32,
+		.reg_stride = 4,
+		.val_bits = 32,
+		.disable_locking = true,
+		.cache_type = REGCACHE_NONE,
+		.val_format_endian = REGMAP_ENDIAN_BIG,
+	};
+	struct regmap *regmap;
+	struct sbefifo *sbefifo;
+
+	regmap = init_i2cr(client, &cfg);
+	if (IS_ERR(regmap))
+		return PTR_ERR(regmap);
+
+	sbefifo = sbefifo_create(&client->dev, regmap, sbefifo_i2c_get_devt);
+	if (IS_ERR(sbefifo)) {
+		regmap_exit(regmap);
+		return PTR_ERR(sbefifo);
+	}
+
+	sbefifo->i2c_client = client;
+	i2c_set_clientdata(client, sbefifo);
+
+	return 0;
+}
+
+static void sbefifo_i2c_remove(struct i2c_client *client)
+{
+	sbefifo_destroy(i2c_get_clientdata(client), NULL);
+}
+
+static const struct of_device_id sbefifo_i2c_ids[] = {
+	{ .compatible = "ibm,sbefifo", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, sbefifo_i2c_ids);
+
+static struct i2c_driver sbefifo_i2c_driver = {
+	.probe_new = sbefifo_i2c_probe,
+	.remove = sbefifo_i2c_remove,
+	.driver = {
+		.name = SBEFIFO_DEVICE_NAME,
+		.of_match_table = sbefifo_i2c_ids,
+	},
+};
+
+module_i2c_driver(sbefifo_i2c_driver);
diff --git a/drivers/fsi/sbefifo.c b/drivers/fsi/sbefifo.c
index 634405b7aaf4..5743912a1b00 100644
--- a/drivers/fsi/sbefifo.c
+++ b/drivers/fsi/sbefifo.c
@@ -1,50 +1,19 @@
 // SPDX-License-Identifier: GPL-2.0
-/*
- * 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.
- */
+/* Copyright (C) IBM Corporation 2022 */
 
-#include <linux/device.h>
 #include <linux/errno.h>
-#include <linux/fs.h>
 #include <linux/fsi.h>
 #include <linux/fsi-sbefifo.h>
-#include <linux/kernel.h>
-#include <linux/cdev.h>
-#include <linux/module.h>
-#include <linux/mutex.h>
-#include <linux/of.h>
-#include <linux/of_device.h>
-#include <linux/of_platform.h>
-#include <linux/sched.h>
+#include <linux/regmap.h>
 #include <linux/slab.h>
-#include <linux/uaccess.h>
-#include <linux/delay.h>
+#include <linux/sysfs.h>
 #include <linux/uio.h>
 #include <linux/vmalloc.h>
-#include <linux/mm.h>
 
-#include <uapi/linux/fsi.h>
+#include "occ.h"
+#include "sbefifo.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
-
-/*
- * Register layout
- */
+#define SBEFIFO_BASE		0x2400
 
 /* Register banks */
 #define SBEFIFO_UP		0x00		/* FSI -> Host */
@@ -71,6 +40,16 @@
 #define SBEFIFO_EOT_ACK		0x14		/* (Down only) Acknowledge EOT */
 #define SBEFIFO_DOWN_MAX	0x18		/* (Down only) Max transfer */
 
+/* CFAM SCOM ChipID register */
+#define CFAM_SCOM_CHIP_ID	0x1028 /* Converted 0x100a */
+#define CFAM_SCOM_CHIP_ID_CHIP_ID_MASK	0x000FF000
+#define CFAM_SCOM_CHIP_ID_CHIP_ID_SHIFT	12
+
+#define CHIP_ID_P9A		0xd1
+#define CHIP_ID_P9B		0xd4
+#define CHIP_ID_P9plus		0xd9
+#define CHIP_ID_P10		0xda
+
 /* CFAM GP Mailbox SelfBoot Message register */
 #define CFAM_GP_MBOX_SBM_ADDR	0x2824	/* Converted 0x2809 */
 
@@ -79,9 +58,8 @@
 #define CFAM_SBM_SBE_STATE_MASK		0x00f00000
 #define CFAM_SBM_SBE_STATE_SHIFT	20
 
-enum sbe_state
-{
-	SBE_STATE_UNKNOWN = 0x0, // Unkown, initial state
+enum sbe_state {
+	SBE_STATE_UNKNOWN = 0x0, // Unknown, initial state
 	SBE_STATE_IPLING  = 0x1, // IPL'ing - autonomous mode (transient)
 	SBE_STATE_ISTEP   = 0x2, // ISTEP - Running IPL by steps (transient)
 	SBE_STATE_MPIPL   = 0x3, // MPIPL
@@ -93,7 +71,7 @@ enum sbe_state
 };
 
 /* FIFO depth */
-#define SBEFIFO_FIFO_DEPTH		8
+#define SBEFIFO_FIFO_DEPTH	8
 
 /* Helpers */
 #define sbefifo_empty(sts)	((sts) & SBEFIFO_STS_EMPTY)
@@ -109,44 +87,15 @@ enum sbe_state
 /* Timeouts for commands in ms */
 #define SBEFIFO_TIMEOUT_START_CMD	10000
 #define SBEFIFO_TIMEOUT_IN_CMD		1000
-#define SBEFIFO_TIMEOUT_START_RSP	10000
 #define SBEFIFO_TIMEOUT_IN_RSP		1000
 
-/* Other constants */
-#define SBEFIFO_MAX_USER_CMD_LEN	(0x100000 + PAGE_SIZE)
-#define SBEFIFO_RESET_MAGIC		0x52534554 /* "RSET" */
-
-struct sbefifo {
-	uint32_t		magic;
-#define SBEFIFO_MAGIC		0x53424546 /* "SBEF" */
-	struct fsi_device	*fsi_dev;
-	struct device		dev;
-	struct cdev		cdev;
-	struct mutex		lock;
-	bool			broken;
-	bool			dead;
-	bool			async_ffdc;
-	bool			timed_out;
-	u32			timeout_start_rsp_ms;
-};
-
-struct sbefifo_user {
-	struct sbefifo		*sbefifo;
-	struct mutex		file_lock;
-	void			*cmd_page;
-	void			*pending_cmd;
-	size_t			pending_len;
-	u32			read_timeout_ms;
-};
-
 static DEFINE_MUTEX(sbefifo_ffdc_mutex);
 
-static ssize_t timeout_show(struct device *dev, struct device_attribute *attr,
-			    char *buf)
+static ssize_t timeout_show(struct device *dev, struct device_attribute *attr, char *buf)
 {
 	struct sbefifo *sbefifo = container_of(dev, struct sbefifo, dev);
 
-	return sysfs_emit(buf, "%d\n", sbefifo->timed_out ? 1 : 0);
+	return sysfs_emit(buf, "%d\n", sbefifo->timed_out);
 }
 static DEVICE_ATTR_RO(timeout);
 
@@ -160,10 +109,12 @@ static void __sbefifo_dump_ffdc(struct device *dev, const __be32 *ffdc,
 
 	while (ffdc_sz) {
 		u32 w0, w1, w2, i;
+
 		if (ffdc_sz < 3) {
 			dev_err(dev, "SBE invalid FFDC package size %zd\n", ffdc_sz);
 			return;
 		}
+
 		w0 = be32_to_cpu(*(ffdc++));
 		w1 = be32_to_cpu(*(ffdc++));
 		w2 = be32_to_cpu(*(ffdc++));
@@ -173,6 +124,7 @@ static void __sbefifo_dump_ffdc(struct device *dev, const __be32 *ffdc,
 				w0, w1, w2);
 			break;
 		}
+
 		w0 &= 0xffff;
 		if (w0 > ffdc_sz) {
 			dev_err(dev, "SBE FFDC package len %d words but only %zd remaining\n",
@@ -180,6 +132,7 @@ static void __sbefifo_dump_ffdc(struct device *dev, const __be32 *ffdc,
 			w0 = ffdc_sz;
 			break;
 		}
+
 		if (internal) {
 			dev_warn(dev, "+---- SBE FFDC package %d for async err -----+\n",
 				 pack++);
@@ -187,6 +140,7 @@ static void __sbefifo_dump_ffdc(struct device *dev, const __be32 *ffdc,
 			dev_warn(dev, "+---- SBE FFDC package %d for cmd %02x:%02x -----+\n",
 				 pack++, (w1 >> 8) & 0xff, w1 & 0xff);
 		}
+
 		dev_warn(dev, "| Response code: %08x                   |\n", w2);
 		dev_warn(dev, "|-------------------------------------------|\n");
 		for (i = 0; i < w0; i++) {
@@ -204,73 +158,29 @@ static void __sbefifo_dump_ffdc(struct device *dev, const __be32 *ffdc,
 				dev_warn(dev, "%s |\n", ffdc_line);
 			}
 		}
+
 		dev_warn(dev, "+-------------------------------------------+\n");
 	}
 }
 
-static void sbefifo_dump_ffdc(struct device *dev, const __be32 *ffdc,
-			      size_t ffdc_sz, bool internal)
+static void sbefifo_dump_ffdc(struct device *dev, const __be32 *ffdc, size_t ffdc_sz,
+			      bool internal)
 {
 	mutex_lock(&sbefifo_ffdc_mutex);
 	__sbefifo_dump_ffdc(dev, ffdc, ffdc_sz, internal);
 	mutex_unlock(&sbefifo_ffdc_mutex);
 }
 
-int sbefifo_parse_status(struct device *dev, u16 cmd, __be32 *response,
-			 size_t resp_len, size_t *data_len)
-{
-	u32 dh, s0, s1;
-	size_t ffdc_sz;
-
-	if (resp_len < 3) {
-		pr_debug("sbefifo: cmd %04x, response too small: %zd\n",
-			 cmd, resp_len);
-		return -ENXIO;
-	}
-	dh = be32_to_cpu(response[resp_len - 1]);
-	if (dh > resp_len || dh < 3) {
-		dev_err(dev, "SBE cmd %02x:%02x status offset out of range: %d/%zd\n",
-			cmd >> 8, cmd & 0xff, dh, resp_len);
-		return -ENXIO;
-	}
-	s0 = be32_to_cpu(response[resp_len - dh]);
-	s1 = be32_to_cpu(response[resp_len - dh + 1]);
-	if (((s0 >> 16) != 0xC0DE) || ((s0 & 0xffff) != cmd)) {
-		dev_err(dev, "SBE cmd %02x:%02x, status signature invalid: 0x%08x 0x%08x\n",
-			cmd >> 8, cmd & 0xff, s0, s1);
-		return -ENXIO;
-	}
-	if (s1 != 0) {
-		ffdc_sz = dh - 3;
-		dev_warn(dev, "SBE error cmd %02x:%02x status=%04x:%04x\n",
-			 cmd >> 8, cmd & 0xff, s1 >> 16, s1 & 0xffff);
-		if (ffdc_sz)
-			sbefifo_dump_ffdc(dev, &response[resp_len - dh + 2],
-					  ffdc_sz, false);
-	}
-	if (data_len)
-		*data_len = resp_len - dh;
-
-	/*
-	 * Primary status don't have the top bit set, so can't be confused with
-	 * Linux negative error codes, so return the status word whole.
-	 */
-	return s1;
-}
-EXPORT_SYMBOL_GPL(sbefifo_parse_status);
-
 static int sbefifo_regr(struct sbefifo *sbefifo, int reg, u32 *word)
 {
 	__be32 raw_word;
 	int rc;
 
-	rc = fsi_device_read(sbefifo->fsi_dev, reg, &raw_word,
-			     sizeof(raw_word));
+	rc = regmap_read(sbefifo->regmap, SBEFIFO_BASE + reg, (unsigned int *)&raw_word);
 	if (rc)
 		return rc;
 
 	*word = be32_to_cpu(raw_word);
-
 	return 0;
 }
 
@@ -278,8 +188,7 @@ static int sbefifo_regw(struct sbefifo *sbefifo, int reg, u32 word)
 {
 	__be32 raw_word = cpu_to_be32(word);
 
-	return fsi_device_write(sbefifo->fsi_dev, reg, &raw_word,
-				sizeof(raw_word));
+	return regmap_write(sbefifo->regmap, SBEFIFO_BASE + reg, (unsigned int)raw_word);
 }
 
 static int sbefifo_check_sbe_state(struct sbefifo *sbefifo)
@@ -288,13 +197,13 @@ static int sbefifo_check_sbe_state(struct sbefifo *sbefifo)
 	u32 sbm;
 	int rc;
 
-	rc = fsi_slave_read(sbefifo->fsi_dev->slave, CFAM_GP_MBOX_SBM_ADDR,
-			    &raw_word, sizeof(raw_word));
+	rc = regmap_read(sbefifo->regmap, CFAM_GP_MBOX_SBM_ADDR, (unsigned int *)&raw_word);
 	if (rc)
 		return rc;
+
 	sbm = be32_to_cpu(raw_word);
 
-	/* SBE booted at all ? */
+	/* SBE booted at all */
 	if (!(sbm & CFAM_SBM_SBE_BOOTED))
 		return -ESHUTDOWN;
 
@@ -317,40 +226,78 @@ static int sbefifo_check_sbe_state(struct sbefifo *sbefifo)
 
 	/* Is there async FFDC available ? Remember it */
 	if (sbm & CFAM_SBM_SBE_ASYNC_FFDC)
-		sbefifo->async_ffdc = true;
+		sbefifo->async_ffdc = 1;
 
 	return 0;
 }
 
-/* Don't flip endianness of data to/from FIFO, just pass through. */
+int sbefifo_parse_status(struct device *dev, u16 cmd, __be32 *response, size_t resp_len,
+			 size_t *data_len)
+{
+	size_t ffdc_sz;
+	u32 dh;
+	u32 s0;
+	u32 s1;
+
+	if (resp_len < 3)
+		return -ENXIO;
+
+	dh = be32_to_cpu(response[resp_len - 1]);
+	if (dh > resp_len || dh < 3) {
+		dev_err(dev, "SBE cmd %02x:%02x status offset out of range: %d/%zd\n", cmd >> 8,
+			cmd & 0xff, dh, resp_len);
+		return -ENXIO;
+	}
+
+	s0 = be32_to_cpu(response[resp_len - dh]);
+	s1 = be32_to_cpu(response[resp_len - dh + 1]);
+	if (((s0 >> 16) != 0xC0DE) || ((s0 & 0xffff) != cmd)) {
+		dev_err(dev, "SBE cmd %02x:%02x, status signature invalid: 0x%08x 0x%08x\n",
+			cmd >> 8, cmd & 0xff, s0, s1);
+		return -ENXIO;
+	}
+
+	if (s1 != 0) {
+		ffdc_sz = dh - 3;
+		dev_warn(dev, "SBE error cmd %02x:%02x status=%04x:%04x\n",  cmd >> 8, cmd & 0xff,
+			 s1 >> 16, s1 & 0xffff);
+		if (ffdc_sz)
+			sbefifo_dump_ffdc(dev, &response[resp_len - dh + 2], ffdc_sz, false);
+	}
+
+	if (data_len)
+		*data_len = resp_len - dh;
+
+	/*
+	 * Primary status don't have the top bit set, so can't be confused with
+	 * Linux negative error codes, so return the status word whole.
+	 */
+	return s1;
+}
+EXPORT_SYMBOL_GPL(sbefifo_parse_status);
+
 static int sbefifo_down_read(struct sbefifo *sbefifo, __be32 *word)
 {
-	return fsi_device_read(sbefifo->fsi_dev, SBEFIFO_DOWN, word,
-			       sizeof(*word));
+	return regmap_read(sbefifo->regmap, SBEFIFO_BASE + SBEFIFO_DOWN, (unsigned int *)word);
 }
 
 static int sbefifo_up_write(struct sbefifo *sbefifo, __be32 word)
 {
-	return fsi_device_write(sbefifo->fsi_dev, SBEFIFO_UP, &word,
-				sizeof(word));
+	return regmap_write(sbefifo->regmap, SBEFIFO_BASE + SBEFIFO_UP, (unsigned int)word);
 }
 
-static int sbefifo_request_reset(struct sbefifo *sbefifo)
+int sbefifo_request_reset(struct sbefifo *sbefifo)
 {
-	struct device *dev = &sbefifo->fsi_dev->dev;
 	unsigned long end_time;
 	u32 status;
 	int rc;
 
-	dev_dbg(dev, "Requesting FIFO reset\n");
-
-	/* Mark broken first, will be cleared if reset succeeds */
 	sbefifo->broken = true;
 
 	/* Send reset request */
 	rc = sbefifo_regw(sbefifo, SBEFIFO_UP | SBEFIFO_REQ_RESET, 1);
 	if (rc) {
-		dev_err(dev, "Sending reset request failed, rc=%d\n", rc);
+		dev_err(&sbefifo->dev, "Sending reset request failed, rc=%d\n", rc);
 		return rc;
 	}
 
@@ -359,44 +306,40 @@ static int sbefifo_request_reset(struct sbefifo *sbefifo)
 	while (!time_after(jiffies, end_time)) {
 		rc = sbefifo_regr(sbefifo, SBEFIFO_UP | SBEFIFO_STS, &status);
 		if (rc) {
-			dev_err(dev, "Failed to read UP fifo status during reset"
-				" , rc=%d\n", rc);
+			dev_err(&sbefifo->dev, "Failed to read status during reset, rc=%d\n", rc);
 			return rc;
 		}
 
 		if (!(status & SBEFIFO_STS_RESET_REQ)) {
-			dev_dbg(dev, "FIFO reset done\n");
 			sbefifo->broken = false;
 			return 0;
 		}
 
 		cond_resched();
 	}
-	dev_err(dev, "FIFO reset timed out\n");
 
+	dev_err(&sbefifo->dev, "FIFO reset timed out\n");
 	return -ETIMEDOUT;
 }
 
 static int sbefifo_cleanup_hw(struct sbefifo *sbefifo)
 {
-	struct device *dev = &sbefifo->fsi_dev->dev;
-	u32 up_status, down_status;
 	bool need_reset = false;
+	u32 down_status;
+	u32 up_status;
 	int rc;
 
 	rc = sbefifo_check_sbe_state(sbefifo);
-	if (rc) {
-		dev_dbg(dev, "SBE state=%d\n", rc);
+	if (rc)
 		return rc;
-	}
 
-	/* If broken, we don't need to look at status, go straight to reset */
+	/* If broken, we don't need to look at status, just reset */
 	if (sbefifo->broken)
-		goto do_reset;
+		return sbefifo_request_reset(sbefifo);
 
 	rc = sbefifo_regr(sbefifo, SBEFIFO_UP | SBEFIFO_STS, &up_status);
 	if (rc) {
-		dev_err(dev, "Cleanup: Reading UP status failed, rc=%d\n", rc);
+		dev_err(&sbefifo->dev, "Cleanup: Reading UP status failed, rc=%d\n", rc);
 
 		/* Will try reset again on next attempt at using it */
 		sbefifo->broken = true;
@@ -405,7 +348,7 @@ static int sbefifo_cleanup_hw(struct sbefifo *sbefifo)
 
 	rc = sbefifo_regr(sbefifo, SBEFIFO_DOWN | SBEFIFO_STS, &down_status);
 	if (rc) {
-		dev_err(dev, "Cleanup: Reading DOWN status failed, rc=%d\n", rc);
+		dev_err(&sbefifo->dev, "Cleanup: Reading DOWN status failed, rc=%d\n", rc);
 
 		/* Will try reset again on next attempt at using it */
 		sbefifo->broken = true;
@@ -414,13 +357,14 @@ static int sbefifo_cleanup_hw(struct sbefifo *sbefifo)
 
 	/* The FIFO already contains a reset request from the SBE ? */
 	if (down_status & SBEFIFO_STS_RESET_REQ) {
-		dev_info(dev, "Cleanup: FIFO reset request set, resetting\n");
+		dev_info(&sbefifo->dev, "Cleanup: FIFO reset request set, resetting\n");
 		rc = sbefifo_regw(sbefifo, SBEFIFO_DOWN, SBEFIFO_PERFORM_RESET);
 		if (rc) {
 			sbefifo->broken = true;
-			dev_err(dev, "Cleanup: Reset reg write failed, rc=%d\n", rc);
+			dev_err(&sbefifo->dev, "Cleanup: Reset reg write failed, rc=%d\n", rc);
 			return rc;
 		}
+
 		sbefifo->broken = false;
 		return 0;
 	}
@@ -436,52 +380,47 @@ static int sbefifo_cleanup_hw(struct sbefifo *sbefifo)
 	if (!need_reset)
 		return 0;
 
-	dev_info(dev, "Cleanup: FIFO not clean (up=0x%08x down=0x%08x)\n",
-		 up_status, down_status);
-
- do_reset:
-
 	/* Mark broken, will be cleared if/when reset succeeds */
+	dev_info(&sbefifo->dev, "Cleanup: FIFO not clean (up=0x%08x down=0x%08x)\n", up_status,
+		 down_status);
 	return sbefifo_request_reset(sbefifo);
 }
 
-static int sbefifo_wait(struct sbefifo *sbefifo, bool up,
-			u32 *status, unsigned long timeout)
+static int sbefifo_wait(struct sbefifo *sbefifo, u32 *status, unsigned long timeout, bool up)
 {
-	struct device *dev = &sbefifo->fsi_dev->dev;
 	unsigned long end_time;
 	bool ready = false;
-	u32 addr, sts = 0;
+	u32 sts = 0;
+	u32 addr;
 	int rc;
 
-	dev_vdbg(dev, "Wait on %s fifo...\n", up ? "up" : "down");
-
 	addr = (up ? SBEFIFO_UP : SBEFIFO_DOWN) | SBEFIFO_STS;
-
 	end_time = jiffies + timeout;
 	while (!time_after(jiffies, end_time)) {
 		cond_resched();
 		rc = sbefifo_regr(sbefifo, addr, &sts);
 		if (rc < 0) {
-			dev_err(dev, "FSI error %d reading status register\n", rc);
+			dev_err(&sbefifo->dev, "Bus error %d reading status register\n", rc);
 			return rc;
 		}
+
 		if (!up && sbefifo_parity_err(sts)) {
-			dev_err(dev, "Parity error in DOWN FIFO\n");
+			dev_err(&sbefifo->dev, "Parity error in DOWN FIFO\n");
 			return -ENXIO;
 		}
+
 		ready = !(up ? sbefifo_full(sts) : sbefifo_empty(sts));
 		if (ready)
 			break;
 	}
+
 	if (!ready) {
 		sysfs_notify(&sbefifo->dev.kobj, NULL, dev_attr_timeout.attr.name);
 		sbefifo->timed_out = true;
-		dev_err(dev, "%s FIFO Timeout (%u ms)! status=%08x\n",
+		dev_err(&sbefifo->dev, "%s FIFO Timeout (%u ms)! status=%08x\n",
 			up ? "UP" : "DOWN", jiffies_to_msecs(timeout), sts);
 		return -ETIMEDOUT;
 	}
-	dev_vdbg(dev, "End of wait status: %08x\n", sts);
 
 	sbefifo->timed_out = false;
 	*status = sts;
@@ -489,48 +428,45 @@ static int sbefifo_wait(struct sbefifo *sbefifo, bool up,
 	return 0;
 }
 
-static int sbefifo_send_command(struct sbefifo *sbefifo,
-				const __be32 *command, size_t cmd_len)
+static int sbefifo_send_command(struct sbefifo *sbefifo, const __be32 *command, size_t cmd_len)
 {
-	struct device *dev = &sbefifo->fsi_dev->dev;
-	size_t len, chunk, vacant = 0, remaining = cmd_len;
+	size_t remaining = cmd_len;
 	unsigned long timeout;
+	size_t vacant = 0;
+	size_t chunk;
+	size_t len;
 	u32 status;
 	int rc;
 
-	dev_dbg(dev, "sending command (%zd words, cmd=%04x)\n",
-		cmd_len, be32_to_cpu(command[1]));
-
 	/* As long as there's something to send */
 	timeout = msecs_to_jiffies(SBEFIFO_TIMEOUT_START_CMD);
 	while (remaining) {
 		/* Wait for room in the FIFO */
-		rc = sbefifo_wait(sbefifo, true, &status, timeout);
+		rc = sbefifo_wait(sbefifo, &status, timeout, true);
 		if (rc < 0)
 			return rc;
-		timeout = msecs_to_jiffies(SBEFIFO_TIMEOUT_IN_CMD);
 
+		timeout = msecs_to_jiffies(SBEFIFO_TIMEOUT_IN_CMD);
 		vacant = sbefifo_vacant(status);
-		len = chunk = min(vacant, remaining);
-
-		dev_vdbg(dev, "  status=%08x vacant=%zd chunk=%zd\n",
-			 status, vacant, chunk);
+		len = min(vacant, remaining);
+		chunk = len;
 
 		/* Write as much as we can */
 		while (len--) {
 			rc = sbefifo_up_write(sbefifo, *(command++));
 			if (rc) {
-				dev_err(dev, "FSI error %d writing UP FIFO\n", rc);
+				dev_err(&sbefifo->dev, "Bus error %d writing UP FIFO\n", rc);
 				return rc;
 			}
 		}
+
 		remaining -= chunk;
 		vacant -= chunk;
 	}
 
 	/* If there's no room left, wait for some to write EOT */
 	if (!vacant) {
-		rc = sbefifo_wait(sbefifo, true, &status, timeout);
+		rc = sbefifo_wait(sbefifo, &status, timeout, true);
 		if (rc)
 			return rc;
 	}
@@ -538,40 +474,36 @@ static int sbefifo_send_command(struct sbefifo *sbefifo,
 	/* Send an EOT */
 	rc = sbefifo_regw(sbefifo, SBEFIFO_UP | SBEFIFO_EOT_RAISE, 0);
 	if (rc)
-		dev_err(dev, "FSI error %d writing EOT\n", rc);
+		dev_err(&sbefifo->dev, "Bus error %d writing EOT\n", rc);
+
 	return rc;
 }
 
 static int sbefifo_read_response(struct sbefifo *sbefifo, struct iov_iter *response)
 {
-	struct device *dev = &sbefifo->fsi_dev->dev;
-	u32 status, eot_set;
-	unsigned long timeout;
 	bool overflow = false;
+	unsigned long timeout;
 	__be32 data;
+	u32 eot_set;
+	u32 status;
 	size_t len;
 	int rc;
 
-	dev_dbg(dev, "reading response, buflen = %zd\n", iov_iter_count(response));
-
-	timeout = msecs_to_jiffies(sbefifo->timeout_start_rsp_ms);
+	timeout = msecs_to_jiffies(sbefifo->timeout);
 	for (;;) {
 		/* Grab FIFO status (this will handle parity errors) */
-		rc = sbefifo_wait(sbefifo, false, &status, timeout);
-		if (rc < 0) {
-			dev_dbg(dev, "timeout waiting (%u ms)\n", jiffies_to_msecs(timeout));
+		rc = sbefifo_wait(sbefifo, &status, timeout, false);
+		if (rc < 0)
 			return rc;
-		}
+
 		timeout = msecs_to_jiffies(SBEFIFO_TIMEOUT_IN_RSP);
 
 		/* Decode status */
 		len = sbefifo_populated(status);
 		eot_set = sbefifo_eot_set(status);
 
-		dev_dbg(dev, "  chunk size %zd eot_set=0x%x\n", len, eot_set);
-
 		/* Go through the chunk */
-		while(len--) {
+		while (len--) {
 			/* Read the data */
 			rc = sbefifo_down_read(sbefifo, &data);
 			if (rc < 0)
@@ -586,15 +518,13 @@ static int sbefifo_read_response(struct sbefifo *sbefifo, struct iov_iter *respo
 				 * command.
 				 */
 				if (len) {
-					dev_warn(dev, "FIFO read hit"
-						 " EOT with still %zd data\n",
-						 len);
+					dev_warn(&sbefifo->dev,
+						 "FIFO read hit EOT with still %zd data\n", len);
 					sbefifo->broken = true;
 				}
 
 				/* We are done */
-				rc = sbefifo_regw(sbefifo,
-						  SBEFIFO_DOWN | SBEFIFO_EOT_ACK, 0);
+				rc = sbefifo_regw(sbefifo, SBEFIFO_DOWN | SBEFIFO_EOT_ACK, 0);
 
 				/*
 				 * If that write fail, still complete the request but mark
@@ -602,7 +532,7 @@ static int sbefifo_read_response(struct sbefifo *sbefifo, struct iov_iter *respo
 				 * we can do here).
 				 */
 				if (rc) {
-					dev_err(dev, "FSI error %d ack'ing EOT\n", rc);
+					dev_err(&sbefifo->dev, "Bus error %d ack'ing EOT\n", rc);
 					sbefifo->broken = true;
 				}
 
@@ -615,7 +545,7 @@ static int sbefifo_read_response(struct sbefifo *sbefifo, struct iov_iter *respo
 				if (copy_to_iter(&data, sizeof(__be32), response) < sizeof(__be32))
 					return -EFAULT;
 			} else {
-				dev_vdbg(dev, "Response overflowed !\n");
+				dev_warn(&sbefifo->dev, "Response overflowed !\n");
 
 				overflow = true;
 			}
@@ -624,16 +554,18 @@ static int sbefifo_read_response(struct sbefifo *sbefifo, struct iov_iter *respo
 			eot_set <<= 1;
 		}
 	}
+
 	/* Shouldn't happen */
 	return -EIO;
 }
 
-static int sbefifo_do_command(struct sbefifo *sbefifo,
-			      const __be32 *command, size_t cmd_len,
+static int sbefifo_do_command(struct sbefifo *sbefifo, const __be32 *command, size_t cmd_len,
 			      struct iov_iter *response)
 {
+	int rc;
+
 	/* Try sending the command */
-	int rc = sbefifo_send_command(sbefifo, command, cmd_len);
+	rc = sbefifo_send_command(sbefifo, command, cmd_len);
 	if (rc)
 		return rc;
 
@@ -643,58 +575,56 @@ static int sbefifo_do_command(struct sbefifo *sbefifo,
 
 static void sbefifo_collect_async_ffdc(struct sbefifo *sbefifo)
 {
-	struct device *dev = &sbefifo->fsi_dev->dev;
-        struct iov_iter ffdc_iter;
-        struct kvec ffdc_iov;
-	__be32 *ffdc;
+	struct iov_iter ffdc_iter;
+	struct kvec ffdc_iov;
 	size_t ffdc_sz;
 	__be32 cmd[2];
+	__be32 *ffdc;
 	int rc;
 
 	sbefifo->async_ffdc = false;
 	ffdc = vmalloc(SBEFIFO_MAX_FFDC_SIZE);
 	if (!ffdc) {
-		dev_err(dev, "Failed to allocate SBE FFDC buffer\n");
+		dev_err(&sbefifo->dev, "Failed to allocate SBE FFDC buffer\n");
 		return;
 	}
-        ffdc_iov.iov_base = ffdc;
+
+	ffdc_iov.iov_base = ffdc;
 	ffdc_iov.iov_len = SBEFIFO_MAX_FFDC_SIZE;
-        iov_iter_kvec(&ffdc_iter, WRITE, &ffdc_iov, 1, SBEFIFO_MAX_FFDC_SIZE);
+	iov_iter_kvec(&ffdc_iter, WRITE, &ffdc_iov, 1, SBEFIFO_MAX_FFDC_SIZE);
 	cmd[0] = cpu_to_be32(2);
 	cmd[1] = cpu_to_be32(SBEFIFO_CMD_GET_SBE_FFDC);
 	rc = sbefifo_do_command(sbefifo, cmd, 2, &ffdc_iter);
 	if (rc != 0) {
-		dev_err(dev, "Error %d retrieving SBE FFDC\n", rc);
-		goto bail;
+		dev_err(&sbefifo->dev, "Error %d retrieving SBE FFDC\n", rc);
+		goto done;
 	}
 	ffdc_sz = SBEFIFO_MAX_FFDC_SIZE - iov_iter_count(&ffdc_iter);
 	ffdc_sz /= sizeof(__be32);
-	rc = sbefifo_parse_status(dev, SBEFIFO_CMD_GET_SBE_FFDC, ffdc,
-				  ffdc_sz, &ffdc_sz);
+	rc = sbefifo_parse_status(&sbefifo->dev, SBEFIFO_CMD_GET_SBE_FFDC, ffdc, ffdc_sz,
+				  &ffdc_sz);
 	if (rc != 0) {
-		dev_err(dev, "Error %d decoding SBE FFDC\n", rc);
-		goto bail;
+		dev_err(&sbefifo->dev, "Error %d decoding SBE FFDC\n", rc);
+		goto done;
 	}
+
 	if (ffdc_sz > 0)
-		sbefifo_dump_ffdc(dev, ffdc, ffdc_sz, true);
- bail:
+		sbefifo_dump_ffdc(&sbefifo->dev, ffdc, ffdc_sz, true);
+done:
 	vfree(ffdc);
-
 }
 
-static int __sbefifo_submit(struct sbefifo *sbefifo,
-			    const __be32 *command, size_t cmd_len,
+int sbefifo_submit_internal(struct sbefifo *sbefifo, const __be32 *command, size_t words,
 			    struct iov_iter *response)
 {
-	struct device *dev = &sbefifo->fsi_dev->dev;
 	int rc;
 
 	if (sbefifo->dead)
 		return -ENODEV;
 
-	if (cmd_len < 2 || be32_to_cpu(command[0]) != cmd_len) {
-		dev_vdbg(dev, "Invalid command len %zd (header: %d)\n",
-			 cmd_len, be32_to_cpu(command[0]));
+	if (words < 2 || be32_to_cpu(command[0]) != words) {
+		dev_warn(&sbefifo->dev, "Invalid command len %zd (header: %u)\n", words,
+			 be32_to_cpu(command[0]));
 		return -EINVAL;
 	}
 
@@ -707,18 +637,15 @@ static int __sbefifo_submit(struct sbefifo *sbefifo,
 	if (sbefifo->async_ffdc)
 		sbefifo_collect_async_ffdc(sbefifo);
 
-	rc = sbefifo_do_command(sbefifo, command, cmd_len, response);
-	if (rc != 0 && rc != -EOVERFLOW)
-		goto fail;
-	return rc;
- fail:
+	rc = sbefifo_do_command(sbefifo, command, words, response);
+
 	/*
 	 * On failure, attempt a reset. Ignore the result, it will mark
 	 * the fifo broken if the reset fails
 	 */
-        sbefifo_request_reset(sbefifo);
+	if (rc != 0 && rc != -EOVERFLOW)
+		sbefifo_request_reset(sbefifo);
 
-	/* Return original error */
 	return rc;
 }
 
@@ -730,39 +657,34 @@ static int __sbefifo_submit(struct sbefifo *sbefifo,
  * @response: The output response buffer
  * @resp_len: In: Response buffer size, Out: Response size
  *
- * This will perform the entire operation. If the reponse buffer
+ * This will perform the entire operation. If the response buffer
  * overflows, returns -EOVERFLOW
  */
 int sbefifo_submit(struct device *dev, const __be32 *command, size_t cmd_len,
 		   __be32 *response, size_t *resp_len)
 {
 	struct sbefifo *sbefifo;
-        struct iov_iter resp_iter;
-        struct kvec resp_iov;
+	struct iov_iter resp_iter;
+	struct kvec resp_iov;
 	size_t rbytes;
 	int rc;
 
-	if (!dev)
-		return -ENODEV;
-	sbefifo = dev_get_drvdata(dev);
-	if (!sbefifo)
-		return -ENODEV;
-	if (WARN_ON_ONCE(sbefifo->magic != SBEFIFO_MAGIC))
-		return -ENODEV;
 	if (!resp_len || !command || !response)
 		return -EINVAL;
 
+	sbefifo = container_of(dev, struct sbefifo, dev);
+
 	/* Prepare iov iterator */
 	rbytes = (*resp_len) * sizeof(__be32);
 	resp_iov.iov_base = response;
 	resp_iov.iov_len = rbytes;
-        iov_iter_kvec(&resp_iter, WRITE, &resp_iov, 1, rbytes);
+	iov_iter_kvec(&resp_iter, WRITE, &resp_iov, 1, rbytes);
 
 	/* Perform the command */
 	rc = mutex_lock_interruptible(&sbefifo->lock);
 	if (rc)
 		return rc;
-	rc = __sbefifo_submit(sbefifo, command, cmd_len, &resp_iter);
+	rc = sbefifo_submit_internal(sbefifo, command, cmd_len, &resp_iter);
 	mutex_unlock(&sbefifo->lock);
 
 	/* Extract the response length */
@@ -777,153 +699,99 @@ static void sbefifo_free(struct device *dev)
 {
 	struct sbefifo *sbefifo = container_of(dev, struct sbefifo, dev);
 
-	put_device(&sbefifo->fsi_dev->dev);
+	put_device(dev->parent);
 	kfree(sbefifo);
 }
 
-/*
- * Probe/remove
- */
-
-static int sbefifo_probe(struct device *dev)
+struct sbefifo *sbefifo_create(struct device *parent, struct regmap *regmap,
+			       int (*get_devt)(struct device *, int, dev_t *, int *))
 {
-	struct fsi_device *fsi_dev = to_fsi_dev(dev);
 	struct sbefifo *sbefifo;
-	struct device_node *np;
-	struct platform_device *child;
-	char child_name[32];
-	int rc, didx, child_idx = 0;
+	__be32 raw_word;
+	u32 chip_id;
+	int idx;
+	int rc;
 
-	dev_dbg(dev, "Found sbefifo device\n");
+	if (!get_device(parent))
+		return ERR_PTR(-ENODEV);
 
 	sbefifo = kzalloc(sizeof(*sbefifo), GFP_KERNEL);
-	if (!sbefifo)
-		return -ENOMEM;
+	if (!sbefifo) {
+		put_device(parent);
+		return ERR_PTR(-ENOMEM);
+	}
 
-	/* Grab a reference to the device (parent of our cdev), we'll drop it later */
-	if (!get_device(dev)) {
+	rc = get_devt(parent, (int)fsi_dev_sbefifo, &sbefifo->dev.devt, &idx);
+	if (rc) {
 		kfree(sbefifo);
-		return -ENODEV;
+		put_device(parent);
+		return ERR_PTR(rc);
 	}
 
-	sbefifo->magic = SBEFIFO_MAGIC;
-	sbefifo->fsi_dev = fsi_dev;
-	dev_set_drvdata(dev, sbefifo);
-	mutex_init(&sbefifo->lock);
-	sbefifo->timeout_start_rsp_ms = SBEFIFO_TIMEOUT_START_RSP;
-
-	/*
-	 * Try cleaning up the FIFO. If this fails, we still register the
-	 * driver and will try cleaning things up again on the next access.
-	 */
-	rc = sbefifo_cleanup_hw(sbefifo);
-	if (rc && rc != -ESHUTDOWN)
-		dev_err(dev, "Initial HW cleanup failed, will retry later\n");
-
-	/* Create chardev for userspace access */
 	sbefifo->dev.type = &fsi_cdev_type;
-	sbefifo->dev.parent = dev;
+	sbefifo->dev.parent = parent;
 	sbefifo->dev.release = sbefifo_free;
 	device_initialize(&sbefifo->dev);
+	dev_set_name(&sbefifo->dev, "sbefifo%d", idx);
+
+	sbefifo_cdev_init(sbefifo);
+
+	mutex_init(&sbefifo->lock);
 
-	/* Allocate a minor in the FSI space */
-	rc = fsi_get_new_minor(fsi_dev, fsi_dev_sbefifo, &sbefifo->dev.devt, &didx);
+	rc = regmap_read(regmap, CFAM_SCOM_CHIP_ID, (unsigned int *)&raw_word);
 	if (rc)
-		goto err;
+		return ERR_PTR(rc);
 
-	dev_set_name(&sbefifo->dev, "sbefifo%d", didx);
-	cdev_init(&sbefifo->cdev, &sbefifo_fops);
-	rc = cdev_device_add(&sbefifo->cdev, &sbefifo->dev);
-	if (rc) {
-		dev_err(dev, "Error %d creating char device %s\n",
-			rc, dev_name(&sbefifo->dev));
-		goto err_free_minor;
-	}
+	chip_id = be32_to_cpu(raw_word);
+	chip_id = (chip_id & CFAM_SCOM_CHIP_ID_CHIP_ID_MASK) >> CFAM_SCOM_CHIP_ID_CHIP_ID_SHIFT;
 
-	/* Create platform devs for dts child nodes (occ, etc) */
-	for_each_available_child_of_node(dev->of_node, np) {
-		snprintf(child_name, sizeof(child_name), "%s-dev%d",
-			 dev_name(&sbefifo->dev), child_idx++);
-		child = of_platform_device_create(np, child_name, dev);
-		if (!child)
-			dev_warn(dev, "failed to create child %s dev\n",
-				 child_name);
+	switch (chip_id) {
+	case CHIP_ID_P9A:
+	case CHIP_ID_P9B:
+	case CHIP_ID_P9plus:
+		sbefifo->occ = occ_create(sbefifo, get_devt, occ_p9);
+		break;
+	case CHIP_ID_P10:
+		sbefifo->occ = occ_create(sbefifo, get_devt, occ_p10);
+		break;
 	}
 
-	device_create_file(&sbefifo->dev, &dev_attr_timeout);
+	sbefifo->regmap = regmap;
+	sbefifo->timeout = SBEFIFO_TIMEOUT_START_RSP;
 
-	return 0;
- err_free_minor:
-	fsi_free_minor(sbefifo->dev.devt);
- err:
-	put_device(&sbefifo->dev);
-	return rc;
-}
+	if (device_create_file(&sbefifo->dev, &dev_attr_timeout))
+		sbefifo->no_timeout_sysfs = 1;
 
-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);
+	rc = sbefifo_cleanup_hw(sbefifo);
+	if (rc && rc != -ESHUTDOWN)
+		dev_err(&sbefifo->dev, "Initial HW cleanup failed, will retry later\n");
 
-	return 0;
+	return sbefifo;
 }
 
-static int sbefifo_remove(struct device *dev)
+void sbefifo_destroy(struct sbefifo *sbefifo, void(*free_devt)(dev_t))
 {
-	struct sbefifo *sbefifo = dev_get_drvdata(dev);
-
-	dev_dbg(dev, "Removing sbefifo device...\n");
-
-	device_remove_file(&sbefifo->dev, &dev_attr_timeout);
-
 	mutex_lock(&sbefifo->lock);
-	sbefifo->dead = true;
+	sbefifo->dead = 1;
 	mutex_unlock(&sbefifo->lock);
 
-	cdev_device_del(&sbefifo->cdev, &sbefifo->dev);
-	fsi_free_minor(sbefifo->dev.devt);
-	device_for_each_child(dev, NULL, sbefifo_unregister_child);
-	put_device(&sbefifo->dev);
+	if (free_devt)
+		free_devt(sbefifo->dev.devt);
 
-	return 0;
-}
+	if (!sbefifo->no_timeout_sysfs)
+		device_remove_file(&sbefifo->dev, &dev_attr_timeout);
 
-static const struct fsi_device_id sbefifo_ids[] = {
-	{
-		.engine_type = FSI_ENGID_SBE,
-		.version = FSI_VERSION_ANY,
-	},
-	{ 0 }
-};
+	regmap_exit(sbefifo->regmap);
 
-static struct fsi_driver sbefifo_drv = {
-	.id_table = sbefifo_ids,
-	.drv = {
-		.name = DEVICE_NAME,
-		.bus = &fsi_bus_type,
-		.probe = sbefifo_probe,
-		.remove = sbefifo_remove,
-	}
-};
+	if (sbefifo->occ)
+		occ_destroy(sbefifo->occ, free_devt);
 
-static int sbefifo_init(void)
-{
-	return fsi_driver_register(&sbefifo_drv);
-}
+	if (!sbefifo->no_cdev)
+		cdev_device_del(&sbefifo->sbe, &sbefifo->dev);
 
-static void sbefifo_exit(void)
-{
-	fsi_driver_unregister(&sbefifo_drv);
+	put_device(&sbefifo->dev);
 }
 
-module_init(sbefifo_init);
-module_exit(sbefifo_exit);
+MODULE_AUTHOR("Eddie James <eajames@linux.ibm.com>");
+MODULE_DESCRIPTION("POWER Self Boot Engine FIFO driver");
 MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Brad Bishop <bradleyb@fuzziesquirrel.com>");
-MODULE_AUTHOR("Eddie James <eajames@linux.vnet.ibm.com>");
-MODULE_AUTHOR("Andrew Jeffery <andrew@aj.id.au>");
-MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
-MODULE_DESCRIPTION("Linux device interface to the POWER Self Boot Engine");
diff --git a/drivers/fsi/sbefifo.h b/drivers/fsi/sbefifo.h
new file mode 100644
index 000000000000..23f342d712bd
--- /dev/null
+++ b/drivers/fsi/sbefifo.h
@@ -0,0 +1,50 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright (C) IBM Corporation 2022 */
+
+#ifndef DRIVERS_SBEFIFO_H
+#define DRIVERS_SBEFIFO_H
+
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/mutex.h>
+#include <linux/types.h>
+
+#define SBEFIFO_DEVICE_NAME		"sbefifo"
+#define SBEFIFO_MAX_USER_CMD_LEN	(0x100000 + PAGE_SIZE)
+#define SBEFIFO_RESET_MAGIC		0x52534554 /* "RSET" */
+#define SBEFIFO_TIMEOUT_START_RSP	10000
+
+struct fsi_device;
+struct i2c_client;
+struct iov_iter;
+struct occ;
+struct regmap;
+
+struct sbefifo {
+	struct device dev;
+	struct cdev sbe;
+	struct mutex lock;	// device lock
+	union {
+		struct fsi_device *fsi_dev;
+		struct i2c_client *i2c_client;
+	};
+	struct occ *occ;
+	struct regmap *regmap;
+	unsigned int timeout;
+	unsigned int async_ffdc : 1;
+	unsigned int broken : 1;
+	unsigned int dead : 1;
+	unsigned int no_cdev : 1;
+	unsigned int no_timeout_sysfs : 1;
+	unsigned int timed_out : 1;
+};
+
+void sbefifo_cdev_init(struct sbefifo *sbefifo);
+struct sbefifo *sbefifo_create(struct device *parent, struct regmap *regmap,
+			       int (*get_devt)(struct device *, int, dev_t *, int *));
+void sbefifo_destroy(struct sbefifo *sbefifo, void(*free_devt)(dev_t));
+int sbefifo_request_reset(struct sbefifo *sbefifo);
+int sbefifo_submit_internal(struct sbefifo *sbefifo, const __be32 *command, size_t words,
+			    struct iov_iter *response);
+
+#endif /* DRIVERS_SBEFIFO_H */
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [PATCH v2 1/5] regmap: Add FSI bus support
  2022-11-02 20:51 ` [PATCH v2 1/5] regmap: Add FSI bus support Eddie James
@ 2022-11-03 14:54   ` Mark Brown
  2022-11-03 14:59     ` Eddie James
  0 siblings, 1 reply; 10+ messages in thread
From: Mark Brown @ 2022-11-03 14:54 UTC (permalink / raw)
  To: Eddie James; +Cc: linux-fsi, linux-kernel, gregkh, rafael, jk, joel, alistair

[-- Attachment #1: Type: text/plain, Size: 459 bytes --]

On Wed, Nov 02, 2022 at 03:51:44PM -0500, Eddie James wrote:
> Add regmap support for the FSI bus.
> 
> Signed-off-by: Eddie James <eajames@linux.ibm.com>
> ---
>  drivers/base/regmap/Kconfig      |   6 +-
>  drivers/base/regmap/Makefile     |   1 +
>  drivers/base/regmap/regmap-fsi.c | 231 +++++++++++++++++++++++++++++++
>  include/linux/regmap.h           |  37 +++++

I thought the plan was to put this in the MFD since that's the only
user?

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v2 1/5] regmap: Add FSI bus support
  2022-11-03 14:54   ` Mark Brown
@ 2022-11-03 14:59     ` Eddie James
  0 siblings, 0 replies; 10+ messages in thread
From: Eddie James @ 2022-11-03 14:59 UTC (permalink / raw)
  To: Mark Brown; +Cc: linux-fsi, linux-kernel, gregkh, rafael, jk, joel, alistair


On 11/3/22 09:54, Mark Brown wrote:
> On Wed, Nov 02, 2022 at 03:51:44PM -0500, Eddie James wrote:
>> Add regmap support for the FSI bus.
>>
>> Signed-off-by: Eddie James <eajames@linux.ibm.com>
>> ---
>>   drivers/base/regmap/Kconfig      |   6 +-
>>   drivers/base/regmap/Makefile     |   1 +
>>   drivers/base/regmap/regmap-fsi.c | 231 +++++++++++++++++++++++++++++++
>>   include/linux/regmap.h           |  37 +++++
> I thought the plan was to put this in the MFD since that's the only
> user?


For the I2C responder regmap, yes, that's what I've done. The FSI regmap 
could be used anywhere in the future. We could switch a number of 
drivers to use the FSI regmap if desired, so I feel it's worth it to put 
it in the regmap properly.


Thanks,

Eddie



^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: (subset) [PATCH v2 0/5] fsi: Add regmap and refactor sbefifo
  2022-11-02 20:51 [PATCH v2 0/5] fsi: Add regmap and refactor sbefifo Eddie James
                   ` (4 preceding siblings ...)
  2022-11-02 20:51 ` [PATCH v2 5/5] drivers: fsi: occ and sbefifo refactor Eddie James
@ 2022-11-25 21:28 ` Mark Brown
  2023-01-19 17:48 ` Eddie James
  6 siblings, 0 replies; 10+ messages in thread
From: Mark Brown @ 2022-11-25 21:28 UTC (permalink / raw)
  To: Eddie James, linux-fsi; +Cc: linux-kernel, alistair, joel, jk, rafael, gregkh

On Wed, 2 Nov 2022 15:51:43 -0500, Eddie James wrote:
> The SBEFIFO hardware can now be attached over a new I2C endpoint interface
> called the I2C Responder (I2CR). In order to use the existing SBEFIFO
> driver, add a regmap driver for the FSI bus and an endpoint driver for the
> I2CR. Then, refactor the SBEFIFO and OCC drivers to clean up and use the
> new regmap driver or the I2CR interface.
> 
> Changes since v1:
>  - Instead of a regmap driver for the I2CR, just have a private interface
>    driver for FSI, since SBEFIFO is likely the only user.
> 
> [...]

Applied to

   https://git.kernel.org/pub/scm/linux/kernel/git/broonie/regmap.git for-next

Thanks!

[1/5] regmap: Add FSI bus support
      commit: bf0d29fb51ff5e6c13097dbfed7b99e0e35b4a15

All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.

You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.

If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.

Please add any relevant lists and maintainers to the CCs when replying
to this mail.

Thanks,
Mark

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v2 0/5] fsi: Add regmap and refactor sbefifo
  2022-11-02 20:51 [PATCH v2 0/5] fsi: Add regmap and refactor sbefifo Eddie James
                   ` (5 preceding siblings ...)
  2022-11-25 21:28 ` (subset) [PATCH v2 0/5] fsi: Add regmap and refactor sbefifo Mark Brown
@ 2023-01-19 17:48 ` Eddie James
  6 siblings, 0 replies; 10+ messages in thread
From: Eddie James @ 2023-01-19 17:48 UTC (permalink / raw)
  To: linux-fsi; +Cc: linux-kernel, broonie, gregkh, rafael, jk, joel, alistair


On 11/2/22 15:51, Eddie James wrote:
> The SBEFIFO hardware can now be attached over a new I2C endpoint interface
> called the I2C Responder (I2CR). In order to use the existing SBEFIFO
> driver, add a regmap driver for the FSI bus and an endpoint driver for the
> I2CR. Then, refactor the SBEFIFO and OCC drivers to clean up and use the
> new regmap driver or the I2CR interface.


I'm abandoning the rest of this series in favor of an FSI master driver 
through the I2C responder. It makes a lot more sense to implement a 
master driver here because then we get all the engine drivers for free, 
rather than rework them to talk over i2c.

Thanks,

Eddie


>
> Changes since v1:
>   - Instead of a regmap driver for the I2CR, just have a private interface
>     driver for FSI, since SBEFIFO is likely the only user.
>
> Eddie James (5):
>    regmap: Add FSI bus support
>    drivers: fsi: Add I2C Responder driver
>    drivers: fsi: Rename sbefifo and occ sources
>    drivers: fsi: separate char device code for occ and sbefifo
>    drivers: fsi: occ and sbefifo refactor
>
>   drivers/base/regmap/Kconfig      |    6 +-
>   drivers/base/regmap/Makefile     |    1 +
>   drivers/base/regmap/regmap-fsi.c |  231 ++++++
>   drivers/fsi/Kconfig              |   32 +-
>   drivers/fsi/Makefile             |    9 +-
>   drivers/fsi/fsi-occ.c            |  766 --------------------
>   drivers/fsi/fsi-sbefifo.c        | 1144 ------------------------------
>   drivers/fsi/i2cr.c               |  116 +++
>   drivers/fsi/i2cr.h               |   19 +
>   drivers/fsi/occ-cdev.c           |  157 ++++
>   drivers/fsi/occ.c                |  536 ++++++++++++++
>   drivers/fsi/occ.h                |   57 ++
>   drivers/fsi/sbefifo-cdev.c       |  218 ++++++
>   drivers/fsi/sbefifo-fsi.c        |   68 ++
>   drivers/fsi/sbefifo-i2c.c        |   73 ++
>   drivers/fsi/sbefifo.c            |  797 +++++++++++++++++++++
>   drivers/fsi/sbefifo.h            |   50 ++
>   include/linux/regmap.h           |   37 +
>   18 files changed, 2398 insertions(+), 1919 deletions(-)
>   create mode 100644 drivers/base/regmap/regmap-fsi.c
>   delete mode 100644 drivers/fsi/fsi-occ.c
>   delete mode 100644 drivers/fsi/fsi-sbefifo.c
>   create mode 100644 drivers/fsi/i2cr.c
>   create mode 100644 drivers/fsi/i2cr.h
>   create mode 100644 drivers/fsi/occ-cdev.c
>   create mode 100644 drivers/fsi/occ.c
>   create mode 100644 drivers/fsi/occ.h
>   create mode 100644 drivers/fsi/sbefifo-cdev.c
>   create mode 100644 drivers/fsi/sbefifo-fsi.c
>   create mode 100644 drivers/fsi/sbefifo-i2c.c
>   create mode 100644 drivers/fsi/sbefifo.c
>   create mode 100644 drivers/fsi/sbefifo.h
>

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2023-01-19 17:49 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-11-02 20:51 [PATCH v2 0/5] fsi: Add regmap and refactor sbefifo Eddie James
2022-11-02 20:51 ` [PATCH v2 1/5] regmap: Add FSI bus support Eddie James
2022-11-03 14:54   ` Mark Brown
2022-11-03 14:59     ` Eddie James
2022-11-02 20:51 ` [PATCH v2 2/5] drivers: fsi: Add I2C Responder driver Eddie James
2022-11-02 20:51 ` [PATCH v2 3/5] drivers: fsi: Rename sbefifo and occ sources Eddie James
2022-11-02 20:51 ` [PATCH v2 4/5] drivers: fsi: separate char device code for occ and sbefifo Eddie James
2022-11-02 20:51 ` [PATCH v2 5/5] drivers: fsi: occ and sbefifo refactor Eddie James
2022-11-25 21:28 ` (subset) [PATCH v2 0/5] fsi: Add regmap and refactor sbefifo Mark Brown
2023-01-19 17:48 ` Eddie James

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.