diff options
| author | Jae Hyun Yoo <jae.hyun.yoo@intel.com> | 2019-05-01 23:27:34 +0300 |
|---|---|---|
| committer | Jae Hyun Yoo <jae.hyun.yoo@intel.com> | 2021-07-14 19:44:45 +0300 |
| commit | 0e7068e2720700007b72da94b5c68ee6e35a4781 (patch) | |
| tree | 1a3ecc3d9b11cd56c0e606235f087a197cfd3f0a | |
| parent | ecbb5f45163371943cb4971e62c39b09ba63f39f (diff) | |
| download | linux-0e7068e2720700007b72da94b5c68ee6e35a4781.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>
| -rw-r--r-- | Documentation/devicetree/bindings/i2c/i2c-aspeed.txt | 1 | ||||
| -rw-r--r-- | drivers/i2c/busses/i2c-aspeed.c | 39 | ||||
| -rw-r--r-- | drivers/i2c/i2c-slave-mqueue.c | 4 | ||||
| -rw-r--r-- | include/linux/i2c.h | 1 |
4 files changed, 44 insertions, 1 deletions
diff --git a/Documentation/devicetree/bindings/i2c/i2c-aspeed.txt b/Documentation/devicetree/bindings/i2c/i2c-aspeed.txt index 805edb3d95e7..64358b9c006c 100644 --- a/Documentation/devicetree/bindings/i2c/i2c-aspeed.txt +++ b/Documentation/devicetree/bindings/i2c/i2c-aspeed.txt @@ -20,6 +20,7 @@ Optional Properties: - bus-frequency : frequency of the bus clock in Hz defaults to 100 kHz when not specified - multi-master : states that there is another master active on this bus. +- general-call : enables general call receiving. - bus-timeout-ms: bus timeout in milliseconds defaults to 1 second when not specified. - #retries : Number of retries for master transfer. diff --git a/drivers/i2c/busses/i2c-aspeed.c b/drivers/i2c/busses/i2c-aspeed.c index 1a6e13c609e4..00cdddebf03c 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 */ }; @@ -424,6 +430,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; @@ -442,6 +454,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; } @@ -488,11 +515,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: @@ -1132,6 +1164,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); } @@ -1170,6 +1204,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; @@ -1317,6 +1353,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: diff --git a/include/linux/i2c.h b/include/linux/i2c.h index 3493fcde1d03..0d996b722b75 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -367,6 +367,7 @@ enum i2c_slave_event { I2C_SLAVE_WRITE_REQUESTED, I2C_SLAVE_READ_PROCESSED, I2C_SLAVE_WRITE_RECEIVED, + I2C_SLAVE_GCALL_REQUESTED, I2C_SLAVE_STOP, }; |
