linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/2] i2c: aspeed: added driver for Aspeed I2C
@ 2016-09-09 19:54 Brendan Higgins
  2016-09-09 19:54 ` [PATCH 1/2] " Brendan Higgins
  2016-09-09 19:54 ` [PATCH " Brendan Higgins
  0 siblings, 2 replies; 17+ messages in thread
From: Brendan Higgins @ 2016-09-09 19:54 UTC (permalink / raw)
  To: wsa, robh+dt, mark.rutland
  Cc: linux-i2c, devicetree, linux-kernel, openbmc, joel, jk

This patch set adds initial master mode and slave mode support for the Aspeed
24xx and 25xx I2C controllers, as well as device binding documentation for the
new driver.

These changes have been tested on the ast2500 evaluation board from Aspeed.

Work still to be done:
 - The driver supports I2C protocol mangling in the form of sending stops after
   every transaction, the remaining mangling support has not yet been added.
 - SMBus Alert support.

Cheers!

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [PATCH 1/2] i2c: aspeed: added driver for Aspeed I2C
  2016-09-09 19:54 [PATCH 0/2] i2c: aspeed: added driver for Aspeed I2C Brendan Higgins
@ 2016-09-09 19:54 ` Brendan Higgins
  2016-09-09 22:48   ` kbuild test robot
  2016-09-10  0:48   ` kbuild test robot
  2016-09-09 19:54 ` [PATCH " Brendan Higgins
  1 sibling, 2 replies; 17+ messages in thread
From: Brendan Higgins @ 2016-09-09 19:54 UTC (permalink / raw)
  To: wsa, robh+dt, mark.rutland
  Cc: linux-i2c, devicetree, linux-kernel, openbmc, joel, jk, Brendan Higgins

Added initial master and slave support for Aspeed I2C controller.
Supports fourteen busses present in ast24xx and ast25xx BMC SoCs by
Aspeed.

Signed-off-by: Brendan Higgins <brendanhiggins@google.com>
---
 drivers/i2c/busses/Kconfig      |  10 +
 drivers/i2c/busses/Makefile     |   1 +
 drivers/i2c/busses/i2c-aspeed.c | 816 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 827 insertions(+)
 create mode 100644 drivers/i2c/busses/i2c-aspeed.c

diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index 5c3993b..0178c6c 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -998,6 +998,16 @@ config I2C_RCAR
 	  This driver can also be built as a module.  If so, the module
 	  will be called i2c-rcar.
 
+config I2C_ASPEED
+	tristate "Aspeed AST2xxx SoC I2C Controller"
+	depends on (ARCH_ASPEED || COMPILE_TEST) && OF
+	help
+	  If you say yes to this option, support will be included for the
+	  Aspeed AST2xxx SoC I2C controller.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called i2c-aspeed.
+
 comment "External I2C/SMBus adapter drivers"
 
 config I2C_DIOLAN_U2C
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index 37f2819..49631cd 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -96,6 +96,7 @@ obj-$(CONFIG_I2C_XILINX)	+= i2c-xiic.o
 obj-$(CONFIG_I2C_XLR)		+= i2c-xlr.o
 obj-$(CONFIG_I2C_XLP9XX)	+= i2c-xlp9xx.o
 obj-$(CONFIG_I2C_RCAR)		+= i2c-rcar.o
+obj-$(CONFIG_I2C_ASPEED)	+= i2c-aspeed.o
 
 # External I2C/SMBus adapter drivers
 obj-$(CONFIG_I2C_DIOLAN_U2C)	+= i2c-diolan-u2c.o
diff --git a/drivers/i2c/busses/i2c-aspeed.c b/drivers/i2c/busses/i2c-aspeed.c
new file mode 100644
index 0000000..9a5ca04
--- /dev/null
+++ b/drivers/i2c/busses/i2c-aspeed.c
@@ -0,0 +1,816 @@
+/*
+ *  I2C adapter for the ASPEED I2C bus.
+ *
+ *  Copyright (C) 2012-2020  ASPEED Technology Inc.
+ *  Copyright 2016 IBM Corporation
+ *  Copyright 2016 Google, Inc.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/completion.h>
+#include <linux/slab.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+
+/* I2C Register */
+#define ASPEED_I2C_FUN_CTRL_REG				0x00
+#define ASPEED_I2C_AC_TIMING_REG1			0x04
+#define ASPEED_I2C_AC_TIMING_REG2			0x08
+#define ASPEED_I2C_INTR_CTRL_REG			0x0c
+#define ASPEED_I2C_INTR_STS_REG				0x10
+#define ASPEED_I2C_CMD_REG				0x14
+#define ASPEED_I2C_DEV_ADDR_REG				0x18
+#define ASPEED_I2C_BYTE_BUF_REG				0x20
+
+#define ASPEED_I2C_NUM_BUS 14
+
+/* Global Register Definition */
+/* 0x00 : I2C Interrupt Status Register  */
+/* 0x08 : I2C Interrupt Target Assignment  */
+
+/* Device Register Definition */
+/* 0x00 : I2CD Function Control Register  */
+#define ASPEED_I2CD_MULTI_MASTER_DIS			BIT(15)
+#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_SLAVE_EN				BIT(1)
+#define ASPEED_I2CD_MASTER_EN				BIT(0)
+
+/* 0x08 : I2CD Clock and AC Timing Control Register #2 */
+#define ASPEED_NO_TIMEOUT_CTRL				0
+
+
+/* 0x0c : I2CD Interrupt Control Register &
+ * 0x10 : I2CD Interrupt Status Register
+ *
+ * These share bit definitions, so use the same values for the enable &
+ * status bits.
+ */
+#define ASPEED_I2CD_INTR_SDA_DL_TIMEOUT			BIT(14)
+#define ASPEED_I2CD_INTR_BUS_RECOVER_DONE		BIT(13)
+#define ASPEED_I2CD_INTR_SLAVE_MATCH			BIT(7)
+#define ASPEED_I2CD_INTR_SCL_TIMEOUT			BIT(6)
+#define ASPEED_I2CD_INTR_ABNORMAL			BIT(5)
+#define ASPEED_I2CD_INTR_NORMAL_STOP			BIT(4)
+#define ASPEED_I2CD_INTR_ARBIT_LOSS			BIT(3)
+#define ASPEED_I2CD_INTR_RX_DONE			BIT(2)
+#define ASPEED_I2CD_INTR_TX_NAK				BIT(1)
+#define ASPEED_I2CD_INTR_TX_ACK				BIT(0)
+
+/* 0x14 : I2CD Command/Status Register   */
+#define ASPEED_I2CD_SCL_LINE_STS			BIT(18)
+#define ASPEED_I2CD_SDA_LINE_STS			BIT(17)
+#define ASPEED_I2CD_BUS_BUSY_STS			BIT(16)
+#define ASPEED_I2CD_BUS_RECOVER_CMD			BIT(11)
+
+/* Command Bit */
+#define ASPEED_I2CD_M_STOP_CMD				BIT(5)
+#define ASPEED_I2CD_M_S_RX_CMD_LAST			BIT(4)
+#define ASPEED_I2CD_M_RX_CMD				BIT(3)
+#define ASPEED_I2CD_S_TX_CMD				BIT(2)
+#define ASPEED_I2CD_M_TX_CMD				BIT(1)
+#define ASPEED_I2CD_M_START_CMD				BIT(0)
+
+/* 0x18 : I2CD Slave Device Address Register   */
+#define ASPEED_I2CD_DEV_ADDR_MASK			GENMASK(6, 0)
+
+enum aspeed_i2c_slave_state {
+	ASPEED_I2C_SLAVE_START,
+	ASPEED_I2C_SLAVE_READ_REQUESTED,
+	ASPEED_I2C_SLAVE_READ_PROCESSED,
+	ASPEED_I2C_SLAVE_WRITE_REQUESTED,
+	ASPEED_I2C_SLAVE_WRITE_RECEIVED,
+	ASPEED_I2C_SLAVE_STOP,
+};
+
+struct aspeed_i2c_bus {
+	struct i2c_adapter		adap;
+	struct device			*dev;
+	void __iomem			*base;
+	spinlock_t			lock;
+	struct completion		cmd_complete;
+	int				irq;
+	/* Transaction state. */
+	struct i2c_msg			*msg;
+	int				msg_pos;
+	u32				cmd_err;
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+	struct i2c_client		*slave;
+	enum aspeed_i2c_slave_state	slave_state;
+#endif
+};
+
+struct aspeed_i2c_controller {
+	struct device		*dev;
+	void __iomem		*base;
+	int			irq;
+	struct irq_domain	*irq_domain;
+};
+
+static inline void aspeed_i2c_write(struct aspeed_i2c_bus *bus, u32 val,
+				    u32 reg)
+{
+	writel(val, bus->base + reg);
+}
+
+static inline u32 aspeed_i2c_read(struct aspeed_i2c_bus *bus, u32 reg)
+{
+	return readl(bus->base + reg);
+}
+
+static u8 aspeed_i2c_recover_bus(struct aspeed_i2c_bus *bus)
+{
+	u32 command;
+	unsigned long time_left;
+	unsigned long flags;
+	int ret = 0;
+
+	spin_lock_irqsave(&bus->lock, flags);
+	command = aspeed_i2c_read(bus, ASPEED_I2C_CMD_REG);
+	/* Bus is idle: no recovery needed. */
+	if ((command & ASPEED_I2CD_SDA_LINE_STS) &&
+	    (command & ASPEED_I2CD_SCL_LINE_STS))
+		goto out;
+
+	dev_dbg(bus->dev, "bus hung (state %x), attempting recovery\n",
+		command);
+
+	/* Bus held: put bus in stop state. */
+	if ((command & ASPEED_I2CD_SDA_LINE_STS) &&
+	    !(command & ASPEED_I2CD_SCL_LINE_STS)) {
+		aspeed_i2c_write(bus, ASPEED_I2CD_M_STOP_CMD,
+				 ASPEED_I2C_CMD_REG);
+		reinit_completion(&bus->cmd_complete);
+		spin_unlock_irqrestore(&bus->lock, flags);
+
+		time_left = wait_for_completion_interruptible_timeout(
+				&bus->cmd_complete, bus->adap.timeout * HZ);
+
+		spin_lock_irqsave(&bus->lock, flags);
+		if (time_left == 0)
+			ret = -ETIMEDOUT;
+		else if (bus->cmd_err)
+			ret = -EIO;
+	/* Bus error. */
+	} else if (!(command & ASPEED_I2CD_SDA_LINE_STS)) {
+		aspeed_i2c_write(bus, ASPEED_I2CD_BUS_RECOVER_CMD,
+				 ASPEED_I2C_CMD_REG);
+		reinit_completion(&bus->cmd_complete);
+		spin_unlock_irqrestore(&bus->lock, flags);
+
+		time_left = wait_for_completion_interruptible_timeout(
+				&bus->cmd_complete, bus->adap.timeout * HZ);
+
+		spin_lock_irqsave(&bus->lock, flags);
+		if (time_left == 0)
+			ret = -ETIMEDOUT;
+		else if (bus->cmd_err)
+			ret = -EIO;
+		/* Recovery failed. */
+		else if (!(aspeed_i2c_read(bus, ASPEED_I2C_CMD_REG) &
+			   ASPEED_I2CD_SDA_LINE_STS))
+			ret = -EIO;
+	}
+
+out:
+	spin_unlock_irqrestore(&bus->lock, flags);
+	return ret;
+}
+
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+static bool aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus)
+{
+	bool irq_handled = true;
+	u32 command;
+	u32 irq_status;
+	u32 status_ack = 0;
+	u8 value;
+	struct i2c_client *slave = bus->slave;
+
+	spin_lock(&bus->lock);
+	if (!slave) {
+		irq_handled = false;
+		goto out;
+	}
+	command = aspeed_i2c_read(bus, ASPEED_I2C_CMD_REG);
+	irq_status = aspeed_i2c_read(bus, ASPEED_I2C_INTR_STS_REG);
+
+	/* Slave was requested, restart state machine. */
+	if (irq_status & ASPEED_I2CD_INTR_SLAVE_MATCH) {
+		status_ack |= ASPEED_I2CD_INTR_SLAVE_MATCH;
+		bus->slave_state = ASPEED_I2C_SLAVE_START;
+	}
+	/* Slave is not currently active, irq was for someone else. */
+	if (bus->slave_state == ASPEED_I2C_SLAVE_STOP) {
+		irq_handled = false;
+		goto out;
+	}
+
+	dev_dbg(bus->dev, "slave irq status 0x%08x, cmd 0x%08x\n",
+		irq_status, command);
+
+	/* Slave was sent something. */
+	if (irq_status & ASPEED_I2CD_INTR_RX_DONE) {
+		value = aspeed_i2c_read(bus, ASPEED_I2C_BYTE_BUF_REG) >> 8;
+		/* Handle address frame. */
+		if (bus->slave_state == ASPEED_I2C_SLAVE_START) {
+			if (value & 0x1)
+				bus->slave_state =
+						ASPEED_I2C_SLAVE_READ_REQUESTED;
+			else
+				bus->slave_state =
+						ASPEED_I2C_SLAVE_WRITE_REQUESTED;
+		}
+		status_ack |= ASPEED_I2CD_INTR_RX_DONE;
+	}
+
+	/* Slave was asked to stop. */
+	if (irq_status & ASPEED_I2CD_INTR_NORMAL_STOP) {
+		status_ack |= ASPEED_I2CD_INTR_NORMAL_STOP;
+		bus->slave_state = ASPEED_I2C_SLAVE_STOP;
+	}
+	if (irq_status & ASPEED_I2CD_INTR_TX_NAK) {
+		status_ack |= ASPEED_I2CD_INTR_TX_NAK;
+		bus->slave_state = ASPEED_I2C_SLAVE_STOP;
+	}
+
+	if (bus->slave_state == ASPEED_I2C_SLAVE_READ_REQUESTED) {
+		if (irq_status & ASPEED_I2CD_INTR_TX_ACK)
+			dev_err(bus->dev, "Unexpected ACK on read request.\n");
+		bus->slave_state = ASPEED_I2C_SLAVE_READ_PROCESSED;
+
+		i2c_slave_event(slave, I2C_SLAVE_READ_REQUESTED, &value);
+		aspeed_i2c_write(bus, value, ASPEED_I2C_BYTE_BUF_REG);
+		aspeed_i2c_write(bus, ASPEED_I2CD_S_TX_CMD, ASPEED_I2C_CMD_REG);
+	} else if (bus->slave_state == ASPEED_I2C_SLAVE_READ_PROCESSED) {
+		status_ack |= ASPEED_I2CD_INTR_TX_ACK;
+		if (!(irq_status & ASPEED_I2CD_INTR_TX_ACK))
+			dev_err(bus->dev,
+				"Expected ACK after processed read.\n");
+		i2c_slave_event(slave, I2C_SLAVE_READ_PROCESSED, &value);
+		aspeed_i2c_write(bus, value, ASPEED_I2C_BYTE_BUF_REG);
+		aspeed_i2c_write(bus, ASPEED_I2CD_S_TX_CMD, ASPEED_I2C_CMD_REG);
+	} else if (bus->slave_state == ASPEED_I2C_SLAVE_WRITE_REQUESTED) {
+		bus->slave_state = ASPEED_I2C_SLAVE_WRITE_RECEIVED;
+		i2c_slave_event(slave, I2C_SLAVE_WRITE_REQUESTED, &value);
+	} else if (bus->slave_state == ASPEED_I2C_SLAVE_WRITE_RECEIVED) {
+		i2c_slave_event(slave, I2C_SLAVE_WRITE_RECEIVED, &value);
+	} else if (bus->slave_state == ASPEED_I2C_SLAVE_STOP) {
+		i2c_slave_event(slave, I2C_SLAVE_STOP, &value);
+	}
+
+	if (status_ack != irq_status)
+		dev_err(bus->dev,
+			"irq handled != irq. expected %x, but was %x\n",
+			irq_status, status_ack);
+	aspeed_i2c_write(bus, status_ack, ASPEED_I2C_INTR_STS_REG);
+
+out:
+	spin_unlock(&bus->lock);
+	return irq_handled;
+}
+#endif
+
+static bool aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus)
+{
+	const u32 errs = ASPEED_I2CD_INTR_ARBIT_LOSS |
+		ASPEED_I2CD_INTR_ABNORMAL |
+		ASPEED_I2CD_INTR_SCL_TIMEOUT |
+		ASPEED_I2CD_INTR_SDA_DL_TIMEOUT |
+		ASPEED_I2CD_INTR_TX_NAK;
+	u32 irq_status;
+
+	spin_lock(&bus->lock);
+	irq_status = aspeed_i2c_read(bus, ASPEED_I2C_INTR_STS_REG);
+	bus->cmd_err = irq_status & errs;
+
+	dev_dbg(bus->dev, "master irq status 0x%08x\n", irq_status);
+
+	/* No message to transfer. */
+	if (bus->cmd_err ||
+	    (irq_status & ASPEED_I2CD_INTR_NORMAL_STOP) ||
+	    (irq_status & ASPEED_I2CD_INTR_BUS_RECOVER_DONE)) {
+		complete(&bus->cmd_complete);
+		goto out;
+	} else if (!bus->msg || bus->msg_pos >= bus->msg->len)
+		goto out;
+
+	if ((bus->msg->flags & I2C_M_RD) &&
+	    (irq_status & ASPEED_I2CD_INTR_RX_DONE)) {
+		bus->msg->buf[bus->msg_pos++] = aspeed_i2c_read(
+				bus, ASPEED_I2C_BYTE_BUF_REG) >> 8;
+		if (bus->msg_pos + 1 < bus->msg->len)
+			aspeed_i2c_write(bus, ASPEED_I2CD_M_RX_CMD,
+					 ASPEED_I2C_CMD_REG);
+		else if (bus->msg_pos < bus->msg->len)
+			aspeed_i2c_write(bus, ASPEED_I2CD_M_RX_CMD |
+				      ASPEED_I2CD_M_S_RX_CMD_LAST,
+				      ASPEED_I2C_CMD_REG);
+	} else if (!(bus->msg->flags & I2C_M_RD) &&
+		   (irq_status & ASPEED_I2CD_INTR_TX_ACK)) {
+		aspeed_i2c_write(bus, bus->msg->buf[bus->msg_pos++],
+			      ASPEED_I2C_BYTE_BUF_REG);
+		aspeed_i2c_write(bus, ASPEED_I2CD_M_TX_CMD, ASPEED_I2C_CMD_REG);
+	}
+
+	/* Transmission complete: notify caller. */
+	if (bus->msg_pos >= bus->msg->len)
+		complete(&bus->cmd_complete);
+out:
+	aspeed_i2c_write(bus, irq_status, ASPEED_I2C_INTR_STS_REG);
+	spin_unlock(&bus->lock);
+	return true;
+}
+
+static irqreturn_t aspeed_i2c_bus_irq(int irq, void *dev_id)
+{
+	struct aspeed_i2c_bus *bus = dev_id;
+
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+	if (aspeed_i2c_slave_irq(bus)) {
+		dev_dbg(bus->dev, "irq handled by slave.\n");
+		return IRQ_HANDLED;
+	}
+#endif
+	if (aspeed_i2c_master_irq(bus)) {
+		dev_dbg(bus->dev, "irq handled by master.\n");
+		return IRQ_HANDLED;
+	}
+	dev_err(bus->dev, "irq not handled properly!\n");
+	return IRQ_HANDLED;
+}
+
+static int aspeed_i2c_master_single_xfer(struct i2c_adapter *adap,
+				      struct i2c_msg *msg)
+{
+	struct aspeed_i2c_bus *bus = adap->algo_data;
+	unsigned long flags;
+	u8 slave_addr;
+	u32 command = ASPEED_I2CD_M_START_CMD | ASPEED_I2CD_M_TX_CMD;
+	int ret = msg->len;
+	unsigned long time_left;
+
+	spin_lock_irqsave(&bus->lock, flags);
+	bus->msg = msg;
+	bus->msg_pos = 0;
+	slave_addr = msg->addr << 1;
+	if (msg->flags & I2C_M_RD) {
+		slave_addr |= 1;
+		command |= ASPEED_I2CD_M_RX_CMD;
+		if (msg->len == 1)
+			command |= ASPEED_I2CD_M_S_RX_CMD_LAST;
+	}
+	aspeed_i2c_write(bus, slave_addr, ASPEED_I2C_BYTE_BUF_REG);
+	aspeed_i2c_write(bus, command, ASPEED_I2C_CMD_REG);
+	reinit_completion(&bus->cmd_complete);
+	spin_unlock_irqrestore(&bus->lock, flags);
+
+	time_left = wait_for_completion_interruptible_timeout(
+			&bus->cmd_complete, bus->adap.timeout * HZ * msg->len);
+	if (time_left == 0)
+		return -ETIMEDOUT;
+
+	spin_lock_irqsave(&bus->lock, flags);
+	if (bus->cmd_err)
+		ret = -EIO;
+	bus->msg = NULL;
+	spin_unlock_irqrestore(&bus->lock, flags);
+
+	return ret;
+}
+
+static int aspeed_i2c_master_xfer(struct i2c_adapter *adap,
+				  struct i2c_msg *msgs, int num)
+{
+	struct aspeed_i2c_bus *bus = adap->algo_data;
+	int ret;
+	int i;
+	unsigned long flags;
+	unsigned long time_left;
+
+	/* If bus is busy, attempt recovery. We assume a single master
+	 * environment.
+	 */
+	if (aspeed_i2c_read(bus, ASPEED_I2C_CMD_REG) &
+	    ASPEED_I2CD_BUS_BUSY_STS) {
+		ret = aspeed_i2c_recover_bus(bus);
+		if (ret)
+			return ret;
+	}
+
+	for (i = 0; i < num; i++) {
+		ret = aspeed_i2c_master_single_xfer(adap, &msgs[i]);
+		if (ret < 0)
+			break;
+		/* TODO: Support other forms of I2C protocol mangling. */
+		if (msgs[i].flags & I2C_M_STOP) {
+			spin_lock_irqsave(&bus->lock, flags);
+			aspeed_i2c_write(bus, ASPEED_I2CD_M_STOP_CMD,
+				      ASPEED_I2C_CMD_REG);
+			reinit_completion(&bus->cmd_complete);
+			spin_unlock_irqrestore(&bus->lock, flags);
+
+			time_left = wait_for_completion_interruptible_timeout(
+					&bus->cmd_complete,
+					bus->adap.timeout * HZ);
+			if (time_left == 0)
+				return -ETIMEDOUT;
+		}
+	}
+
+	spin_lock_irqsave(&bus->lock, flags);
+	aspeed_i2c_write(bus, ASPEED_I2CD_M_STOP_CMD, ASPEED_I2C_CMD_REG);
+	reinit_completion(&bus->cmd_complete);
+	spin_unlock_irqrestore(&bus->lock, flags);
+
+	time_left = wait_for_completion_interruptible_timeout(
+			&bus->cmd_complete, bus->adap.timeout * HZ);
+	if (time_left == 0)
+		return -ETIMEDOUT;
+
+	/* If nothing went wrong, return number of messages transferred. */
+	if (ret < 0)
+		return ret;
+	else
+		return i;
+}
+
+static u32 aspeed_i2c_functionality(struct i2c_adapter *adap)
+{
+	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_SMBUS_BLOCK_DATA;
+}
+
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+static int aspeed_i2c_reg_slave(struct i2c_client *client)
+{
+	struct aspeed_i2c_bus *bus;
+	unsigned long flags;
+	u32 addr_reg_val;
+	u32 func_ctrl_reg_val;
+
+	bus = client->adapter->algo_data;
+	spin_lock_irqsave(&bus->lock, flags);
+	if (bus->slave) {
+		spin_unlock_irqrestore(&bus->lock, flags);
+		return -EINVAL;
+	}
+
+	/* Set slave addr. */
+	addr_reg_val = aspeed_i2c_read(bus, ASPEED_I2C_DEV_ADDR_REG);
+	addr_reg_val &= ~ASPEED_I2CD_DEV_ADDR_MASK;
+	addr_reg_val |= client->addr & ASPEED_I2CD_DEV_ADDR_MASK;
+	aspeed_i2c_write(bus, addr_reg_val, ASPEED_I2C_DEV_ADDR_REG);
+
+	/* Switch from master mode to slave mode. */
+	func_ctrl_reg_val = aspeed_i2c_read(bus, ASPEED_I2C_FUN_CTRL_REG);
+	func_ctrl_reg_val &= ~ASPEED_I2CD_MASTER_EN;
+	func_ctrl_reg_val |= ASPEED_I2CD_SLAVE_EN;
+	aspeed_i2c_write(bus, func_ctrl_reg_val, ASPEED_I2C_FUN_CTRL_REG);
+
+	bus->slave = client;
+	bus->slave_state = ASPEED_I2C_SLAVE_STOP;
+	spin_unlock_irqrestore(&bus->lock, flags);
+	return 0;
+}
+
+static int aspeed_i2c_unreg_slave(struct i2c_client *client)
+{
+	struct aspeed_i2c_bus *bus = client->adapter->algo_data;
+	unsigned long flags;
+	u32 func_ctrl_reg_val;
+
+	spin_lock_irqsave(&bus->lock, flags);
+	if (!bus->slave) {
+		spin_unlock_irqrestore(&bus->lock, flags);
+		return -EINVAL;
+	}
+
+	/* Switch from slave mode to master mode. */
+	func_ctrl_reg_val = aspeed_i2c_read(bus, ASPEED_I2C_FUN_CTRL_REG);
+	func_ctrl_reg_val &= ~ASPEED_I2CD_SLAVE_EN;
+	func_ctrl_reg_val |= ASPEED_I2CD_MASTER_EN;
+	aspeed_i2c_write(bus, func_ctrl_reg_val, ASPEED_I2C_FUN_CTRL_REG);
+
+	bus->slave = NULL;
+	spin_unlock_irqrestore(&bus->lock, flags);
+	return 0;
+}
+#endif
+
+static const struct i2c_algorithm aspeed_i2c_algo = {
+	.master_xfer	= aspeed_i2c_master_xfer,
+	.functionality	= aspeed_i2c_functionality,
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+	.reg_slave	= aspeed_i2c_reg_slave,
+	.unreg_slave	= aspeed_i2c_unreg_slave,
+#endif
+};
+
+static u32 aspeed_i2c_get_clk_reg_val(u32 divider_ratio)
+{
+	unsigned int inc = 0, div;
+	u32 scl_low, scl_high, data;
+
+	for (div = 0; divider_ratio >= 16; div++) {
+		inc |= (divider_ratio & 1);
+		divider_ratio >>= 1;
+	}
+	divider_ratio += inc;
+	scl_low = (divider_ratio >> 1) - 1;
+	scl_high = divider_ratio - scl_low - 2;
+	data = 0x77700300 | (scl_high << 16) | (scl_low << 12) | div;
+	return data;
+}
+
+static int aspeed_i2c_init_clk(struct aspeed_i2c_bus *bus,
+			    struct platform_device *pdev)
+{
+	struct clk *pclk;
+	u32 clk_freq;
+	u32 divider_ratio;
+	int ret;
+
+	pclk = devm_clk_get(&pdev->dev, NULL);
+	if (IS_ERR(pclk)) {
+		dev_err(&pdev->dev, "clk_get failed\n");
+		return PTR_ERR(pclk);
+	}
+	ret = of_property_read_u32(pdev->dev.of_node,
+			"clock-frequency", &clk_freq);
+	if (ret < 0) {
+		dev_err(&pdev->dev,
+				"Could not read clock-frequency property\n");
+		clk_freq = 100000;
+	}
+	divider_ratio = clk_get_rate(pclk) / clk_freq;
+	/* We just need the clock rate, we don't actually use the clk object. */
+	devm_clk_put(&pdev->dev, pclk);
+
+	/* Set AC Timing */
+	if (clk_freq / 1000 > 400) {
+		aspeed_i2c_write(bus, aspeed_i2c_read(bus,
+						      ASPEED_I2C_FUN_CTRL_REG) |
+				ASPEED_I2CD_M_HIGH_SPEED_EN |
+				ASPEED_I2CD_M_SDA_DRIVE_1T_EN |
+				ASPEED_I2CD_SDA_DRIVE_1T_EN,
+				ASPEED_I2C_FUN_CTRL_REG);
+
+		aspeed_i2c_write(bus, 0x3, ASPEED_I2C_AC_TIMING_REG2);
+		aspeed_i2c_write(bus, aspeed_i2c_get_clk_reg_val(divider_ratio),
+			      ASPEED_I2C_AC_TIMING_REG1);
+	} else {
+		aspeed_i2c_write(bus, aspeed_i2c_get_clk_reg_val(divider_ratio),
+			      ASPEED_I2C_AC_TIMING_REG1);
+		aspeed_i2c_write(bus, ASPEED_NO_TIMEOUT_CTRL,
+				 ASPEED_I2C_AC_TIMING_REG2);
+	}
+
+	return 0;
+}
+
+static void noop(struct irq_data *data) { }
+
+static struct irq_chip aspeed_i2c_irqchip = {
+	.name		= "ast-i2c",
+	.irq_unmask	= noop,
+	.irq_mask	= noop,
+};
+
+static int aspeed_i2c_probe_bus(struct platform_device *pdev)
+{
+	struct aspeed_i2c_bus *bus;
+	struct aspeed_i2c_controller *controller =
+			dev_get_drvdata(pdev->dev.parent);
+	struct resource *res;
+	int ret, bus_num, irq;
+
+	bus = devm_kzalloc(&pdev->dev, sizeof(*bus), GFP_KERNEL);
+	if (!bus)
+		return -ENOMEM;
+
+	ret = of_property_read_u32(pdev->dev.of_node, "bus", &bus_num);
+	if (ret)
+		return -ENXIO;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	bus->base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(bus->base))
+		return PTR_ERR(bus->base);
+
+	bus->irq = platform_get_irq(pdev, 0);
+	if (bus->irq < 0) {
+		dev_err(&pdev->dev, "platform_get_irq failed\n");
+		return -ENXIO;
+	}
+
+	irq = irq_create_mapping(controller->irq_domain, bus_num);
+	irq_set_chip_data(irq, controller);
+	irq_set_chip_and_handler(irq, &aspeed_i2c_irqchip, handle_simple_irq);
+	ret = devm_request_irq(&pdev->dev, bus->irq, aspeed_i2c_bus_irq,
+			0, dev_name(&pdev->dev), bus);
+	if (ret) {
+		dev_err(&pdev->dev, "devm_request_irq failed\n");
+		return -ENXIO;
+	}
+
+	/* Initialize the I2C adapter */
+	spin_lock_init(&bus->lock);
+	init_completion(&bus->cmd_complete);
+	bus->adap.nr = bus_num;
+	bus->adap.owner = THIS_MODULE;
+	bus->adap.retries = 0;
+	bus->adap.timeout = 5;
+	bus->adap.algo = &aspeed_i2c_algo;
+	bus->adap.algo_data = bus;
+	bus->adap.dev.parent = &pdev->dev;
+	bus->adap.dev.of_node = pdev->dev.of_node;
+	snprintf(bus->adap.name, sizeof(bus->adap.name), "Aspeed i2c-%d",
+			bus_num);
+
+	bus->dev = &pdev->dev;
+
+	/* reset device: disable master & slave functions */
+	aspeed_i2c_write(bus, 0, ASPEED_I2C_FUN_CTRL_REG);
+
+	ret = aspeed_i2c_init_clk(bus, pdev);
+	if (ret < 0)
+		return ret;
+
+	/* Enable Master Mode */
+	aspeed_i2c_write(bus, aspeed_i2c_read(bus, ASPEED_I2C_FUN_CTRL_REG) |
+		      ASPEED_I2CD_MASTER_EN |
+		      ASPEED_I2CD_MULTI_MASTER_DIS, ASPEED_I2C_FUN_CTRL_REG);
+
+	/* Set interrupt generation of I2C controller */
+	aspeed_i2c_write(bus, ASPEED_I2CD_INTR_SDA_DL_TIMEOUT |
+			ASPEED_I2CD_INTR_BUS_RECOVER_DONE |
+			ASPEED_I2CD_INTR_SCL_TIMEOUT |
+			ASPEED_I2CD_INTR_ABNORMAL |
+			ASPEED_I2CD_INTR_NORMAL_STOP |
+			ASPEED_I2CD_INTR_ARBIT_LOSS |
+			ASPEED_I2CD_INTR_RX_DONE |
+			ASPEED_I2CD_INTR_TX_NAK |
+			ASPEED_I2CD_INTR_TX_ACK,
+			ASPEED_I2C_INTR_CTRL_REG);
+
+	ret = i2c_add_numbered_adapter(&bus->adap);
+	if (ret < 0)
+		return -ENXIO;
+
+	platform_set_drvdata(pdev, bus);
+
+	dev_info(bus->dev, "i2c bus %d registered, irq %d\n",
+			bus->adap.nr, bus->irq);
+
+	return 0;
+}
+
+static int aspeed_i2c_remove_bus(struct platform_device *pdev)
+{
+	struct aspeed_i2c_bus *bus = platform_get_drvdata(pdev);
+
+	i2c_del_adapter(&bus->adap);
+	return 0;
+}
+
+static const struct of_device_id aspeed_i2c_bus_of_table[] = {
+	{ .compatible = "aspeed,ast2400-i2c-bus", },
+	{ .compatible = "aspeed,ast2500-i2c-bus", },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, aspeed_i2c_of_table);
+
+static struct platform_driver aspeed_i2c_bus_driver = {
+	.probe		= aspeed_i2c_probe_bus,
+	.remove		= aspeed_i2c_remove_bus,
+	.driver         = {
+		.name   = "ast-i2c-bus",
+		.of_match_table = aspeed_i2c_bus_of_table,
+	},
+};
+module_platform_driver(aspeed_i2c_bus_driver);
+
+static void aspeed_i2c_controller_irq(struct irq_desc *desc)
+{
+	struct aspeed_i2c_controller *c = irq_desc_get_handler_data(desc);
+	unsigned long p, status;
+	unsigned int bus_irq;
+
+	status = readl(c->base);
+	for_each_set_bit(p, &status, ASPEED_I2C_NUM_BUS) {
+		bus_irq = irq_find_mapping(c->irq_domain, p);
+		generic_handle_irq(bus_irq);
+	}
+}
+
+static int aspeed_i2c_probe_controller(struct platform_device *pdev)
+{
+	struct aspeed_i2c_controller *controller;
+	struct device_node *np;
+	struct resource *res;
+
+	controller = kzalloc(sizeof(*controller), GFP_KERNEL);
+	if (!controller)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	controller->base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(controller->base))
+		return PTR_ERR(controller->base);
+
+	controller->irq = platform_get_irq(pdev, 0);
+	if (controller->irq < 0) {
+		dev_err(&pdev->dev, "no platform IRQ\n");
+		return -ENXIO;
+	}
+
+	controller->irq_domain = irq_domain_add_linear(pdev->dev.of_node,
+			ASPEED_I2C_NUM_BUS, &irq_domain_simple_ops, NULL);
+	if (!controller->irq_domain) {
+		dev_err(&pdev->dev, "no IRQ domain\n");
+		return -ENXIO;
+	}
+	controller->irq_domain->name = "ast-i2c-domain";
+
+	irq_set_chained_handler_and_data(controller->irq,
+			aspeed_i2c_controller_irq, controller);
+
+	controller->dev = &pdev->dev;
+
+	platform_set_drvdata(pdev, controller);
+
+	dev_info(controller->dev, "i2c controller registered, irq %d\n",
+			controller->irq);
+
+	for_each_child_of_node(pdev->dev.of_node, np) {
+		int ret;
+		u32 bus_num;
+		char bus_id[sizeof("i2c-12345")];
+
+		/*
+		 * Set a useful name derived from the bus number; the device
+		 * tree should provide us with one that corresponds to the
+		 * hardware numbering.  If the property is missing the
+		 * probe would fail so just skip it here.
+		 */
+
+		ret = of_property_read_u32(np, "bus", &bus_num);
+		if (ret)
+			continue;
+
+		ret = snprintf(bus_id, sizeof(bus_id), "i2c-%u", bus_num);
+		if (ret >= sizeof(bus_id))
+			continue;
+
+		of_platform_device_create(np, bus_id, &pdev->dev);
+		of_node_put(np);
+	}
+
+	return 0;
+}
+
+static int aspeed_i2c_remove_controller(struct platform_device *pdev)
+{
+	struct aspeed_i2c_controller *controller = platform_get_drvdata(pdev);
+
+	irq_domain_remove(controller->irq_domain);
+	return 0;
+}
+
+static const struct of_device_id aspeed_i2c_controller_of_table[] = {
+	{ .compatible = "aspeed,ast2400-i2c-controller", },
+	{ .compatible = "aspeed,ast2500-i2c-controller", },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, aspeed_i2c_of_table);
+
+static struct platform_driver aspeed_i2c_controller_driver = {
+	.probe		= aspeed_i2c_probe_controller,
+	.remove		= aspeed_i2c_remove_controller,
+	.driver         = {
+		.name   = "ast-i2c-controller",
+		.of_match_table = aspeed_i2c_controller_of_table,
+	},
+};
+
+module_platform_driver(aspeed_i2c_controller_driver);
+
+MODULE_AUTHOR("Brendan Higgins <brendanhiggins@google.com>");
+MODULE_DESCRIPTION("Aspeed I2C Bus Driver");
+MODULE_LICENSE("GPL");
-- 
2.8.0.rc3.226.g39d4020

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH 2/2] i2c: aspeed: added documentation for Aspeed I2C driver
  2016-09-09 19:54 [PATCH 0/2] i2c: aspeed: added driver for Aspeed I2C Brendan Higgins
  2016-09-09 19:54 ` [PATCH 1/2] " Brendan Higgins
@ 2016-09-09 19:54 ` Brendan Higgins
  1 sibling, 0 replies; 17+ messages in thread
From: Brendan Higgins @ 2016-09-09 19:54 UTC (permalink / raw)
  To: wsa, robh+dt, mark.rutland
  Cc: linux-i2c, devicetree, linux-kernel, openbmc, joel, jk, Brendan Higgins

Added device tree binding documentation for Aspeed I2C controller and
busses.

Signed-off-by: Brendan Higgins <brendanhiggins@google.com>
---
 .../devicetree/bindings/i2c/i2c-aspeed.txt         | 63 ++++++++++++++++++++++
 1 file changed, 63 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/i2c/i2c-aspeed.txt

diff --git a/Documentation/devicetree/bindings/i2c/i2c-aspeed.txt b/Documentation/devicetree/bindings/i2c/i2c-aspeed.txt
new file mode 100644
index 0000000..df68f2a
--- /dev/null
+++ b/Documentation/devicetree/bindings/i2c/i2c-aspeed.txt
@@ -0,0 +1,63 @@
+Device tree configuration for the I2C controller and busses on the AST24XX
+and AST25XX SoCs.
+
+Controller:
+
+	Required Properties:
+	- #address-cells	: should be 1
+	- #size-cells 		: should be 1
+	- #interrupt-cells 	: should be 1
+	- compatible 		: should be "aspeed,ast2400-i2c-controller"
+				  or "aspeed,ast2500-i2c-controller"
+	- reg			: address start and range of controller
+	- ranges		: defines address offset and range for busses
+	- interrupts		: interrupt number
+	- clocks		: root clock of bus, should reference the APB
+				  clock
+	- clock-ranges		: specifies that child busses can inherit clocks
+	- interrupt-controller	: denotes that the controller receives and fires
+				  new interrupts for child busses
+
+Bus:
+
+	Required Properties:
+	- #address-cells	: should be 1
+	- #size-cells		: should be 0
+	- reg			: address offset and range of bus
+	- compatible		: should be "aspeed,ast2400-i2c-bus"
+				  or "aspeed,ast2500-i2c-bus"
+	- bus			: the bus's number
+	- interrupts		: interrupt number
+
+	Optional Properties:
+	- clock-frequency	: frequency of the bus clock in Hz
+				  defaults to 100 kHz when not specified
+
+Example:
+
+i2c: i2c@1e78a000 {
+	#address-cells = <1>;
+	#size-cells = <1>;
+	#interrupt-cells = <1>;
+
+	compatible = "aspeed,ast2400-i2c-controller";
+	reg = <0x1e78a000 0x40>;
+	ranges = <0 0x1e78a000 0x1000>;
+	interrupts = <12>;
+	clocks = <&clk_apb>;
+	clock-ranges;
+	interrupt-controller;
+
+	i2c0: i2c-bus@40 {
+		#address-cells = <1>;
+		#size-cells = <0>;
+		reg = <0x40 0x40>;
+		compatible = "aspeed,ast2400-i2c-bus";
+		bus = <0>;
+		clock-frequency = <100000>;
+		status = "disabled";
+		interrupts = <0>;
+		interrupt-parent = <&i2c>;
+	};
+};
+
-- 
2.8.0.rc3.226.g39d4020

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* Re: [PATCH 1/2] i2c: aspeed: added driver for Aspeed I2C
  2016-09-09 19:54 ` [PATCH 1/2] " Brendan Higgins
@ 2016-09-09 22:48   ` kbuild test robot
  2016-09-10  0:48   ` kbuild test robot
  1 sibling, 0 replies; 17+ messages in thread
From: kbuild test robot @ 2016-09-09 22:48 UTC (permalink / raw)
  To: Brendan Higgins
  Cc: kbuild-all, wsa, robh+dt, mark.rutland, linux-i2c, devicetree,
	linux-kernel, openbmc, joel, jk, Brendan Higgins

[-- Attachment #1: Type: text/plain, Size: 11516 bytes --]

Hi Brendan,

[auto build test ERROR on wsa/i2c/for-next]
[also build test ERROR on v4.8-rc5 next-20160909]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
[Suggest to use git(>=2.9.0) format-patch --base=<commit> (or --base=auto for convenience) to record what (public, well-known) commit your patch series was built on]
[Check https://git-scm.com/docs/git-format-patch for more information]

url:    https://github.com/0day-ci/linux/commits/Brendan-Higgins/i2c-aspeed-added-driver-for-Aspeed-I2C/20160910-035912
base:   https://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux.git i2c/for-next
config: x86_64-allmodconfig (attached as .config)
compiler: gcc-6 (Debian 6.1.1-9) 6.1.1 20160705
reproduce:
        # save the attached .config to linux build tree
        make ARCH=x86_64 

All error/warnings (new ones prefixed by >>):

   In file included from drivers/i2c/busses/i2c-aspeed.c:14:0:
>> drivers/i2c/busses/i2c-aspeed.c:697:25: error: 'aspeed_i2c_of_table' undeclared here (not in a function)
    MODULE_DEVICE_TABLE(of, aspeed_i2c_of_table);
                            ^
   include/linux/module.h:213:21: note: in definition of macro 'MODULE_DEVICE_TABLE'
    extern const typeof(name) __mod_##type##__##name##_device_table  \
                        ^~~~
>> include/linux/module.h:130:27: error: redefinition of '__inittest'
     static inline initcall_t __inittest(void)  \
                              ^
   include/linux/device.h:1350:1: note: in expansion of macro 'module_init'
    module_init(__driver##_init); \
    ^~~~~~~~~~~
>> include/linux/platform_device.h:228:2: note: in expansion of macro 'module_driver'
     module_driver(__platform_driver, platform_driver_register, \
     ^~~~~~~~~~~~~
>> drivers/i2c/busses/i2c-aspeed.c:812:1: note: in expansion of macro 'module_platform_driver'
    module_platform_driver(aspeed_i2c_controller_driver);
    ^~~~~~~~~~~~~~~~~~~~~~
   include/linux/module.h:130:27: note: previous definition of '__inittest' was here
     static inline initcall_t __inittest(void)  \
                              ^
   include/linux/device.h:1350:1: note: in expansion of macro 'module_init'
    module_init(__driver##_init); \
    ^~~~~~~~~~~
>> include/linux/platform_device.h:228:2: note: in expansion of macro 'module_driver'
     module_driver(__platform_driver, platform_driver_register, \
     ^~~~~~~~~~~~~
   drivers/i2c/busses/i2c-aspeed.c:707:1: note: in expansion of macro 'module_platform_driver'
    module_platform_driver(aspeed_i2c_bus_driver);
    ^~~~~~~~~~~~~~~~~~~~~~
>> include/linux/module.h:132:6: error: redefinition of 'init_module'
     int init_module(void) __attribute__((alias(#initfn)));
         ^
   include/linux/device.h:1350:1: note: in expansion of macro 'module_init'
    module_init(__driver##_init); \
    ^~~~~~~~~~~
>> include/linux/platform_device.h:228:2: note: in expansion of macro 'module_driver'
     module_driver(__platform_driver, platform_driver_register, \
     ^~~~~~~~~~~~~
>> drivers/i2c/busses/i2c-aspeed.c:812:1: note: in expansion of macro 'module_platform_driver'
    module_platform_driver(aspeed_i2c_controller_driver);
    ^~~~~~~~~~~~~~~~~~~~~~
   include/linux/module.h:132:6: note: previous definition of 'init_module' was here
     int init_module(void) __attribute__((alias(#initfn)));
         ^
   include/linux/device.h:1350:1: note: in expansion of macro 'module_init'
    module_init(__driver##_init); \
    ^~~~~~~~~~~
>> include/linux/platform_device.h:228:2: note: in expansion of macro 'module_driver'
     module_driver(__platform_driver, platform_driver_register, \
     ^~~~~~~~~~~~~
   drivers/i2c/busses/i2c-aspeed.c:707:1: note: in expansion of macro 'module_platform_driver'
    module_platform_driver(aspeed_i2c_bus_driver);
    ^~~~~~~~~~~~~~~~~~~~~~
>> include/linux/module.h:136:27: error: redefinition of '__exittest'
     static inline exitcall_t __exittest(void)  \
                              ^
   include/linux/device.h:1355:1: note: in expansion of macro 'module_exit'
    module_exit(__driver##_exit);
    ^~~~~~~~~~~
>> include/linux/platform_device.h:228:2: note: in expansion of macro 'module_driver'
     module_driver(__platform_driver, platform_driver_register, \
     ^~~~~~~~~~~~~
>> drivers/i2c/busses/i2c-aspeed.c:812:1: note: in expansion of macro 'module_platform_driver'
    module_platform_driver(aspeed_i2c_controller_driver);
    ^~~~~~~~~~~~~~~~~~~~~~
   include/linux/module.h:136:27: note: previous definition of '__exittest' was here
     static inline exitcall_t __exittest(void)  \
                              ^
   include/linux/device.h:1355:1: note: in expansion of macro 'module_exit'
    module_exit(__driver##_exit);
    ^~~~~~~~~~~
>> include/linux/platform_device.h:228:2: note: in expansion of macro 'module_driver'
     module_driver(__platform_driver, platform_driver_register, \
     ^~~~~~~~~~~~~
   drivers/i2c/busses/i2c-aspeed.c:707:1: note: in expansion of macro 'module_platform_driver'
    module_platform_driver(aspeed_i2c_bus_driver);
    ^~~~~~~~~~~~~~~~~~~~~~
>> include/linux/module.h:138:7: error: redefinition of 'cleanup_module'
     void cleanup_module(void) __attribute__((alias(#exitfn)));
          ^
   include/linux/device.h:1355:1: note: in expansion of macro 'module_exit'
    module_exit(__driver##_exit);
    ^~~~~~~~~~~
>> include/linux/platform_device.h:228:2: note: in expansion of macro 'module_driver'
     module_driver(__platform_driver, platform_driver_register, \
     ^~~~~~~~~~~~~
>> drivers/i2c/busses/i2c-aspeed.c:812:1: note: in expansion of macro 'module_platform_driver'
    module_platform_driver(aspeed_i2c_controller_driver);
    ^~~~~~~~~~~~~~~~~~~~~~
   include/linux/module.h:138:7: note: previous definition of 'cleanup_module' was here
     void cleanup_module(void) __attribute__((alias(#exitfn)));
          ^
   include/linux/device.h:1355:1: note: in expansion of macro 'module_exit'
    module_exit(__driver##_exit);
    ^~~~~~~~~~~
>> include/linux/platform_device.h:228:2: note: in expansion of macro 'module_driver'
     module_driver(__platform_driver, platform_driver_register, \
     ^~~~~~~~~~~~~
   drivers/i2c/busses/i2c-aspeed.c:707:1: note: in expansion of macro 'module_platform_driver'
    module_platform_driver(aspeed_i2c_bus_driver);
    ^~~~~~~~~~~~~~~~~~~~~~
>> include/linux/module.h:213:27: error: '__mod_of__aspeed_i2c_of_table_device_table' aliased to undefined symbol 'aspeed_i2c_of_table'
    extern const typeof(name) __mod_##type##__##name##_device_table  \
                              ^
>> drivers/i2c/busses/i2c-aspeed.c:801:1: note: in expansion of macro 'MODULE_DEVICE_TABLE'
    MODULE_DEVICE_TABLE(of, aspeed_i2c_of_table);
    ^~~~~~~~~~~~~~~~~~~
>> include/linux/module.h:213:27: error: '__mod_of__aspeed_i2c_of_table_device_table' aliased to undefined symbol 'aspeed_i2c_of_table'
    extern const typeof(name) __mod_##type##__##name##_device_table  \
                              ^

vim +/aspeed_i2c_of_table +697 drivers/i2c/busses/i2c-aspeed.c

   691	
   692	static const struct of_device_id aspeed_i2c_bus_of_table[] = {
   693		{ .compatible = "aspeed,ast2400-i2c-bus", },
   694		{ .compatible = "aspeed,ast2500-i2c-bus", },
   695		{ },
   696	};
 > 697	MODULE_DEVICE_TABLE(of, aspeed_i2c_of_table);
   698	
   699	static struct platform_driver aspeed_i2c_bus_driver = {
   700		.probe		= aspeed_i2c_probe_bus,
   701		.remove		= aspeed_i2c_remove_bus,
   702		.driver         = {
   703			.name   = "ast-i2c-bus",
   704			.of_match_table = aspeed_i2c_bus_of_table,
   705		},
   706	};
 > 707	module_platform_driver(aspeed_i2c_bus_driver);
   708	
   709	static void aspeed_i2c_controller_irq(struct irq_desc *desc)
   710	{
   711		struct aspeed_i2c_controller *c = irq_desc_get_handler_data(desc);
   712		unsigned long p, status;
   713		unsigned int bus_irq;
   714	
   715		status = readl(c->base);
   716		for_each_set_bit(p, &status, ASPEED_I2C_NUM_BUS) {
   717			bus_irq = irq_find_mapping(c->irq_domain, p);
   718			generic_handle_irq(bus_irq);
   719		}
   720	}
   721	
   722	static int aspeed_i2c_probe_controller(struct platform_device *pdev)
   723	{
   724		struct aspeed_i2c_controller *controller;
   725		struct device_node *np;
   726		struct resource *res;
   727	
   728		controller = kzalloc(sizeof(*controller), GFP_KERNEL);
   729		if (!controller)
   730			return -ENOMEM;
   731	
   732		res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
   733		controller->base = devm_ioremap_resource(&pdev->dev, res);
   734		if (IS_ERR(controller->base))
   735			return PTR_ERR(controller->base);
   736	
   737		controller->irq = platform_get_irq(pdev, 0);
   738		if (controller->irq < 0) {
   739			dev_err(&pdev->dev, "no platform IRQ\n");
   740			return -ENXIO;
   741		}
   742	
   743		controller->irq_domain = irq_domain_add_linear(pdev->dev.of_node,
   744				ASPEED_I2C_NUM_BUS, &irq_domain_simple_ops, NULL);
   745		if (!controller->irq_domain) {
   746			dev_err(&pdev->dev, "no IRQ domain\n");
   747			return -ENXIO;
   748		}
   749		controller->irq_domain->name = "ast-i2c-domain";
   750	
   751		irq_set_chained_handler_and_data(controller->irq,
   752				aspeed_i2c_controller_irq, controller);
   753	
   754		controller->dev = &pdev->dev;
   755	
   756		platform_set_drvdata(pdev, controller);
   757	
   758		dev_info(controller->dev, "i2c controller registered, irq %d\n",
   759				controller->irq);
   760	
   761		for_each_child_of_node(pdev->dev.of_node, np) {
   762			int ret;
   763			u32 bus_num;
   764			char bus_id[sizeof("i2c-12345")];
   765	
   766			/*
   767			 * Set a useful name derived from the bus number; the device
   768			 * tree should provide us with one that corresponds to the
   769			 * hardware numbering.  If the property is missing the
   770			 * probe would fail so just skip it here.
   771			 */
   772	
   773			ret = of_property_read_u32(np, "bus", &bus_num);
   774			if (ret)
   775				continue;
   776	
   777			ret = snprintf(bus_id, sizeof(bus_id), "i2c-%u", bus_num);
   778			if (ret >= sizeof(bus_id))
   779				continue;
   780	
   781			of_platform_device_create(np, bus_id, &pdev->dev);
   782			of_node_put(np);
   783		}
   784	
   785		return 0;
   786	}
   787	
   788	static int aspeed_i2c_remove_controller(struct platform_device *pdev)
   789	{
   790		struct aspeed_i2c_controller *controller = platform_get_drvdata(pdev);
   791	
   792		irq_domain_remove(controller->irq_domain);
   793		return 0;
   794	}
   795	
   796	static const struct of_device_id aspeed_i2c_controller_of_table[] = {
   797		{ .compatible = "aspeed,ast2400-i2c-controller", },
   798		{ .compatible = "aspeed,ast2500-i2c-controller", },
   799		{ },
   800	};
 > 801	MODULE_DEVICE_TABLE(of, aspeed_i2c_of_table);
   802	
   803	static struct platform_driver aspeed_i2c_controller_driver = {
   804		.probe		= aspeed_i2c_probe_controller,
   805		.remove		= aspeed_i2c_remove_controller,
   806		.driver         = {
   807			.name   = "ast-i2c-controller",
   808			.of_match_table = aspeed_i2c_controller_of_table,
   809		},
   810	};
   811	
 > 812	module_platform_driver(aspeed_i2c_controller_driver);
   813	
   814	MODULE_AUTHOR("Brendan Higgins <brendanhiggins@google.com>");
   815	MODULE_DESCRIPTION("Aspeed I2C Bus Driver");

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

[-- Attachment #2: .config.gz --]
[-- Type: application/octet-stream, Size: 55598 bytes --]

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH 1/2] i2c: aspeed: added driver for Aspeed I2C
  2016-09-09 19:54 ` [PATCH 1/2] " Brendan Higgins
  2016-09-09 22:48   ` kbuild test robot
@ 2016-09-10  0:48   ` kbuild test robot
  2016-09-10  1:55     ` [PATCH v2 " Brendan Higgins
  1 sibling, 1 reply; 17+ messages in thread
From: kbuild test robot @ 2016-09-10  0:48 UTC (permalink / raw)
  To: Brendan Higgins
  Cc: kbuild-all, wsa, robh+dt, mark.rutland, linux-i2c, devicetree,
	linux-kernel, openbmc, joel, jk, Brendan Higgins

[-- Attachment #1: Type: text/plain, Size: 6617 bytes --]

Hi Brendan,

[auto build test ERROR on wsa/i2c/for-next]
[also build test ERROR on v4.8-rc5 next-20160909]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
[Suggest to use git(>=2.9.0) format-patch --base=<commit> (or --base=auto for convenience) to record what (public, well-known) commit your patch series was built on]
[Check https://git-scm.com/docs/git-format-patch for more information]

url:    https://github.com/0day-ci/linux/commits/Brendan-Higgins/i2c-aspeed-added-driver-for-Aspeed-I2C/20160910-035912
base:   https://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux.git i2c/for-next
config: blackfin-allmodconfig (attached as .config)
compiler: bfin-uclinux-gcc (GCC) 4.6.3
reproduce:
        wget https://git.kernel.org/cgit/linux/kernel/git/wfg/lkp-tests.git/plain/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # save the attached .config to linux build tree
        make.cross ARCH=blackfin 

All errors (new ones prefixed by >>):

   drivers/i2c/busses/i2c-aspeed.c:697:1: error: 'aspeed_i2c_of_table' undeclared here (not in a function)
>> drivers/i2c/busses/i2c-aspeed.c:812:1: error: redefinition of '__inittest'
   drivers/i2c/busses/i2c-aspeed.c:707:1: note: previous definition of '__inittest' was here
>> drivers/i2c/busses/i2c-aspeed.c:812:1: error: redefinition of 'init_module'
   drivers/i2c/busses/i2c-aspeed.c:707:1: note: previous definition of 'init_module' was here
>> drivers/i2c/busses/i2c-aspeed.c:812:1: error: redefinition of '__exittest'
   drivers/i2c/busses/i2c-aspeed.c:707:1: note: previous definition of '__exittest' was here
>> drivers/i2c/busses/i2c-aspeed.c:812:1: error: redefinition of 'cleanup_module'
   drivers/i2c/busses/i2c-aspeed.c:707:1: note: previous definition of 'cleanup_module' was here
>> drivers/i2c/busses/i2c-aspeed.c:801:1: error: '__mod_of__aspeed_i2c_of_table_device_table' aliased to undefined symbol 'aspeed_i2c_of_table'
>> drivers/i2c/busses/i2c-aspeed.c:801:1: error: '__mod_of__aspeed_i2c_of_table_device_table' aliased to undefined symbol 'aspeed_i2c_of_table'

vim +/__inittest +812 drivers/i2c/busses/i2c-aspeed.c

   691	
   692	static const struct of_device_id aspeed_i2c_bus_of_table[] = {
   693		{ .compatible = "aspeed,ast2400-i2c-bus", },
   694		{ .compatible = "aspeed,ast2500-i2c-bus", },
   695		{ },
   696	};
 > 697	MODULE_DEVICE_TABLE(of, aspeed_i2c_of_table);
   698	
   699	static struct platform_driver aspeed_i2c_bus_driver = {
   700		.probe		= aspeed_i2c_probe_bus,
   701		.remove		= aspeed_i2c_remove_bus,
   702		.driver         = {
   703			.name   = "ast-i2c-bus",
   704			.of_match_table = aspeed_i2c_bus_of_table,
   705		},
   706	};
   707	module_platform_driver(aspeed_i2c_bus_driver);
   708	
   709	static void aspeed_i2c_controller_irq(struct irq_desc *desc)
   710	{
   711		struct aspeed_i2c_controller *c = irq_desc_get_handler_data(desc);
   712		unsigned long p, status;
   713		unsigned int bus_irq;
   714	
   715		status = readl(c->base);
   716		for_each_set_bit(p, &status, ASPEED_I2C_NUM_BUS) {
   717			bus_irq = irq_find_mapping(c->irq_domain, p);
   718			generic_handle_irq(bus_irq);
   719		}
   720	}
   721	
   722	static int aspeed_i2c_probe_controller(struct platform_device *pdev)
   723	{
   724		struct aspeed_i2c_controller *controller;
   725		struct device_node *np;
   726		struct resource *res;
   727	
   728		controller = kzalloc(sizeof(*controller), GFP_KERNEL);
   729		if (!controller)
   730			return -ENOMEM;
   731	
   732		res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
   733		controller->base = devm_ioremap_resource(&pdev->dev, res);
   734		if (IS_ERR(controller->base))
   735			return PTR_ERR(controller->base);
   736	
   737		controller->irq = platform_get_irq(pdev, 0);
   738		if (controller->irq < 0) {
   739			dev_err(&pdev->dev, "no platform IRQ\n");
   740			return -ENXIO;
   741		}
   742	
   743		controller->irq_domain = irq_domain_add_linear(pdev->dev.of_node,
   744				ASPEED_I2C_NUM_BUS, &irq_domain_simple_ops, NULL);
   745		if (!controller->irq_domain) {
   746			dev_err(&pdev->dev, "no IRQ domain\n");
   747			return -ENXIO;
   748		}
   749		controller->irq_domain->name = "ast-i2c-domain";
   750	
   751		irq_set_chained_handler_and_data(controller->irq,
   752				aspeed_i2c_controller_irq, controller);
   753	
   754		controller->dev = &pdev->dev;
   755	
   756		platform_set_drvdata(pdev, controller);
   757	
   758		dev_info(controller->dev, "i2c controller registered, irq %d\n",
   759				controller->irq);
   760	
   761		for_each_child_of_node(pdev->dev.of_node, np) {
   762			int ret;
   763			u32 bus_num;
   764			char bus_id[sizeof("i2c-12345")];
   765	
   766			/*
   767			 * Set a useful name derived from the bus number; the device
   768			 * tree should provide us with one that corresponds to the
   769			 * hardware numbering.  If the property is missing the
   770			 * probe would fail so just skip it here.
   771			 */
   772	
   773			ret = of_property_read_u32(np, "bus", &bus_num);
   774			if (ret)
   775				continue;
   776	
   777			ret = snprintf(bus_id, sizeof(bus_id), "i2c-%u", bus_num);
   778			if (ret >= sizeof(bus_id))
   779				continue;
   780	
   781			of_platform_device_create(np, bus_id, &pdev->dev);
   782			of_node_put(np);
   783		}
   784	
   785		return 0;
   786	}
   787	
   788	static int aspeed_i2c_remove_controller(struct platform_device *pdev)
   789	{
   790		struct aspeed_i2c_controller *controller = platform_get_drvdata(pdev);
   791	
   792		irq_domain_remove(controller->irq_domain);
   793		return 0;
   794	}
   795	
   796	static const struct of_device_id aspeed_i2c_controller_of_table[] = {
   797		{ .compatible = "aspeed,ast2400-i2c-controller", },
   798		{ .compatible = "aspeed,ast2500-i2c-controller", },
   799		{ },
   800	};
 > 801	MODULE_DEVICE_TABLE(of, aspeed_i2c_of_table);
   802	
   803	static struct platform_driver aspeed_i2c_controller_driver = {
   804		.probe		= aspeed_i2c_probe_controller,
   805		.remove		= aspeed_i2c_remove_controller,
   806		.driver         = {
   807			.name   = "ast-i2c-controller",
   808			.of_match_table = aspeed_i2c_controller_of_table,
   809		},
   810	};
   811	
 > 812	module_platform_driver(aspeed_i2c_controller_driver);
   813	
   814	MODULE_AUTHOR("Brendan Higgins <brendanhiggins@google.com>");
   815	MODULE_DESCRIPTION("Aspeed I2C Bus Driver");

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

[-- Attachment #2: .config.gz --]
[-- Type: application/octet-stream, Size: 41250 bytes --]

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [PATCH v2 1/2] i2c: aspeed: added driver for Aspeed I2C
  2016-09-10  0:48   ` kbuild test robot
@ 2016-09-10  1:55     ` Brendan Higgins
  2016-09-10  1:55       ` [PATCH v2 2/2] i2c: aspeed: added documentation for Aspeed I2C driver Brendan Higgins
  2016-09-19 17:56       ` [PATCH v2 1/2] i2c: aspeed: added driver for Aspeed I2C Brendan Higgins
  0 siblings, 2 replies; 17+ messages in thread
From: Brendan Higgins @ 2016-09-10  1:55 UTC (permalink / raw)
  To: wsa, robh+dt, mark.rutland
  Cc: linux-i2c, devicetree, linux-kernel, openbmc, joel, jk, Brendan Higgins

Added initial master and slave support for Aspeed I2C controller.
Supports fourteen busses present in ast24xx and ast25xx BMC SoCs by
Aspeed.

Signed-off-by: Brendan Higgins <brendanhiggins@google.com>
---
Changes for v2:
  - Added single module_init (multiple was breaking some builds).
---
 drivers/i2c/busses/Kconfig      |  10 +
 drivers/i2c/busses/Makefile     |   1 +
 drivers/i2c/busses/i2c-aspeed.c | 831 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 842 insertions(+)
 create mode 100644 drivers/i2c/busses/i2c-aspeed.c

diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index 5c3993b..0178c6c 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -998,6 +998,16 @@ config I2C_RCAR
 	  This driver can also be built as a module.  If so, the module
 	  will be called i2c-rcar.
 
+config I2C_ASPEED
+	tristate "Aspeed AST2xxx SoC I2C Controller"
+	depends on (ARCH_ASPEED || COMPILE_TEST) && OF
+	help
+	  If you say yes to this option, support will be included for the
+	  Aspeed AST2xxx SoC I2C controller.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called i2c-aspeed.
+
 comment "External I2C/SMBus adapter drivers"
 
 config I2C_DIOLAN_U2C
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index 37f2819..49631cd 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -96,6 +96,7 @@ obj-$(CONFIG_I2C_XILINX)	+= i2c-xiic.o
 obj-$(CONFIG_I2C_XLR)		+= i2c-xlr.o
 obj-$(CONFIG_I2C_XLP9XX)	+= i2c-xlp9xx.o
 obj-$(CONFIG_I2C_RCAR)		+= i2c-rcar.o
+obj-$(CONFIG_I2C_ASPEED)	+= i2c-aspeed.o
 
 # External I2C/SMBus adapter drivers
 obj-$(CONFIG_I2C_DIOLAN_U2C)	+= i2c-diolan-u2c.o
diff --git a/drivers/i2c/busses/i2c-aspeed.c b/drivers/i2c/busses/i2c-aspeed.c
new file mode 100644
index 0000000..b19f13c
--- /dev/null
+++ b/drivers/i2c/busses/i2c-aspeed.c
@@ -0,0 +1,831 @@
+/*
+ *  I2C adapter for the ASPEED I2C bus.
+ *
+ *  Copyright (C) 2012-2020  ASPEED Technology Inc.
+ *  Copyright 2016 IBM Corporation
+ *  Copyright 2016 Google, Inc.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/completion.h>
+#include <linux/slab.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+
+/* I2C Register */
+#define ASPEED_I2C_FUN_CTRL_REG				0x00
+#define ASPEED_I2C_AC_TIMING_REG1			0x04
+#define ASPEED_I2C_AC_TIMING_REG2			0x08
+#define ASPEED_I2C_INTR_CTRL_REG			0x0c
+#define ASPEED_I2C_INTR_STS_REG				0x10
+#define ASPEED_I2C_CMD_REG				0x14
+#define ASPEED_I2C_DEV_ADDR_REG				0x18
+#define ASPEED_I2C_BYTE_BUF_REG				0x20
+
+#define ASPEED_I2C_NUM_BUS 14
+
+/* Global Register Definition */
+/* 0x00 : I2C Interrupt Status Register  */
+/* 0x08 : I2C Interrupt Target Assignment  */
+
+/* Device Register Definition */
+/* 0x00 : I2CD Function Control Register  */
+#define ASPEED_I2CD_MULTI_MASTER_DIS			BIT(15)
+#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_SLAVE_EN				BIT(1)
+#define ASPEED_I2CD_MASTER_EN				BIT(0)
+
+/* 0x08 : I2CD Clock and AC Timing Control Register #2 */
+#define ASPEED_NO_TIMEOUT_CTRL				0
+
+
+/* 0x0c : I2CD Interrupt Control Register &
+ * 0x10 : I2CD Interrupt Status Register
+ *
+ * These share bit definitions, so use the same values for the enable &
+ * status bits.
+ */
+#define ASPEED_I2CD_INTR_SDA_DL_TIMEOUT			BIT(14)
+#define ASPEED_I2CD_INTR_BUS_RECOVER_DONE		BIT(13)
+#define ASPEED_I2CD_INTR_SLAVE_MATCH			BIT(7)
+#define ASPEED_I2CD_INTR_SCL_TIMEOUT			BIT(6)
+#define ASPEED_I2CD_INTR_ABNORMAL			BIT(5)
+#define ASPEED_I2CD_INTR_NORMAL_STOP			BIT(4)
+#define ASPEED_I2CD_INTR_ARBIT_LOSS			BIT(3)
+#define ASPEED_I2CD_INTR_RX_DONE			BIT(2)
+#define ASPEED_I2CD_INTR_TX_NAK				BIT(1)
+#define ASPEED_I2CD_INTR_TX_ACK				BIT(0)
+
+/* 0x14 : I2CD Command/Status Register   */
+#define ASPEED_I2CD_SCL_LINE_STS			BIT(18)
+#define ASPEED_I2CD_SDA_LINE_STS			BIT(17)
+#define ASPEED_I2CD_BUS_BUSY_STS			BIT(16)
+#define ASPEED_I2CD_BUS_RECOVER_CMD			BIT(11)
+
+/* Command Bit */
+#define ASPEED_I2CD_M_STOP_CMD				BIT(5)
+#define ASPEED_I2CD_M_S_RX_CMD_LAST			BIT(4)
+#define ASPEED_I2CD_M_RX_CMD				BIT(3)
+#define ASPEED_I2CD_S_TX_CMD				BIT(2)
+#define ASPEED_I2CD_M_TX_CMD				BIT(1)
+#define ASPEED_I2CD_M_START_CMD				BIT(0)
+
+/* 0x18 : I2CD Slave Device Address Register   */
+#define ASPEED_I2CD_DEV_ADDR_MASK			GENMASK(6, 0)
+
+enum aspeed_i2c_slave_state {
+	ASPEED_I2C_SLAVE_START,
+	ASPEED_I2C_SLAVE_READ_REQUESTED,
+	ASPEED_I2C_SLAVE_READ_PROCESSED,
+	ASPEED_I2C_SLAVE_WRITE_REQUESTED,
+	ASPEED_I2C_SLAVE_WRITE_RECEIVED,
+	ASPEED_I2C_SLAVE_STOP,
+};
+
+struct aspeed_i2c_bus {
+	struct i2c_adapter		adap;
+	struct device			*dev;
+	void __iomem			*base;
+	spinlock_t			lock;
+	struct completion		cmd_complete;
+	int				irq;
+	/* Transaction state. */
+	struct i2c_msg			*msg;
+	int				msg_pos;
+	u32				cmd_err;
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+	struct i2c_client		*slave;
+	enum aspeed_i2c_slave_state	slave_state;
+#endif
+};
+
+struct aspeed_i2c_controller {
+	struct device		*dev;
+	void __iomem		*base;
+	int			irq;
+	struct irq_domain	*irq_domain;
+};
+
+static inline void aspeed_i2c_write(struct aspeed_i2c_bus *bus, u32 val,
+				    u32 reg)
+{
+	writel(val, bus->base + reg);
+}
+
+static inline u32 aspeed_i2c_read(struct aspeed_i2c_bus *bus, u32 reg)
+{
+	return readl(bus->base + reg);
+}
+
+static u8 aspeed_i2c_recover_bus(struct aspeed_i2c_bus *bus)
+{
+	u32 command;
+	unsigned long time_left;
+	unsigned long flags;
+	int ret = 0;
+
+	spin_lock_irqsave(&bus->lock, flags);
+	command = aspeed_i2c_read(bus, ASPEED_I2C_CMD_REG);
+	/* Bus is idle: no recovery needed. */
+	if ((command & ASPEED_I2CD_SDA_LINE_STS) &&
+	    (command & ASPEED_I2CD_SCL_LINE_STS))
+		goto out;
+
+	dev_dbg(bus->dev, "bus hung (state %x), attempting recovery\n",
+		command);
+
+	/* Bus held: put bus in stop state. */
+	if ((command & ASPEED_I2CD_SDA_LINE_STS) &&
+	    !(command & ASPEED_I2CD_SCL_LINE_STS)) {
+		aspeed_i2c_write(bus, ASPEED_I2CD_M_STOP_CMD,
+				 ASPEED_I2C_CMD_REG);
+		reinit_completion(&bus->cmd_complete);
+		spin_unlock_irqrestore(&bus->lock, flags);
+
+		time_left = wait_for_completion_interruptible_timeout(
+				&bus->cmd_complete, bus->adap.timeout * HZ);
+
+		spin_lock_irqsave(&bus->lock, flags);
+		if (time_left == 0)
+			ret = -ETIMEDOUT;
+		else if (bus->cmd_err)
+			ret = -EIO;
+	/* Bus error. */
+	} else if (!(command & ASPEED_I2CD_SDA_LINE_STS)) {
+		aspeed_i2c_write(bus, ASPEED_I2CD_BUS_RECOVER_CMD,
+				 ASPEED_I2C_CMD_REG);
+		reinit_completion(&bus->cmd_complete);
+		spin_unlock_irqrestore(&bus->lock, flags);
+
+		time_left = wait_for_completion_interruptible_timeout(
+				&bus->cmd_complete, bus->adap.timeout * HZ);
+
+		spin_lock_irqsave(&bus->lock, flags);
+		if (time_left == 0)
+			ret = -ETIMEDOUT;
+		else if (bus->cmd_err)
+			ret = -EIO;
+		/* Recovery failed. */
+		else if (!(aspeed_i2c_read(bus, ASPEED_I2C_CMD_REG) &
+			   ASPEED_I2CD_SDA_LINE_STS))
+			ret = -EIO;
+	}
+
+out:
+	spin_unlock_irqrestore(&bus->lock, flags);
+	return ret;
+}
+
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+static bool aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus)
+{
+	bool irq_handled = true;
+	u32 command;
+	u32 irq_status;
+	u32 status_ack = 0;
+	u8 value;
+	struct i2c_client *slave = bus->slave;
+
+	spin_lock(&bus->lock);
+	if (!slave) {
+		irq_handled = false;
+		goto out;
+	}
+	command = aspeed_i2c_read(bus, ASPEED_I2C_CMD_REG);
+	irq_status = aspeed_i2c_read(bus, ASPEED_I2C_INTR_STS_REG);
+
+	/* Slave was requested, restart state machine. */
+	if (irq_status & ASPEED_I2CD_INTR_SLAVE_MATCH) {
+		status_ack |= ASPEED_I2CD_INTR_SLAVE_MATCH;
+		bus->slave_state = ASPEED_I2C_SLAVE_START;
+	}
+	/* Slave is not currently active, irq was for someone else. */
+	if (bus->slave_state == ASPEED_I2C_SLAVE_STOP) {
+		irq_handled = false;
+		goto out;
+	}
+
+	dev_dbg(bus->dev, "slave irq status 0x%08x, cmd 0x%08x\n",
+		irq_status, command);
+
+	/* Slave was sent something. */
+	if (irq_status & ASPEED_I2CD_INTR_RX_DONE) {
+		value = aspeed_i2c_read(bus, ASPEED_I2C_BYTE_BUF_REG) >> 8;
+		/* Handle address frame. */
+		if (bus->slave_state == ASPEED_I2C_SLAVE_START) {
+			if (value & 0x1)
+				bus->slave_state =
+						ASPEED_I2C_SLAVE_READ_REQUESTED;
+			else
+				bus->slave_state =
+						ASPEED_I2C_SLAVE_WRITE_REQUESTED;
+		}
+		status_ack |= ASPEED_I2CD_INTR_RX_DONE;
+	}
+
+	/* Slave was asked to stop. */
+	if (irq_status & ASPEED_I2CD_INTR_NORMAL_STOP) {
+		status_ack |= ASPEED_I2CD_INTR_NORMAL_STOP;
+		bus->slave_state = ASPEED_I2C_SLAVE_STOP;
+	}
+	if (irq_status & ASPEED_I2CD_INTR_TX_NAK) {
+		status_ack |= ASPEED_I2CD_INTR_TX_NAK;
+		bus->slave_state = ASPEED_I2C_SLAVE_STOP;
+	}
+
+	if (bus->slave_state == ASPEED_I2C_SLAVE_READ_REQUESTED) {
+		if (irq_status & ASPEED_I2CD_INTR_TX_ACK)
+			dev_err(bus->dev, "Unexpected ACK on read request.\n");
+		bus->slave_state = ASPEED_I2C_SLAVE_READ_PROCESSED;
+
+		i2c_slave_event(slave, I2C_SLAVE_READ_REQUESTED, &value);
+		aspeed_i2c_write(bus, value, ASPEED_I2C_BYTE_BUF_REG);
+		aspeed_i2c_write(bus, ASPEED_I2CD_S_TX_CMD, ASPEED_I2C_CMD_REG);
+	} else if (bus->slave_state == ASPEED_I2C_SLAVE_READ_PROCESSED) {
+		status_ack |= ASPEED_I2CD_INTR_TX_ACK;
+		if (!(irq_status & ASPEED_I2CD_INTR_TX_ACK))
+			dev_err(bus->dev,
+				"Expected ACK after processed read.\n");
+		i2c_slave_event(slave, I2C_SLAVE_READ_PROCESSED, &value);
+		aspeed_i2c_write(bus, value, ASPEED_I2C_BYTE_BUF_REG);
+		aspeed_i2c_write(bus, ASPEED_I2CD_S_TX_CMD, ASPEED_I2C_CMD_REG);
+	} else if (bus->slave_state == ASPEED_I2C_SLAVE_WRITE_REQUESTED) {
+		bus->slave_state = ASPEED_I2C_SLAVE_WRITE_RECEIVED;
+		i2c_slave_event(slave, I2C_SLAVE_WRITE_REQUESTED, &value);
+	} else if (bus->slave_state == ASPEED_I2C_SLAVE_WRITE_RECEIVED) {
+		i2c_slave_event(slave, I2C_SLAVE_WRITE_RECEIVED, &value);
+	} else if (bus->slave_state == ASPEED_I2C_SLAVE_STOP) {
+		i2c_slave_event(slave, I2C_SLAVE_STOP, &value);
+	}
+
+	if (status_ack != irq_status)
+		dev_err(bus->dev,
+			"irq handled != irq. expected %x, but was %x\n",
+			irq_status, status_ack);
+	aspeed_i2c_write(bus, status_ack, ASPEED_I2C_INTR_STS_REG);
+
+out:
+	spin_unlock(&bus->lock);
+	return irq_handled;
+}
+#endif
+
+static bool aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus)
+{
+	const u32 errs = ASPEED_I2CD_INTR_ARBIT_LOSS |
+		ASPEED_I2CD_INTR_ABNORMAL |
+		ASPEED_I2CD_INTR_SCL_TIMEOUT |
+		ASPEED_I2CD_INTR_SDA_DL_TIMEOUT |
+		ASPEED_I2CD_INTR_TX_NAK;
+	u32 irq_status;
+
+	spin_lock(&bus->lock);
+	irq_status = aspeed_i2c_read(bus, ASPEED_I2C_INTR_STS_REG);
+	bus->cmd_err = irq_status & errs;
+
+	dev_dbg(bus->dev, "master irq status 0x%08x\n", irq_status);
+
+	/* No message to transfer. */
+	if (bus->cmd_err ||
+	    (irq_status & ASPEED_I2CD_INTR_NORMAL_STOP) ||
+	    (irq_status & ASPEED_I2CD_INTR_BUS_RECOVER_DONE)) {
+		complete(&bus->cmd_complete);
+		goto out;
+	} else if (!bus->msg || bus->msg_pos >= bus->msg->len)
+		goto out;
+
+	if ((bus->msg->flags & I2C_M_RD) &&
+	    (irq_status & ASPEED_I2CD_INTR_RX_DONE)) {
+		bus->msg->buf[bus->msg_pos++] = aspeed_i2c_read(
+				bus, ASPEED_I2C_BYTE_BUF_REG) >> 8;
+		if (bus->msg_pos + 1 < bus->msg->len)
+			aspeed_i2c_write(bus, ASPEED_I2CD_M_RX_CMD,
+					 ASPEED_I2C_CMD_REG);
+		else if (bus->msg_pos < bus->msg->len)
+			aspeed_i2c_write(bus, ASPEED_I2CD_M_RX_CMD |
+				      ASPEED_I2CD_M_S_RX_CMD_LAST,
+				      ASPEED_I2C_CMD_REG);
+	} else if (!(bus->msg->flags & I2C_M_RD) &&
+		   (irq_status & ASPEED_I2CD_INTR_TX_ACK)) {
+		aspeed_i2c_write(bus, bus->msg->buf[bus->msg_pos++],
+			      ASPEED_I2C_BYTE_BUF_REG);
+		aspeed_i2c_write(bus, ASPEED_I2CD_M_TX_CMD, ASPEED_I2C_CMD_REG);
+	}
+
+	/* Transmission complete: notify caller. */
+	if (bus->msg_pos >= bus->msg->len)
+		complete(&bus->cmd_complete);
+out:
+	aspeed_i2c_write(bus, irq_status, ASPEED_I2C_INTR_STS_REG);
+	spin_unlock(&bus->lock);
+	return true;
+}
+
+static irqreturn_t aspeed_i2c_bus_irq(int irq, void *dev_id)
+{
+	struct aspeed_i2c_bus *bus = dev_id;
+
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+	if (aspeed_i2c_slave_irq(bus)) {
+		dev_dbg(bus->dev, "irq handled by slave.\n");
+		return IRQ_HANDLED;
+	}
+#endif
+	if (aspeed_i2c_master_irq(bus)) {
+		dev_dbg(bus->dev, "irq handled by master.\n");
+		return IRQ_HANDLED;
+	}
+	dev_err(bus->dev, "irq not handled properly!\n");
+	return IRQ_HANDLED;
+}
+
+static int aspeed_i2c_master_single_xfer(struct i2c_adapter *adap,
+				      struct i2c_msg *msg)
+{
+	struct aspeed_i2c_bus *bus = adap->algo_data;
+	unsigned long flags;
+	u8 slave_addr;
+	u32 command = ASPEED_I2CD_M_START_CMD | ASPEED_I2CD_M_TX_CMD;
+	int ret = msg->len;
+	unsigned long time_left;
+
+	spin_lock_irqsave(&bus->lock, flags);
+	bus->msg = msg;
+	bus->msg_pos = 0;
+	slave_addr = msg->addr << 1;
+	if (msg->flags & I2C_M_RD) {
+		slave_addr |= 1;
+		command |= ASPEED_I2CD_M_RX_CMD;
+		if (msg->len == 1)
+			command |= ASPEED_I2CD_M_S_RX_CMD_LAST;
+	}
+	aspeed_i2c_write(bus, slave_addr, ASPEED_I2C_BYTE_BUF_REG);
+	aspeed_i2c_write(bus, command, ASPEED_I2C_CMD_REG);
+	reinit_completion(&bus->cmd_complete);
+	spin_unlock_irqrestore(&bus->lock, flags);
+
+	time_left = wait_for_completion_interruptible_timeout(
+			&bus->cmd_complete, bus->adap.timeout * HZ * msg->len);
+	if (time_left == 0)
+		return -ETIMEDOUT;
+
+	spin_lock_irqsave(&bus->lock, flags);
+	if (bus->cmd_err)
+		ret = -EIO;
+	bus->msg = NULL;
+	spin_unlock_irqrestore(&bus->lock, flags);
+
+	return ret;
+}
+
+static int aspeed_i2c_master_xfer(struct i2c_adapter *adap,
+				  struct i2c_msg *msgs, int num)
+{
+	struct aspeed_i2c_bus *bus = adap->algo_data;
+	int ret;
+	int i;
+	unsigned long flags;
+	unsigned long time_left;
+
+	/* If bus is busy, attempt recovery. We assume a single master
+	 * environment.
+	 */
+	if (aspeed_i2c_read(bus, ASPEED_I2C_CMD_REG) &
+	    ASPEED_I2CD_BUS_BUSY_STS) {
+		ret = aspeed_i2c_recover_bus(bus);
+		if (ret)
+			return ret;
+	}
+
+	for (i = 0; i < num; i++) {
+		ret = aspeed_i2c_master_single_xfer(adap, &msgs[i]);
+		if (ret < 0)
+			break;
+		/* TODO: Support other forms of I2C protocol mangling. */
+		if (msgs[i].flags & I2C_M_STOP) {
+			spin_lock_irqsave(&bus->lock, flags);
+			aspeed_i2c_write(bus, ASPEED_I2CD_M_STOP_CMD,
+				      ASPEED_I2C_CMD_REG);
+			reinit_completion(&bus->cmd_complete);
+			spin_unlock_irqrestore(&bus->lock, flags);
+
+			time_left = wait_for_completion_interruptible_timeout(
+					&bus->cmd_complete,
+					bus->adap.timeout * HZ);
+			if (time_left == 0)
+				return -ETIMEDOUT;
+		}
+	}
+
+	spin_lock_irqsave(&bus->lock, flags);
+	aspeed_i2c_write(bus, ASPEED_I2CD_M_STOP_CMD, ASPEED_I2C_CMD_REG);
+	reinit_completion(&bus->cmd_complete);
+	spin_unlock_irqrestore(&bus->lock, flags);
+
+	time_left = wait_for_completion_interruptible_timeout(
+			&bus->cmd_complete, bus->adap.timeout * HZ);
+	if (time_left == 0)
+		return -ETIMEDOUT;
+
+	/* If nothing went wrong, return number of messages transferred. */
+	if (ret < 0)
+		return ret;
+	else
+		return i;
+}
+
+static u32 aspeed_i2c_functionality(struct i2c_adapter *adap)
+{
+	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_SMBUS_BLOCK_DATA;
+}
+
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+static int aspeed_i2c_reg_slave(struct i2c_client *client)
+{
+	struct aspeed_i2c_bus *bus;
+	unsigned long flags;
+	u32 addr_reg_val;
+	u32 func_ctrl_reg_val;
+
+	bus = client->adapter->algo_data;
+	spin_lock_irqsave(&bus->lock, flags);
+	if (bus->slave) {
+		spin_unlock_irqrestore(&bus->lock, flags);
+		return -EINVAL;
+	}
+
+	/* Set slave addr. */
+	addr_reg_val = aspeed_i2c_read(bus, ASPEED_I2C_DEV_ADDR_REG);
+	addr_reg_val &= ~ASPEED_I2CD_DEV_ADDR_MASK;
+	addr_reg_val |= client->addr & ASPEED_I2CD_DEV_ADDR_MASK;
+	aspeed_i2c_write(bus, addr_reg_val, ASPEED_I2C_DEV_ADDR_REG);
+
+	/* Switch from master mode to slave mode. */
+	func_ctrl_reg_val = aspeed_i2c_read(bus, ASPEED_I2C_FUN_CTRL_REG);
+	func_ctrl_reg_val &= ~ASPEED_I2CD_MASTER_EN;
+	func_ctrl_reg_val |= ASPEED_I2CD_SLAVE_EN;
+	aspeed_i2c_write(bus, func_ctrl_reg_val, ASPEED_I2C_FUN_CTRL_REG);
+
+	bus->slave = client;
+	bus->slave_state = ASPEED_I2C_SLAVE_STOP;
+	spin_unlock_irqrestore(&bus->lock, flags);
+	return 0;
+}
+
+static int aspeed_i2c_unreg_slave(struct i2c_client *client)
+{
+	struct aspeed_i2c_bus *bus = client->adapter->algo_data;
+	unsigned long flags;
+	u32 func_ctrl_reg_val;
+
+	spin_lock_irqsave(&bus->lock, flags);
+	if (!bus->slave) {
+		spin_unlock_irqrestore(&bus->lock, flags);
+		return -EINVAL;
+	}
+
+	/* Switch from slave mode to master mode. */
+	func_ctrl_reg_val = aspeed_i2c_read(bus, ASPEED_I2C_FUN_CTRL_REG);
+	func_ctrl_reg_val &= ~ASPEED_I2CD_SLAVE_EN;
+	func_ctrl_reg_val |= ASPEED_I2CD_MASTER_EN;
+	aspeed_i2c_write(bus, func_ctrl_reg_val, ASPEED_I2C_FUN_CTRL_REG);
+
+	bus->slave = NULL;
+	spin_unlock_irqrestore(&bus->lock, flags);
+	return 0;
+}
+#endif
+
+static const struct i2c_algorithm aspeed_i2c_algo = {
+	.master_xfer	= aspeed_i2c_master_xfer,
+	.functionality	= aspeed_i2c_functionality,
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+	.reg_slave	= aspeed_i2c_reg_slave,
+	.unreg_slave	= aspeed_i2c_unreg_slave,
+#endif
+};
+
+static u32 aspeed_i2c_get_clk_reg_val(u32 divider_ratio)
+{
+	unsigned int inc = 0, div;
+	u32 scl_low, scl_high, data;
+
+	for (div = 0; divider_ratio >= 16; div++) {
+		inc |= (divider_ratio & 1);
+		divider_ratio >>= 1;
+	}
+	divider_ratio += inc;
+	scl_low = (divider_ratio >> 1) - 1;
+	scl_high = divider_ratio - scl_low - 2;
+	data = 0x77700300 | (scl_high << 16) | (scl_low << 12) | div;
+	return data;
+}
+
+static int aspeed_i2c_init_clk(struct aspeed_i2c_bus *bus,
+			    struct platform_device *pdev)
+{
+	struct clk *pclk;
+	u32 clk_freq;
+	u32 divider_ratio;
+	int ret;
+
+	pclk = devm_clk_get(&pdev->dev, NULL);
+	if (IS_ERR(pclk)) {
+		dev_err(&pdev->dev, "clk_get failed\n");
+		return PTR_ERR(pclk);
+	}
+	ret = of_property_read_u32(pdev->dev.of_node,
+			"clock-frequency", &clk_freq);
+	if (ret < 0) {
+		dev_err(&pdev->dev,
+				"Could not read clock-frequency property\n");
+		clk_freq = 100000;
+	}
+	divider_ratio = clk_get_rate(pclk) / clk_freq;
+	/* We just need the clock rate, we don't actually use the clk object. */
+	devm_clk_put(&pdev->dev, pclk);
+
+	/* Set AC Timing */
+	if (clk_freq / 1000 > 400) {
+		aspeed_i2c_write(bus, aspeed_i2c_read(bus,
+						      ASPEED_I2C_FUN_CTRL_REG) |
+				ASPEED_I2CD_M_HIGH_SPEED_EN |
+				ASPEED_I2CD_M_SDA_DRIVE_1T_EN |
+				ASPEED_I2CD_SDA_DRIVE_1T_EN,
+				ASPEED_I2C_FUN_CTRL_REG);
+
+		aspeed_i2c_write(bus, 0x3, ASPEED_I2C_AC_TIMING_REG2);
+		aspeed_i2c_write(bus, aspeed_i2c_get_clk_reg_val(divider_ratio),
+			      ASPEED_I2C_AC_TIMING_REG1);
+	} else {
+		aspeed_i2c_write(bus, aspeed_i2c_get_clk_reg_val(divider_ratio),
+			      ASPEED_I2C_AC_TIMING_REG1);
+		aspeed_i2c_write(bus, ASPEED_NO_TIMEOUT_CTRL,
+				 ASPEED_I2C_AC_TIMING_REG2);
+	}
+
+	return 0;
+}
+
+static void noop(struct irq_data *data) { }
+
+static struct irq_chip aspeed_i2c_irqchip = {
+	.name		= "ast-i2c",
+	.irq_unmask	= noop,
+	.irq_mask	= noop,
+};
+
+static int aspeed_i2c_probe_bus(struct platform_device *pdev)
+{
+	struct aspeed_i2c_bus *bus;
+	struct aspeed_i2c_controller *controller =
+			dev_get_drvdata(pdev->dev.parent);
+	struct resource *res;
+	int ret, bus_num, irq;
+
+	bus = devm_kzalloc(&pdev->dev, sizeof(*bus), GFP_KERNEL);
+	if (!bus)
+		return -ENOMEM;
+
+	ret = of_property_read_u32(pdev->dev.of_node, "bus", &bus_num);
+	if (ret)
+		return -ENXIO;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	bus->base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(bus->base))
+		return PTR_ERR(bus->base);
+
+	bus->irq = platform_get_irq(pdev, 0);
+	if (bus->irq < 0) {
+		dev_err(&pdev->dev, "platform_get_irq failed\n");
+		return -ENXIO;
+	}
+
+	irq = irq_create_mapping(controller->irq_domain, bus_num);
+	irq_set_chip_data(irq, controller);
+	irq_set_chip_and_handler(irq, &aspeed_i2c_irqchip, handle_simple_irq);
+	ret = devm_request_irq(&pdev->dev, bus->irq, aspeed_i2c_bus_irq,
+			0, dev_name(&pdev->dev), bus);
+	if (ret) {
+		dev_err(&pdev->dev, "devm_request_irq failed\n");
+		return -ENXIO;
+	}
+
+	/* Initialize the I2C adapter */
+	spin_lock_init(&bus->lock);
+	init_completion(&bus->cmd_complete);
+	bus->adap.nr = bus_num;
+	bus->adap.owner = THIS_MODULE;
+	bus->adap.retries = 0;
+	bus->adap.timeout = 5;
+	bus->adap.algo = &aspeed_i2c_algo;
+	bus->adap.algo_data = bus;
+	bus->adap.dev.parent = &pdev->dev;
+	bus->adap.dev.of_node = pdev->dev.of_node;
+	snprintf(bus->adap.name, sizeof(bus->adap.name), "Aspeed i2c-%d",
+			bus_num);
+
+	bus->dev = &pdev->dev;
+
+	/* reset device: disable master & slave functions */
+	aspeed_i2c_write(bus, 0, ASPEED_I2C_FUN_CTRL_REG);
+
+	ret = aspeed_i2c_init_clk(bus, pdev);
+	if (ret < 0)
+		return ret;
+
+	/* Enable Master Mode */
+	aspeed_i2c_write(bus, aspeed_i2c_read(bus, ASPEED_I2C_FUN_CTRL_REG) |
+		      ASPEED_I2CD_MASTER_EN |
+		      ASPEED_I2CD_MULTI_MASTER_DIS, ASPEED_I2C_FUN_CTRL_REG);
+
+	/* Set interrupt generation of I2C controller */
+	aspeed_i2c_write(bus, ASPEED_I2CD_INTR_SDA_DL_TIMEOUT |
+			ASPEED_I2CD_INTR_BUS_RECOVER_DONE |
+			ASPEED_I2CD_INTR_SCL_TIMEOUT |
+			ASPEED_I2CD_INTR_ABNORMAL |
+			ASPEED_I2CD_INTR_NORMAL_STOP |
+			ASPEED_I2CD_INTR_ARBIT_LOSS |
+			ASPEED_I2CD_INTR_RX_DONE |
+			ASPEED_I2CD_INTR_TX_NAK |
+			ASPEED_I2CD_INTR_TX_ACK,
+			ASPEED_I2C_INTR_CTRL_REG);
+
+	ret = i2c_add_numbered_adapter(&bus->adap);
+	if (ret < 0)
+		return -ENXIO;
+
+	platform_set_drvdata(pdev, bus);
+
+	dev_info(bus->dev, "i2c bus %d registered, irq %d\n",
+			bus->adap.nr, bus->irq);
+
+	return 0;
+}
+
+static int aspeed_i2c_remove_bus(struct platform_device *pdev)
+{
+	struct aspeed_i2c_bus *bus = platform_get_drvdata(pdev);
+
+	i2c_del_adapter(&bus->adap);
+	return 0;
+}
+
+static const struct of_device_id aspeed_i2c_bus_of_table[] = {
+	{ .compatible = "aspeed,ast2400-i2c-bus", },
+	{ .compatible = "aspeed,ast2500-i2c-bus", },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, aspeed_i2c_bus_of_table);
+
+static struct platform_driver aspeed_i2c_bus_driver = {
+	.probe		= aspeed_i2c_probe_bus,
+	.remove		= aspeed_i2c_remove_bus,
+	.driver		= {
+		.name		= "ast-i2c-bus",
+		.of_match_table	= aspeed_i2c_bus_of_table,
+	},
+};
+
+static void aspeed_i2c_controller_irq(struct irq_desc *desc)
+{
+	struct aspeed_i2c_controller *c = irq_desc_get_handler_data(desc);
+	unsigned long p, status;
+	unsigned int bus_irq;
+
+	status = readl(c->base);
+	for_each_set_bit(p, &status, ASPEED_I2C_NUM_BUS) {
+		bus_irq = irq_find_mapping(c->irq_domain, p);
+		generic_handle_irq(bus_irq);
+	}
+}
+
+static int aspeed_i2c_probe_controller(struct platform_device *pdev)
+{
+	struct aspeed_i2c_controller *controller;
+	struct device_node *np;
+	struct resource *res;
+
+	controller = kzalloc(sizeof(*controller), GFP_KERNEL);
+	if (!controller)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	controller->base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(controller->base))
+		return PTR_ERR(controller->base);
+
+	controller->irq = platform_get_irq(pdev, 0);
+	if (controller->irq < 0) {
+		dev_err(&pdev->dev, "no platform IRQ\n");
+		return -ENXIO;
+	}
+
+	controller->irq_domain = irq_domain_add_linear(pdev->dev.of_node,
+			ASPEED_I2C_NUM_BUS, &irq_domain_simple_ops, NULL);
+	if (!controller->irq_domain) {
+		dev_err(&pdev->dev, "no IRQ domain\n");
+		return -ENXIO;
+	}
+	controller->irq_domain->name = "ast-i2c-domain";
+
+	irq_set_chained_handler_and_data(controller->irq,
+			aspeed_i2c_controller_irq, controller);
+
+	controller->dev = &pdev->dev;
+
+	platform_set_drvdata(pdev, controller);
+
+	dev_info(controller->dev, "i2c controller registered, irq %d\n",
+			controller->irq);
+
+	for_each_child_of_node(pdev->dev.of_node, np) {
+		int ret;
+		u32 bus_num;
+		char bus_id[sizeof("i2c-12345")];
+
+		/*
+		 * Set a useful name derived from the bus number; the device
+		 * tree should provide us with one that corresponds to the
+		 * hardware numbering.  If the property is missing the
+		 * probe would fail so just skip it here.
+		 */
+
+		ret = of_property_read_u32(np, "bus", &bus_num);
+		if (ret)
+			continue;
+
+		ret = snprintf(bus_id, sizeof(bus_id), "i2c-%u", bus_num);
+		if (ret >= sizeof(bus_id))
+			continue;
+
+		of_platform_device_create(np, bus_id, &pdev->dev);
+		of_node_put(np);
+	}
+
+	return 0;
+}
+
+static int aspeed_i2c_remove_controller(struct platform_device *pdev)
+{
+	struct aspeed_i2c_controller *controller = platform_get_drvdata(pdev);
+
+	irq_domain_remove(controller->irq_domain);
+	return 0;
+}
+
+static const struct of_device_id aspeed_i2c_controller_of_table[] = {
+	{ .compatible = "aspeed,ast2400-i2c-controller", },
+	{ .compatible = "aspeed,ast2500-i2c-controller", },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, aspeed_i2c_controller_of_table);
+
+static struct platform_driver aspeed_i2c_controller_driver = {
+	.probe		= aspeed_i2c_probe_controller,
+	.remove		= aspeed_i2c_remove_controller,
+	.driver		= {
+		.name		= "ast-i2c-controller",
+		.of_match_table	= aspeed_i2c_controller_of_table,
+	},
+};
+
+static int __init aspeed_i2c_driver_init(void)
+{
+	int ret;
+
+	ret = platform_driver_register(&aspeed_i2c_controller_driver);
+	if (ret < 0)
+		return ret;
+	return platform_driver_register(&aspeed_i2c_bus_driver);
+}
+module_init(aspeed_i2c_driver_init);
+
+static void __exit aspeed_i2c_driver_exit(void)
+{
+	platform_driver_unregister(&aspeed_i2c_bus_driver);
+	platform_driver_unregister(&aspeed_i2c_controller_driver);
+}
+module_exit(aspeed_i2c_driver_exit);
+
+MODULE_AUTHOR("Brendan Higgins <brendanhiggins@google.com>");
+MODULE_DESCRIPTION("Aspeed I2C Bus Driver");
+MODULE_LICENSE("GPL");
-- 
2.8.0.rc3.226.g39d4020

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH v2 2/2] i2c: aspeed: added documentation for Aspeed I2C driver
  2016-09-10  1:55     ` [PATCH v2 " Brendan Higgins
@ 2016-09-10  1:55       ` Brendan Higgins
  2016-09-19 21:35         ` Rob Herring
  2016-09-19 17:56       ` [PATCH v2 1/2] i2c: aspeed: added driver for Aspeed I2C Brendan Higgins
  1 sibling, 1 reply; 17+ messages in thread
From: Brendan Higgins @ 2016-09-10  1:55 UTC (permalink / raw)
  To: wsa, robh+dt, mark.rutland
  Cc: linux-i2c, devicetree, linux-kernel, openbmc, joel, jk, Brendan Higgins

Added device tree binding documentation for Aspeed I2C controller and
busses.

Signed-off-by: Brendan Higgins <brendanhiggins@google.com>
---
Changes for v2:
  - None
---
 .../devicetree/bindings/i2c/i2c-aspeed.txt         | 63 ++++++++++++++++++++++
 1 file changed, 63 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/i2c/i2c-aspeed.txt

diff --git a/Documentation/devicetree/bindings/i2c/i2c-aspeed.txt b/Documentation/devicetree/bindings/i2c/i2c-aspeed.txt
new file mode 100644
index 0000000..df68f2a
--- /dev/null
+++ b/Documentation/devicetree/bindings/i2c/i2c-aspeed.txt
@@ -0,0 +1,63 @@
+Device tree configuration for the I2C controller and busses on the AST24XX
+and AST25XX SoCs.
+
+Controller:
+
+	Required Properties:
+	- #address-cells	: should be 1
+	- #size-cells 		: should be 1
+	- #interrupt-cells 	: should be 1
+	- compatible 		: should be "aspeed,ast2400-i2c-controller"
+				  or "aspeed,ast2500-i2c-controller"
+	- reg			: address start and range of controller
+	- ranges		: defines address offset and range for busses
+	- interrupts		: interrupt number
+	- clocks		: root clock of bus, should reference the APB
+				  clock
+	- clock-ranges		: specifies that child busses can inherit clocks
+	- interrupt-controller	: denotes that the controller receives and fires
+				  new interrupts for child busses
+
+Bus:
+
+	Required Properties:
+	- #address-cells	: should be 1
+	- #size-cells		: should be 0
+	- reg			: address offset and range of bus
+	- compatible		: should be "aspeed,ast2400-i2c-bus"
+				  or "aspeed,ast2500-i2c-bus"
+	- bus			: the bus's number
+	- interrupts		: interrupt number
+
+	Optional Properties:
+	- clock-frequency	: frequency of the bus clock in Hz
+				  defaults to 100 kHz when not specified
+
+Example:
+
+i2c: i2c@1e78a000 {
+	#address-cells = <1>;
+	#size-cells = <1>;
+	#interrupt-cells = <1>;
+
+	compatible = "aspeed,ast2400-i2c-controller";
+	reg = <0x1e78a000 0x40>;
+	ranges = <0 0x1e78a000 0x1000>;
+	interrupts = <12>;
+	clocks = <&clk_apb>;
+	clock-ranges;
+	interrupt-controller;
+
+	i2c0: i2c-bus@40 {
+		#address-cells = <1>;
+		#size-cells = <0>;
+		reg = <0x40 0x40>;
+		compatible = "aspeed,ast2400-i2c-bus";
+		bus = <0>;
+		clock-frequency = <100000>;
+		status = "disabled";
+		interrupts = <0>;
+		interrupt-parent = <&i2c>;
+	};
+};
+
-- 
2.8.0.rc3.226.g39d4020

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* Re: [PATCH v2 1/2] i2c: aspeed: added driver for Aspeed I2C
  2016-09-10  1:55     ` [PATCH v2 " Brendan Higgins
  2016-09-10  1:55       ` [PATCH v2 2/2] i2c: aspeed: added documentation for Aspeed I2C driver Brendan Higgins
@ 2016-09-19 17:56       ` Brendan Higgins
  2016-09-19 23:23         ` [PATCH v3 " Brendan Higgins
  1 sibling, 1 reply; 17+ messages in thread
From: Brendan Higgins @ 2016-09-19 17:56 UTC (permalink / raw)
  To: Wolfram Sang, robh+dt, mark.rutland
  Cc: linux-i2c, devicetree, linux-kernel, OpenBMC Maillist,
	Joel Stanley, Jeremy Kerr, Brendan Higgins

Gentle ping

On Fri, Sep 9, 2016 at 6:55 PM, Brendan Higgins
<brendanhiggins@google.com> wrote:
> Added initial master and slave support for Aspeed I2C controller.
> Supports fourteen busses present in ast24xx and ast25xx BMC SoCs by
> Aspeed.
>
> Signed-off-by: Brendan Higgins <brendanhiggins@google.com>
> ---
> Changes for v2:
>   - Added single module_init (multiple was breaking some builds).
> ---
>  drivers/i2c/busses/Kconfig      |  10 +
>  drivers/i2c/busses/Makefile     |   1 +
>  drivers/i2c/busses/i2c-aspeed.c | 831 ++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 842 insertions(+)
>  create mode 100644 drivers/i2c/busses/i2c-aspeed.c
>
> diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
> index 5c3993b..0178c6c 100644
> --- a/drivers/i2c/busses/Kconfig
> +++ b/drivers/i2c/busses/Kconfig
> @@ -998,6 +998,16 @@ config I2C_RCAR
>           This driver can also be built as a module.  If so, the module
>           will be called i2c-rcar.
>
> +config I2C_ASPEED
> +       tristate "Aspeed AST2xxx SoC I2C Controller"
> +       depends on (ARCH_ASPEED || COMPILE_TEST) && OF
> +       help
> +         If you say yes to this option, support will be included for the
> +         Aspeed AST2xxx SoC I2C controller.
> +
> +         This driver can also be built as a module.  If so, the module
> +         will be called i2c-aspeed.
> +
>  comment "External I2C/SMBus adapter drivers"
>
>  config I2C_DIOLAN_U2C
> diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
> index 37f2819..49631cd 100644
> --- a/drivers/i2c/busses/Makefile
> +++ b/drivers/i2c/busses/Makefile
> @@ -96,6 +96,7 @@ obj-$(CONFIG_I2C_XILINX)      += i2c-xiic.o
>  obj-$(CONFIG_I2C_XLR)          += i2c-xlr.o
>  obj-$(CONFIG_I2C_XLP9XX)       += i2c-xlp9xx.o
>  obj-$(CONFIG_I2C_RCAR)         += i2c-rcar.o
> +obj-$(CONFIG_I2C_ASPEED)       += i2c-aspeed.o
>
>  # External I2C/SMBus adapter drivers
>  obj-$(CONFIG_I2C_DIOLAN_U2C)   += i2c-diolan-u2c.o
> diff --git a/drivers/i2c/busses/i2c-aspeed.c b/drivers/i2c/busses/i2c-aspeed.c
> new file mode 100644
> index 0000000..b19f13c
> --- /dev/null
> +++ b/drivers/i2c/busses/i2c-aspeed.c
> @@ -0,0 +1,831 @@
> +/*
> + *  I2C adapter for the ASPEED I2C bus.
> + *
> + *  Copyright (C) 2012-2020  ASPEED Technology Inc.
> + *  Copyright 2016 IBM Corporation
> + *  Copyright 2016 Google, Inc.
> + *
> + *  This program is free software; you can redistribute it and/or modify
> + *  it under the terms of the GNU General Public License version 2 as
> + *  published by the Free Software Foundation.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/i2c.h>
> +#include <linux/irq.h>
> +#include <linux/irqdomain.h>
> +#include <linux/init.h>
> +#include <linux/io.h>
> +#include <linux/errno.h>
> +#include <linux/interrupt.h>
> +#include <linux/completion.h>
> +#include <linux/slab.h>
> +#include <linux/of_address.h>
> +#include <linux/of_platform.h>
> +#include <linux/platform_device.h>
> +#include <linux/err.h>
> +#include <linux/clk.h>
> +
> +/* I2C Register */
> +#define ASPEED_I2C_FUN_CTRL_REG                                0x00
> +#define ASPEED_I2C_AC_TIMING_REG1                      0x04
> +#define ASPEED_I2C_AC_TIMING_REG2                      0x08
> +#define ASPEED_I2C_INTR_CTRL_REG                       0x0c
> +#define ASPEED_I2C_INTR_STS_REG                                0x10
> +#define ASPEED_I2C_CMD_REG                             0x14
> +#define ASPEED_I2C_DEV_ADDR_REG                                0x18
> +#define ASPEED_I2C_BYTE_BUF_REG                                0x20
> +
> +#define ASPEED_I2C_NUM_BUS 14
> +
> +/* Global Register Definition */
> +/* 0x00 : I2C Interrupt Status Register  */
> +/* 0x08 : I2C Interrupt Target Assignment  */
> +
> +/* Device Register Definition */
> +/* 0x00 : I2CD Function Control Register  */
> +#define ASPEED_I2CD_MULTI_MASTER_DIS                   BIT(15)
> +#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_SLAVE_EN                           BIT(1)
> +#define ASPEED_I2CD_MASTER_EN                          BIT(0)
> +
> +/* 0x08 : I2CD Clock and AC Timing Control Register #2 */
> +#define ASPEED_NO_TIMEOUT_CTRL                         0
> +
> +
> +/* 0x0c : I2CD Interrupt Control Register &
> + * 0x10 : I2CD Interrupt Status Register
> + *
> + * These share bit definitions, so use the same values for the enable &
> + * status bits.
> + */
> +#define ASPEED_I2CD_INTR_SDA_DL_TIMEOUT                        BIT(14)
> +#define ASPEED_I2CD_INTR_BUS_RECOVER_DONE              BIT(13)
> +#define ASPEED_I2CD_INTR_SLAVE_MATCH                   BIT(7)
> +#define ASPEED_I2CD_INTR_SCL_TIMEOUT                   BIT(6)
> +#define ASPEED_I2CD_INTR_ABNORMAL                      BIT(5)
> +#define ASPEED_I2CD_INTR_NORMAL_STOP                   BIT(4)
> +#define ASPEED_I2CD_INTR_ARBIT_LOSS                    BIT(3)
> +#define ASPEED_I2CD_INTR_RX_DONE                       BIT(2)
> +#define ASPEED_I2CD_INTR_TX_NAK                                BIT(1)
> +#define ASPEED_I2CD_INTR_TX_ACK                                BIT(0)
> +
> +/* 0x14 : I2CD Command/Status Register   */
> +#define ASPEED_I2CD_SCL_LINE_STS                       BIT(18)
> +#define ASPEED_I2CD_SDA_LINE_STS                       BIT(17)
> +#define ASPEED_I2CD_BUS_BUSY_STS                       BIT(16)
> +#define ASPEED_I2CD_BUS_RECOVER_CMD                    BIT(11)
> +
> +/* Command Bit */
> +#define ASPEED_I2CD_M_STOP_CMD                         BIT(5)
> +#define ASPEED_I2CD_M_S_RX_CMD_LAST                    BIT(4)
> +#define ASPEED_I2CD_M_RX_CMD                           BIT(3)
> +#define ASPEED_I2CD_S_TX_CMD                           BIT(2)
> +#define ASPEED_I2CD_M_TX_CMD                           BIT(1)
> +#define ASPEED_I2CD_M_START_CMD                                BIT(0)
> +
> +/* 0x18 : I2CD Slave Device Address Register   */
> +#define ASPEED_I2CD_DEV_ADDR_MASK                      GENMASK(6, 0)
> +
> +enum aspeed_i2c_slave_state {
> +       ASPEED_I2C_SLAVE_START,
> +       ASPEED_I2C_SLAVE_READ_REQUESTED,
> +       ASPEED_I2C_SLAVE_READ_PROCESSED,
> +       ASPEED_I2C_SLAVE_WRITE_REQUESTED,
> +       ASPEED_I2C_SLAVE_WRITE_RECEIVED,
> +       ASPEED_I2C_SLAVE_STOP,
> +};
> +
> +struct aspeed_i2c_bus {
> +       struct i2c_adapter              adap;
> +       struct device                   *dev;
> +       void __iomem                    *base;
> +       spinlock_t                      lock;
> +       struct completion               cmd_complete;
> +       int                             irq;
> +       /* Transaction state. */
> +       struct i2c_msg                  *msg;
> +       int                             msg_pos;
> +       u32                             cmd_err;
> +#if IS_ENABLED(CONFIG_I2C_SLAVE)
> +       struct i2c_client               *slave;
> +       enum aspeed_i2c_slave_state     slave_state;
> +#endif
> +};
> +
> +struct aspeed_i2c_controller {
> +       struct device           *dev;
> +       void __iomem            *base;
> +       int                     irq;
> +       struct irq_domain       *irq_domain;
> +};
> +
> +static inline void aspeed_i2c_write(struct aspeed_i2c_bus *bus, u32 val,
> +                                   u32 reg)
> +{
> +       writel(val, bus->base + reg);
> +}
> +
> +static inline u32 aspeed_i2c_read(struct aspeed_i2c_bus *bus, u32 reg)
> +{
> +       return readl(bus->base + reg);
> +}
> +
> +static u8 aspeed_i2c_recover_bus(struct aspeed_i2c_bus *bus)
> +{
> +       u32 command;
> +       unsigned long time_left;
> +       unsigned long flags;
> +       int ret = 0;
> +
> +       spin_lock_irqsave(&bus->lock, flags);
> +       command = aspeed_i2c_read(bus, ASPEED_I2C_CMD_REG);
> +       /* Bus is idle: no recovery needed. */
> +       if ((command & ASPEED_I2CD_SDA_LINE_STS) &&
> +           (command & ASPEED_I2CD_SCL_LINE_STS))
> +               goto out;
> +
> +       dev_dbg(bus->dev, "bus hung (state %x), attempting recovery\n",
> +               command);
> +
> +       /* Bus held: put bus in stop state. */
> +       if ((command & ASPEED_I2CD_SDA_LINE_STS) &&
> +           !(command & ASPEED_I2CD_SCL_LINE_STS)) {
> +               aspeed_i2c_write(bus, ASPEED_I2CD_M_STOP_CMD,
> +                                ASPEED_I2C_CMD_REG);
> +               reinit_completion(&bus->cmd_complete);
> +               spin_unlock_irqrestore(&bus->lock, flags);
> +
> +               time_left = wait_for_completion_interruptible_timeout(
> +                               &bus->cmd_complete, bus->adap.timeout * HZ);
> +
> +               spin_lock_irqsave(&bus->lock, flags);
> +               if (time_left == 0)
> +                       ret = -ETIMEDOUT;
> +               else if (bus->cmd_err)
> +                       ret = -EIO;
> +       /* Bus error. */
> +       } else if (!(command & ASPEED_I2CD_SDA_LINE_STS)) {
> +               aspeed_i2c_write(bus, ASPEED_I2CD_BUS_RECOVER_CMD,
> +                                ASPEED_I2C_CMD_REG);
> +               reinit_completion(&bus->cmd_complete);
> +               spin_unlock_irqrestore(&bus->lock, flags);
> +
> +               time_left = wait_for_completion_interruptible_timeout(
> +                               &bus->cmd_complete, bus->adap.timeout * HZ);
> +
> +               spin_lock_irqsave(&bus->lock, flags);
> +               if (time_left == 0)
> +                       ret = -ETIMEDOUT;
> +               else if (bus->cmd_err)
> +                       ret = -EIO;
> +               /* Recovery failed. */
> +               else if (!(aspeed_i2c_read(bus, ASPEED_I2C_CMD_REG) &
> +                          ASPEED_I2CD_SDA_LINE_STS))
> +                       ret = -EIO;
> +       }
> +
> +out:
> +       spin_unlock_irqrestore(&bus->lock, flags);
> +       return ret;
> +}
> +
> +#if IS_ENABLED(CONFIG_I2C_SLAVE)
> +static bool aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus)
> +{
> +       bool irq_handled = true;
> +       u32 command;
> +       u32 irq_status;
> +       u32 status_ack = 0;
> +       u8 value;
> +       struct i2c_client *slave = bus->slave;
> +
> +       spin_lock(&bus->lock);
> +       if (!slave) {
> +               irq_handled = false;
> +               goto out;
> +       }
> +       command = aspeed_i2c_read(bus, ASPEED_I2C_CMD_REG);
> +       irq_status = aspeed_i2c_read(bus, ASPEED_I2C_INTR_STS_REG);
> +
> +       /* Slave was requested, restart state machine. */
> +       if (irq_status & ASPEED_I2CD_INTR_SLAVE_MATCH) {
> +               status_ack |= ASPEED_I2CD_INTR_SLAVE_MATCH;
> +               bus->slave_state = ASPEED_I2C_SLAVE_START;
> +       }
> +       /* Slave is not currently active, irq was for someone else. */
> +       if (bus->slave_state == ASPEED_I2C_SLAVE_STOP) {
> +               irq_handled = false;
> +               goto out;
> +       }
> +
> +       dev_dbg(bus->dev, "slave irq status 0x%08x, cmd 0x%08x\n",
> +               irq_status, command);
> +
> +       /* Slave was sent something. */
> +       if (irq_status & ASPEED_I2CD_INTR_RX_DONE) {
> +               value = aspeed_i2c_read(bus, ASPEED_I2C_BYTE_BUF_REG) >> 8;
> +               /* Handle address frame. */
> +               if (bus->slave_state == ASPEED_I2C_SLAVE_START) {
> +                       if (value & 0x1)
> +                               bus->slave_state =
> +                                               ASPEED_I2C_SLAVE_READ_REQUESTED;
> +                       else
> +                               bus->slave_state =
> +                                               ASPEED_I2C_SLAVE_WRITE_REQUESTED;
> +               }
> +               status_ack |= ASPEED_I2CD_INTR_RX_DONE;
> +       }
> +
> +       /* Slave was asked to stop. */
> +       if (irq_status & ASPEED_I2CD_INTR_NORMAL_STOP) {
> +               status_ack |= ASPEED_I2CD_INTR_NORMAL_STOP;
> +               bus->slave_state = ASPEED_I2C_SLAVE_STOP;
> +       }
> +       if (irq_status & ASPEED_I2CD_INTR_TX_NAK) {
> +               status_ack |= ASPEED_I2CD_INTR_TX_NAK;
> +               bus->slave_state = ASPEED_I2C_SLAVE_STOP;
> +       }
> +
> +       if (bus->slave_state == ASPEED_I2C_SLAVE_READ_REQUESTED) {
> +               if (irq_status & ASPEED_I2CD_INTR_TX_ACK)
> +                       dev_err(bus->dev, "Unexpected ACK on read request.\n");
> +               bus->slave_state = ASPEED_I2C_SLAVE_READ_PROCESSED;
> +
> +               i2c_slave_event(slave, I2C_SLAVE_READ_REQUESTED, &value);
> +               aspeed_i2c_write(bus, value, ASPEED_I2C_BYTE_BUF_REG);
> +               aspeed_i2c_write(bus, ASPEED_I2CD_S_TX_CMD, ASPEED_I2C_CMD_REG);
> +       } else if (bus->slave_state == ASPEED_I2C_SLAVE_READ_PROCESSED) {
> +               status_ack |= ASPEED_I2CD_INTR_TX_ACK;
> +               if (!(irq_status & ASPEED_I2CD_INTR_TX_ACK))
> +                       dev_err(bus->dev,
> +                               "Expected ACK after processed read.\n");
> +               i2c_slave_event(slave, I2C_SLAVE_READ_PROCESSED, &value);
> +               aspeed_i2c_write(bus, value, ASPEED_I2C_BYTE_BUF_REG);
> +               aspeed_i2c_write(bus, ASPEED_I2CD_S_TX_CMD, ASPEED_I2C_CMD_REG);
> +       } else if (bus->slave_state == ASPEED_I2C_SLAVE_WRITE_REQUESTED) {
> +               bus->slave_state = ASPEED_I2C_SLAVE_WRITE_RECEIVED;
> +               i2c_slave_event(slave, I2C_SLAVE_WRITE_REQUESTED, &value);
> +       } else if (bus->slave_state == ASPEED_I2C_SLAVE_WRITE_RECEIVED) {
> +               i2c_slave_event(slave, I2C_SLAVE_WRITE_RECEIVED, &value);
> +       } else if (bus->slave_state == ASPEED_I2C_SLAVE_STOP) {
> +               i2c_slave_event(slave, I2C_SLAVE_STOP, &value);
> +       }
> +
> +       if (status_ack != irq_status)
> +               dev_err(bus->dev,
> +                       "irq handled != irq. expected %x, but was %x\n",
> +                       irq_status, status_ack);
> +       aspeed_i2c_write(bus, status_ack, ASPEED_I2C_INTR_STS_REG);
> +
> +out:
> +       spin_unlock(&bus->lock);
> +       return irq_handled;
> +}
> +#endif
> +
> +static bool aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus)
> +{
> +       const u32 errs = ASPEED_I2CD_INTR_ARBIT_LOSS |
> +               ASPEED_I2CD_INTR_ABNORMAL |
> +               ASPEED_I2CD_INTR_SCL_TIMEOUT |
> +               ASPEED_I2CD_INTR_SDA_DL_TIMEOUT |
> +               ASPEED_I2CD_INTR_TX_NAK;
> +       u32 irq_status;
> +
> +       spin_lock(&bus->lock);
> +       irq_status = aspeed_i2c_read(bus, ASPEED_I2C_INTR_STS_REG);
> +       bus->cmd_err = irq_status & errs;
> +
> +       dev_dbg(bus->dev, "master irq status 0x%08x\n", irq_status);
> +
> +       /* No message to transfer. */
> +       if (bus->cmd_err ||
> +           (irq_status & ASPEED_I2CD_INTR_NORMAL_STOP) ||
> +           (irq_status & ASPEED_I2CD_INTR_BUS_RECOVER_DONE)) {
> +               complete(&bus->cmd_complete);
> +               goto out;
> +       } else if (!bus->msg || bus->msg_pos >= bus->msg->len)
> +               goto out;
> +
> +       if ((bus->msg->flags & I2C_M_RD) &&
> +           (irq_status & ASPEED_I2CD_INTR_RX_DONE)) {
> +               bus->msg->buf[bus->msg_pos++] = aspeed_i2c_read(
> +                               bus, ASPEED_I2C_BYTE_BUF_REG) >> 8;
> +               if (bus->msg_pos + 1 < bus->msg->len)
> +                       aspeed_i2c_write(bus, ASPEED_I2CD_M_RX_CMD,
> +                                        ASPEED_I2C_CMD_REG);
> +               else if (bus->msg_pos < bus->msg->len)
> +                       aspeed_i2c_write(bus, ASPEED_I2CD_M_RX_CMD |
> +                                     ASPEED_I2CD_M_S_RX_CMD_LAST,
> +                                     ASPEED_I2C_CMD_REG);
> +       } else if (!(bus->msg->flags & I2C_M_RD) &&
> +                  (irq_status & ASPEED_I2CD_INTR_TX_ACK)) {
> +               aspeed_i2c_write(bus, bus->msg->buf[bus->msg_pos++],
> +                             ASPEED_I2C_BYTE_BUF_REG);
> +               aspeed_i2c_write(bus, ASPEED_I2CD_M_TX_CMD, ASPEED_I2C_CMD_REG);
> +       }
> +
> +       /* Transmission complete: notify caller. */
> +       if (bus->msg_pos >= bus->msg->len)
> +               complete(&bus->cmd_complete);
> +out:
> +       aspeed_i2c_write(bus, irq_status, ASPEED_I2C_INTR_STS_REG);
> +       spin_unlock(&bus->lock);
> +       return true;
> +}
> +
> +static irqreturn_t aspeed_i2c_bus_irq(int irq, void *dev_id)
> +{
> +       struct aspeed_i2c_bus *bus = dev_id;
> +
> +#if IS_ENABLED(CONFIG_I2C_SLAVE)
> +       if (aspeed_i2c_slave_irq(bus)) {
> +               dev_dbg(bus->dev, "irq handled by slave.\n");
> +               return IRQ_HANDLED;
> +       }
> +#endif
> +       if (aspeed_i2c_master_irq(bus)) {
> +               dev_dbg(bus->dev, "irq handled by master.\n");
> +               return IRQ_HANDLED;
> +       }
> +       dev_err(bus->dev, "irq not handled properly!\n");
> +       return IRQ_HANDLED;
> +}
> +
> +static int aspeed_i2c_master_single_xfer(struct i2c_adapter *adap,
> +                                     struct i2c_msg *msg)
> +{
> +       struct aspeed_i2c_bus *bus = adap->algo_data;
> +       unsigned long flags;
> +       u8 slave_addr;
> +       u32 command = ASPEED_I2CD_M_START_CMD | ASPEED_I2CD_M_TX_CMD;
> +       int ret = msg->len;
> +       unsigned long time_left;
> +
> +       spin_lock_irqsave(&bus->lock, flags);
> +       bus->msg = msg;
> +       bus->msg_pos = 0;
> +       slave_addr = msg->addr << 1;
> +       if (msg->flags & I2C_M_RD) {
> +               slave_addr |= 1;
> +               command |= ASPEED_I2CD_M_RX_CMD;
> +               if (msg->len == 1)
> +                       command |= ASPEED_I2CD_M_S_RX_CMD_LAST;
> +       }
> +       aspeed_i2c_write(bus, slave_addr, ASPEED_I2C_BYTE_BUF_REG);
> +       aspeed_i2c_write(bus, command, ASPEED_I2C_CMD_REG);
> +       reinit_completion(&bus->cmd_complete);
> +       spin_unlock_irqrestore(&bus->lock, flags);
> +
> +       time_left = wait_for_completion_interruptible_timeout(
> +                       &bus->cmd_complete, bus->adap.timeout * HZ * msg->len);
> +       if (time_left == 0)
> +               return -ETIMEDOUT;
> +
> +       spin_lock_irqsave(&bus->lock, flags);
> +       if (bus->cmd_err)
> +               ret = -EIO;
> +       bus->msg = NULL;
> +       spin_unlock_irqrestore(&bus->lock, flags);
> +
> +       return ret;
> +}
> +
> +static int aspeed_i2c_master_xfer(struct i2c_adapter *adap,
> +                                 struct i2c_msg *msgs, int num)
> +{
> +       struct aspeed_i2c_bus *bus = adap->algo_data;
> +       int ret;
> +       int i;
> +       unsigned long flags;
> +       unsigned long time_left;
> +
> +       /* If bus is busy, attempt recovery. We assume a single master
> +        * environment.
> +        */
> +       if (aspeed_i2c_read(bus, ASPEED_I2C_CMD_REG) &
> +           ASPEED_I2CD_BUS_BUSY_STS) {
> +               ret = aspeed_i2c_recover_bus(bus);
> +               if (ret)
> +                       return ret;
> +       }
> +
> +       for (i = 0; i < num; i++) {
> +               ret = aspeed_i2c_master_single_xfer(adap, &msgs[i]);
> +               if (ret < 0)
> +                       break;
> +               /* TODO: Support other forms of I2C protocol mangling. */
> +               if (msgs[i].flags & I2C_M_STOP) {
> +                       spin_lock_irqsave(&bus->lock, flags);
> +                       aspeed_i2c_write(bus, ASPEED_I2CD_M_STOP_CMD,
> +                                     ASPEED_I2C_CMD_REG);
> +                       reinit_completion(&bus->cmd_complete);
> +                       spin_unlock_irqrestore(&bus->lock, flags);
> +
> +                       time_left = wait_for_completion_interruptible_timeout(
> +                                       &bus->cmd_complete,
> +                                       bus->adap.timeout * HZ);
> +                       if (time_left == 0)
> +                               return -ETIMEDOUT;
> +               }
> +       }
> +
> +       spin_lock_irqsave(&bus->lock, flags);
> +       aspeed_i2c_write(bus, ASPEED_I2CD_M_STOP_CMD, ASPEED_I2C_CMD_REG);
> +       reinit_completion(&bus->cmd_complete);
> +       spin_unlock_irqrestore(&bus->lock, flags);
> +
> +       time_left = wait_for_completion_interruptible_timeout(
> +                       &bus->cmd_complete, bus->adap.timeout * HZ);
> +       if (time_left == 0)
> +               return -ETIMEDOUT;
> +
> +       /* If nothing went wrong, return number of messages transferred. */
> +       if (ret < 0)
> +               return ret;
> +       else
> +               return i;
> +}
> +
> +static u32 aspeed_i2c_functionality(struct i2c_adapter *adap)
> +{
> +       return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_SMBUS_BLOCK_DATA;
> +}
> +
> +#if IS_ENABLED(CONFIG_I2C_SLAVE)
> +static int aspeed_i2c_reg_slave(struct i2c_client *client)
> +{
> +       struct aspeed_i2c_bus *bus;
> +       unsigned long flags;
> +       u32 addr_reg_val;
> +       u32 func_ctrl_reg_val;
> +
> +       bus = client->adapter->algo_data;
> +       spin_lock_irqsave(&bus->lock, flags);
> +       if (bus->slave) {
> +               spin_unlock_irqrestore(&bus->lock, flags);
> +               return -EINVAL;
> +       }
> +
> +       /* Set slave addr. */
> +       addr_reg_val = aspeed_i2c_read(bus, ASPEED_I2C_DEV_ADDR_REG);
> +       addr_reg_val &= ~ASPEED_I2CD_DEV_ADDR_MASK;
> +       addr_reg_val |= client->addr & ASPEED_I2CD_DEV_ADDR_MASK;
> +       aspeed_i2c_write(bus, addr_reg_val, ASPEED_I2C_DEV_ADDR_REG);
> +
> +       /* Switch from master mode to slave mode. */
> +       func_ctrl_reg_val = aspeed_i2c_read(bus, ASPEED_I2C_FUN_CTRL_REG);
> +       func_ctrl_reg_val &= ~ASPEED_I2CD_MASTER_EN;
> +       func_ctrl_reg_val |= ASPEED_I2CD_SLAVE_EN;
> +       aspeed_i2c_write(bus, func_ctrl_reg_val, ASPEED_I2C_FUN_CTRL_REG);
> +
> +       bus->slave = client;
> +       bus->slave_state = ASPEED_I2C_SLAVE_STOP;
> +       spin_unlock_irqrestore(&bus->lock, flags);
> +       return 0;
> +}
> +
> +static int aspeed_i2c_unreg_slave(struct i2c_client *client)
> +{
> +       struct aspeed_i2c_bus *bus = client->adapter->algo_data;
> +       unsigned long flags;
> +       u32 func_ctrl_reg_val;
> +
> +       spin_lock_irqsave(&bus->lock, flags);
> +       if (!bus->slave) {
> +               spin_unlock_irqrestore(&bus->lock, flags);
> +               return -EINVAL;
> +       }
> +
> +       /* Switch from slave mode to master mode. */
> +       func_ctrl_reg_val = aspeed_i2c_read(bus, ASPEED_I2C_FUN_CTRL_REG);
> +       func_ctrl_reg_val &= ~ASPEED_I2CD_SLAVE_EN;
> +       func_ctrl_reg_val |= ASPEED_I2CD_MASTER_EN;
> +       aspeed_i2c_write(bus, func_ctrl_reg_val, ASPEED_I2C_FUN_CTRL_REG);
> +
> +       bus->slave = NULL;
> +       spin_unlock_irqrestore(&bus->lock, flags);
> +       return 0;
> +}
> +#endif
> +
> +static const struct i2c_algorithm aspeed_i2c_algo = {
> +       .master_xfer    = aspeed_i2c_master_xfer,
> +       .functionality  = aspeed_i2c_functionality,
> +#if IS_ENABLED(CONFIG_I2C_SLAVE)
> +       .reg_slave      = aspeed_i2c_reg_slave,
> +       .unreg_slave    = aspeed_i2c_unreg_slave,
> +#endif
> +};
> +
> +static u32 aspeed_i2c_get_clk_reg_val(u32 divider_ratio)
> +{
> +       unsigned int inc = 0, div;
> +       u32 scl_low, scl_high, data;
> +
> +       for (div = 0; divider_ratio >= 16; div++) {
> +               inc |= (divider_ratio & 1);
> +               divider_ratio >>= 1;
> +       }
> +       divider_ratio += inc;
> +       scl_low = (divider_ratio >> 1) - 1;
> +       scl_high = divider_ratio - scl_low - 2;
> +       data = 0x77700300 | (scl_high << 16) | (scl_low << 12) | div;
> +       return data;
> +}
> +
> +static int aspeed_i2c_init_clk(struct aspeed_i2c_bus *bus,
> +                           struct platform_device *pdev)
> +{
> +       struct clk *pclk;
> +       u32 clk_freq;
> +       u32 divider_ratio;
> +       int ret;
> +
> +       pclk = devm_clk_get(&pdev->dev, NULL);
> +       if (IS_ERR(pclk)) {
> +               dev_err(&pdev->dev, "clk_get failed\n");
> +               return PTR_ERR(pclk);
> +       }
> +       ret = of_property_read_u32(pdev->dev.of_node,
> +                       "clock-frequency", &clk_freq);
> +       if (ret < 0) {
> +               dev_err(&pdev->dev,
> +                               "Could not read clock-frequency property\n");
> +               clk_freq = 100000;
> +       }
> +       divider_ratio = clk_get_rate(pclk) / clk_freq;
> +       /* We just need the clock rate, we don't actually use the clk object. */
> +       devm_clk_put(&pdev->dev, pclk);
> +
> +       /* Set AC Timing */
> +       if (clk_freq / 1000 > 400) {
> +               aspeed_i2c_write(bus, aspeed_i2c_read(bus,
> +                                                     ASPEED_I2C_FUN_CTRL_REG) |
> +                               ASPEED_I2CD_M_HIGH_SPEED_EN |
> +                               ASPEED_I2CD_M_SDA_DRIVE_1T_EN |
> +                               ASPEED_I2CD_SDA_DRIVE_1T_EN,
> +                               ASPEED_I2C_FUN_CTRL_REG);
> +
> +               aspeed_i2c_write(bus, 0x3, ASPEED_I2C_AC_TIMING_REG2);
> +               aspeed_i2c_write(bus, aspeed_i2c_get_clk_reg_val(divider_ratio),
> +                             ASPEED_I2C_AC_TIMING_REG1);
> +       } else {
> +               aspeed_i2c_write(bus, aspeed_i2c_get_clk_reg_val(divider_ratio),
> +                             ASPEED_I2C_AC_TIMING_REG1);
> +               aspeed_i2c_write(bus, ASPEED_NO_TIMEOUT_CTRL,
> +                                ASPEED_I2C_AC_TIMING_REG2);
> +       }
> +
> +       return 0;
> +}
> +
> +static void noop(struct irq_data *data) { }
> +
> +static struct irq_chip aspeed_i2c_irqchip = {
> +       .name           = "ast-i2c",
> +       .irq_unmask     = noop,
> +       .irq_mask       = noop,
> +};
> +
> +static int aspeed_i2c_probe_bus(struct platform_device *pdev)
> +{
> +       struct aspeed_i2c_bus *bus;
> +       struct aspeed_i2c_controller *controller =
> +                       dev_get_drvdata(pdev->dev.parent);
> +       struct resource *res;
> +       int ret, bus_num, irq;
> +
> +       bus = devm_kzalloc(&pdev->dev, sizeof(*bus), GFP_KERNEL);
> +       if (!bus)
> +               return -ENOMEM;
> +
> +       ret = of_property_read_u32(pdev->dev.of_node, "bus", &bus_num);
> +       if (ret)
> +               return -ENXIO;
> +
> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +       bus->base = devm_ioremap_resource(&pdev->dev, res);
> +       if (IS_ERR(bus->base))
> +               return PTR_ERR(bus->base);
> +
> +       bus->irq = platform_get_irq(pdev, 0);
> +       if (bus->irq < 0) {
> +               dev_err(&pdev->dev, "platform_get_irq failed\n");
> +               return -ENXIO;
> +       }
> +
> +       irq = irq_create_mapping(controller->irq_domain, bus_num);
> +       irq_set_chip_data(irq, controller);
> +       irq_set_chip_and_handler(irq, &aspeed_i2c_irqchip, handle_simple_irq);
> +       ret = devm_request_irq(&pdev->dev, bus->irq, aspeed_i2c_bus_irq,
> +                       0, dev_name(&pdev->dev), bus);
> +       if (ret) {
> +               dev_err(&pdev->dev, "devm_request_irq failed\n");
> +               return -ENXIO;
> +       }
> +
> +       /* Initialize the I2C adapter */
> +       spin_lock_init(&bus->lock);
> +       init_completion(&bus->cmd_complete);
> +       bus->adap.nr = bus_num;
> +       bus->adap.owner = THIS_MODULE;
> +       bus->adap.retries = 0;
> +       bus->adap.timeout = 5;
> +       bus->adap.algo = &aspeed_i2c_algo;
> +       bus->adap.algo_data = bus;
> +       bus->adap.dev.parent = &pdev->dev;
> +       bus->adap.dev.of_node = pdev->dev.of_node;
> +       snprintf(bus->adap.name, sizeof(bus->adap.name), "Aspeed i2c-%d",
> +                       bus_num);
> +
> +       bus->dev = &pdev->dev;
> +
> +       /* reset device: disable master & slave functions */
> +       aspeed_i2c_write(bus, 0, ASPEED_I2C_FUN_CTRL_REG);
> +
> +       ret = aspeed_i2c_init_clk(bus, pdev);
> +       if (ret < 0)
> +               return ret;
> +
> +       /* Enable Master Mode */
> +       aspeed_i2c_write(bus, aspeed_i2c_read(bus, ASPEED_I2C_FUN_CTRL_REG) |
> +                     ASPEED_I2CD_MASTER_EN |
> +                     ASPEED_I2CD_MULTI_MASTER_DIS, ASPEED_I2C_FUN_CTRL_REG);
> +
> +       /* Set interrupt generation of I2C controller */
> +       aspeed_i2c_write(bus, ASPEED_I2CD_INTR_SDA_DL_TIMEOUT |
> +                       ASPEED_I2CD_INTR_BUS_RECOVER_DONE |
> +                       ASPEED_I2CD_INTR_SCL_TIMEOUT |
> +                       ASPEED_I2CD_INTR_ABNORMAL |
> +                       ASPEED_I2CD_INTR_NORMAL_STOP |
> +                       ASPEED_I2CD_INTR_ARBIT_LOSS |
> +                       ASPEED_I2CD_INTR_RX_DONE |
> +                       ASPEED_I2CD_INTR_TX_NAK |
> +                       ASPEED_I2CD_INTR_TX_ACK,
> +                       ASPEED_I2C_INTR_CTRL_REG);
> +
> +       ret = i2c_add_numbered_adapter(&bus->adap);
> +       if (ret < 0)
> +               return -ENXIO;
> +
> +       platform_set_drvdata(pdev, bus);
> +
> +       dev_info(bus->dev, "i2c bus %d registered, irq %d\n",
> +                       bus->adap.nr, bus->irq);
> +
> +       return 0;
> +}
> +
> +static int aspeed_i2c_remove_bus(struct platform_device *pdev)
> +{
> +       struct aspeed_i2c_bus *bus = platform_get_drvdata(pdev);
> +
> +       i2c_del_adapter(&bus->adap);
> +       return 0;
> +}
> +
> +static const struct of_device_id aspeed_i2c_bus_of_table[] = {
> +       { .compatible = "aspeed,ast2400-i2c-bus", },
> +       { .compatible = "aspeed,ast2500-i2c-bus", },
> +       { },
> +};
> +MODULE_DEVICE_TABLE(of, aspeed_i2c_bus_of_table);
> +
> +static struct platform_driver aspeed_i2c_bus_driver = {
> +       .probe          = aspeed_i2c_probe_bus,
> +       .remove         = aspeed_i2c_remove_bus,
> +       .driver         = {
> +               .name           = "ast-i2c-bus",
> +               .of_match_table = aspeed_i2c_bus_of_table,
> +       },
> +};
> +
> +static void aspeed_i2c_controller_irq(struct irq_desc *desc)
> +{
> +       struct aspeed_i2c_controller *c = irq_desc_get_handler_data(desc);
> +       unsigned long p, status;
> +       unsigned int bus_irq;
> +
> +       status = readl(c->base);
> +       for_each_set_bit(p, &status, ASPEED_I2C_NUM_BUS) {
> +               bus_irq = irq_find_mapping(c->irq_domain, p);
> +               generic_handle_irq(bus_irq);
> +       }
> +}
> +
> +static int aspeed_i2c_probe_controller(struct platform_device *pdev)
> +{
> +       struct aspeed_i2c_controller *controller;
> +       struct device_node *np;
> +       struct resource *res;
> +
> +       controller = kzalloc(sizeof(*controller), GFP_KERNEL);
> +       if (!controller)
> +               return -ENOMEM;
> +
> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +       controller->base = devm_ioremap_resource(&pdev->dev, res);
> +       if (IS_ERR(controller->base))
> +               return PTR_ERR(controller->base);
> +
> +       controller->irq = platform_get_irq(pdev, 0);
> +       if (controller->irq < 0) {
> +               dev_err(&pdev->dev, "no platform IRQ\n");
> +               return -ENXIO;
> +       }
> +
> +       controller->irq_domain = irq_domain_add_linear(pdev->dev.of_node,
> +                       ASPEED_I2C_NUM_BUS, &irq_domain_simple_ops, NULL);
> +       if (!controller->irq_domain) {
> +               dev_err(&pdev->dev, "no IRQ domain\n");
> +               return -ENXIO;
> +       }
> +       controller->irq_domain->name = "ast-i2c-domain";
> +
> +       irq_set_chained_handler_and_data(controller->irq,
> +                       aspeed_i2c_controller_irq, controller);
> +
> +       controller->dev = &pdev->dev;
> +
> +       platform_set_drvdata(pdev, controller);
> +
> +       dev_info(controller->dev, "i2c controller registered, irq %d\n",
> +                       controller->irq);
> +
> +       for_each_child_of_node(pdev->dev.of_node, np) {
> +               int ret;
> +               u32 bus_num;
> +               char bus_id[sizeof("i2c-12345")];
> +
> +               /*
> +                * Set a useful name derived from the bus number; the device
> +                * tree should provide us with one that corresponds to the
> +                * hardware numbering.  If the property is missing the
> +                * probe would fail so just skip it here.
> +                */
> +
> +               ret = of_property_read_u32(np, "bus", &bus_num);
> +               if (ret)
> +                       continue;
> +
> +               ret = snprintf(bus_id, sizeof(bus_id), "i2c-%u", bus_num);
> +               if (ret >= sizeof(bus_id))
> +                       continue;
> +
> +               of_platform_device_create(np, bus_id, &pdev->dev);
> +               of_node_put(np);
> +       }
> +
> +       return 0;
> +}
> +
> +static int aspeed_i2c_remove_controller(struct platform_device *pdev)
> +{
> +       struct aspeed_i2c_controller *controller = platform_get_drvdata(pdev);
> +
> +       irq_domain_remove(controller->irq_domain);
> +       return 0;
> +}
> +
> +static const struct of_device_id aspeed_i2c_controller_of_table[] = {
> +       { .compatible = "aspeed,ast2400-i2c-controller", },
> +       { .compatible = "aspeed,ast2500-i2c-controller", },
> +       { },
> +};
> +MODULE_DEVICE_TABLE(of, aspeed_i2c_controller_of_table);
> +
> +static struct platform_driver aspeed_i2c_controller_driver = {
> +       .probe          = aspeed_i2c_probe_controller,
> +       .remove         = aspeed_i2c_remove_controller,
> +       .driver         = {
> +               .name           = "ast-i2c-controller",
> +               .of_match_table = aspeed_i2c_controller_of_table,
> +       },
> +};
> +
> +static int __init aspeed_i2c_driver_init(void)
> +{
> +       int ret;
> +
> +       ret = platform_driver_register(&aspeed_i2c_controller_driver);
> +       if (ret < 0)
> +               return ret;
> +       return platform_driver_register(&aspeed_i2c_bus_driver);
> +}
> +module_init(aspeed_i2c_driver_init);
> +
> +static void __exit aspeed_i2c_driver_exit(void)
> +{
> +       platform_driver_unregister(&aspeed_i2c_bus_driver);
> +       platform_driver_unregister(&aspeed_i2c_controller_driver);
> +}
> +module_exit(aspeed_i2c_driver_exit);
> +
> +MODULE_AUTHOR("Brendan Higgins <brendanhiggins@google.com>");
> +MODULE_DESCRIPTION("Aspeed I2C Bus Driver");
> +MODULE_LICENSE("GPL");
> --
> 2.8.0.rc3.226.g39d4020
>

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v2 2/2] i2c: aspeed: added documentation for Aspeed I2C driver
  2016-09-10  1:55       ` [PATCH v2 2/2] i2c: aspeed: added documentation for Aspeed I2C driver Brendan Higgins
@ 2016-09-19 21:35         ` Rob Herring
  2016-09-19 23:26           ` Brendan Higgins
  0 siblings, 1 reply; 17+ messages in thread
From: Rob Herring @ 2016-09-19 21:35 UTC (permalink / raw)
  To: Brendan Higgins
  Cc: wsa, mark.rutland, linux-i2c, devicetree, linux-kernel, openbmc,
	joel, jk

On Fri, Sep 09, 2016 at 06:55:51PM -0700, Brendan Higgins wrote:
> Added device tree binding documentation for Aspeed I2C controller and
> busses.
> 
> Signed-off-by: Brendan Higgins <brendanhiggins@google.com>
> ---
> Changes for v2:
>   - None
> ---
>  .../devicetree/bindings/i2c/i2c-aspeed.txt         | 63 ++++++++++++++++++++++
>  1 file changed, 63 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/i2c/i2c-aspeed.txt
> 
> diff --git a/Documentation/devicetree/bindings/i2c/i2c-aspeed.txt b/Documentation/devicetree/bindings/i2c/i2c-aspeed.txt
> new file mode 100644
> index 0000000..df68f2a
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/i2c/i2c-aspeed.txt
> @@ -0,0 +1,63 @@
> +Device tree configuration for the I2C controller and busses on the AST24XX
> +and AST25XX SoCs.
> +
> +Controller:
> +
> +	Required Properties:
> +	- #address-cells	: should be 1
> +	- #size-cells 		: should be 1
> +	- #interrupt-cells 	: should be 1
> +	- compatible 		: should be "aspeed,ast2400-i2c-controller"
> +				  or "aspeed,ast2500-i2c-controller"
> +	- reg			: address start and range of controller
> +	- ranges		: defines address offset and range for busses
> +	- interrupts		: interrupt number
> +	- clocks		: root clock of bus, should reference the APB
> +				  clock
> +	- clock-ranges		: specifies that child busses can inherit clocks
> +	- interrupt-controller	: denotes that the controller receives and fires
> +				  new interrupts for child busses
> +
> +Bus:
> +
> +	Required Properties:
> +	- #address-cells	: should be 1
> +	- #size-cells		: should be 0
> +	- reg			: address offset and range of bus
> +	- compatible		: should be "aspeed,ast2400-i2c-bus"
> +				  or "aspeed,ast2500-i2c-bus"
> +	- bus			: the bus's number

Don't use indexes. The reg property is enough to id which bus is which.

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [PATCH v3 1/2] i2c: aspeed: added driver for Aspeed I2C
  2016-09-19 17:56       ` [PATCH v2 1/2] i2c: aspeed: added driver for Aspeed I2C Brendan Higgins
@ 2016-09-19 23:23         ` Brendan Higgins
  2016-09-19 23:23           ` [PATCH v3 2/2] i2c: aspeed: added documentation for Aspeed I2C driver Brendan Higgins
  0 siblings, 1 reply; 17+ messages in thread
From: Brendan Higgins @ 2016-09-19 23:23 UTC (permalink / raw)
  To: wsa, robh+dt, mark.rutland
  Cc: linux-i2c, devicetree, linux-kernel, openbmc, joel, jk, Brendan Higgins

Added initial master and slave support for Aspeed I2C controller.
Supports fourteen busses present in ast24xx and ast25xx BMC SoCs by
Aspeed.

Signed-off-by: Brendan Higgins <brendanhiggins@google.com>
---
Changes for v2:
  - Added single module_init (multiple was breaking some builds).
Changes for v3:
  - Removed "bus" device tree param; now extracted from bus address offset
---
 drivers/i2c/busses/Kconfig      |  10 +
 drivers/i2c/busses/Makefile     |   1 +
 drivers/i2c/busses/i2c-aspeed.c | 846 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 857 insertions(+)
 create mode 100644 drivers/i2c/busses/i2c-aspeed.c

diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index 5c3993b..0178c6c 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -998,6 +998,16 @@ config I2C_RCAR
 	  This driver can also be built as a module.  If so, the module
 	  will be called i2c-rcar.
 
+config I2C_ASPEED
+	tristate "Aspeed AST2xxx SoC I2C Controller"
+	depends on (ARCH_ASPEED || COMPILE_TEST) && OF
+	help
+	  If you say yes to this option, support will be included for the
+	  Aspeed AST2xxx SoC I2C controller.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called i2c-aspeed.
+
 comment "External I2C/SMBus adapter drivers"
 
 config I2C_DIOLAN_U2C
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index 37f2819..49631cd 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -96,6 +96,7 @@ obj-$(CONFIG_I2C_XILINX)	+= i2c-xiic.o
 obj-$(CONFIG_I2C_XLR)		+= i2c-xlr.o
 obj-$(CONFIG_I2C_XLP9XX)	+= i2c-xlp9xx.o
 obj-$(CONFIG_I2C_RCAR)		+= i2c-rcar.o
+obj-$(CONFIG_I2C_ASPEED)	+= i2c-aspeed.o
 
 # External I2C/SMBus adapter drivers
 obj-$(CONFIG_I2C_DIOLAN_U2C)	+= i2c-diolan-u2c.o
diff --git a/drivers/i2c/busses/i2c-aspeed.c b/drivers/i2c/busses/i2c-aspeed.c
new file mode 100644
index 0000000..207533e
--- /dev/null
+++ b/drivers/i2c/busses/i2c-aspeed.c
@@ -0,0 +1,846 @@
+/*
+ *  I2C adapter for the ASPEED I2C bus.
+ *
+ *  Copyright (C) 2012-2020  ASPEED Technology Inc.
+ *  Copyright 2016 IBM Corporation
+ *  Copyright 2016 Google, Inc.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/completion.h>
+#include <linux/slab.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+
+/* I2C Register */
+#define ASPEED_I2C_FUN_CTRL_REG				0x00
+#define ASPEED_I2C_AC_TIMING_REG1			0x04
+#define ASPEED_I2C_AC_TIMING_REG2			0x08
+#define ASPEED_I2C_INTR_CTRL_REG			0x0c
+#define ASPEED_I2C_INTR_STS_REG				0x10
+#define ASPEED_I2C_CMD_REG				0x14
+#define ASPEED_I2C_DEV_ADDR_REG				0x18
+#define ASPEED_I2C_BYTE_BUF_REG				0x20
+#define ASPEED_I2C_OFFSET_START				0x40
+#define ASPEED_I2C_OFFSET_INCREMENT			0x40
+
+#define ASPEED_I2C_NUM_BUS 14
+
+/* Global Register Definition */
+/* 0x00 : I2C Interrupt Status Register  */
+/* 0x08 : I2C Interrupt Target Assignment  */
+
+/* Device Register Definition */
+/* 0x00 : I2CD Function Control Register  */
+#define ASPEED_I2CD_MULTI_MASTER_DIS			BIT(15)
+#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_SLAVE_EN				BIT(1)
+#define ASPEED_I2CD_MASTER_EN				BIT(0)
+
+/* 0x08 : I2CD Clock and AC Timing Control Register #2 */
+#define ASPEED_NO_TIMEOUT_CTRL				0
+
+
+/* 0x0c : I2CD Interrupt Control Register &
+ * 0x10 : I2CD Interrupt Status Register
+ *
+ * These share bit definitions, so use the same values for the enable &
+ * status bits.
+ */
+#define ASPEED_I2CD_INTR_SDA_DL_TIMEOUT			BIT(14)
+#define ASPEED_I2CD_INTR_BUS_RECOVER_DONE		BIT(13)
+#define ASPEED_I2CD_INTR_SLAVE_MATCH			BIT(7)
+#define ASPEED_I2CD_INTR_SCL_TIMEOUT			BIT(6)
+#define ASPEED_I2CD_INTR_ABNORMAL			BIT(5)
+#define ASPEED_I2CD_INTR_NORMAL_STOP			BIT(4)
+#define ASPEED_I2CD_INTR_ARBIT_LOSS			BIT(3)
+#define ASPEED_I2CD_INTR_RX_DONE			BIT(2)
+#define ASPEED_I2CD_INTR_TX_NAK				BIT(1)
+#define ASPEED_I2CD_INTR_TX_ACK				BIT(0)
+
+/* 0x14 : I2CD Command/Status Register   */
+#define ASPEED_I2CD_SCL_LINE_STS			BIT(18)
+#define ASPEED_I2CD_SDA_LINE_STS			BIT(17)
+#define ASPEED_I2CD_BUS_BUSY_STS			BIT(16)
+#define ASPEED_I2CD_BUS_RECOVER_CMD			BIT(11)
+
+/* Command Bit */
+#define ASPEED_I2CD_M_STOP_CMD				BIT(5)
+#define ASPEED_I2CD_M_S_RX_CMD_LAST			BIT(4)
+#define ASPEED_I2CD_M_RX_CMD				BIT(3)
+#define ASPEED_I2CD_S_TX_CMD				BIT(2)
+#define ASPEED_I2CD_M_TX_CMD				BIT(1)
+#define ASPEED_I2CD_M_START_CMD				BIT(0)
+
+/* 0x18 : I2CD Slave Device Address Register   */
+#define ASPEED_I2CD_DEV_ADDR_MASK			GENMASK(6, 0)
+
+enum aspeed_i2c_slave_state {
+	ASPEED_I2C_SLAVE_START,
+	ASPEED_I2C_SLAVE_READ_REQUESTED,
+	ASPEED_I2C_SLAVE_READ_PROCESSED,
+	ASPEED_I2C_SLAVE_WRITE_REQUESTED,
+	ASPEED_I2C_SLAVE_WRITE_RECEIVED,
+	ASPEED_I2C_SLAVE_STOP,
+};
+
+struct aspeed_i2c_bus {
+	struct i2c_adapter		adap;
+	struct device			*dev;
+	void __iomem			*base;
+	spinlock_t			lock;
+	struct completion		cmd_complete;
+	int				irq;
+	/* Transaction state. */
+	struct i2c_msg			*msg;
+	int				msg_pos;
+	u32				cmd_err;
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+	struct i2c_client		*slave;
+	enum aspeed_i2c_slave_state	slave_state;
+#endif
+};
+
+struct aspeed_i2c_controller {
+	struct device		*dev;
+	void __iomem		*base;
+	int			irq;
+	struct irq_domain	*irq_domain;
+};
+
+static inline void aspeed_i2c_write(struct aspeed_i2c_bus *bus, u32 val,
+				    u32 reg)
+{
+	writel(val, bus->base + reg);
+}
+
+static inline u32 aspeed_i2c_read(struct aspeed_i2c_bus *bus, u32 reg)
+{
+	return readl(bus->base + reg);
+}
+
+static u8 aspeed_i2c_recover_bus(struct aspeed_i2c_bus *bus)
+{
+	u32 command;
+	unsigned long time_left;
+	unsigned long flags;
+	int ret = 0;
+
+	spin_lock_irqsave(&bus->lock, flags);
+	command = aspeed_i2c_read(bus, ASPEED_I2C_CMD_REG);
+	/* Bus is idle: no recovery needed. */
+	if ((command & ASPEED_I2CD_SDA_LINE_STS) &&
+	    (command & ASPEED_I2CD_SCL_LINE_STS))
+		goto out;
+
+	dev_dbg(bus->dev, "bus hung (state %x), attempting recovery\n",
+		command);
+
+	/* Bus held: put bus in stop state. */
+	if ((command & ASPEED_I2CD_SDA_LINE_STS) &&
+	    !(command & ASPEED_I2CD_SCL_LINE_STS)) {
+		aspeed_i2c_write(bus, ASPEED_I2CD_M_STOP_CMD,
+				 ASPEED_I2C_CMD_REG);
+		reinit_completion(&bus->cmd_complete);
+		spin_unlock_irqrestore(&bus->lock, flags);
+
+		time_left = wait_for_completion_interruptible_timeout(
+				&bus->cmd_complete, bus->adap.timeout * HZ);
+
+		spin_lock_irqsave(&bus->lock, flags);
+		if (time_left == 0)
+			ret = -ETIMEDOUT;
+		else if (bus->cmd_err)
+			ret = -EIO;
+	/* Bus error. */
+	} else if (!(command & ASPEED_I2CD_SDA_LINE_STS)) {
+		aspeed_i2c_write(bus, ASPEED_I2CD_BUS_RECOVER_CMD,
+				 ASPEED_I2C_CMD_REG);
+		reinit_completion(&bus->cmd_complete);
+		spin_unlock_irqrestore(&bus->lock, flags);
+
+		time_left = wait_for_completion_interruptible_timeout(
+				&bus->cmd_complete, bus->adap.timeout * HZ);
+
+		spin_lock_irqsave(&bus->lock, flags);
+		if (time_left == 0)
+			ret = -ETIMEDOUT;
+		else if (bus->cmd_err)
+			ret = -EIO;
+		/* Recovery failed. */
+		else if (!(aspeed_i2c_read(bus, ASPEED_I2C_CMD_REG) &
+			   ASPEED_I2CD_SDA_LINE_STS))
+			ret = -EIO;
+	}
+
+out:
+	spin_unlock_irqrestore(&bus->lock, flags);
+	return ret;
+}
+
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+static bool aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus)
+{
+	bool irq_handled = true;
+	u32 command;
+	u32 irq_status;
+	u32 status_ack = 0;
+	u8 value;
+	struct i2c_client *slave = bus->slave;
+
+	spin_lock(&bus->lock);
+	if (!slave) {
+		irq_handled = false;
+		goto out;
+	}
+	command = aspeed_i2c_read(bus, ASPEED_I2C_CMD_REG);
+	irq_status = aspeed_i2c_read(bus, ASPEED_I2C_INTR_STS_REG);
+
+	/* Slave was requested, restart state machine. */
+	if (irq_status & ASPEED_I2CD_INTR_SLAVE_MATCH) {
+		status_ack |= ASPEED_I2CD_INTR_SLAVE_MATCH;
+		bus->slave_state = ASPEED_I2C_SLAVE_START;
+	}
+	/* Slave is not currently active, irq was for someone else. */
+	if (bus->slave_state == ASPEED_I2C_SLAVE_STOP) {
+		irq_handled = false;
+		goto out;
+	}
+
+	dev_dbg(bus->dev, "slave irq status 0x%08x, cmd 0x%08x\n",
+		irq_status, command);
+
+	/* Slave was sent something. */
+	if (irq_status & ASPEED_I2CD_INTR_RX_DONE) {
+		value = aspeed_i2c_read(bus, ASPEED_I2C_BYTE_BUF_REG) >> 8;
+		/* Handle address frame. */
+		if (bus->slave_state == ASPEED_I2C_SLAVE_START) {
+			if (value & 0x1)
+				bus->slave_state =
+						ASPEED_I2C_SLAVE_READ_REQUESTED;
+			else
+				bus->slave_state =
+						ASPEED_I2C_SLAVE_WRITE_REQUESTED;
+		}
+		status_ack |= ASPEED_I2CD_INTR_RX_DONE;
+	}
+
+	/* Slave was asked to stop. */
+	if (irq_status & ASPEED_I2CD_INTR_NORMAL_STOP) {
+		status_ack |= ASPEED_I2CD_INTR_NORMAL_STOP;
+		bus->slave_state = ASPEED_I2C_SLAVE_STOP;
+	}
+	if (irq_status & ASPEED_I2CD_INTR_TX_NAK) {
+		status_ack |= ASPEED_I2CD_INTR_TX_NAK;
+		bus->slave_state = ASPEED_I2C_SLAVE_STOP;
+	}
+
+	if (bus->slave_state == ASPEED_I2C_SLAVE_READ_REQUESTED) {
+		if (irq_status & ASPEED_I2CD_INTR_TX_ACK)
+			dev_err(bus->dev, "Unexpected ACK on read request.\n");
+		bus->slave_state = ASPEED_I2C_SLAVE_READ_PROCESSED;
+
+		i2c_slave_event(slave, I2C_SLAVE_READ_REQUESTED, &value);
+		aspeed_i2c_write(bus, value, ASPEED_I2C_BYTE_BUF_REG);
+		aspeed_i2c_write(bus, ASPEED_I2CD_S_TX_CMD, ASPEED_I2C_CMD_REG);
+	} else if (bus->slave_state == ASPEED_I2C_SLAVE_READ_PROCESSED) {
+		status_ack |= ASPEED_I2CD_INTR_TX_ACK;
+		if (!(irq_status & ASPEED_I2CD_INTR_TX_ACK))
+			dev_err(bus->dev,
+				"Expected ACK after processed read.\n");
+		i2c_slave_event(slave, I2C_SLAVE_READ_PROCESSED, &value);
+		aspeed_i2c_write(bus, value, ASPEED_I2C_BYTE_BUF_REG);
+		aspeed_i2c_write(bus, ASPEED_I2CD_S_TX_CMD, ASPEED_I2C_CMD_REG);
+	} else if (bus->slave_state == ASPEED_I2C_SLAVE_WRITE_REQUESTED) {
+		bus->slave_state = ASPEED_I2C_SLAVE_WRITE_RECEIVED;
+		i2c_slave_event(slave, I2C_SLAVE_WRITE_REQUESTED, &value);
+	} else if (bus->slave_state == ASPEED_I2C_SLAVE_WRITE_RECEIVED) {
+		i2c_slave_event(slave, I2C_SLAVE_WRITE_RECEIVED, &value);
+	} else if (bus->slave_state == ASPEED_I2C_SLAVE_STOP) {
+		i2c_slave_event(slave, I2C_SLAVE_STOP, &value);
+	}
+
+	if (status_ack != irq_status)
+		dev_err(bus->dev,
+			"irq handled != irq. expected %x, but was %x\n",
+			irq_status, status_ack);
+	aspeed_i2c_write(bus, status_ack, ASPEED_I2C_INTR_STS_REG);
+
+out:
+	spin_unlock(&bus->lock);
+	return irq_handled;
+}
+#endif
+
+static bool aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus)
+{
+	const u32 errs = ASPEED_I2CD_INTR_ARBIT_LOSS |
+		ASPEED_I2CD_INTR_ABNORMAL |
+		ASPEED_I2CD_INTR_SCL_TIMEOUT |
+		ASPEED_I2CD_INTR_SDA_DL_TIMEOUT |
+		ASPEED_I2CD_INTR_TX_NAK;
+	u32 irq_status;
+
+	spin_lock(&bus->lock);
+	irq_status = aspeed_i2c_read(bus, ASPEED_I2C_INTR_STS_REG);
+	bus->cmd_err = irq_status & errs;
+
+	dev_dbg(bus->dev, "master irq status 0x%08x\n", irq_status);
+
+	/* No message to transfer. */
+	if (bus->cmd_err ||
+	    (irq_status & ASPEED_I2CD_INTR_NORMAL_STOP) ||
+	    (irq_status & ASPEED_I2CD_INTR_BUS_RECOVER_DONE)) {
+		complete(&bus->cmd_complete);
+		goto out;
+	} else if (!bus->msg || bus->msg_pos >= bus->msg->len)
+		goto out;
+
+	if ((bus->msg->flags & I2C_M_RD) &&
+	    (irq_status & ASPEED_I2CD_INTR_RX_DONE)) {
+		bus->msg->buf[bus->msg_pos++] = aspeed_i2c_read(
+				bus, ASPEED_I2C_BYTE_BUF_REG) >> 8;
+		if (bus->msg_pos + 1 < bus->msg->len)
+			aspeed_i2c_write(bus, ASPEED_I2CD_M_RX_CMD,
+					 ASPEED_I2C_CMD_REG);
+		else if (bus->msg_pos < bus->msg->len)
+			aspeed_i2c_write(bus, ASPEED_I2CD_M_RX_CMD |
+				      ASPEED_I2CD_M_S_RX_CMD_LAST,
+				      ASPEED_I2C_CMD_REG);
+	} else if (!(bus->msg->flags & I2C_M_RD) &&
+		   (irq_status & ASPEED_I2CD_INTR_TX_ACK)) {
+		aspeed_i2c_write(bus, bus->msg->buf[bus->msg_pos++],
+			      ASPEED_I2C_BYTE_BUF_REG);
+		aspeed_i2c_write(bus, ASPEED_I2CD_M_TX_CMD, ASPEED_I2C_CMD_REG);
+	}
+
+	/* Transmission complete: notify caller. */
+	if (bus->msg_pos >= bus->msg->len)
+		complete(&bus->cmd_complete);
+out:
+	aspeed_i2c_write(bus, irq_status, ASPEED_I2C_INTR_STS_REG);
+	spin_unlock(&bus->lock);
+	return true;
+}
+
+static irqreturn_t aspeed_i2c_bus_irq(int irq, void *dev_id)
+{
+	struct aspeed_i2c_bus *bus = dev_id;
+
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+	if (aspeed_i2c_slave_irq(bus)) {
+		dev_dbg(bus->dev, "irq handled by slave.\n");
+		return IRQ_HANDLED;
+	}
+#endif
+	if (aspeed_i2c_master_irq(bus)) {
+		dev_dbg(bus->dev, "irq handled by master.\n");
+		return IRQ_HANDLED;
+	}
+	dev_err(bus->dev, "irq not handled properly!\n");
+	return IRQ_HANDLED;
+}
+
+static int aspeed_i2c_master_single_xfer(struct i2c_adapter *adap,
+				      struct i2c_msg *msg)
+{
+	struct aspeed_i2c_bus *bus = adap->algo_data;
+	unsigned long flags;
+	u8 slave_addr;
+	u32 command = ASPEED_I2CD_M_START_CMD | ASPEED_I2CD_M_TX_CMD;
+	int ret = msg->len;
+	unsigned long time_left;
+
+	spin_lock_irqsave(&bus->lock, flags);
+	bus->msg = msg;
+	bus->msg_pos = 0;
+	slave_addr = msg->addr << 1;
+	if (msg->flags & I2C_M_RD) {
+		slave_addr |= 1;
+		command |= ASPEED_I2CD_M_RX_CMD;
+		if (msg->len == 1)
+			command |= ASPEED_I2CD_M_S_RX_CMD_LAST;
+	}
+	aspeed_i2c_write(bus, slave_addr, ASPEED_I2C_BYTE_BUF_REG);
+	aspeed_i2c_write(bus, command, ASPEED_I2C_CMD_REG);
+	reinit_completion(&bus->cmd_complete);
+	spin_unlock_irqrestore(&bus->lock, flags);
+
+	time_left = wait_for_completion_interruptible_timeout(
+			&bus->cmd_complete, bus->adap.timeout * HZ * msg->len);
+	if (time_left == 0)
+		return -ETIMEDOUT;
+
+	spin_lock_irqsave(&bus->lock, flags);
+	if (bus->cmd_err)
+		ret = -EIO;
+	bus->msg = NULL;
+	spin_unlock_irqrestore(&bus->lock, flags);
+
+	return ret;
+}
+
+static int aspeed_i2c_master_xfer(struct i2c_adapter *adap,
+				  struct i2c_msg *msgs, int num)
+{
+	struct aspeed_i2c_bus *bus = adap->algo_data;
+	int ret;
+	int i;
+	unsigned long flags;
+	unsigned long time_left;
+
+	/* If bus is busy, attempt recovery. We assume a single master
+	 * environment.
+	 */
+	if (aspeed_i2c_read(bus, ASPEED_I2C_CMD_REG) &
+	    ASPEED_I2CD_BUS_BUSY_STS) {
+		ret = aspeed_i2c_recover_bus(bus);
+		if (ret)
+			return ret;
+	}
+
+	for (i = 0; i < num; i++) {
+		ret = aspeed_i2c_master_single_xfer(adap, &msgs[i]);
+		if (ret < 0)
+			break;
+		/* TODO: Support other forms of I2C protocol mangling. */
+		if (msgs[i].flags & I2C_M_STOP) {
+			spin_lock_irqsave(&bus->lock, flags);
+			aspeed_i2c_write(bus, ASPEED_I2CD_M_STOP_CMD,
+				      ASPEED_I2C_CMD_REG);
+			reinit_completion(&bus->cmd_complete);
+			spin_unlock_irqrestore(&bus->lock, flags);
+
+			time_left = wait_for_completion_interruptible_timeout(
+					&bus->cmd_complete,
+					bus->adap.timeout * HZ);
+			if (time_left == 0)
+				return -ETIMEDOUT;
+		}
+	}
+
+	spin_lock_irqsave(&bus->lock, flags);
+	aspeed_i2c_write(bus, ASPEED_I2CD_M_STOP_CMD, ASPEED_I2C_CMD_REG);
+	reinit_completion(&bus->cmd_complete);
+	spin_unlock_irqrestore(&bus->lock, flags);
+
+	time_left = wait_for_completion_interruptible_timeout(
+			&bus->cmd_complete, bus->adap.timeout * HZ);
+	if (time_left == 0)
+		return -ETIMEDOUT;
+
+	/* If nothing went wrong, return number of messages transferred. */
+	if (ret < 0)
+		return ret;
+	else
+		return i;
+}
+
+static u32 aspeed_i2c_functionality(struct i2c_adapter *adap)
+{
+	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_SMBUS_BLOCK_DATA;
+}
+
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+static int aspeed_i2c_reg_slave(struct i2c_client *client)
+{
+	struct aspeed_i2c_bus *bus;
+	unsigned long flags;
+	u32 addr_reg_val;
+	u32 func_ctrl_reg_val;
+
+	bus = client->adapter->algo_data;
+	spin_lock_irqsave(&bus->lock, flags);
+	if (bus->slave) {
+		spin_unlock_irqrestore(&bus->lock, flags);
+		return -EINVAL;
+	}
+
+	/* Set slave addr. */
+	addr_reg_val = aspeed_i2c_read(bus, ASPEED_I2C_DEV_ADDR_REG);
+	addr_reg_val &= ~ASPEED_I2CD_DEV_ADDR_MASK;
+	addr_reg_val |= client->addr & ASPEED_I2CD_DEV_ADDR_MASK;
+	aspeed_i2c_write(bus, addr_reg_val, ASPEED_I2C_DEV_ADDR_REG);
+
+	/* Switch from master mode to slave mode. */
+	func_ctrl_reg_val = aspeed_i2c_read(bus, ASPEED_I2C_FUN_CTRL_REG);
+	func_ctrl_reg_val &= ~ASPEED_I2CD_MASTER_EN;
+	func_ctrl_reg_val |= ASPEED_I2CD_SLAVE_EN;
+	aspeed_i2c_write(bus, func_ctrl_reg_val, ASPEED_I2C_FUN_CTRL_REG);
+
+	bus->slave = client;
+	bus->slave_state = ASPEED_I2C_SLAVE_STOP;
+	spin_unlock_irqrestore(&bus->lock, flags);
+	return 0;
+}
+
+static int aspeed_i2c_unreg_slave(struct i2c_client *client)
+{
+	struct aspeed_i2c_bus *bus = client->adapter->algo_data;
+	unsigned long flags;
+	u32 func_ctrl_reg_val;
+
+	spin_lock_irqsave(&bus->lock, flags);
+	if (!bus->slave) {
+		spin_unlock_irqrestore(&bus->lock, flags);
+		return -EINVAL;
+	}
+
+	/* Switch from slave mode to master mode. */
+	func_ctrl_reg_val = aspeed_i2c_read(bus, ASPEED_I2C_FUN_CTRL_REG);
+	func_ctrl_reg_val &= ~ASPEED_I2CD_SLAVE_EN;
+	func_ctrl_reg_val |= ASPEED_I2CD_MASTER_EN;
+	aspeed_i2c_write(bus, func_ctrl_reg_val, ASPEED_I2C_FUN_CTRL_REG);
+
+	bus->slave = NULL;
+	spin_unlock_irqrestore(&bus->lock, flags);
+	return 0;
+}
+#endif
+
+static const struct i2c_algorithm aspeed_i2c_algo = {
+	.master_xfer	= aspeed_i2c_master_xfer,
+	.functionality	= aspeed_i2c_functionality,
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+	.reg_slave	= aspeed_i2c_reg_slave,
+	.unreg_slave	= aspeed_i2c_unreg_slave,
+#endif
+};
+
+static u32 aspeed_i2c_get_clk_reg_val(u32 divider_ratio)
+{
+	unsigned int inc = 0, div;
+	u32 scl_low, scl_high, data;
+
+	for (div = 0; divider_ratio >= 16; div++) {
+		inc |= (divider_ratio & 1);
+		divider_ratio >>= 1;
+	}
+	divider_ratio += inc;
+	scl_low = (divider_ratio >> 1) - 1;
+	scl_high = divider_ratio - scl_low - 2;
+	data = 0x77700300 | (scl_high << 16) | (scl_low << 12) | div;
+	return data;
+}
+
+static int aspeed_i2c_init_clk(struct aspeed_i2c_bus *bus,
+			    struct platform_device *pdev)
+{
+	struct clk *pclk;
+	u32 clk_freq;
+	u32 divider_ratio;
+	int ret;
+
+	pclk = devm_clk_get(&pdev->dev, NULL);
+	if (IS_ERR(pclk)) {
+		dev_err(&pdev->dev, "clk_get failed\n");
+		return PTR_ERR(pclk);
+	}
+	ret = of_property_read_u32(pdev->dev.of_node,
+			"clock-frequency", &clk_freq);
+	if (ret < 0) {
+		dev_err(&pdev->dev,
+				"Could not read clock-frequency property\n");
+		clk_freq = 100000;
+	}
+	divider_ratio = clk_get_rate(pclk) / clk_freq;
+	/* We just need the clock rate, we don't actually use the clk object. */
+	devm_clk_put(&pdev->dev, pclk);
+
+	/* Set AC Timing */
+	if (clk_freq / 1000 > 400) {
+		aspeed_i2c_write(bus, aspeed_i2c_read(bus,
+						      ASPEED_I2C_FUN_CTRL_REG) |
+				ASPEED_I2CD_M_HIGH_SPEED_EN |
+				ASPEED_I2CD_M_SDA_DRIVE_1T_EN |
+				ASPEED_I2CD_SDA_DRIVE_1T_EN,
+				ASPEED_I2C_FUN_CTRL_REG);
+
+		aspeed_i2c_write(bus, 0x3, ASPEED_I2C_AC_TIMING_REG2);
+		aspeed_i2c_write(bus, aspeed_i2c_get_clk_reg_val(divider_ratio),
+			      ASPEED_I2C_AC_TIMING_REG1);
+	} else {
+		aspeed_i2c_write(bus, aspeed_i2c_get_clk_reg_val(divider_ratio),
+			      ASPEED_I2C_AC_TIMING_REG1);
+		aspeed_i2c_write(bus, ASPEED_NO_TIMEOUT_CTRL,
+				 ASPEED_I2C_AC_TIMING_REG2);
+	}
+
+	return 0;
+}
+
+static int aspeed_i2c_bus_num(struct device_node *of_node)
+{
+	u32 bus_offset;
+	int ret;
+
+	ret = of_property_read_u32(of_node, "reg", &bus_offset);
+	if (ret < 0)
+		return ret;
+	/* Each bus resides at a fixed increment after the first. */
+	return (bus_offset - ASPEED_I2C_OFFSET_START) /
+			ASPEED_I2C_OFFSET_INCREMENT;
+}
+
+static void noop(struct irq_data *data) { }
+
+static struct irq_chip aspeed_i2c_irqchip = {
+	.name		= "ast-i2c",
+	.irq_unmask	= noop,
+	.irq_mask	= noop,
+};
+
+static int aspeed_i2c_probe_bus(struct platform_device *pdev)
+{
+	struct aspeed_i2c_bus *bus;
+	struct aspeed_i2c_controller *controller =
+			dev_get_drvdata(pdev->dev.parent);
+	struct resource *res;
+	int ret, bus_num, irq;
+
+	bus = devm_kzalloc(&pdev->dev, sizeof(*bus), GFP_KERNEL);
+	if (!bus)
+		return -ENOMEM;
+
+	bus_num = aspeed_i2c_bus_num(pdev->dev.of_node);
+	if (bus_num < 0)
+		return -ENXIO;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	bus->base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(bus->base))
+		return PTR_ERR(bus->base);
+
+	bus->irq = platform_get_irq(pdev, 0);
+	if (bus->irq < 0) {
+		dev_err(&pdev->dev, "platform_get_irq failed\n");
+		return -ENXIO;
+	}
+
+	irq = irq_create_mapping(controller->irq_domain, bus_num);
+	irq_set_chip_data(irq, controller);
+	irq_set_chip_and_handler(irq, &aspeed_i2c_irqchip, handle_simple_irq);
+	ret = devm_request_irq(&pdev->dev, bus->irq, aspeed_i2c_bus_irq,
+			0, dev_name(&pdev->dev), bus);
+	if (ret) {
+		dev_err(&pdev->dev, "devm_request_irq failed\n");
+		return -ENXIO;
+	}
+
+	/* Initialize the I2C adapter */
+	spin_lock_init(&bus->lock);
+	init_completion(&bus->cmd_complete);
+	bus->adap.nr = bus_num;
+	bus->adap.owner = THIS_MODULE;
+	bus->adap.retries = 0;
+	bus->adap.timeout = 5;
+	bus->adap.algo = &aspeed_i2c_algo;
+	bus->adap.algo_data = bus;
+	bus->adap.dev.parent = &pdev->dev;
+	bus->adap.dev.of_node = pdev->dev.of_node;
+	snprintf(bus->adap.name, sizeof(bus->adap.name), "Aspeed i2c-%d",
+			bus_num);
+
+	bus->dev = &pdev->dev;
+
+	/* reset device: disable master & slave functions */
+	aspeed_i2c_write(bus, 0, ASPEED_I2C_FUN_CTRL_REG);
+
+	ret = aspeed_i2c_init_clk(bus, pdev);
+	if (ret < 0)
+		return ret;
+
+	/* Enable Master Mode */
+	aspeed_i2c_write(bus, aspeed_i2c_read(bus, ASPEED_I2C_FUN_CTRL_REG) |
+		      ASPEED_I2CD_MASTER_EN |
+		      ASPEED_I2CD_MULTI_MASTER_DIS, ASPEED_I2C_FUN_CTRL_REG);
+
+	/* Set interrupt generation of I2C controller */
+	aspeed_i2c_write(bus, ASPEED_I2CD_INTR_SDA_DL_TIMEOUT |
+			ASPEED_I2CD_INTR_BUS_RECOVER_DONE |
+			ASPEED_I2CD_INTR_SCL_TIMEOUT |
+			ASPEED_I2CD_INTR_ABNORMAL |
+			ASPEED_I2CD_INTR_NORMAL_STOP |
+			ASPEED_I2CD_INTR_ARBIT_LOSS |
+			ASPEED_I2CD_INTR_RX_DONE |
+			ASPEED_I2CD_INTR_TX_NAK |
+			ASPEED_I2CD_INTR_TX_ACK,
+			ASPEED_I2C_INTR_CTRL_REG);
+
+	ret = i2c_add_numbered_adapter(&bus->adap);
+	if (ret < 0)
+		return -ENXIO;
+
+	platform_set_drvdata(pdev, bus);
+
+	dev_info(bus->dev, "i2c bus %d registered, irq %d\n",
+			bus->adap.nr, bus->irq);
+
+	return 0;
+}
+
+static int aspeed_i2c_remove_bus(struct platform_device *pdev)
+{
+	struct aspeed_i2c_bus *bus = platform_get_drvdata(pdev);
+
+	i2c_del_adapter(&bus->adap);
+	return 0;
+}
+
+static const struct of_device_id aspeed_i2c_bus_of_table[] = {
+	{ .compatible = "aspeed,ast2400-i2c-bus", },
+	{ .compatible = "aspeed,ast2500-i2c-bus", },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, aspeed_i2c_bus_of_table);
+
+static struct platform_driver aspeed_i2c_bus_driver = {
+	.probe		= aspeed_i2c_probe_bus,
+	.remove		= aspeed_i2c_remove_bus,
+	.driver		= {
+		.name		= "ast-i2c-bus",
+		.of_match_table	= aspeed_i2c_bus_of_table,
+	},
+};
+
+static void aspeed_i2c_controller_irq(struct irq_desc *desc)
+{
+	struct aspeed_i2c_controller *c = irq_desc_get_handler_data(desc);
+	unsigned long p, status;
+	unsigned int bus_irq;
+
+	status = readl(c->base);
+	for_each_set_bit(p, &status, ASPEED_I2C_NUM_BUS) {
+		bus_irq = irq_find_mapping(c->irq_domain, p);
+		generic_handle_irq(bus_irq);
+	}
+}
+
+static int aspeed_i2c_probe_controller(struct platform_device *pdev)
+{
+	struct aspeed_i2c_controller *controller;
+	struct device_node *np;
+	struct resource *res;
+
+	controller = kzalloc(sizeof(*controller), GFP_KERNEL);
+	if (!controller)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	controller->base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(controller->base))
+		return PTR_ERR(controller->base);
+
+	controller->irq = platform_get_irq(pdev, 0);
+	if (controller->irq < 0) {
+		dev_err(&pdev->dev, "no platform IRQ\n");
+		return -ENXIO;
+	}
+
+	controller->irq_domain = irq_domain_add_linear(pdev->dev.of_node,
+			ASPEED_I2C_NUM_BUS, &irq_domain_simple_ops, NULL);
+	if (!controller->irq_domain) {
+		dev_err(&pdev->dev, "no IRQ domain\n");
+		return -ENXIO;
+	}
+	controller->irq_domain->name = "ast-i2c-domain";
+
+	irq_set_chained_handler_and_data(controller->irq,
+			aspeed_i2c_controller_irq, controller);
+
+	controller->dev = &pdev->dev;
+
+	platform_set_drvdata(pdev, controller);
+
+	dev_info(controller->dev, "i2c controller registered, irq %d\n",
+			controller->irq);
+
+	for_each_child_of_node(pdev->dev.of_node, np) {
+		int ret;
+		int bus_num;
+		char bus_id[sizeof("i2c-12345")];
+
+		/*
+		 * Set a useful name derived from the bus number; the device
+		 * tree should provide us with one that corresponds to the
+		 * hardware numbering.  If the property is missing the
+		 * probe would fail so just skip it here.
+		 */
+
+		bus_num = aspeed_i2c_bus_num(np);
+		if (bus_num < 0)
+			continue;
+
+		ret = snprintf(bus_id, sizeof(bus_id), "i2c-%d", bus_num);
+		if (ret >= sizeof(bus_id))
+			continue;
+
+		of_platform_device_create(np, bus_id, &pdev->dev);
+		of_node_put(np);
+	}
+
+	return 0;
+}
+
+static int aspeed_i2c_remove_controller(struct platform_device *pdev)
+{
+	struct aspeed_i2c_controller *controller = platform_get_drvdata(pdev);
+
+	irq_domain_remove(controller->irq_domain);
+	return 0;
+}
+
+static const struct of_device_id aspeed_i2c_controller_of_table[] = {
+	{ .compatible = "aspeed,ast2400-i2c-controller", },
+	{ .compatible = "aspeed,ast2500-i2c-controller", },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, aspeed_i2c_controller_of_table);
+
+static struct platform_driver aspeed_i2c_controller_driver = {
+	.probe		= aspeed_i2c_probe_controller,
+	.remove		= aspeed_i2c_remove_controller,
+	.driver		= {
+		.name		= "ast-i2c-controller",
+		.of_match_table	= aspeed_i2c_controller_of_table,
+	},
+};
+
+static int __init aspeed_i2c_driver_init(void)
+{
+	int ret;
+
+	ret = platform_driver_register(&aspeed_i2c_controller_driver);
+	if (ret < 0)
+		return ret;
+	return platform_driver_register(&aspeed_i2c_bus_driver);
+}
+module_init(aspeed_i2c_driver_init);
+
+static void __exit aspeed_i2c_driver_exit(void)
+{
+	platform_driver_unregister(&aspeed_i2c_bus_driver);
+	platform_driver_unregister(&aspeed_i2c_controller_driver);
+}
+module_exit(aspeed_i2c_driver_exit);
+
+MODULE_AUTHOR("Brendan Higgins <brendanhiggins@google.com>");
+MODULE_DESCRIPTION("Aspeed I2C Bus Driver");
+MODULE_LICENSE("GPL");
-- 
2.8.0.rc3.226.g39d4020

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH v3 2/2] i2c: aspeed: added documentation for Aspeed I2C driver
  2016-09-19 23:23         ` [PATCH v3 " Brendan Higgins
@ 2016-09-19 23:23           ` Brendan Higgins
  2016-09-23 18:02             ` Rob Herring
  0 siblings, 1 reply; 17+ messages in thread
From: Brendan Higgins @ 2016-09-19 23:23 UTC (permalink / raw)
  To: wsa, robh+dt, mark.rutland
  Cc: linux-i2c, devicetree, linux-kernel, openbmc, joel, jk, Brendan Higgins

Added device tree binding documentation for Aspeed I2C controller and
busses.

Signed-off-by: Brendan Higgins <brendanhiggins@google.com>
---
Changes for v2:
  - None
Changes for v3:
  - Removed reference to "bus" device tree param
---
 .../devicetree/bindings/i2c/i2c-aspeed.txt         | 61 ++++++++++++++++++++++
 1 file changed, 61 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/i2c/i2c-aspeed.txt

diff --git a/Documentation/devicetree/bindings/i2c/i2c-aspeed.txt b/Documentation/devicetree/bindings/i2c/i2c-aspeed.txt
new file mode 100644
index 0000000..dd11a97
--- /dev/null
+++ b/Documentation/devicetree/bindings/i2c/i2c-aspeed.txt
@@ -0,0 +1,61 @@
+Device tree configuration for the I2C controller and busses on the AST24XX
+and AST25XX SoCs.
+
+Controller:
+
+	Required Properties:
+	- #address-cells	: should be 1
+	- #size-cells 		: should be 1
+	- #interrupt-cells 	: should be 1
+	- compatible 		: should be "aspeed,ast2400-i2c-controller"
+				  or "aspeed,ast2500-i2c-controller"
+	- reg			: address start and range of controller
+	- ranges		: defines address offset and range for busses
+	- interrupts		: interrupt number
+	- clocks		: root clock of bus, should reference the APB
+				  clock
+	- clock-ranges		: specifies that child busses can inherit clocks
+	- interrupt-controller	: denotes that the controller receives and fires
+				  new interrupts for child busses
+
+Bus:
+
+	Required Properties:
+	- #address-cells	: should be 1
+	- #size-cells		: should be 0
+	- reg			: address offset and range of bus
+	- compatible		: should be "aspeed,ast2400-i2c-bus"
+				  or "aspeed,ast2500-i2c-bus"
+	- interrupts		: interrupt number
+
+	Optional Properties:
+	- clock-frequency	: frequency of the bus clock in Hz
+				  defaults to 100 kHz when not specified
+
+Example:
+
+i2c: i2c@1e78a000 {
+	#address-cells = <1>;
+	#size-cells = <1>;
+	#interrupt-cells = <1>;
+
+	compatible = "aspeed,ast2400-i2c-controller";
+	reg = <0x1e78a000 0x40>;
+	ranges = <0 0x1e78a000 0x1000>;
+	interrupts = <12>;
+	clocks = <&clk_apb>;
+	clock-ranges;
+	interrupt-controller;
+
+	i2c0: i2c-bus@40 {
+		#address-cells = <1>;
+		#size-cells = <0>;
+		reg = <0x40 0x40>;
+		compatible = "aspeed,ast2400-i2c-bus";
+		clock-frequency = <100000>;
+		status = "disabled";
+		interrupts = <0>;
+		interrupt-parent = <&i2c>;
+	};
+};
+
-- 
2.8.0.rc3.226.g39d4020

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* Re: [PATCH v2 2/2] i2c: aspeed: added documentation for Aspeed I2C driver
  2016-09-19 21:35         ` Rob Herring
@ 2016-09-19 23:26           ` Brendan Higgins
  2016-09-20 18:08             ` Brendan Higgins
       [not found]             ` <CAFd5g46j02sb2w_DwaXAgnfcO=ONmJGZwqdRneNd0QHq-t53rA@mail.gmail.com>
  0 siblings, 2 replies; 17+ messages in thread
From: Brendan Higgins @ 2016-09-19 23:26 UTC (permalink / raw)
  To: Rob Herring
  Cc: Wolfram Sang, mark.rutland, linux-i2c, devicetree, linux-kernel,
	OpenBMC Maillist, Joel Stanley, Jeremy Kerr

Addressed in v3.

Thanks!

On Mon, Sep 19, 2016 at 2:35 PM, Rob Herring <robh@kernel.org> wrote:
> On Fri, Sep 09, 2016 at 06:55:51PM -0700, Brendan Higgins wrote:
>> Added device tree binding documentation for Aspeed I2C controller and
>> busses.
>>
>> Signed-off-by: Brendan Higgins <brendanhiggins@google.com>
>> ---
>> Changes for v2:
>>   - None
>> ---
>>  .../devicetree/bindings/i2c/i2c-aspeed.txt         | 63 ++++++++++++++++++++++
>>  1 file changed, 63 insertions(+)
>>  create mode 100644 Documentation/devicetree/bindings/i2c/i2c-aspeed.txt
>>
>> diff --git a/Documentation/devicetree/bindings/i2c/i2c-aspeed.txt b/Documentation/devicetree/bindings/i2c/i2c-aspeed.txt
>> new file mode 100644
>> index 0000000..df68f2a
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/i2c/i2c-aspeed.txt
>> @@ -0,0 +1,63 @@
>> +Device tree configuration for the I2C controller and busses on the AST24XX
>> +and AST25XX SoCs.
>> +
>> +Controller:
>> +
>> +     Required Properties:
>> +     - #address-cells        : should be 1
>> +     - #size-cells           : should be 1
>> +     - #interrupt-cells      : should be 1
>> +     - compatible            : should be "aspeed,ast2400-i2c-controller"
>> +                               or "aspeed,ast2500-i2c-controller"
>> +     - reg                   : address start and range of controller
>> +     - ranges                : defines address offset and range for busses
>> +     - interrupts            : interrupt number
>> +     - clocks                : root clock of bus, should reference the APB
>> +                               clock
>> +     - clock-ranges          : specifies that child busses can inherit clocks
>> +     - interrupt-controller  : denotes that the controller receives and fires
>> +                               new interrupts for child busses
>> +
>> +Bus:
>> +
>> +     Required Properties:
>> +     - #address-cells        : should be 1
>> +     - #size-cells           : should be 0
>> +     - reg                   : address offset and range of bus
>> +     - compatible            : should be "aspeed,ast2400-i2c-bus"
>> +                               or "aspeed,ast2500-i2c-bus"
>> +     - bus                   : the bus's number
>
> Don't use indexes. The reg property is enough to id which bus is which.

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v2 2/2] i2c: aspeed: added documentation for Aspeed I2C driver
  2016-09-19 23:26           ` Brendan Higgins
@ 2016-09-20 18:08             ` Brendan Higgins
  2016-09-20 23:03               ` Brendan Higgins
       [not found]             ` <CAFd5g46j02sb2w_DwaXAgnfcO=ONmJGZwqdRneNd0QHq-t53rA@mail.gmail.com>
  1 sibling, 1 reply; 17+ messages in thread
From: Brendan Higgins @ 2016-09-20 18:08 UTC (permalink / raw)
  To: Rob Herring
  Cc: Wolfram Sang, mark.rutland, linux-i2c, devicetree, linux-kernel,
	OpenBMC Maillist, Joel Stanley, Jeremy Kerr

(sorry if you get a duplicate, I forgot to send plain text)

First off, someone pointed out to me that the mapping that I used
between addresses and bus numbers is not actually valid for busses
8-14.

This could be fixed by checking the offset, but I am wondering if that
is the right way to do it. It seems like this is not completely
trivial so maybe this should be specified in the device tree? If that
is the case, should I do this as another reg entry or go back to the
way I was doing it before?

On Mon, Sep 19, 2016 at 4:26 PM, Brendan Higgins
<brendanhiggins@google.com> wrote:
> Addressed in v3.
>
> Thanks!
>
> On Mon, Sep 19, 2016 at 2:35 PM, Rob Herring <robh@kernel.org> wrote:
>> On Fri, Sep 09, 2016 at 06:55:51PM -0700, Brendan Higgins wrote:
>>> Added device tree binding documentation for Aspeed I2C controller and
>>> busses.
>>>
>>> Signed-off-by: Brendan Higgins <brendanhiggins@google.com>
>>> ---
>>> Changes for v2:
>>>   - None
>>> ---
>>>  .../devicetree/bindings/i2c/i2c-aspeed.txt         | 63 ++++++++++++++++++++++
>>>  1 file changed, 63 insertions(+)
>>>  create mode 100644 Documentation/devicetree/bindings/i2c/i2c-aspeed.txt
>>>
>>> diff --git a/Documentation/devicetree/bindings/i2c/i2c-aspeed.txt b/Documentation/devicetree/bindings/i2c/i2c-aspeed.txt
>>> new file mode 100644
>>> index 0000000..df68f2a
>>> --- /dev/null
>>> +++ b/Documentation/devicetree/bindings/i2c/i2c-aspeed.txt
>>> @@ -0,0 +1,63 @@
>>> +Device tree configuration for the I2C controller and busses on the AST24XX
>>> +and AST25XX SoCs.
>>> +
>>> +Controller:
>>> +
>>> +     Required Properties:
>>> +     - #address-cells        : should be 1
>>> +     - #size-cells           : should be 1
>>> +     - #interrupt-cells      : should be 1
>>> +     - compatible            : should be "aspeed,ast2400-i2c-controller"
>>> +                               or "aspeed,ast2500-i2c-controller"
>>> +     - reg                   : address start and range of controller
>>> +     - ranges                : defines address offset and range for busses
>>> +     - interrupts            : interrupt number
>>> +     - clocks                : root clock of bus, should reference the APB
>>> +                               clock
>>> +     - clock-ranges          : specifies that child busses can inherit clocks
>>> +     - interrupt-controller  : denotes that the controller receives and fires
>>> +                               new interrupts for child busses
>>> +
>>> +Bus:
>>> +
>>> +     Required Properties:
>>> +     - #address-cells        : should be 1
>>> +     - #size-cells           : should be 0
>>> +     - reg                   : address offset and range of bus
>>> +     - compatible            : should be "aspeed,ast2400-i2c-bus"
>>> +                               or "aspeed,ast2500-i2c-bus"
>>> +     - bus                   : the bus's number
>>
>> Don't use indexes. The reg property is enough to id which bus is which.

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v2 2/2] i2c: aspeed: added documentation for Aspeed I2C driver
  2016-09-20 18:08             ` Brendan Higgins
@ 2016-09-20 23:03               ` Brendan Higgins
  0 siblings, 0 replies; 17+ messages in thread
From: Brendan Higgins @ 2016-09-20 23:03 UTC (permalink / raw)
  To: Rob Herring
  Cc: Wolfram Sang, mark.rutland, linux-i2c, devicetree, linux-kernel,
	OpenBMC Maillist, Joel Stanley, Jeremy Kerr

To be clear, these bus numbers are defined as part of the hardware
specification. I am not sure if that was clear before.

On Tue, Sep 20, 2016 at 11:08 AM, Brendan Higgins
<brendanhiggins@google.com> wrote:
> (sorry if you get a duplicate, I forgot to send plain text)
>
> First off, someone pointed out to me that the mapping that I used
> between addresses and bus numbers is not actually valid for busses
> 8-14.
>
> This could be fixed by checking the offset, but I am wondering if that
> is the right way to do it. It seems like this is not completely
> trivial so maybe this should be specified in the device tree? If that
> is the case, should I do this as another reg entry or go back to the
> way I was doing it before?
>
> On Mon, Sep 19, 2016 at 4:26 PM, Brendan Higgins
> <brendanhiggins@google.com> wrote:
>> Addressed in v3.
>>
>> Thanks!
>>
>> On Mon, Sep 19, 2016 at 2:35 PM, Rob Herring <robh@kernel.org> wrote:
>>> On Fri, Sep 09, 2016 at 06:55:51PM -0700, Brendan Higgins wrote:
>>>> Added device tree binding documentation for Aspeed I2C controller and
>>>> busses.
>>>>
>>>> Signed-off-by: Brendan Higgins <brendanhiggins@google.com>
>>>> ---
>>>> Changes for v2:
>>>>   - None
>>>> ---
>>>>  .../devicetree/bindings/i2c/i2c-aspeed.txt         | 63 ++++++++++++++++++++++
>>>>  1 file changed, 63 insertions(+)
>>>>  create mode 100644 Documentation/devicetree/bindings/i2c/i2c-aspeed.txt
>>>>
>>>> diff --git a/Documentation/devicetree/bindings/i2c/i2c-aspeed.txt b/Documentation/devicetree/bindings/i2c/i2c-aspeed.txt
>>>> new file mode 100644
>>>> index 0000000..df68f2a
>>>> --- /dev/null
>>>> +++ b/Documentation/devicetree/bindings/i2c/i2c-aspeed.txt
>>>> @@ -0,0 +1,63 @@
>>>> +Device tree configuration for the I2C controller and busses on the AST24XX
>>>> +and AST25XX SoCs.
>>>> +
>>>> +Controller:
>>>> +
>>>> +     Required Properties:
>>>> +     - #address-cells        : should be 1
>>>> +     - #size-cells           : should be 1
>>>> +     - #interrupt-cells      : should be 1
>>>> +     - compatible            : should be "aspeed,ast2400-i2c-controller"
>>>> +                               or "aspeed,ast2500-i2c-controller"
>>>> +     - reg                   : address start and range of controller
>>>> +     - ranges                : defines address offset and range for busses
>>>> +     - interrupts            : interrupt number
>>>> +     - clocks                : root clock of bus, should reference the APB
>>>> +                               clock
>>>> +     - clock-ranges          : specifies that child busses can inherit clocks
>>>> +     - interrupt-controller  : denotes that the controller receives and fires
>>>> +                               new interrupts for child busses
>>>> +
>>>> +Bus:
>>>> +
>>>> +     Required Properties:
>>>> +     - #address-cells        : should be 1
>>>> +     - #size-cells           : should be 0
>>>> +     - reg                   : address offset and range of bus
>>>> +     - compatible            : should be "aspeed,ast2400-i2c-bus"
>>>> +                               or "aspeed,ast2500-i2c-bus"
>>>> +     - bus                   : the bus's number
>>>
>>> Don't use indexes. The reg property is enough to id which bus is which.

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v2 2/2] i2c: aspeed: added documentation for Aspeed I2C driver
       [not found]             ` <CAFd5g46j02sb2w_DwaXAgnfcO=ONmJGZwqdRneNd0QHq-t53rA@mail.gmail.com>
@ 2016-09-23  6:02               ` Joel Stanley
  0 siblings, 0 replies; 17+ messages in thread
From: Joel Stanley @ 2016-09-23  6:02 UTC (permalink / raw)
  To: Brendan Higgins
  Cc: Rob Herring, Wolfram Sang, Mark Rutland, linux-i2c, devicetree,
	linux-kernel, OpenBMC Maillist, Jeremy Kerr

Hi Brendan,

On Wed, Sep 21, 2016 at 3:35 AM, Brendan Higgins
<brendanhiggins@google.com> wrote:
> First off, someone pointed out to me that the mapping that I used between
> addresses and bus numbers is not actually valid for busses 8-14.
>
> This could be fixed by checking the offset, but I am wondering if that is
> the right way to do it. It seems like this is not completely trivial so
> maybe this should be specified in the device tree? If that is the case,
> should I do this as another reg entry or go back to the way I was doing it
> before?

I don't see an alternative way to derive these numbers. As you
mention, the block of SRAM in the middle of the IP screwing up the
linear mapping.

I suggest going back to what you have in v2, with a mention in the
commit message as to why the bus property is necessary.

Cheers,

Joel

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v3 2/2] i2c: aspeed: added documentation for Aspeed I2C driver
  2016-09-19 23:23           ` [PATCH v3 2/2] i2c: aspeed: added documentation for Aspeed I2C driver Brendan Higgins
@ 2016-09-23 18:02             ` Rob Herring
  2016-10-12 20:01               ` Brendan Higgins
  0 siblings, 1 reply; 17+ messages in thread
From: Rob Herring @ 2016-09-23 18:02 UTC (permalink / raw)
  To: Brendan Higgins
  Cc: wsa, mark.rutland, linux-i2c, devicetree, linux-kernel, openbmc,
	joel, jk

On Mon, Sep 19, 2016 at 04:23:34PM -0700, Brendan Higgins wrote:
> Added device tree binding documentation for Aspeed I2C controller and
> busses.
> 
> Signed-off-by: Brendan Higgins <brendanhiggins@google.com>
> ---
> Changes for v2:
>   - None
> Changes for v3:
>   - Removed reference to "bus" device tree param
> ---
>  .../devicetree/bindings/i2c/i2c-aspeed.txt         | 61 ++++++++++++++++++++++
>  1 file changed, 61 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/i2c/i2c-aspeed.txt

Acked-by: Rob Herring <robh@kernel.org>

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v3 2/2] i2c: aspeed: added documentation for Aspeed I2C driver
  2016-09-23 18:02             ` Rob Herring
@ 2016-10-12 20:01               ` Brendan Higgins
  0 siblings, 0 replies; 17+ messages in thread
From: Brendan Higgins @ 2016-10-12 20:01 UTC (permalink / raw)
  To: Rob Herring
  Cc: Wolfram Sang, mark.rutland, linux-i2c, devicetree, linux-kernel,
	OpenBMC Maillist, Joel Stanley, Jeremy Kerr

I never got an acknowledgement to this email:
http://www.spinics.net/lists/devicetree/msg143944.html
The "bus" number is something that is defined as part of the hardware
specification for this device.

It seems we will not be able to do away with the "bus" number parameter.

Joel suggested going back to the way I was originally doing it:
http://www.spinics.net/lists/devicetree/msg144134.html

Cheers

^ permalink raw reply	[flat|nested] 17+ messages in thread

end of thread, other threads:[~2016-10-12 20:01 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-09-09 19:54 [PATCH 0/2] i2c: aspeed: added driver for Aspeed I2C Brendan Higgins
2016-09-09 19:54 ` [PATCH 1/2] " Brendan Higgins
2016-09-09 22:48   ` kbuild test robot
2016-09-10  0:48   ` kbuild test robot
2016-09-10  1:55     ` [PATCH v2 " Brendan Higgins
2016-09-10  1:55       ` [PATCH v2 2/2] i2c: aspeed: added documentation for Aspeed I2C driver Brendan Higgins
2016-09-19 21:35         ` Rob Herring
2016-09-19 23:26           ` Brendan Higgins
2016-09-20 18:08             ` Brendan Higgins
2016-09-20 23:03               ` Brendan Higgins
     [not found]             ` <CAFd5g46j02sb2w_DwaXAgnfcO=ONmJGZwqdRneNd0QHq-t53rA@mail.gmail.com>
2016-09-23  6:02               ` Joel Stanley
2016-09-19 17:56       ` [PATCH v2 1/2] i2c: aspeed: added driver for Aspeed I2C Brendan Higgins
2016-09-19 23:23         ` [PATCH v3 " Brendan Higgins
2016-09-19 23:23           ` [PATCH v3 2/2] i2c: aspeed: added documentation for Aspeed I2C driver Brendan Higgins
2016-09-23 18:02             ` Rob Herring
2016-10-12 20:01               ` Brendan Higgins
2016-09-09 19:54 ` [PATCH " Brendan Higgins

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).