[PATCH v6 8/9] i2c: iproc: add NIC I2C support

From: Ray Jui
Date: Tue Apr 02 2019 - 21:19:17 EST


From: Rayagonda Kokatanur <rayagonda.kokatanur@xxxxxxxxxxxx>

Add NIC I2C support to the iProc I2C driver. Access to the NIC I2C base
registers requires going through the IDM wrapper to map into the NIC's
address space

Signed-off-by: Rayagonda Kokatanur <rayagonda.kokatanur@xxxxxxxxxxxx>
Signed-off-by: Ray Jui <ray.jui@xxxxxxxxxxxx>
---
drivers/i2c/busses/i2c-bcm-iproc.c | 80 ++++++++++++++++++++++++++++--
1 file changed, 75 insertions(+), 5 deletions(-)

diff --git a/drivers/i2c/busses/i2c-bcm-iproc.c b/drivers/i2c/busses/i2c-bcm-iproc.c
index dba1a9d925b2..562942d0c05c 100644
--- a/drivers/i2c/busses/i2c-bcm-iproc.c
+++ b/drivers/i2c/busses/i2c-bcm-iproc.c
@@ -17,9 +17,11 @@
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/slab.h>

+#define IDM_CTRL_DIRECT_OFFSET 0x00
#define CFG_OFFSET 0x00
#define CFG_RESET_SHIFT 31
#define CFG_EN_SHIFT 30
@@ -174,11 +176,23 @@ enum bus_speed_index {
I2C_SPD_400K,
};

+enum bcm_iproc_i2c_type {
+ IPROC_I2C,
+ IPROC_I2C_NIC
+};
+
struct bcm_iproc_i2c_dev {
struct device *device;
+ enum bcm_iproc_i2c_type type;
int irq;

void __iomem *base;
+ void __iomem *idm_base;
+
+ u32 ape_addr_mask;
+
+ /* lock for indirect access through IDM */
+ spinlock_t idm_lock;

struct i2c_adapter adapter;
unsigned int bus_speed;
@@ -215,13 +229,33 @@ static void bcm_iproc_i2c_enable_disable(struct bcm_iproc_i2c_dev *iproc_i2c,
static inline u32 iproc_i2c_rd_reg(struct bcm_iproc_i2c_dev *iproc_i2c,
u32 offset)
{
- return readl(iproc_i2c->base + offset);
+ u32 val;
+
+ if (iproc_i2c->idm_base) {
+ spin_lock(&iproc_i2c->idm_lock);
+ writel(iproc_i2c->ape_addr_mask,
+ iproc_i2c->idm_base + IDM_CTRL_DIRECT_OFFSET);
+ val = readl(iproc_i2c->base + offset);
+ spin_unlock(&iproc_i2c->idm_lock);
+ } else {
+ val = readl(iproc_i2c->base + offset);
+ }
+
+ return val;
}

static inline void iproc_i2c_wr_reg(struct bcm_iproc_i2c_dev *iproc_i2c,
u32 offset, u32 val)
{
- writel(val, iproc_i2c->base + offset);
+ if (iproc_i2c->idm_base) {
+ spin_lock(&iproc_i2c->idm_lock);
+ writel(iproc_i2c->ape_addr_mask,
+ iproc_i2c->idm_base + IDM_CTRL_DIRECT_OFFSET);
+ writel(val, iproc_i2c->base + offset);
+ spin_unlock(&iproc_i2c->idm_lock);
+ } else {
+ writel(val, iproc_i2c->base + offset);
+ }
}

static void bcm_iproc_i2c_slave_init(
@@ -765,10 +799,15 @@ static int bcm_iproc_i2c_xfer(struct i2c_adapter *adapter,

static uint32_t bcm_iproc_i2c_functionality(struct i2c_adapter *adap)
{
- return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_SLAVE;
+ u32 val = I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+
+ if (adap->algo->reg_slave)
+ val |= I2C_FUNC_SLAVE;
+
+ return val;
}

-static const struct i2c_algorithm bcm_iproc_algo = {
+static struct i2c_algorithm bcm_iproc_algo = {
.master_xfer = bcm_iproc_i2c_xfer,
.functionality = bcm_iproc_i2c_functionality,
.reg_slave = bcm_iproc_i2c_reg_slave,
@@ -828,6 +867,8 @@ static int bcm_iproc_i2c_probe(struct platform_device *pdev)

platform_set_drvdata(pdev, iproc_i2c);
iproc_i2c->device = &pdev->dev;
+ iproc_i2c->type =
+ (enum bcm_iproc_i2c_type)of_device_get_match_data(&pdev->dev);
init_completion(&iproc_i2c->done);

res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -835,6 +876,29 @@ static int bcm_iproc_i2c_probe(struct platform_device *pdev)
if (IS_ERR(iproc_i2c->base))
return PTR_ERR(iproc_i2c->base);

+ if (iproc_i2c->type == IPROC_I2C_NIC) {
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ iproc_i2c->idm_base = devm_ioremap_resource(iproc_i2c->device,
+ res);
+ if (IS_ERR(iproc_i2c->idm_base))
+ return PTR_ERR(iproc_i2c->idm_base);
+
+ ret = of_property_read_u32(iproc_i2c->device->of_node,
+ "brcm,ape-hsls-addr-mask",
+ &iproc_i2c->ape_addr_mask);
+ if (ret < 0) {
+ dev_err(iproc_i2c->device,
+ "'brcm,ape-hsls-addr-mask' missing\n");
+ return -EINVAL;
+ }
+
+ spin_lock_init(&iproc_i2c->idm_lock);
+
+ /* no slave support */
+ bcm_iproc_algo.reg_slave = NULL;
+ bcm_iproc_algo.unreg_slave = NULL;
+ }
+
ret = bcm_iproc_i2c_init(iproc_i2c);
if (ret)
return ret;
@@ -991,7 +1055,13 @@ static int bcm_iproc_i2c_unreg_slave(struct i2c_client *slave)
}

static const struct of_device_id bcm_iproc_i2c_of_match[] = {
- { .compatible = "brcm,iproc-i2c" },
+ {
+ .compatible = "brcm,iproc-i2c",
+ .data = (int *)IPROC_I2C,
+ }, {
+ .compatible = "brcm,iproc-nic-i2c",
+ .data = (int *)IPROC_I2C_NIC,
+ },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, bcm_iproc_i2c_of_match);
--
2.17.1