diff mbox series

[v2,2/5] drivers: fsi: Add I2C Responder driver

Message ID 20221102205148.1334459-3-eajames@linux.ibm.com
State New
Headers show
Series fsi: Add regmap and refactor sbefifo | expand

Commit Message

Eddie James Nov. 2, 2022, 8:51 p.m. UTC
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 mbox series

Patch

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 */