Messages in this thread Patch in this message | | | From | Eddie James <> | Subject | [PATCH v2 2/5] drivers: fsi: Add I2C Responder driver | Date | Wed, 2 Nov 2022 15:51:45 -0500 |
| |
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, ®map_bus_i2cr, \ + client, config) +#define devm_init_i2cr(client, config) \ + __regmap_lockdep_wrapper(__devm_regmap_init, #config, &(client)->dev, ®map_bus_i2cr, \ + client, config) + +#endif /* DRIVERS_I2CR_H */ -- 2.31.1
| |