+ for (i = 0; i < len; i++) {
+ value = readb(bus->buf_base + i);
+ i2c_slave_event(slave,
+ I2C_SLAVE_WRITE_RECEIVED,
+ &value);
+ }
+ }
+ }
irq_handled |= ASPEED_I2CD_INTR_NORMAL_STOP;
bus->slave_state = ASPEED_I2C_SLAVE_STOP;
}
@@ -307,9 +349,36 @@ 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);
+ }
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);
+ }
break;
case ASPEED_I2C_SLAVE_STOP:
i2c_slave_event(slave, I2C_SLAVE_STOP, &value);
@@ -335,6 +404,8 @@ 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;
#if IS_ENABLED(CONFIG_I2C_SLAVE)
/*
@@ -353,12 +424,66 @@ 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 (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))
+ 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);
+ }
}
- 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(command, bus->base + ASPEED_I2C_CMD_REG);
}
@@ -398,7 +523,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;
if (irq_status & ASPEED_I2CD_INTR_BUS_RECOVER_DONE) {
bus->master_state = ASPEED_I2C_MASTER_INACTIVE;
@@ -511,11 +636,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;
+
+ if (bus->buf_base) {
+ u8 wbuf[4];
+ 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;
+
+ 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));
+
+ bus->buf_index += len;
+
+ 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);
+ }
+ writel(command, bus->base + ASPEED_I2C_CMD_REG);
bus->master_state = ASPEED_I2C_MASTER_TX;
- writel(msg->buf[bus->buf_index++],
- bus->base + ASPEED_I2C_BYTE_BUF_REG);
- writel(ASPEED_I2CD_M_TX_CMD,
- bus->base + ASPEED_I2C_CMD_REG);
} else {
aspeed_i2c_next_msg_or_stop(bus);
}
@@ -532,25 +689,56 @@ 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;
}
- msg->len = recv_byte +
- ((msg->flags & I2C_CLIENT_PEC) ? 2 : 1);
- msg->flags &= ~I2C_M_RECV_LEN;
}
if (bus->buf_index < msg->len) {
- bus->master_state = ASPEED_I2C_MASTER_RX;
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;
+ }
writel(command, bus->base + ASPEED_I2C_CMD_REG);
} else {
aspeed_i2c_next_msg_or_stop(bus);
@@ -890,6 +1078,9 @@ static int aspeed_i2c_init(struct aspeed_i2c_bus *bus,
if (ret < 0)
return ret;
+ fun_ctrl_reg |= FIELD_PREP(ASPEED_I2CD_BUFFER_PAGE_SEL_MASK,
+ bus->buf_page);
+
if (of_property_read_bool(pdev->dev.of_node, "multi-master"))
bus->multi_master = true;
else
@@ -947,16 +1138,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);
@@ -990,6 +1180,45 @@ static int aspeed_i2c_probe_bus(struct platform_device *pdev)
bus->get_clk_reg_val = (u32 (*)(struct device *, u32))
match->data;
+ /*
+ * Enable I2C SRAM in case of AST2500.
+ * SRAM is enabled by default in AST2400 and AST2600.
+ */