summaryrefslogtreecommitdiff
path: root/drivers/i2c
diff options
context:
space:
mode:
authorJae Hyun Yoo <jae.hyun.yoo@intel.com>2019-05-01 23:27:34 +0300
committerJae Hyun Yoo <jae.hyun.yoo@linux.intel.com>2021-11-05 10:22:08 +0300
commit9e8b19854f7a64143fc1177f82f185e3051154b3 (patch)
tree2895c93e07ff708eeb5c7f7ff407fd7eadcb3805 /drivers/i2c
parent9ae84a5a6a44cf5625c088fa99876372c613ee63 (diff)
downloadlinux-9e8b19854f7a64143fc1177f82f185e3051154b3.tar.xz
i2c: aspeed: add general call support
This commit adds general call support into Aspeed I2C driver. This is downstream only customization so it should not go into upstream. Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
Diffstat (limited to 'drivers/i2c')
-rw-r--r--drivers/i2c/busses/i2c-aspeed.c39
-rw-r--r--drivers/i2c/i2c-slave-mqueue.c4
2 files changed, 42 insertions, 1 deletions
diff --git a/drivers/i2c/busses/i2c-aspeed.c b/drivers/i2c/busses/i2c-aspeed.c
index 3323b4f72374..4ed33ac2045c 100644
--- a/drivers/i2c/busses/i2c-aspeed.c
+++ b/drivers/i2c/busses/i2c-aspeed.c
@@ -59,6 +59,7 @@
#define ASPEED_I2CD_SDA_DRIVE_1T_EN BIT(8)
#define ASPEED_I2CD_M_SDA_DRIVE_1T_EN BIT(7)
#define ASPEED_I2CD_M_HIGH_SPEED_EN BIT(6)
+#define ASPEED_I2CD_GCALL_EN BIT(2)
#define ASPEED_I2CD_SLAVE_EN BIT(1)
#define ASPEED_I2CD_MASTER_EN BIT(0)
@@ -84,6 +85,7 @@
#define ASPEED_I2CD_INTR_RECV_MASK 0xf000ffff
#define ASPEED_I2CD_INTR_SDA_DL_TIMEOUT BIT(14)
#define ASPEED_I2CD_INTR_BUS_RECOVER_DONE BIT(13)
+#define ASPEED_I2CD_INTR_GCALL_ADDR BIT(8)
#define ASPEED_I2CD_INTR_SLAVE_MATCH BIT(7)
#define ASPEED_I2CD_INTR_SCL_TIMEOUT BIT(6)
#define ASPEED_I2CD_INTR_ABNORMAL BIT(5)
@@ -168,6 +170,8 @@ enum aspeed_i2c_slave_state {
ASPEED_I2C_SLAVE_READ_PROCESSED,
ASPEED_I2C_SLAVE_WRITE_REQUESTED,
ASPEED_I2C_SLAVE_WRITE_RECEIVED,
+ ASPEED_I2C_SLAVE_GCALL_START,
+ ASPEED_I2C_SLAVE_GCALL_REQUESTED,
ASPEED_I2C_SLAVE_STOP,
};
@@ -209,6 +213,8 @@ struct aspeed_i2c_bus {
#if IS_ENABLED(CONFIG_I2C_SLAVE)
struct i2c_client *slave;
enum aspeed_i2c_slave_state slave_state;
+ /* General call */
+ bool general_call;
#endif /* CONFIG_I2C_SLAVE */
};
@@ -427,6 +433,12 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status)
bus->slave_state = ASPEED_I2C_SLAVE_START;
}
+ /* General call was requested, restart state machine. */
+ if (irq_status & ASPEED_I2CD_INTR_GCALL_ADDR) {
+ irq_handled |= ASPEED_I2CD_INTR_GCALL_ADDR;
+ bus->slave_state = ASPEED_I2C_SLAVE_GCALL_START;
+ }
+
/* Slave is not currently active, irq was for someone else. */
if (bus->slave_state == ASPEED_I2C_SLAVE_INACTIVE)
return irq_handled;
@@ -445,6 +457,21 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status)
else
bus->slave_state =
ASPEED_I2C_SLAVE_WRITE_REQUESTED;
+ } else if (bus->slave_state == ASPEED_I2C_SLAVE_GCALL_START) {
+ /*
+ * I2C spec defines the second byte meaning like below.
+ * 0x06 : Reset and write programmable part of slave
+ * address by hardware.
+ * 0x04 : Write programmable part of slave address by
+ * hardware.
+ * 0x00 : No allowed.
+ *
+ * But in OpenBMC, we are going to use this
+ * 'General call' feature for IPMB message broadcasting
+ * so it delivers all data as is without any specific
+ * handling of the second byte.
+ */
+ bus->slave_state = ASPEED_I2C_SLAVE_GCALL_REQUESTED;
}
irq_handled |= ASPEED_I2CD_INTR_RX_DONE;
}
@@ -491,11 +518,16 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status)
i2c_slave_event(slave, I2C_SLAVE_WRITE_RECEIVED, &value);
aspeed_i2c_slave_handle_write_received(bus, &value);
break;
+ case ASPEED_I2C_SLAVE_GCALL_REQUESTED:
+ bus->slave_state = ASPEED_I2C_SLAVE_WRITE_RECEIVED;
+ i2c_slave_event(slave, I2C_SLAVE_GCALL_REQUESTED, &value);
+ break;
case ASPEED_I2C_SLAVE_STOP:
i2c_slave_event(slave, I2C_SLAVE_STOP, &value);
bus->slave_state = ASPEED_I2C_SLAVE_INACTIVE;
break;
case ASPEED_I2C_SLAVE_START:
+ case ASPEED_I2C_SLAVE_GCALL_START:
/* Slave was just started. Waiting for the next event. */;
break;
default:
@@ -1139,6 +1171,8 @@ static void __aspeed_i2c_reg_slave(struct aspeed_i2c_bus *bus, u16 slave_addr)
/* Turn on slave mode. */
func_ctrl_reg_val = readl(bus->base + ASPEED_I2C_FUN_CTRL_REG);
func_ctrl_reg_val |= ASPEED_I2CD_SLAVE_EN;
+ if (bus->general_call)
+ func_ctrl_reg_val |= ASPEED_I2CD_GCALL_EN;
writel(func_ctrl_reg_val, bus->base + ASPEED_I2C_FUN_CTRL_REG);
}
@@ -1177,6 +1211,8 @@ static int aspeed_i2c_unreg_slave(struct i2c_client *client)
/* Turn off slave mode. */
func_ctrl_reg_val = readl(bus->base + ASPEED_I2C_FUN_CTRL_REG);
func_ctrl_reg_val &= ~ASPEED_I2CD_SLAVE_EN;
+ if (bus->general_call)
+ func_ctrl_reg_val &= ~ASPEED_I2CD_GCALL_EN;
writel(func_ctrl_reg_val, bus->base + ASPEED_I2C_FUN_CTRL_REG);
bus->slave = NULL;
@@ -1324,6 +1360,9 @@ static int aspeed_i2c_init(struct aspeed_i2c_bus *bus,
bus->base + ASPEED_I2C_FUN_CTRL_REG);
#if IS_ENABLED(CONFIG_I2C_SLAVE)
+ if (of_property_read_bool(pdev->dev.of_node, "general-call"))
+ bus->general_call = true;
+
/* If slave has already been registered, re-enable it. */
if (bus->slave)
__aspeed_i2c_reg_slave(bus, bus->slave->addr);
diff --git a/drivers/i2c/i2c-slave-mqueue.c b/drivers/i2c/i2c-slave-mqueue.c
index 2c7a6038409c..1d4db584b393 100644
--- a/drivers/i2c/i2c-slave-mqueue.c
+++ b/drivers/i2c/i2c-slave-mqueue.c
@@ -56,10 +56,12 @@ static int i2c_slave_mqueue_callback(struct i2c_client *client,
switch (event) {
case I2C_SLAVE_WRITE_REQUESTED:
+ case I2C_SLAVE_GCALL_REQUESTED:
mq->truncated = 0;
msg->len = 1;
- msg->buf[0] = client->addr << 1;
+ msg->buf[0] = event == I2C_SLAVE_GCALL_REQUESTED ?
+ 0 : client->addr << 1;
break;
case I2C_SLAVE_WRITE_RECEIVED: