diff options
Diffstat (limited to 'meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0061-i2c-aspeed-add-buffer-mode-transfer-support.patch')
-rw-r--r-- | meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0061-i2c-aspeed-add-buffer-mode-transfer-support.patch | 635 |
1 files changed, 374 insertions, 261 deletions
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0061-i2c-aspeed-add-buffer-mode-transfer-support.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0061-i2c-aspeed-add-buffer-mode-transfer-support.patch index 24032087b..fbf422a35 100644 --- a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0061-i2c-aspeed-add-buffer-mode-transfer-support.patch +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0061-i2c-aspeed-add-buffer-mode-transfer-support.patch @@ -1,4 +1,4 @@ -From fcee7b9515140486ad8c58beedf88cf12cd09b8b Mon Sep 17 00:00:00 2001 +From b8d7d0f3513abdf014345b240a79d23c63409c4c Mon Sep 17 00:00:00 2001 From: Jae Hyun Yoo <jae.hyun.yoo@intel.com> Date: Tue, 11 Jun 2019 15:07:08 -0700 Subject: [PATCH] i2c: aspeed: add buffer mode transfer support @@ -31,11 +31,11 @@ It provides buffer based master and slave data transfer. Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com> --- .../devicetree/bindings/i2c/i2c-aspeed.txt | 40 ++- - arch/arm/boot/dts/aspeed-g4.dtsi | 47 ++-- - arch/arm/boot/dts/aspeed-g5.dtsi | 47 ++-- - arch/arm/boot/dts/aspeed-g6.dtsi | 32 +-- - drivers/i2c/busses/i2c-aspeed.c | 294 ++++++++++++++++++--- - 5 files changed, 365 insertions(+), 95 deletions(-) + arch/arm/boot/dts/aspeed-g4.dtsi | 47 +-- + arch/arm/boot/dts/aspeed-g5.dtsi | 47 +-- + arch/arm/boot/dts/aspeed-g6.dtsi | 32 +- + drivers/i2c/busses/i2c-aspeed.c | 376 +++++++++++++++++++-- + 5 files changed, 451 insertions(+), 91 deletions(-) diff --git a/Documentation/devicetree/bindings/i2c/i2c-aspeed.txt b/Documentation/devicetree/bindings/i2c/i2c-aspeed.txt index 7da7e813b2b0..0ff3539cee95 100644 @@ -422,10 +422,10 @@ index eb1f9c9d9cca..51593a0a8a23 100644 clocks = <&syscon ASPEED_CLK_APB>; resets = <&syscon ASPEED_RESET_I2C>; diff --git a/arch/arm/boot/dts/aspeed-g6.dtsi b/arch/arm/boot/dts/aspeed-g6.dtsi -index 0e35c4598df5..eeace4b7b725 100644 +index f92a2aa999d1..3ef312543269 100644 --- a/arch/arm/boot/dts/aspeed-g6.dtsi +++ b/arch/arm/boot/dts/aspeed-g6.dtsi -@@ -713,7 +713,7 @@ +@@ -712,7 +712,7 @@ #address-cells = <1>; #size-cells = <0>; #interrupt-cells = <1>; @@ -434,7 +434,7 @@ index 0e35c4598df5..eeace4b7b725 100644 compatible = "aspeed,ast2600-i2c-bus"; clocks = <&syscon ASPEED_CLK_APB2>; resets = <&syscon ASPEED_RESET_I2C>; -@@ -728,7 +728,7 @@ +@@ -727,7 +727,7 @@ #address-cells = <1>; #size-cells = <0>; #interrupt-cells = <1>; @@ -443,7 +443,7 @@ index 0e35c4598df5..eeace4b7b725 100644 compatible = "aspeed,ast2600-i2c-bus"; clocks = <&syscon ASPEED_CLK_APB2>; resets = <&syscon ASPEED_RESET_I2C>; -@@ -743,7 +743,7 @@ +@@ -742,7 +742,7 @@ #address-cells = <1>; #size-cells = <0>; #interrupt-cells = <1>; @@ -452,7 +452,7 @@ index 0e35c4598df5..eeace4b7b725 100644 compatible = "aspeed,ast2600-i2c-bus"; clocks = <&syscon ASPEED_CLK_APB2>; resets = <&syscon ASPEED_RESET_I2C>; -@@ -758,7 +758,7 @@ +@@ -757,7 +757,7 @@ #address-cells = <1>; #size-cells = <0>; #interrupt-cells = <1>; @@ -461,7 +461,7 @@ index 0e35c4598df5..eeace4b7b725 100644 compatible = "aspeed,ast2600-i2c-bus"; clocks = <&syscon ASPEED_CLK_APB2>; resets = <&syscon ASPEED_RESET_I2C>; -@@ -773,7 +773,7 @@ +@@ -772,7 +772,7 @@ #address-cells = <1>; #size-cells = <0>; #interrupt-cells = <1>; @@ -470,7 +470,7 @@ index 0e35c4598df5..eeace4b7b725 100644 compatible = "aspeed,ast2600-i2c-bus"; clocks = <&syscon ASPEED_CLK_APB2>; resets = <&syscon ASPEED_RESET_I2C>; -@@ -788,7 +788,7 @@ +@@ -787,7 +787,7 @@ #address-cells = <1>; #size-cells = <0>; #interrupt-cells = <1>; @@ -479,7 +479,7 @@ index 0e35c4598df5..eeace4b7b725 100644 compatible = "aspeed,ast2600-i2c-bus"; clocks = <&syscon ASPEED_CLK_APB2>; resets = <&syscon ASPEED_RESET_I2C>; -@@ -803,7 +803,7 @@ +@@ -802,7 +802,7 @@ #address-cells = <1>; #size-cells = <0>; #interrupt-cells = <1>; @@ -488,7 +488,7 @@ index 0e35c4598df5..eeace4b7b725 100644 compatible = "aspeed,ast2600-i2c-bus"; clocks = <&syscon ASPEED_CLK_APB2>; resets = <&syscon ASPEED_RESET_I2C>; -@@ -818,7 +818,7 @@ +@@ -817,7 +817,7 @@ #address-cells = <1>; #size-cells = <0>; #interrupt-cells = <1>; @@ -497,7 +497,7 @@ index 0e35c4598df5..eeace4b7b725 100644 compatible = "aspeed,ast2600-i2c-bus"; clocks = <&syscon ASPEED_CLK_APB2>; resets = <&syscon ASPEED_RESET_I2C>; -@@ -833,7 +833,7 @@ +@@ -832,7 +832,7 @@ #address-cells = <1>; #size-cells = <0>; #interrupt-cells = <1>; @@ -506,7 +506,7 @@ index 0e35c4598df5..eeace4b7b725 100644 compatible = "aspeed,ast2600-i2c-bus"; clocks = <&syscon ASPEED_CLK_APB2>; resets = <&syscon ASPEED_RESET_I2C>; -@@ -848,7 +848,7 @@ +@@ -847,7 +847,7 @@ #address-cells = <1>; #size-cells = <0>; #interrupt-cells = <1>; @@ -515,7 +515,7 @@ index 0e35c4598df5..eeace4b7b725 100644 compatible = "aspeed,ast2600-i2c-bus"; clocks = <&syscon ASPEED_CLK_APB2>; resets = <&syscon ASPEED_RESET_I2C>; -@@ -863,7 +863,7 @@ +@@ -862,7 +862,7 @@ #address-cells = <1>; #size-cells = <0>; #interrupt-cells = <1>; @@ -524,7 +524,7 @@ index 0e35c4598df5..eeace4b7b725 100644 compatible = "aspeed,ast2600-i2c-bus"; clocks = <&syscon ASPEED_CLK_APB2>; resets = <&syscon ASPEED_RESET_I2C>; -@@ -878,7 +878,7 @@ +@@ -877,7 +877,7 @@ #address-cells = <1>; #size-cells = <0>; #interrupt-cells = <1>; @@ -533,7 +533,7 @@ index 0e35c4598df5..eeace4b7b725 100644 compatible = "aspeed,ast2600-i2c-bus"; clocks = <&syscon ASPEED_CLK_APB2>; resets = <&syscon ASPEED_RESET_I2C>; -@@ -893,7 +893,7 @@ +@@ -892,7 +892,7 @@ #address-cells = <1>; #size-cells = <0>; #interrupt-cells = <1>; @@ -542,7 +542,7 @@ index 0e35c4598df5..eeace4b7b725 100644 compatible = "aspeed,ast2600-i2c-bus"; clocks = <&syscon ASPEED_CLK_APB2>; resets = <&syscon ASPEED_RESET_I2C>; -@@ -908,7 +908,7 @@ +@@ -907,7 +907,7 @@ #address-cells = <1>; #size-cells = <0>; #interrupt-cells = <1>; @@ -551,7 +551,7 @@ index 0e35c4598df5..eeace4b7b725 100644 compatible = "aspeed,ast2600-i2c-bus"; clocks = <&syscon ASPEED_CLK_APB2>; resets = <&syscon ASPEED_RESET_I2C>; -@@ -923,7 +923,7 @@ +@@ -922,7 +922,7 @@ #address-cells = <1>; #size-cells = <0>; #interrupt-cells = <1>; @@ -560,7 +560,7 @@ index 0e35c4598df5..eeace4b7b725 100644 compatible = "aspeed,ast2600-i2c-bus"; clocks = <&syscon ASPEED_CLK_APB2>; resets = <&syscon ASPEED_RESET_I2C>; -@@ -938,7 +938,7 @@ +@@ -937,7 +937,7 @@ #address-cells = <1>; #size-cells = <0>; #interrupt-cells = <1>; @@ -570,7 +570,7 @@ index 0e35c4598df5..eeace4b7b725 100644 clocks = <&syscon ASPEED_CLK_APB2>; resets = <&syscon ASPEED_RESET_I2C>; diff --git a/drivers/i2c/busses/i2c-aspeed.c b/drivers/i2c/busses/i2c-aspeed.c -index 7becfcd67142..1b338492c68a 100644 +index 7becfcd67142..bf72e151d11f 100644 --- a/drivers/i2c/busses/i2c-aspeed.c +++ b/drivers/i2c/busses/i2c-aspeed.c @@ -7,6 +7,7 @@ @@ -653,220 +653,338 @@ index 7becfcd67142..1b338492c68a 100644 bool multi_master; + /* Buffer mode */ + void __iomem *buf_base; -+ size_t buf_size; + u8 buf_offset; + u8 buf_page; ++ size_t buf_size; #if IS_ENABLED(CONFIG_I2C_SLAVE) struct i2c_client *slave; enum aspeed_i2c_slave_state slave_state; -@@ -259,6 +281,7 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status) +@@ -255,6 +277,77 @@ static int aspeed_i2c_recover_bus(struct aspeed_i2c_bus *bus) + } + + #if IS_ENABLED(CONFIG_I2C_SLAVE) ++static inline void ++aspeed_i2c_slave_handle_rx_done(struct aspeed_i2c_bus *bus, u32 irq_status, ++ u8 *value) ++{ ++ if (bus->buf_base && ++ bus->slave_state == ASPEED_I2C_SLAVE_WRITE_RECEIVED && ++ !(irq_status & ASPEED_I2CD_INTR_NORMAL_STOP)) ++ *value = readb(bus->buf_base); ++ else ++ *value = readl(bus->base + ASPEED_I2C_BYTE_BUF_REG) >> 8; ++} ++ ++static inline void ++aspeed_i2c_slave_handle_normal_stop(struct aspeed_i2c_bus *bus, u32 irq_status, ++ u8 *value) ++{ ++ int i, len; ++ ++ if (bus->slave_state == ASPEED_I2C_SLAVE_WRITE_RECEIVED && ++ irq_status & ASPEED_I2CD_INTR_RX_DONE) { ++ if (bus->buf_base) { ++ len = FIELD_GET(ASPEED_I2CD_BUF_RX_COUNT_MASK, ++ readl(bus->base + ++ ASPEED_I2C_BUF_CTRL_REG)); ++ for (i = 0; i < len; i++) { ++ *value = readb(bus->buf_base + i); ++ i2c_slave_event(bus->slave, ++ I2C_SLAVE_WRITE_RECEIVED, ++ value); ++ } ++ } ++ } ++} ++ ++static inline void ++aspeed_i2c_slave_handle_write_requested(struct aspeed_i2c_bus *bus, u8 *value) ++{ ++ if (bus->buf_base) { ++ writel(FIELD_PREP(ASPEED_I2CD_BUF_RX_SIZE_MASK, ++ bus->buf_size - 1) | ++ FIELD_PREP(ASPEED_I2CD_BUF_OFFSET_MASK, ++ bus->buf_offset), ++ bus->base + ASPEED_I2C_BUF_CTRL_REG); ++ writel(ASPEED_I2CD_RX_BUFF_ENABLE, ++ bus->base + ASPEED_I2C_CMD_REG); ++ } ++} ++ ++static inline void ++aspeed_i2c_slave_handle_write_received(struct aspeed_i2c_bus *bus, u8 *value) ++{ ++ int i, len; ++ ++ if (bus->buf_base) { ++ len = FIELD_GET(ASPEED_I2CD_BUF_RX_COUNT_MASK, ++ readl(bus->base + ++ ASPEED_I2C_BUF_CTRL_REG)); ++ for (i = 1; i < len; i++) { ++ *value = readb(bus->buf_base + i); ++ i2c_slave_event(bus->slave, I2C_SLAVE_WRITE_RECEIVED, ++ value); ++ } ++ writel(FIELD_PREP(ASPEED_I2CD_BUF_RX_SIZE_MASK, ++ bus->buf_size - 1) | ++ FIELD_PREP(ASPEED_I2CD_BUF_OFFSET_MASK, bus->buf_offset), ++ bus->base + ASPEED_I2C_BUF_CTRL_REG); ++ writel(ASPEED_I2CD_RX_BUFF_ENABLE, ++ bus->base + ASPEED_I2C_CMD_REG); ++ } ++} ++ + static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status) { u32 command, irq_handled = 0; - struct i2c_client *slave = bus->slave; -+ int i, len; - u8 value; - - if (!slave) -@@ -281,7 +304,12 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status) +@@ -281,7 +374,7 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status) /* Slave was sent something. */ if (irq_status & ASPEED_I2CD_INTR_RX_DONE) { - value = readl(bus->base + ASPEED_I2C_BYTE_BUF_REG) >> 8; -+ if (bus->buf_base && -+ bus->slave_state == ASPEED_I2C_SLAVE_WRITE_RECEIVED && -+ !(irq_status & ASPEED_I2CD_INTR_NORMAL_STOP)) -+ value = readb(bus->buf_base); -+ else -+ value = readl(bus->base + ASPEED_I2C_BYTE_BUF_REG) >> 8; ++ aspeed_i2c_slave_handle_rx_done(bus, irq_status, &value); /* Handle address frame. */ if (bus->slave_state == ASPEED_I2C_SLAVE_START) { if (value & 0x1) -@@ -296,6 +324,20 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status) +@@ -296,9 +389,11 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status) /* Slave was asked to stop. */ if (irq_status & ASPEED_I2CD_INTR_NORMAL_STOP) { -+ if (bus->slave_state == ASPEED_I2C_SLAVE_WRITE_RECEIVED && -+ irq_status & ASPEED_I2CD_INTR_RX_DONE) { -+ if (bus->buf_base) { -+ len = FIELD_GET(ASPEED_I2CD_BUF_RX_COUNT_MASK, -+ readl(bus->base + -+ ASPEED_I2C_BUF_CTRL_REG)); -+ for (i = 0; i < len; i++) { -+ value = readb(bus->buf_base + i); -+ i2c_slave_event(slave, -+ I2C_SLAVE_WRITE_RECEIVED, -+ &value); -+ } -+ } -+ } ++ aspeed_i2c_slave_handle_normal_stop(bus, irq_status, &value); irq_handled |= ASPEED_I2CD_INTR_NORMAL_STOP; bus->slave_state = ASPEED_I2C_SLAVE_STOP; } -@@ -328,9 +370,36 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status) ++ + if (irq_status & ASPEED_I2CD_INTR_TX_NAK && + bus->slave_state == ASPEED_I2C_SLAVE_READ_PROCESSED) { + irq_handled |= ASPEED_I2CD_INTR_TX_NAK; +@@ -328,9 +423,11 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status) case ASPEED_I2C_SLAVE_WRITE_REQUESTED: bus->slave_state = ASPEED_I2C_SLAVE_WRITE_RECEIVED; i2c_slave_event(slave, I2C_SLAVE_WRITE_REQUESTED, &value); -+ if (bus->buf_base) { -+ writel(FIELD_PREP(ASPEED_I2CD_BUF_RX_SIZE_MASK, -+ bus->buf_size - 1) | -+ FIELD_PREP(ASPEED_I2CD_BUF_OFFSET_MASK, -+ bus->buf_offset), -+ bus->base + ASPEED_I2C_BUF_CTRL_REG); -+ writel(ASPEED_I2CD_RX_BUFF_ENABLE, -+ bus->base + ASPEED_I2C_CMD_REG); -+ } ++ aspeed_i2c_slave_handle_write_requested(bus, &value); break; case ASPEED_I2C_SLAVE_WRITE_RECEIVED: i2c_slave_event(slave, I2C_SLAVE_WRITE_RECEIVED, &value); -+ if (bus->buf_base) { -+ len = FIELD_GET(ASPEED_I2CD_BUF_RX_COUNT_MASK, -+ readl(bus->base + -+ ASPEED_I2C_BUF_CTRL_REG)); -+ for (i = 1; i < len; i++) { -+ value = readb(bus->buf_base + i); -+ i2c_slave_event(slave, -+ I2C_SLAVE_WRITE_RECEIVED, -+ &value); -+ } -+ writel(FIELD_PREP(ASPEED_I2CD_BUF_RX_SIZE_MASK, -+ bus->buf_size - 1) | -+ FIELD_PREP(ASPEED_I2CD_BUF_OFFSET_MASK, -+ bus->buf_offset), -+ bus->base + ASPEED_I2C_BUF_CTRL_REG); -+ writel(ASPEED_I2CD_RX_BUFF_ENABLE, -+ bus->base + ASPEED_I2C_CMD_REG); -+ } ++ aspeed_i2c_slave_handle_write_received(bus, &value); break; case ASPEED_I2C_SLAVE_STOP: i2c_slave_event(slave, I2C_SLAVE_STOP, &value); -@@ -356,6 +425,8 @@ static void aspeed_i2c_do_start(struct aspeed_i2c_bus *bus) +@@ -350,12 +447,76 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status) + } + #endif /* CONFIG_I2C_SLAVE */ + ++static inline u32 ++aspeed_i2c_prepare_rx_buf(struct aspeed_i2c_bus *bus, struct i2c_msg *msg) ++{ ++ u32 command = 0; ++ int len; ++ ++ if (msg->len > bus->buf_size) { ++ len = bus->buf_size; ++ } else { ++ len = msg->len; ++ command |= ASPEED_I2CD_M_S_RX_CMD_LAST; ++ } ++ ++ if (bus->buf_base) { ++ command |= ASPEED_I2CD_RX_BUFF_ENABLE; ++ ++ writel(FIELD_PREP(ASPEED_I2CD_BUF_RX_SIZE_MASK, len - 1) | ++ FIELD_PREP(ASPEED_I2CD_BUF_OFFSET_MASK, bus->buf_offset), ++ bus->base + ASPEED_I2C_BUF_CTRL_REG); ++ } ++ ++ return command; ++} ++ ++static inline u32 ++aspeed_i2c_prepare_tx_buf(struct aspeed_i2c_bus *bus, struct i2c_msg *msg) ++{ ++ u8 slave_addr = i2c_8bit_addr_from_msg(msg); ++ u32 command = 0; ++ int len; ++ ++ if (msg->len + 1 > bus->buf_size) ++ len = bus->buf_size; ++ else ++ len = msg->len + 1; ++ ++ if (bus->buf_base) { ++ u8 wbuf[4]; ++ int i; ++ ++ command |= ASPEED_I2CD_TX_BUFF_ENABLE; ++ ++ /* ++ * Yeah, it looks bad but byte writing on remapped I2C SRAM ++ * causes corruption so use this way to make dword writings. ++ */ ++ wbuf[0] = slave_addr; ++ for (i = 1; i < len; i++) { ++ wbuf[i % 4] = msg->buf[i - 1]; ++ if (i % 4 == 3) ++ writel(*(u32 *)wbuf, bus->buf_base + i - 3); ++ } ++ if (--i % 4 != 3) ++ writel(*(u32 *)wbuf, bus->buf_base + i - (i % 4)); ++ ++ writel(FIELD_PREP(ASPEED_I2CD_BUF_TX_COUNT_MASK, len - 1) | ++ FIELD_PREP(ASPEED_I2CD_BUF_OFFSET_MASK, bus->buf_offset), ++ bus->base + ASPEED_I2C_BUF_CTRL_REG); ++ } ++ ++ bus->buf_index = len - 1; ++ ++ return command; ++} ++ + /* precondition: bus.lock has been acquired. */ + static void aspeed_i2c_do_start(struct aspeed_i2c_bus *bus) + { u32 command = ASPEED_I2CD_M_START_CMD | ASPEED_I2CD_M_TX_CMD; struct i2c_msg *msg = &bus->msgs[bus->msgs_index]; - u8 slave_addr = i2c_8bit_addr_from_msg(msg); -+ u8 wbuf[4]; -+ int len; +- u8 slave_addr = i2c_8bit_addr_from_msg(msg); #if IS_ENABLED(CONFIG_I2C_SLAVE) /* -@@ -374,12 +445,66 @@ static void aspeed_i2c_do_start(struct aspeed_i2c_bus *bus) +@@ -374,12 +535,21 @@ static void aspeed_i2c_do_start(struct aspeed_i2c_bus *bus) if (msg->flags & I2C_M_RD) { command |= ASPEED_I2CD_M_RX_CMD; - /* Need to let the hardware know to NACK after RX. */ - if (msg->len == 1 && !(msg->flags & I2C_M_RECV_LEN)) - command |= ASPEED_I2CD_M_S_RX_CMD_LAST; ++ if (!(msg->flags & I2C_M_RECV_LEN)) { ++ if (msg->len && bus->buf_base) ++ command |= aspeed_i2c_prepare_rx_buf(bus, msg); + -+ if (bus->buf_base && !(msg->flags & I2C_M_RECV_LEN)) { -+ command |= ASPEED_I2CD_RX_BUFF_ENABLE; -+ -+ if (msg->len > bus->buf_size) { -+ len = bus->buf_size; -+ } else { -+ len = msg->len; -+ command |= ASPEED_I2CD_M_S_RX_CMD_LAST; -+ } -+ -+ writel(FIELD_PREP(ASPEED_I2CD_BUF_RX_SIZE_MASK, -+ len - 1) | -+ FIELD_PREP(ASPEED_I2CD_BUF_OFFSET_MASK, -+ bus->buf_offset), -+ bus->base + ASPEED_I2C_BUF_CTRL_REG); -+ } else { + /* Need to let the hardware know to NACK after RX. */ -+ if (msg->len == 1 && !(msg->flags & I2C_M_RECV_LEN)) ++ if (msg->len <= 1) + command |= ASPEED_I2CD_M_S_RX_CMD_LAST; + } -+ } else { -+ if (bus->buf_base) { -+ int i; -+ -+ command |= ASPEED_I2CD_TX_BUFF_ENABLE; -+ -+ if (msg->len + 1 > bus->buf_size) -+ len = bus->buf_size; -+ else -+ len = msg->len + 1; -+ -+ /* -+ * Yeah, it looks clumsy but byte writings on a remapped -+ * I2C SRAM cause corruptions so use this way to make -+ * dword writings. -+ */ -+ wbuf[0] = slave_addr; -+ for (i = 1; i < len; i++) { -+ wbuf[i % 4] = msg->buf[i - 1]; -+ if (i % 4 == 3) -+ writel(*(u32 *)wbuf, -+ bus->buf_base + i - 3); -+ } -+ if (--i % 4 != 3) -+ writel(*(u32 *)wbuf, -+ bus->buf_base + i - (i % 4)); -+ -+ bus->buf_index = len - 1; -+ -+ writel(FIELD_PREP(ASPEED_I2CD_BUF_TX_COUNT_MASK, -+ len - 1) | -+ FIELD_PREP(ASPEED_I2CD_BUF_OFFSET_MASK, -+ bus->buf_offset), -+ bus->base + ASPEED_I2C_BUF_CTRL_REG); -+ } ++ } else if (msg->len && bus->buf_base) { ++ command |= aspeed_i2c_prepare_tx_buf(bus, msg); } - writel(slave_addr, bus->base + ASPEED_I2C_BYTE_BUF_REG); + if (!(command & ASPEED_I2CD_TX_BUFF_ENABLE)) -+ writel(slave_addr, bus->base + ASPEED_I2C_BYTE_BUF_REG); ++ writel(i2c_8bit_addr_from_msg(msg), ++ bus->base + ASPEED_I2C_BYTE_BUF_REG); writel(command, bus->base + ASPEED_I2C_CMD_REG); } -@@ -419,7 +544,7 @@ static u32 aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus, u32 irq_status) - u32 irq_handled = 0, command = 0; - struct i2c_msg *msg; - u8 recv_byte; -- int ret; -+ int ret, len; +@@ -414,6 +584,104 @@ static int aspeed_i2c_is_irq_error(u32 irq_status) + return 0; + } - if (irq_status & ASPEED_I2CD_INTR_BUS_RECOVER_DONE) { - bus->master_state = ASPEED_I2C_MASTER_INACTIVE; -@@ -522,11 +647,43 @@ static u32 aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus, u32 irq_status) - /* fall through */ - case ASPEED_I2C_MASTER_TX_FIRST: - if (bus->buf_index < msg->len) { -+ command = ASPEED_I2CD_M_TX_CMD; ++static inline u32 ++aspeed_i2c_master_handle_tx_first(struct aspeed_i2c_bus *bus, ++ struct i2c_msg *msg) ++{ ++ u32 command = 0; + -+ if (bus->buf_base) { -+ u8 wbuf[4]; -+ int i; ++ if (bus->buf_base) { ++ u8 wbuf[4]; ++ int len; ++ int i; + -+ command |= ASPEED_I2CD_TX_BUFF_ENABLE; ++ if (msg->len - bus->buf_index > bus->buf_size) ++ len = bus->buf_size; ++ else ++ len = msg->len - bus->buf_index; + -+ if (msg->len - bus->buf_index > bus->buf_size) -+ len = bus->buf_size; -+ else -+ len = msg->len - bus->buf_index; + -+ for (i = 0; i < len; i++) { -+ wbuf[i % 4] = msg->buf[bus->buf_index -+ + i]; -+ if (i % 4 == 3) -+ writel(*(u32 *)wbuf, -+ bus->buf_base + i - 3); -+ } -+ if (--i % 4 != 3) -+ writel(*(u32 *)wbuf, -+ bus->buf_base + i - (i % 4)); ++ command |= ASPEED_I2CD_TX_BUFF_ENABLE; + -+ bus->buf_index += len; ++ if (msg->len - bus->buf_index > bus->buf_size) ++ len = bus->buf_size; ++ else ++ len = msg->len - bus->buf_index; + -+ writel(FIELD_PREP(ASPEED_I2CD_BUF_TX_COUNT_MASK, -+ len - 1) | -+ FIELD_PREP(ASPEED_I2CD_BUF_OFFSET_MASK, -+ bus->buf_offset), -+ bus->base + ASPEED_I2C_BUF_CTRL_REG); -+ } else { -+ writel(msg->buf[bus->buf_index++], -+ bus->base + ASPEED_I2C_BYTE_BUF_REG); -+ } ++ for (i = 0; i < len; i++) { ++ wbuf[i % 4] = msg->buf[bus->buf_index + i]; ++ if (i % 4 == 3) ++ writel(*(u32 *)wbuf, ++ bus->buf_base + i - 3); ++ } ++ if (--i % 4 != 3) ++ writel(*(u32 *)wbuf, ++ bus->buf_base + i - (i % 4)); ++ ++ writel(FIELD_PREP(ASPEED_I2CD_BUF_TX_COUNT_MASK, ++ len - 1) | ++ FIELD_PREP(ASPEED_I2CD_BUF_OFFSET_MASK, ++ bus->buf_offset), ++ bus->base + ASPEED_I2C_BUF_CTRL_REG); ++ ++ bus->buf_index += len; ++ } else { ++ writel(msg->buf[bus->buf_index++], ++ bus->base + ASPEED_I2C_BYTE_BUF_REG); ++ } ++ ++ return command; ++} ++ ++static inline void ++aspeed_i2c_master_handle_rx(struct aspeed_i2c_bus *bus, struct i2c_msg *msg) ++{ ++ u8 recv_byte; ++ int len; ++ ++ if (bus->buf_base) { ++ len = FIELD_GET(ASPEED_I2CD_BUF_RX_COUNT_MASK, ++ readl(bus->base + ASPEED_I2C_BUF_CTRL_REG)); ++ memcpy_fromio(msg->buf + bus->buf_index, bus->buf_base, len); ++ bus->buf_index += len; ++ } else { ++ recv_byte = readl(bus->base + ASPEED_I2C_BYTE_BUF_REG) >> 8; ++ msg->buf[bus->buf_index++] = recv_byte; ++ } ++} ++ ++static inline u32 ++aspeed_i2c_master_handle_rx_next(struct aspeed_i2c_bus *bus, ++ struct i2c_msg *msg) ++{ ++ u32 command = 0; ++ ++ if (bus->buf_base) { ++ int len; ++ ++ if (msg->len - bus->buf_index > bus->buf_size) { ++ len = bus->buf_size; ++ } else { ++ len = msg->len - bus->buf_index; ++ command |= ASPEED_I2CD_M_S_RX_CMD_LAST; ++ } ++ ++ command |= ASPEED_I2CD_RX_BUFF_ENABLE; ++ ++ writel(FIELD_PREP(ASPEED_I2CD_BUF_RX_SIZE_MASK, ++ len - 1) | ++ FIELD_PREP(ASPEED_I2CD_BUF_TX_COUNT_MASK, 0) | ++ FIELD_PREP(ASPEED_I2CD_BUF_OFFSET_MASK, ++ bus->buf_offset), ++ bus->base + ASPEED_I2C_BUF_CTRL_REG); ++ } else { ++ if (bus->buf_index + 1 == msg->len) ++ command |= ASPEED_I2CD_M_S_RX_CMD_LAST; ++ } ++ ++ return command; ++} ++ + static u32 aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus, u32 irq_status) + { + u32 irq_handled = 0, command = 0; +@@ -522,11 +790,10 @@ static u32 aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus, u32 irq_status) + /* fall through */ + case ASPEED_I2C_MASTER_TX_FIRST: + if (bus->buf_index < msg->len) { ++ command = ASPEED_I2CD_M_TX_CMD; ++ command |= aspeed_i2c_master_handle_tx_first(bus, msg); + writel(command, bus->base + ASPEED_I2C_CMD_REG); bus->master_state = ASPEED_I2C_MASTER_TX; - writel(msg->buf[bus->buf_index++], @@ -876,44 +994,28 @@ index 7becfcd67142..1b338492c68a 100644 } else { aspeed_i2c_next_msg_or_stop(bus); } -@@ -543,25 +700,56 @@ static u32 aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus, u32 irq_status) +@@ -543,26 +810,26 @@ static u32 aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus, u32 irq_status) } irq_handled |= ASPEED_I2CD_INTR_RX_DONE; - recv_byte = readl(bus->base + ASPEED_I2C_BYTE_BUF_REG) >> 8; - msg->buf[bus->buf_index++] = recv_byte; - -- if (msg->flags & I2C_M_RECV_LEN) { -- if (unlikely(recv_byte > I2C_SMBUS_BLOCK_MAX)) { -- bus->cmd_err = -EPROTO; -- aspeed_i2c_do_stop(bus); -- goto out_no_complete; -+ if (bus->buf_base && !(msg->flags & I2C_M_RECV_LEN)) { -+ len = FIELD_GET(ASPEED_I2CD_BUF_RX_COUNT_MASK, -+ readl(bus->base + -+ ASPEED_I2C_BUF_CTRL_REG)); -+ memcpy_fromio(msg->buf + bus->buf_index, -+ bus->buf_base, len); -+ bus->buf_index += len; -+ } else { -+ recv_byte = readl(bus->base + ASPEED_I2C_BYTE_BUF_REG) -+ >> 8; -+ msg->buf[bus->buf_index++] = recv_byte; -+ -+ if (msg->flags & I2C_M_RECV_LEN) { -+ if (unlikely(recv_byte > I2C_SMBUS_BLOCK_MAX)) { -+ bus->cmd_err = -EPROTO; -+ aspeed_i2c_do_stop(bus); -+ goto out_no_complete; -+ } -+ msg->len = recv_byte + -+ ((msg->flags & I2C_CLIENT_PEC) ? -+ 2 : 1); -+ msg->flags &= ~I2C_M_RECV_LEN; + if (msg->flags & I2C_M_RECV_LEN) { ++ recv_byte = readl(bus->base + ++ ASPEED_I2C_BYTE_BUF_REG) >> 8; + if (unlikely(recv_byte > I2C_SMBUS_BLOCK_MAX)) { + bus->cmd_err = -EPROTO; + aspeed_i2c_do_stop(bus); + goto out_no_complete; } - msg->len = recv_byte + - ((msg->flags & I2C_CLIENT_PEC) ? 2 : 1); -- msg->flags &= ~I2C_M_RECV_LEN; ++ msg->len = recv_byte + ((msg->flags & I2C_CLIENT_PEC) ? ++ 2 : 1); + msg->flags &= ~I2C_M_RECV_LEN; ++ } else if (msg->len) { ++ aspeed_i2c_master_handle_rx(bus, msg); } if (bus->buf_index < msg->len) { @@ -921,33 +1023,13 @@ index 7becfcd67142..1b338492c68a 100644 command = ASPEED_I2CD_M_RX_CMD; - if (bus->buf_index + 1 == msg->len) - command |= ASPEED_I2CD_M_S_RX_CMD_LAST; -+ bus->master_state = ASPEED_I2C_MASTER_RX; -+ if (bus->buf_base) { -+ command |= ASPEED_I2CD_RX_BUFF_ENABLE; -+ -+ if (msg->len - bus->buf_index > -+ bus->buf_size) { -+ len = bus->buf_size; -+ } else { -+ len = msg->len - bus->buf_index; -+ command |= ASPEED_I2CD_M_S_RX_CMD_LAST; -+ } -+ -+ writel(FIELD_PREP(ASPEED_I2CD_BUF_RX_SIZE_MASK, -+ len - 1) | -+ FIELD_PREP(ASPEED_I2CD_BUF_TX_COUNT_MASK, -+ 0) | -+ FIELD_PREP(ASPEED_I2CD_BUF_OFFSET_MASK, -+ bus->buf_offset), -+ bus->base + ASPEED_I2C_BUF_CTRL_REG); -+ } else { -+ if (bus->buf_index + 1 == msg->len) -+ command |= ASPEED_I2CD_M_S_RX_CMD_LAST; -+ } ++ command |= aspeed_i2c_master_handle_rx_next(bus, msg); writel(command, bus->base + ASPEED_I2C_CMD_REG); ++ bus->master_state = ASPEED_I2C_MASTER_RX; } else { aspeed_i2c_next_msg_or_stop(bus); -@@ -924,6 +1112,9 @@ static int aspeed_i2c_init(struct aspeed_i2c_bus *bus, + } +@@ -924,6 +1191,9 @@ static int aspeed_i2c_init(struct aspeed_i2c_bus *bus, if (ret < 0) return ret; @@ -957,34 +1039,24 @@ index 7becfcd67142..1b338492c68a 100644 if (of_property_read_bool(pdev->dev.of_node, "multi-master")) bus->multi_master = true; else -@@ -985,16 +1176,15 @@ static int aspeed_i2c_probe_bus(struct platform_device *pdev) - { - const struct of_device_id *match; - struct aspeed_i2c_bus *bus; -+ bool sram_enabled = true; - struct clk *parent_clk; -- struct resource *res; - int irq, ret; - - bus = devm_kzalloc(&pdev->dev, sizeof(*bus), GFP_KERNEL); - if (!bus) - return -ENOMEM; - -- res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -- bus->base = devm_ioremap_resource(&pdev->dev, res); -+ bus->base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(bus->base)) - return PTR_ERR(bus->base); - -@@ -1028,6 +1218,42 @@ static int aspeed_i2c_probe_bus(struct platform_device *pdev) - bus->get_clk_reg_val = (u32 (*)(struct device *, u32)) - match->data; +@@ -964,6 +1234,52 @@ static int aspeed_i2c_reset(struct aspeed_i2c_bus *bus) + return ret; + } -+ /* Enable I2C SRAM in case of AST2500 */ ++static void aspeed_i2c_set_xfer_mode(struct aspeed_i2c_bus *bus) ++{ ++ struct platform_device *pdev = to_platform_device(bus->dev); ++ bool sram_enabled = true; ++ int ret; ++ ++ /* ++ * Enable I2C SRAM in case of AST2500. ++ * SRAM is enabled by default in AST2400 and AST2600. ++ */ + if (of_device_is_compatible(pdev->dev.of_node, + "aspeed,ast2500-i2c-bus")) { -+ struct regmap *gr_regmap = syscon_regmap_lookup_by_compatible( -+ "aspeed,ast2500-i2c-gr"); ++ struct regmap *gr_regmap = syscon_regmap_lookup_by_compatible("aspeed,ast2500-i2c-gr"); ++ + if (IS_ERR(gr_regmap)) + ret = PTR_ERR(gr_regmap); + else @@ -1001,10 +1073,10 @@ index 7becfcd67142..1b338492c68a 100644 + struct resource *res = platform_get_resource(pdev, + IORESOURCE_MEM, 1); + -+ if (res) ++ if (res && resource_size(res) >= 2) + bus->buf_base = devm_ioremap_resource(&pdev->dev, res); + -+ if (!IS_ERR_OR_NULL(bus->buf_base) && resource_size(res) >= 2) { ++ if (!IS_ERR_OR_NULL(bus->buf_base)) { + bus->buf_size = resource_size(res); + if (of_device_is_compatible(pdev->dev.of_node, + "aspeed,ast2400-i2c-bus")) { @@ -1015,11 +1087,52 @@ index 7becfcd67142..1b338492c68a 100644 + } + } + } ++} ++ + static const struct of_device_id aspeed_i2c_bus_of_table[] = { + { + .compatible = "aspeed,ast2400-i2c-bus", +@@ -986,18 +1302,18 @@ static int aspeed_i2c_probe_bus(struct platform_device *pdev) + const struct of_device_id *match; + struct aspeed_i2c_bus *bus; + struct clk *parent_clk; +- struct resource *res; + int irq, ret; + + bus = devm_kzalloc(&pdev->dev, sizeof(*bus), GFP_KERNEL); + if (!bus) + return -ENOMEM; + +- res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +- bus->base = devm_ioremap_resource(&pdev->dev, res); ++ bus->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(bus->base)) + return PTR_ERR(bus->base); + ++ bus->dev = &pdev->dev; ++ + parent_clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(parent_clk)) + return PTR_ERR(parent_clk); +@@ -1028,6 +1344,8 @@ static int aspeed_i2c_probe_bus(struct platform_device *pdev) + bus->get_clk_reg_val = (u32 (*)(struct device *, u32)) + match->data; + ++ aspeed_i2c_set_xfer_mode(bus); + /* Initialize the I2C adapter */ spin_lock_init(&bus->lock); init_completion(&bus->cmd_complete); -@@ -1063,8 +1289,8 @@ static int aspeed_i2c_probe_bus(struct platform_device *pdev) +@@ -1038,8 +1356,6 @@ static int aspeed_i2c_probe_bus(struct platform_device *pdev) + strlcpy(bus->adap.name, pdev->name, sizeof(bus->adap.name)); + i2c_set_adapdata(&bus->adap, bus); + +- bus->dev = &pdev->dev; +- + /* Clean up any left over interrupt state. */ + writel(0, bus->base + ASPEED_I2C_INTR_CTRL_REG); + writel(0xffffffff, bus->base + ASPEED_I2C_INTR_STS_REG); +@@ -1063,8 +1379,8 @@ static int aspeed_i2c_probe_bus(struct platform_device *pdev) platform_set_drvdata(pdev, bus); |