[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20221014220540.55570-3-eajames@linux.ibm.com>
Date: Fri, 14 Oct 2022 17:05:37 -0500
From: Eddie James <eajames@...ux.ibm.com>
To: joel@....id.au
Cc: broonie@...nel.org, jk@...abs.org, alistair@...ple.id.au,
linux-kernel@...r.kernel.org, linux-fsi@...ts.ozlabs.org,
eajames@...ux.ibm.com
Subject: [PATCH 2/5] regmap: Add IBM I2CR support
Add regmap support for the IBM I2CR. The I2CR (I2C Responder) is an
I2C end-point device that provides access to an IBM POWER CFAM, which
has traditionally been accessed over FSI.
Signed-off-by: Eddie James <eajames@...ux.ibm.com>
---
drivers/base/regmap/Kconfig | 4 +
drivers/base/regmap/Makefile | 1 +
drivers/base/regmap/regmap-ibm-i2cr.c | 159 ++++++++++++++++++++++++++
include/linux/regmap.h | 35 ++++++
4 files changed, 199 insertions(+)
create mode 100644 drivers/base/regmap/regmap-ibm-i2cr.c
diff --git a/drivers/base/regmap/Kconfig b/drivers/base/regmap/Kconfig
index cd4bb642b9de..71af079a1b67 100644
--- a/drivers/base/regmap/Kconfig
+++ b/drivers/base/regmap/Kconfig
@@ -69,3 +69,7 @@ config REGMAP_SPI_AVMM
config REGMAP_FSI
tristate
depends on FSI
+
+config REGMAP_IBM_I2CR
+ tristate
+ depends on I2C
diff --git a/drivers/base/regmap/Makefile b/drivers/base/regmap/Makefile
index 6990de7ca9a9..871069903ad5 100644
--- a/drivers/base/regmap/Makefile
+++ b/drivers/base/regmap/Makefile
@@ -21,3 +21,4 @@ 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
+obj-$(CONFIG_REGMAP_IBM_I2CR) += regmap-ibm-i2cr.o
diff --git a/drivers/base/regmap/regmap-ibm-i2cr.c b/drivers/base/regmap/regmap-ibm-i2cr.c
new file mode 100644
index 000000000000..799ad9e43a45
--- /dev/null
+++ b/drivers/base/regmap/regmap-ibm-i2cr.c
@@ -0,0 +1,159 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Register map access API - IBM I2CR for POWER CFAM access
+//
+// Copyright 2022 IBM Corp
+//
+// Author: Eddie James <eajames@...ux.ibm.com>
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+
+#include "internal.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 regmap_ibm_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 regmap_ibm_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;
+}
+
+static const struct regmap_bus regmap_ibm_i2cr = {
+ .reg_write = regmap_ibm_i2cr_reg_write,
+ .reg_read = regmap_ibm_i2cr_reg_read,
+};
+
+static const struct regmap_bus *regmap_get_ibm_i2cr_bus(struct i2c_client *client,
+ const struct regmap_config *config)
+{
+ const struct regmap_bus *bus = NULL;
+
+ if (config->reg_bits == 32 && config->val_bits == 32)
+ bus = ®map_ibm_i2cr;
+
+ return bus ?: ERR_PTR(-EOPNOTSUPP);
+}
+
+struct regmap *__regmap_init_ibm_i2cr(struct i2c_client *client,
+ const struct regmap_config *config,
+ struct lock_class_key *lock_key, const char *lock_name)
+{
+ const struct regmap_bus *bus = regmap_get_ibm_i2cr_bus(client, config);
+
+ if (IS_ERR(bus))
+ return ERR_CAST(bus);
+
+ return __regmap_init(&client->dev, bus, client, config, lock_key, lock_name);
+}
+EXPORT_SYMBOL_GPL(__regmap_init_ibm_i2cr);
+
+struct regmap *__devm_regmap_init_ibm_i2cr(struct i2c_client *client,
+ const struct regmap_config *config,
+ struct lock_class_key *lock_key, const char *lock_name)
+{
+ const struct regmap_bus *bus = regmap_get_ibm_i2cr_bus(client, config);
+
+ if (IS_ERR(bus))
+ return ERR_CAST(bus);
+
+ return __devm_regmap_init(&client->dev, bus, client, config, lock_key, lock_name);
+}
+EXPORT_SYMBOL_GPL(__devm_regmap_init_ibm_i2cr);
+
+MODULE_LICENSE("GPL");
diff --git a/include/linux/regmap.h b/include/linux/regmap.h
index e477112fb1c7..1821ebfa640c 100644
--- a/include/linux/regmap.h
+++ b/include/linux/regmap.h
@@ -633,6 +633,10 @@ 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 *__regmap_init_ibm_i2cr(struct i2c_client *client,
+ 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,
@@ -702,6 +706,10 @@ 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);
+struct regmap *__devm_regmap_init_ibm_i2cr(struct i2c_client *client,
+ 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
@@ -942,6 +950,19 @@ bool regmap_ac97_default_volatile(struct device *dev, unsigned int reg);
__regmap_lockdep_wrapper(__regmap_init_fsi, #config, fsi_dev, \
config)
+/**
+ * regmap_init_ibm_i2cr() - Initialise register map
+ *
+ * @client: 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_ibm_i2cr(client, config) \
+ __regmap_lockdep_wrapper(__regmap_init_ibm_i2cr, #config, \
+ client, config)
+
/**
* devm_regmap_init() - Initialise managed register map
*
@@ -1185,6 +1206,20 @@ bool regmap_ac97_default_volatile(struct device *dev, unsigned int reg);
__regmap_lockdep_wrapper(__devm_regmap_init_fsi, #config, \
fsi_dev, config)
+/**
+ * devm_regmap_init_ibm_i2cr() - Initialise managed register map
+ *
+ * @client: 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_ibm_i2cr(client, config) \
+ __regmap_lockdep_wrapper(__devm_regmap_init_ibm_i2cr, #config, \
+ client, 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
Powered by blists - more mailing lists