summaryrefslogtreecommitdiff
path: root/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0061-i2c-aspeed-add-buffer-mode-transfer-support.patch
diff options
context:
space:
mode:
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.patch635
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);