Messages in this thread Patch in this message | | | From | Eddie James <> | Subject | [PATCH 2/5] regmap: Add IBM I2CR support | Date | Fri, 14 Oct 2022 17:05:37 -0500 |
| |
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@linux.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@linux.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
| |