All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 0/2] i2c: add support for Zhaoxin I2C controller
@ 2023-08-14  8:40 Hans Hu
  2023-08-14  8:40 ` [PATCH v2 1/2] i2c: wmt: split out i2c-viai2c-common.{c,h} Hans Hu
  2023-08-14  8:40 ` [PATCH v2 2/2] i2c: add support for Zhaoxin I2C controller Hans Hu
  0 siblings, 2 replies; 10+ messages in thread
From: Hans Hu @ 2023-08-14  8:40 UTC (permalink / raw)
  To: andi.shyti, wsa, linux-i2c; +Cc: cobechen, TonyWWang

Hi Andi, Wolfram,

The Zhaoxin I2C controller uses the same I2C IP as Wonder Media.
Both Zhaoxin and Wonder Media's I2C IP are come from VIA I2C IP.
Therefore, some codes of i2c-wmt.c can be re-used by i2c-zhaoxin.c.

As suggested earlier, created two files, named i2c-viai2c-common.{c,h}.
They contains the macro definitions, functions declaration and data
structure used by both i2c-zhaoxin.c and i2c-wmt.c.

v1->v2:
1. Fixed a compilation error for header file references.
2. use I2C platform device's name to define the irq's name.

Hans Hu (2):
  i2c: wmt: split out i2c-viai2c-common.{c,h}
  i2c: add support for Zhaoxin I2C controller

 MAINTAINERS                            |   7 +
 drivers/i2c/busses/Kconfig             |  15 +
 drivers/i2c/busses/Makefile            |   2 +
 drivers/i2c/busses/i2c-viai2c-common.c | 239 +++++++++++++++
 drivers/i2c/busses/i2c-viai2c-common.h |  76 +++++
 drivers/i2c/busses/i2c-wmt.c           | 402 ++-----------------------
 drivers/i2c/busses/i2c-zhaoxin.c       | 274 +++++++++++++++++
 7 files changed, 644 insertions(+), 371 deletions(-)
 create mode 100644 drivers/i2c/busses/i2c-viai2c-common.c
 create mode 100644 drivers/i2c/busses/i2c-viai2c-common.h
 create mode 100644 drivers/i2c/busses/i2c-zhaoxin.c

-- 
2.34.1


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

* [PATCH v2 1/2] i2c: wmt: split out i2c-viai2c-common.{c,h}
  2023-08-14  8:40 [PATCH v2 0/2] i2c: add support for Zhaoxin I2C controller Hans Hu
@ 2023-08-14  8:40 ` Hans Hu
  2023-08-25 20:02   ` Wolfram Sang
  2023-08-14  8:40 ` [PATCH v2 2/2] i2c: add support for Zhaoxin I2C controller Hans Hu
  1 sibling, 1 reply; 10+ messages in thread
From: Hans Hu @ 2023-08-14  8:40 UTC (permalink / raw)
  To: andi.shyti, wsa, linux-i2c; +Cc: cobechen, TonyWWang

1. export common marcos and use prefix VIAI2C rename them.
2. export common functions and use prefix via_i2c rename them.
3. rename data structure wmt_i2c_dev to via_i2c and export it.
4. adjust some redundant code.

Signed-off-by: Hans Hu <hanshu-oc@zhaoxin.com>
---
 drivers/i2c/busses/Kconfig             |   4 +
 drivers/i2c/busses/Makefile            |   1 +
 drivers/i2c/busses/i2c-viai2c-common.c | 233 ++++++++++++++
 drivers/i2c/busses/i2c-viai2c-common.h |  65 ++++
 drivers/i2c/busses/i2c-wmt.c           | 402 ++-----------------------
 5 files changed, 333 insertions(+), 372 deletions(-)
 create mode 100644 drivers/i2c/busses/i2c-viai2c-common.c
 create mode 100644 drivers/i2c/busses/i2c-viai2c-common.h

diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index 9cfe8fc509d7..b810030b21cd 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -1178,9 +1178,13 @@ config I2C_VERSATILE
 	  This driver can also be built as a module.  If so, the module
 	  will be called i2c-versatile.
 
+config I2C_VIAI2C_COMMON
+	tristate
+
 config I2C_WMT
 	tristate "Wondermedia WM8xxx SoC I2C bus support"
 	depends on ARCH_VT8500 || COMPILE_TEST
+	select I2C_VIAI2C_COMMON
 	help
 	  Say yes if you want to support the I2C bus on Wondermedia 8xxx-series
 	  SoCs.
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index af56fe2c75c0..b7e20c3531b5 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -120,6 +120,7 @@ obj-$(CONFIG_I2C_TEGRA_BPMP)	+= i2c-tegra-bpmp.o
 obj-$(CONFIG_I2C_UNIPHIER)	+= i2c-uniphier.o
 obj-$(CONFIG_I2C_UNIPHIER_F)	+= i2c-uniphier-f.o
 obj-$(CONFIG_I2C_VERSATILE)	+= i2c-versatile.o
+obj-$(CONFIG_I2C_VIAI2C_COMMON)	+= i2c-viai2c-common.o
 obj-$(CONFIG_I2C_WMT)		+= i2c-wmt.o
 i2c-octeon-objs := i2c-octeon-core.o i2c-octeon-platdrv.o
 obj-$(CONFIG_I2C_OCTEON)	+= i2c-octeon.o
diff --git a/drivers/i2c/busses/i2c-viai2c-common.c b/drivers/i2c/busses/i2c-viai2c-common.c
new file mode 100644
index 000000000000..ffba29ce17c5
--- /dev/null
+++ b/drivers/i2c/busses/i2c-viai2c-common.c
@@ -0,0 +1,233 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ *  Common driver for VIA i2c drivers
+ */
+#include <linux/of_irq.h>
+#include "i2c-viai2c-common.h"
+
+int via_i2c_wait_bus_ready(struct via_i2c *i2c)
+{
+	unsigned long timeout;
+
+	timeout = jiffies + VIAI2C_TIMEOUT;
+	while (!(readw(i2c->base + VIAI2C_CSR) & VIAI2C_CSR_READY_MASK)) {
+		if (time_after(jiffies, timeout)) {
+			dev_warn(i2c->dev, "timeout waiting for bus ready\n");
+			return -EBUSY;
+		}
+		msleep(20);
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(via_i2c_wait_bus_ready);
+
+int via_i2c_wait_event(struct via_i2c *i2c, u16 event)
+{
+	if (!wait_for_completion_timeout(&i2c->complete, VIAI2C_TIMEOUT)) {
+		dev_err(i2c->dev, "wait timeout(SW)\n");
+		return -ETIMEDOUT;
+	}
+
+	if (i2c->event & event)
+		return 0;
+
+	return -EIO;
+}
+EXPORT_SYMBOL(via_i2c_wait_event);
+
+static int via_i2c_write(struct via_i2c *i2c, struct i2c_msg *pmsg, int last)
+{
+	int xfer_len = 0;
+	u16 val, tcr_val = i2c->tcr;
+	void __iomem *base = i2c->base;
+
+	if (pmsg->len == 0) {
+		/*
+		 * We still need to run through the while (..) once, so
+		 * start at -1 and break out early from the loop
+		 */
+		xfer_len = -1;
+		writew(0, base + VIAI2C_CDR);
+	} else {
+		writew(pmsg->buf[0], base + VIAI2C_CDR);
+	}
+
+	if (!(pmsg->flags & I2C_M_NOSTART)) {
+		val = readb(base + VIAI2C_CR);
+		val &= ~VIAI2C_CR_TX_END;
+		val |= VIAI2C_CR_CPU_RDY;
+		writeb(val, base + VIAI2C_CR);
+	}
+
+	reinit_completion(&i2c->complete);
+
+	tcr_val |= (pmsg->addr & VIAI2C_TCR_ADDR_MASK);
+
+	writew(tcr_val, base + VIAI2C_TCR);
+
+	if (pmsg->flags & I2C_M_NOSTART) {
+		val = readb(base + VIAI2C_CR);
+		val |= VIAI2C_CR_CPU_RDY;
+		writeb(val, base + VIAI2C_CR);
+	}
+
+	while (xfer_len < pmsg->len) {
+		int err;
+
+		err = via_i2c_wait_event(i2c, VIAI2C_ISR_BYTE_END);
+		if (err)
+			return err;
+
+		xfer_len++;
+
+		val = readw(base + VIAI2C_CSR);
+		if (val & VIAI2C_CSR_RCV_NACK) {
+			dev_dbg(i2c->dev, "write RCV NACK error\n");
+			return -EIO;
+		}
+
+		if (pmsg->len == 0) {
+			val = VIAI2C_CR_TX_END | VIAI2C_CR_CONTINUE_MASK;
+			writeb(val, base + VIAI2C_CR);
+			break;
+		}
+
+		if (xfer_len == pmsg->len) {
+			if (last != 1)
+				writeb(VIAI2C_CR_ENABLE, base + VIAI2C_CR);
+		} else {
+			writeb(pmsg->buf[xfer_len] & 0xFF, base + VIAI2C_CDR);
+			writeb(VIAI2C_CR_CONTINUE_MASK, base + VIAI2C_CR);
+		}
+	}
+
+	return 0;
+}
+
+static int via_i2c_read(struct via_i2c *i2c, struct i2c_msg *pmsg)
+{
+	u32 xfer_len = 0;
+	u16 val, tcr_val = i2c->tcr;
+	void __iomem *base = i2c->base;
+
+	val = readb(base + VIAI2C_CR);
+	val &= ~VIAI2C_CR_END_MASK;
+	if (!(pmsg->flags & I2C_M_NOSTART))
+		val |= VIAI2C_CR_CPU_RDY;
+	if (pmsg->len == 1)
+		val |= VIAI2C_CR_TX_NEXT_NO_ACK;
+	writeb(val, base + VIAI2C_CR);
+
+	reinit_completion(&i2c->complete);
+
+	tcr_val |= VIAI2C_TCR_READ | (pmsg->addr & VIAI2C_TCR_ADDR_MASK);
+
+	writew(tcr_val, base + VIAI2C_TCR);
+
+	if (pmsg->flags & I2C_M_NOSTART) {
+		val = readb(base + VIAI2C_CR);
+		val |= VIAI2C_CR_CPU_RDY;
+		writeb(val, base + VIAI2C_CR);
+	}
+
+	while (xfer_len < pmsg->len) {
+		int err;
+
+		err = via_i2c_wait_event(i2c, VIAI2C_ISR_BYTE_END);
+		if (err)
+			return err;
+
+		pmsg->buf[xfer_len] = readw(base + VIAI2C_CDR) >> 8;
+		xfer_len++;
+
+		val = readb(base + VIAI2C_CR) | VIAI2C_CR_CPU_RDY;
+		if (xfer_len == pmsg->len - 1)
+			val |= VIAI2C_CR_TX_NEXT_NO_ACK;
+		writeb(val, base + VIAI2C_CR);
+	}
+
+	return 0;
+}
+
+int via_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
+{
+	int i;
+	int ret = 0;
+	struct via_i2c *i2c = i2c_get_adapdata(adap);
+
+	for (i = 0; ret >= 0 && i < num; i++) {
+		struct i2c_msg * pmsg = &msgs[i];
+
+		if (!(pmsg->flags & I2C_M_NOSTART)) {
+			ret = via_i2c_wait_bus_ready(i2c);
+			if (ret < 0)
+				return ret;
+		}
+
+		if (pmsg->flags & I2C_M_RD)
+			ret = via_i2c_read(i2c, pmsg);
+		else
+			ret = via_i2c_write(i2c, pmsg, (i + 1) == num);
+	}
+
+	return (ret < 0) ? ret : i;
+}
+EXPORT_SYMBOL(via_i2c_xfer);
+
+static irqreturn_t via_i2c_isr(int irq, void *data)
+{
+	struct via_i2c *i2c = data;
+
+	/* save the status and write-clear it */
+	i2c->event = readw(i2c->base + VIAI2C_ISR);
+	writew(i2c->event, i2c->base + VIAI2C_ISR);
+
+	complete(&i2c->complete);
+
+	return IRQ_HANDLED;
+}
+
+int via_i2c_init(struct platform_device *pdev, struct via_i2c **pi2c)
+{
+	int err;
+	int irq_flags;
+	struct via_i2c *i2c;
+	struct device_node *np = pdev->dev.of_node;
+
+	i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL);
+	if (!i2c)
+		return -ENOMEM;
+
+	i2c->base = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(i2c->base))
+		return PTR_ERR(i2c->base);
+
+	if (np) {
+		irq_flags = 0;
+		i2c->irq = irq_of_parse_and_map(np, 0);
+	} else {
+		irq_flags = IRQF_SHARED;
+		i2c->irq = platform_get_irq(pdev, 0);
+	}
+	if (i2c->irq < 0)
+		return i2c->irq;
+
+	err = devm_request_irq(&pdev->dev, i2c->irq, via_i2c_isr,
+				irq_flags, pdev->name, i2c);
+	if (err)
+		return dev_err_probe(&pdev->dev, err,
+				"failure requesting irq %d\n", i2c->irq);
+
+	i2c->dev = &pdev->dev;
+	platform_set_drvdata(pdev, i2c);
+	init_completion(&i2c->complete);
+
+	*pi2c = i2c;
+
+	return 0;
+}
+EXPORT_SYMBOL(via_i2c_init);
+
+MODULE_DESCRIPTION("Common driver for VIA i2c drivers");
+MODULE_LICENSE("GPL");
diff --git a/drivers/i2c/busses/i2c-viai2c-common.h b/drivers/i2c/busses/i2c-viai2c-common.h
new file mode 100644
index 000000000000..f3a86f005eb9
--- /dev/null
+++ b/drivers/i2c/busses/i2c-viai2c-common.h
@@ -0,0 +1,65 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#ifndef I2C_VIAI2C_COMMON_H
+#define I2C_VIAI2C_COMMON_H
+
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#define VIAI2C_CR		0x00
+#define VIAI2C_CR_ENABLE		BIT(0)
+#define VIAI2C_CR_TX_NEXT_NO_ACK	BIT(1)
+#define VIAI2C_CR_TX_END		BIT(2)
+#define VIAI2C_CR_CPU_RDY		BIT(3)
+#define VIAI2C_CR_END_MASK		GENMASK(2, 1)
+#define VIAI2C_CR_CONTINUE_MASK		(BIT(3) | BIT(0))
+
+#define VIAI2C_TCR		0x02
+#define VIAI2C_TCR_HS_MODE		BIT(13)
+#define VIAI2C_TCR_READ			BIT(14)
+#define VIAI2C_TCR_FAST			BIT(15)
+#define VIAI2C_TCR_ADDR_MASK		GENMASK(6, 0)
+
+#define VIAI2C_CSR		0x04
+#define VIAI2C_CSR_RCV_NACK		BIT(0)
+#define VIAI2C_CSR_READY_MASK		BIT(1)
+
+#define VIAI2C_ISR		0x06
+#define VIAI2C_ISR_NACK_ADDR		BIT(0)
+#define VIAI2C_ISR_BYTE_END		BIT(1)
+#define VIAI2C_ISR_SCL_TIMEOUT		BIT(2)
+#define VIAI2C_ISR_WRITE_ALL		GENMASK(2, 0)
+
+#define VIAI2C_IMR		0x08
+#define VIAI2C_IMR_ADDRNACK		BIT(0)
+#define VIAI2C_IMR_BYTE			BIT(1)
+#define VIAI2C_IMR_SCL_TIMEOUT		BIT(2)
+#define VIAI2C_IMR_EN_ALL		GENMASK(2, 0)
+
+#define VIAI2C_CDR		0x0A
+#define VIAI2C_TR		0x0C
+#define VIAI2C_MCR		0x0E
+
+#define VIAI2C_TIMEOUT		(msecs_to_jiffies(1000))
+
+struct via_i2c {
+	struct i2c_adapter	adapter;
+	struct completion	complete;
+	struct device		*dev;
+	void __iomem		*base;
+	struct clk		*clk;
+	int			irq;
+	u16			event;
+	u16			tcr;
+};
+
+int via_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num);
+int via_i2c_wait_bus_ready(struct via_i2c *i2c);
+int via_i2c_wait_event(struct via_i2c *i2c, u16 event);
+int via_i2c_init(struct platform_device *pdev, struct via_i2c **pi2c);
+
+#endif
diff --git a/drivers/i2c/busses/i2c-wmt.c b/drivers/i2c/busses/i2c-wmt.c
index 76118abc6e10..d282f2146fb3 100644
--- a/drivers/i2c/busses/i2c-wmt.c
+++ b/drivers/i2c/busses/i2c-wmt.c
@@ -7,309 +7,21 @@
  *  Derived from GPLv2+ licensed source:
  *  - Copyright (C) 2008 WonderMedia Technologies, Inc.
  */
-
 #include <linux/clk.h>
-#include <linux/delay.h>
-#include <linux/err.h>
-#include <linux/i2c.h>
-#include <linux/interrupt.h>
-#include <linux/io.h>
-#include <linux/module.h>
 #include <linux/of.h>
 #include <linux/of_address.h>
-#include <linux/of_irq.h>
-#include <linux/platform_device.h>
-
-#define REG_CR		0x00
-#define REG_TCR		0x02
-#define REG_CSR		0x04
-#define REG_ISR		0x06
-#define REG_IMR		0x08
-#define REG_CDR		0x0A
-#define REG_TR		0x0C
-#define REG_MCR		0x0E
-#define REG_SLAVE_CR	0x10
-#define REG_SLAVE_SR	0x12
-#define REG_SLAVE_ISR	0x14
-#define REG_SLAVE_IMR	0x16
-#define REG_SLAVE_DR	0x18
-#define REG_SLAVE_TR	0x1A
-
-/* REG_CR Bit fields */
-#define CR_TX_NEXT_ACK		0x0000
-#define CR_ENABLE		0x0001
-#define CR_TX_NEXT_NO_ACK	0x0002
-#define CR_TX_END		0x0004
-#define CR_CPU_RDY		0x0008
-#define SLAV_MODE_SEL		0x8000
-
-/* REG_TCR Bit fields */
-#define TCR_STANDARD_MODE	0x0000
-#define TCR_MASTER_WRITE	0x0000
-#define TCR_HS_MODE		0x2000
-#define TCR_MASTER_READ		0x4000
-#define TCR_FAST_MODE		0x8000
-#define TCR_SLAVE_ADDR_MASK	0x007F
-
-/* REG_ISR Bit fields */
-#define ISR_NACK_ADDR		0x0001
-#define ISR_BYTE_END		0x0002
-#define ISR_SCL_TIMEOUT		0x0004
-#define ISR_WRITE_ALL		0x0007
-
-/* REG_IMR Bit fields */
-#define IMR_ENABLE_ALL		0x0007
-
-/* REG_CSR Bit fields */
-#define CSR_RCV_NOT_ACK		0x0001
-#define CSR_RCV_ACK_MASK	0x0001
-#define CSR_READY_MASK		0x0002
-
-/* REG_TR */
-#define SCL_TIMEOUT(x)		(((x) & 0xFF) << 8)
-#define TR_STD			0x0064
-#define TR_HS			0x0019
-
-/* REG_MCR */
-#define MCR_APB_96M		7
-#define MCR_APB_166M		12
-
-#define I2C_MODE_STANDARD	0
-#define I2C_MODE_FAST		1
-
-#define WMT_I2C_TIMEOUT		(msecs_to_jiffies(1000))
-
-struct wmt_i2c_dev {
-	struct i2c_adapter	adapter;
-	struct completion	complete;
-	struct device		*dev;
-	void __iomem		*base;
-	struct clk		*clk;
-	int			mode;
-	int			irq;
-	u16			cmd_status;
-};
-
-static int wmt_i2c_wait_bus_not_busy(struct wmt_i2c_dev *i2c_dev)
-{
-	unsigned long timeout;
-
-	timeout = jiffies + WMT_I2C_TIMEOUT;
-	while (!(readw(i2c_dev->base + REG_CSR) & CSR_READY_MASK)) {
-		if (time_after(jiffies, timeout)) {
-			dev_warn(i2c_dev->dev, "timeout waiting for bus ready\n");
-			return -EBUSY;
-		}
-		msleep(20);
-	}
-
-	return 0;
-}
-
-static int wmt_check_status(struct wmt_i2c_dev *i2c_dev)
-{
-	int ret = 0;
-
-	if (i2c_dev->cmd_status & ISR_NACK_ADDR)
-		ret = -EIO;
-
-	if (i2c_dev->cmd_status & ISR_SCL_TIMEOUT)
-		ret = -ETIMEDOUT;
-
-	return ret;
-}
-
-static int wmt_i2c_write(struct i2c_adapter *adap, struct i2c_msg *pmsg,
-			 int last)
-{
-	struct wmt_i2c_dev *i2c_dev = i2c_get_adapdata(adap);
-	u16 val, tcr_val;
-	int ret;
-	unsigned long wait_result;
-	int xfer_len = 0;
-
-	if (!(pmsg->flags & I2C_M_NOSTART)) {
-		ret = wmt_i2c_wait_bus_not_busy(i2c_dev);
-		if (ret < 0)
-			return ret;
-	}
-
-	if (pmsg->len == 0) {
-		/*
-		 * We still need to run through the while (..) once, so
-		 * start at -1 and break out early from the loop
-		 */
-		xfer_len = -1;
-		writew(0, i2c_dev->base + REG_CDR);
-	} else {
-		writew(pmsg->buf[0] & 0xFF, i2c_dev->base + REG_CDR);
-	}
-
-	if (!(pmsg->flags & I2C_M_NOSTART)) {
-		val = readw(i2c_dev->base + REG_CR);
-		val &= ~CR_TX_END;
-		writew(val, i2c_dev->base + REG_CR);
-
-		val = readw(i2c_dev->base + REG_CR);
-		val |= CR_CPU_RDY;
-		writew(val, i2c_dev->base + REG_CR);
-	}
-
-	reinit_completion(&i2c_dev->complete);
-
-	if (i2c_dev->mode == I2C_MODE_STANDARD)
-		tcr_val = TCR_STANDARD_MODE;
-	else
-		tcr_val = TCR_FAST_MODE;
-
-	tcr_val |= (TCR_MASTER_WRITE | (pmsg->addr & TCR_SLAVE_ADDR_MASK));
-
-	writew(tcr_val, i2c_dev->base + REG_TCR);
-
-	if (pmsg->flags & I2C_M_NOSTART) {
-		val = readw(i2c_dev->base + REG_CR);
-		val |= CR_CPU_RDY;
-		writew(val, i2c_dev->base + REG_CR);
-	}
-
-	while (xfer_len < pmsg->len) {
-		wait_result = wait_for_completion_timeout(&i2c_dev->complete,
-							msecs_to_jiffies(500));
-
-		if (wait_result == 0)
-			return -ETIMEDOUT;
-
-		ret = wmt_check_status(i2c_dev);
-		if (ret)
-			return ret;
-
-		xfer_len++;
-
-		val = readw(i2c_dev->base + REG_CSR);
-		if ((val & CSR_RCV_ACK_MASK) == CSR_RCV_NOT_ACK) {
-			dev_dbg(i2c_dev->dev, "write RCV NACK error\n");
-			return -EIO;
-		}
-
-		if (pmsg->len == 0) {
-			val = CR_TX_END | CR_CPU_RDY | CR_ENABLE;
-			writew(val, i2c_dev->base + REG_CR);
-			break;
-		}
-
-		if (xfer_len == pmsg->len) {
-			if (last != 1)
-				writew(CR_ENABLE, i2c_dev->base + REG_CR);
-		} else {
-			writew(pmsg->buf[xfer_len] & 0xFF, i2c_dev->base +
-								REG_CDR);
-			writew(CR_CPU_RDY | CR_ENABLE, i2c_dev->base + REG_CR);
-		}
-	}
-
-	return 0;
-}
-
-static int wmt_i2c_read(struct i2c_adapter *adap, struct i2c_msg *pmsg,
-			int last)
-{
-	struct wmt_i2c_dev *i2c_dev = i2c_get_adapdata(adap);
-	u16 val, tcr_val;
-	int ret;
-	unsigned long wait_result;
-	u32 xfer_len = 0;
-
-	if (!(pmsg->flags & I2C_M_NOSTART)) {
-		ret = wmt_i2c_wait_bus_not_busy(i2c_dev);
-		if (ret < 0)
-			return ret;
-	}
-
-	val = readw(i2c_dev->base + REG_CR);
-	val &= ~CR_TX_END;
-	writew(val, i2c_dev->base + REG_CR);
-
-	val = readw(i2c_dev->base + REG_CR);
-	val &= ~CR_TX_NEXT_NO_ACK;
-	writew(val, i2c_dev->base + REG_CR);
-
-	if (!(pmsg->flags & I2C_M_NOSTART)) {
-		val = readw(i2c_dev->base + REG_CR);
-		val |= CR_CPU_RDY;
-		writew(val, i2c_dev->base + REG_CR);
-	}
-
-	if (pmsg->len == 1) {
-		val = readw(i2c_dev->base + REG_CR);
-		val |= CR_TX_NEXT_NO_ACK;
-		writew(val, i2c_dev->base + REG_CR);
-	}
-
-	reinit_completion(&i2c_dev->complete);
-
-	if (i2c_dev->mode == I2C_MODE_STANDARD)
-		tcr_val = TCR_STANDARD_MODE;
-	else
-		tcr_val = TCR_FAST_MODE;
-
-	tcr_val |= TCR_MASTER_READ | (pmsg->addr & TCR_SLAVE_ADDR_MASK);
-
-	writew(tcr_val, i2c_dev->base + REG_TCR);
-
-	if (pmsg->flags & I2C_M_NOSTART) {
-		val = readw(i2c_dev->base + REG_CR);
-		val |= CR_CPU_RDY;
-		writew(val, i2c_dev->base + REG_CR);
-	}
+#include "i2c-viai2c-common.h"
 
-	while (xfer_len < pmsg->len) {
-		wait_result = wait_for_completion_timeout(&i2c_dev->complete,
-							msecs_to_jiffies(500));
+#define wmt_i2c_dev		via_i2c
 
-		if (!wait_result)
-			return -ETIMEDOUT;
+/* VIAI2C_TR */
+#define WMT_SCL_TOUT		(((128) & 0xFF) << 8)
+#define WMT_TR_STD		0x0064
+#define WMT_TR_HS		0x0019
 
-		ret = wmt_check_status(i2c_dev);
-		if (ret)
-			return ret;
-
-		pmsg->buf[xfer_len] = readw(i2c_dev->base + REG_CDR) >> 8;
-		xfer_len++;
-
-		if (xfer_len == pmsg->len - 1) {
-			val = readw(i2c_dev->base + REG_CR);
-			val |= (CR_TX_NEXT_NO_ACK | CR_CPU_RDY);
-			writew(val, i2c_dev->base + REG_CR);
-		} else {
-			val = readw(i2c_dev->base + REG_CR);
-			val |= CR_CPU_RDY;
-			writew(val, i2c_dev->base + REG_CR);
-		}
-	}
-
-	return 0;
-}
-
-static int wmt_i2c_xfer(struct i2c_adapter *adap,
-			struct i2c_msg msgs[],
-			int num)
-{
-	struct i2c_msg *pmsg;
-	int i, is_last;
-	int ret = 0;
-
-	for (i = 0; ret >= 0 && i < num; i++) {
-		is_last = ((i + 1) == num);
-
-		pmsg = &msgs[i];
-		if (pmsg->flags & I2C_M_RD)
-			ret = wmt_i2c_read(adap, pmsg, is_last);
-		else
-			ret = wmt_i2c_write(adap, pmsg, is_last);
-	}
-
-	return (ret < 0) ? ret : i;
-}
+/* VIAI2C_MCR */
+#define WMT_MCR_APB_96M		7
+#define WMT_MCR_APB_166M	12
 
 static u32 wmt_i2c_func(struct i2c_adapter *adap)
 {
@@ -317,23 +29,10 @@ static u32 wmt_i2c_func(struct i2c_adapter *adap)
 }
 
 static const struct i2c_algorithm wmt_i2c_algo = {
-	.master_xfer	= wmt_i2c_xfer,
+	.master_xfer	= via_i2c_xfer,
 	.functionality	= wmt_i2c_func,
 };
 
-static irqreturn_t wmt_i2c_isr(int irq, void *data)
-{
-	struct wmt_i2c_dev *i2c_dev = data;
-
-	/* save the status and write-clear it */
-	i2c_dev->cmd_status = readw(i2c_dev->base + REG_ISR);
-	writew(i2c_dev->cmd_status, i2c_dev->base + REG_ISR);
-
-	complete(&i2c_dev->complete);
-
-	return IRQ_HANDLED;
-}
-
 static int wmt_i2c_reset_hardware(struct wmt_i2c_dev *i2c_dev)
 {
 	int err;
@@ -351,18 +50,18 @@ static int wmt_i2c_reset_hardware(struct wmt_i2c_dev *i2c_dev)
 		return err;
 	}
 
-	writew(0, i2c_dev->base + REG_CR);
-	writew(MCR_APB_166M, i2c_dev->base + REG_MCR);
-	writew(ISR_WRITE_ALL, i2c_dev->base + REG_ISR);
-	writew(IMR_ENABLE_ALL, i2c_dev->base + REG_IMR);
-	writew(CR_ENABLE, i2c_dev->base + REG_CR);
-	readw(i2c_dev->base + REG_CSR);		/* read clear */
-	writew(ISR_WRITE_ALL, i2c_dev->base + REG_ISR);
+	writew(0, i2c_dev->base + VIAI2C_CR);
+	writew(WMT_MCR_APB_166M, i2c_dev->base + VIAI2C_MCR);
+	writew(VIAI2C_ISR_WRITE_ALL, i2c_dev->base + VIAI2C_ISR);
+	writew(VIAI2C_IMR_EN_ALL, i2c_dev->base + VIAI2C_IMR);
+	writew(VIAI2C_CR_ENABLE, i2c_dev->base + VIAI2C_CR);
+	readw(i2c_dev->base + VIAI2C_CSR);		/* read clear */
+	writew(VIAI2C_ISR_WRITE_ALL, i2c_dev->base + VIAI2C_ISR);
 
-	if (i2c_dev->mode == I2C_MODE_STANDARD)
-		writew(SCL_TIMEOUT(128) | TR_STD, i2c_dev->base + REG_TR);
+	if (i2c_dev->tcr == VIAI2C_TCR_FAST)
+		writew(WMT_SCL_TOUT | WMT_TR_HS, i2c_dev->base + VIAI2C_TR);
 	else
-		writew(SCL_TIMEOUT(128) | TR_HS, i2c_dev->base + REG_TR);
+		writew(WMT_SCL_TOUT | WMT_TR_STD, i2c_dev->base + VIAI2C_TR);
 
 	return 0;
 }
@@ -375,39 +74,18 @@ static int wmt_i2c_probe(struct platform_device *pdev)
 	int err;
 	u32 clk_rate;
 
-	i2c_dev = devm_kzalloc(&pdev->dev, sizeof(*i2c_dev), GFP_KERNEL);
-	if (!i2c_dev)
-		return -ENOMEM;
-
-	i2c_dev->base = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);
-	if (IS_ERR(i2c_dev->base))
-		return PTR_ERR(i2c_dev->base);
-
-	i2c_dev->irq = irq_of_parse_and_map(np, 0);
-	if (!i2c_dev->irq) {
-		dev_err(&pdev->dev, "irq missing or invalid\n");
-		return -EINVAL;
-	}
+	err = via_i2c_init(pdev, &i2c_dev);
+	if (err)
+		return err;
 
 	i2c_dev->clk = of_clk_get(np, 0);
-	if (IS_ERR(i2c_dev->clk)) {
-		dev_err(&pdev->dev, "unable to request clock\n");
-		return PTR_ERR(i2c_dev->clk);
-	}
+	if (IS_ERR(i2c_dev->clk))
+		return dev_err_probe(&pdev->dev, PTR_ERR(i2c_dev->clk),
+				"unable to request clock\n");
 
-	i2c_dev->mode = I2C_MODE_STANDARD;
 	err = of_property_read_u32(np, "clock-frequency", &clk_rate);
 	if (!err && (clk_rate == I2C_MAX_FAST_MODE_FREQ))
-		i2c_dev->mode = I2C_MODE_FAST;
-
-	i2c_dev->dev = &pdev->dev;
-
-	err = devm_request_irq(&pdev->dev, i2c_dev->irq, wmt_i2c_isr, 0,
-							"i2c", i2c_dev);
-	if (err) {
-		dev_err(&pdev->dev, "failed to request irq %i\n", i2c_dev->irq);
-		return err;
-	}
+		i2c_dev->tcr = VIAI2C_TCR_FAST;
 
 	adap = &i2c_dev->adapter;
 	i2c_set_adapdata(adap, i2c_dev);
@@ -417,31 +95,12 @@ static int wmt_i2c_probe(struct platform_device *pdev)
 	adap->dev.parent = &pdev->dev;
 	adap->dev.of_node = pdev->dev.of_node;
 
-	init_completion(&i2c_dev->complete);
-
 	err = wmt_i2c_reset_hardware(i2c_dev);
-	if (err) {
-		dev_err(&pdev->dev, "error initializing hardware\n");
-		return err;
-	}
-
-	err = i2c_add_adapter(adap);
 	if (err)
-		return err;
-
-	platform_set_drvdata(pdev, i2c_dev);
-
-	return 0;
-}
-
-static void wmt_i2c_remove(struct platform_device *pdev)
-{
-	struct wmt_i2c_dev *i2c_dev = platform_get_drvdata(pdev);
+		return dev_err_probe(&pdev->dev, err,
+				"error initializing hardware\n");
 
-	/* Disable interrupts, clock and delete adapter */
-	writew(0, i2c_dev->base + REG_IMR);
-	clk_disable_unprepare(i2c_dev->clk);
-	i2c_del_adapter(&i2c_dev->adapter);
+	return devm_i2c_add_adapter(&pdev->dev, &i2c_dev->adapter);
 }
 
 static const struct of_device_id wmt_i2c_dt_ids[] = {
@@ -451,7 +110,6 @@ static const struct of_device_id wmt_i2c_dt_ids[] = {
 
 static struct platform_driver wmt_i2c_driver = {
 	.probe		= wmt_i2c_probe,
-	.remove_new	= wmt_i2c_remove,
 	.driver		= {
 		.name	= "wmt-i2c",
 		.of_match_table = wmt_i2c_dt_ids,
-- 
2.34.1


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

* [PATCH v2 2/2] i2c: add support for Zhaoxin I2C controller
  2023-08-14  8:40 [PATCH v2 0/2] i2c: add support for Zhaoxin I2C controller Hans Hu
  2023-08-14  8:40 ` [PATCH v2 1/2] i2c: wmt: split out i2c-viai2c-common.{c,h} Hans Hu
@ 2023-08-14  8:40 ` Hans Hu
  2023-08-25 20:04   ` Wolfram Sang
  1 sibling, 1 reply; 10+ messages in thread
From: Hans Hu @ 2023-08-14  8:40 UTC (permalink / raw)
  To: andi.shyti, wsa, linux-i2c; +Cc: cobechen, TonyWWang

Add Zhaoxin I2C controller driver. It provides the access to the i2c
busses, which connects to the touchpad, eeprom, etc.

Zhaoxin I2C controller provides two modes of operation:
FIFO mode: works in access requests without restart.
Byte mode: same IP with i2c-wmt and use driver i2c-viai2c-common.

Zhaoxin I2C controller has two separate busses, so may accommodate up
to two I2C adapters. Those adapters are listed in the ACPI namespace
with the "IIC1D17" HID, and probed by a platform driver.

The driver works with IRQ mode, and supports basic I2C features. Flags
I2C_AQ_NO_ZERO_LEN and I2C_AQ_COMB_WRITE_THEN_READ are used to limit
the unsupported access.

Signed-off-by: Hans Hu <hanshu-oc@zhaoxin.com>
---
 MAINTAINERS                            |   7 +
 drivers/i2c/busses/Kconfig             |  11 +
 drivers/i2c/busses/Makefile            |   1 +
 drivers/i2c/busses/i2c-viai2c-common.c |  12 +-
 drivers/i2c/busses/i2c-viai2c-common.h |  11 +
 drivers/i2c/busses/i2c-wmt.c           |   2 +
 drivers/i2c/busses/i2c-zhaoxin.c       | 274 +++++++++++++++++++++++++
 7 files changed, 315 insertions(+), 3 deletions(-)
 create mode 100644 drivers/i2c/busses/i2c-zhaoxin.c

diff --git a/MAINTAINERS b/MAINTAINERS
index b3648da1c5b2..bb65f7d748eb 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -9864,6 +9864,13 @@ L:	linux-i2c@vger.kernel.org
 F:	Documentation/i2c/busses/i2c-ismt.rst
 F:	drivers/i2c/busses/i2c-ismt.c
 
+I2C/SMBUS ZHAOXIN DRIVER
+M:	Hans Hu <hanshu@zhaoxin.com>
+L:	linux-i2c@vger.kernel.org
+S:	Maintained
+W:	https://www.zhaoxin.com
+F:	drivers/i2c/busses/i2c-zhaoxin.c
+
 I2C/SMBUS STUB DRIVER
 M:	Jean Delvare <jdelvare@suse.com>
 L:	linux-i2c@vger.kernel.org
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index b810030b21cd..4941093299b2 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -347,6 +347,17 @@ config I2C_SCMI
 	  To compile this driver as a module, choose M here:
 	  the module will be called i2c-scmi.
 
+config I2C_ZHAOXIN
+	tristate "Zhaoxin I2C Interface"
+	depends on PCI || COMPILE_TEST
+	select I2C_VIAI2C_COMMON
+	help
+	  If you say yes to this option, support will be included for the
+	  ZHAOXIN I2C interface
+
+	  This driver can also be built as a module. If so, the module
+	  will be called i2c-zhaoxin.
+
 endif # ACPI
 
 comment "Mac SMBus host controller drivers"
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index b7e20c3531b5..47bfab15b0a1 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -29,6 +29,7 @@ obj-$(CONFIG_I2C_SIS630)	+= i2c-sis630.o
 obj-$(CONFIG_I2C_SIS96X)	+= i2c-sis96x.o
 obj-$(CONFIG_I2C_VIA)		+= i2c-via.o
 obj-$(CONFIG_I2C_VIAPRO)	+= i2c-viapro.o
+obj-$(CONFIG_I2C_ZHAOXIN)	+= i2c-zhaoxin.o
 
 # Mac SMBus host controller drivers
 obj-$(CONFIG_I2C_HYDRA)		+= i2c-hydra.o
diff --git a/drivers/i2c/busses/i2c-viai2c-common.c b/drivers/i2c/busses/i2c-viai2c-common.c
index ffba29ce17c5..9d400ab10c25 100644
--- a/drivers/i2c/busses/i2c-viai2c-common.c
+++ b/drivers/i2c/busses/i2c-viai2c-common.c
@@ -53,7 +53,8 @@ static int via_i2c_write(struct via_i2c *i2c, struct i2c_msg *pmsg, int last)
 		writew(pmsg->buf[0], base + VIAI2C_CDR);
 	}
 
-	if (!(pmsg->flags & I2C_M_NOSTART)) {
+	if (!(pmsg->flags & I2C_M_NOSTART)
+	   && (i2c->platform & VIAI2C_PLAT_WMT)) {
 		val = readb(base + VIAI2C_CR);
 		val &= ~VIAI2C_CR_TX_END;
 		val |= VIAI2C_CR_CPU_RDY;
@@ -94,8 +95,10 @@ static int via_i2c_write(struct via_i2c *i2c, struct i2c_msg *pmsg, int last)
 		}
 
 		if (xfer_len == pmsg->len) {
-			if (last != 1)
+			if (last != 1 && (i2c->platform & VIAI2C_PLAT_WMT))
 				writeb(VIAI2C_CR_ENABLE, base + VIAI2C_CR);
+			else if (last && (i2c->platform & VIAI2C_PLAT_ZHAOXIN))
+				writeb(VIAI2C_CR_TX_END, base + VIAI2C_CR);
 		} else {
 			writeb(pmsg->buf[xfer_len] & 0xFF, base + VIAI2C_CDR);
 			writeb(VIAI2C_CR_CONTINUE_MASK, base + VIAI2C_CR);
@@ -159,7 +162,8 @@ int via_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
 	for (i = 0; ret >= 0 && i < num; i++) {
 		struct i2c_msg * pmsg = &msgs[i];
 
-		if (!(pmsg->flags & I2C_M_NOSTART)) {
+		if (!(pmsg->flags & I2C_M_NOSTART)
+		   && (i2c->platform & VIAI2C_PLAT_WMT)) {
 			ret = via_i2c_wait_bus_ready(i2c);
 			if (ret < 0)
 				return ret;
@@ -181,6 +185,8 @@ static irqreturn_t via_i2c_isr(int irq, void *data)
 
 	/* save the status and write-clear it */
 	i2c->event = readw(i2c->base + VIAI2C_ISR);
+	if (!i2c->event)
+		return IRQ_NONE;
 	writew(i2c->event, i2c->base + VIAI2C_ISR);
 
 	complete(&i2c->complete);
diff --git a/drivers/i2c/busses/i2c-viai2c-common.h b/drivers/i2c/busses/i2c-viai2c-common.h
index f3a86f005eb9..c81018b440b2 100644
--- a/drivers/i2c/busses/i2c-viai2c-common.h
+++ b/drivers/i2c/busses/i2c-viai2c-common.h
@@ -46,6 +46,11 @@
 
 #define VIAI2C_TIMEOUT		(msecs_to_jiffies(1000))
 
+enum {
+	VIAI2C_PLAT_WMT,
+	VIAI2C_PLAT_ZHAOXIN
+};
+
 struct via_i2c {
 	struct i2c_adapter	adapter;
 	struct completion	complete;
@@ -55,6 +60,12 @@ struct via_i2c {
 	int			irq;
 	u16			event;
 	u16			tcr;
+	bool			platform;
+	u16			tr;
+	u16			mcr;
+	u16			csr;
+	u8			fstp;
+	u8			hrv;
 };
 
 int via_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num);
diff --git a/drivers/i2c/busses/i2c-wmt.c b/drivers/i2c/busses/i2c-wmt.c
index d282f2146fb3..c9c3d27e8331 100644
--- a/drivers/i2c/busses/i2c-wmt.c
+++ b/drivers/i2c/busses/i2c-wmt.c
@@ -78,6 +78,8 @@ static int wmt_i2c_probe(struct platform_device *pdev)
 	if (err)
 		return err;
 
+	i2c_dev->platform = VIAI2C_PLAT_WMT;
+
 	i2c_dev->clk = of_clk_get(np, 0);
 	if (IS_ERR(i2c_dev->clk))
 		return dev_err_probe(&pdev->dev, PTR_ERR(i2c_dev->clk),
diff --git a/drivers/i2c/busses/i2c-zhaoxin.c b/drivers/i2c/busses/i2c-zhaoxin.c
new file mode 100644
index 000000000000..73b10af9a7b9
--- /dev/null
+++ b/drivers/i2c/busses/i2c-zhaoxin.c
@@ -0,0 +1,274 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ *  Copyright(c) 2023 Shanghai Zhaoxin Semiconductor Corporation.
+ *                    All rights reserved.
+ */
+
+#include <linux/pci.h>
+#include "i2c-viai2c-common.h"
+
+#define ZXI2C_NAME		"Zhaoxin-I2C"
+
+/*
+ * zhaoxin custom registers and bits
+ */
+/* reg CR Bit fields */
+#define ZXI2C_CR_FIFO_EN		BIT(14)
+/* reg ISR Bit fields */
+#define ZXI2C_ISR_FIFO			BIT(3)
+/* reg IMR Bit fields */
+#define ZXI2C_IMR_FIFO			BIT(3)
+#define ZXI2C_IMR_FIFO_MASK		(BIT(3) | VIAI2C_IMR_ADDRNACK)
+#define ZXI2C_IMR_BYTE_MASK		(BIT(1) | VIAI2C_IMR_ADDRNACK)
+#define ZXI2C_CS		0x10
+#define   ZXI2C_CS_CLK_50M		BIT(0)
+#define ZXI2C_REV		0x11
+#define ZXI2C_HCR		0x12
+#define   ZXI2C_HCR_FIFO_RST		GENMASK(1, 0)
+#define ZXI2C_HTDR		0x13
+#define ZXI2C_HRDR		0x14
+#define ZXI2C_HTLR		0x15
+#define ZXI2C_HRLR		0x16
+#define ZXI2C_HWCNTR		0x18
+#define ZXI2C_HRCNTR		0x19
+
+/* parameters Constants */
+#define ZXI2C_GOLD_FSTP_100K	0xF3
+#define ZXI2C_GOLD_FSTP_400K	0x38
+#define ZXI2C_GOLD_FSTP_1M	0x13
+#define ZXI2C_GOLD_FSTP_3400K	0x37
+#define ZXI2C_HS_MASTER_CODE	(0x08 << 8)
+
+#define ZXI2C_FIFO_SIZE		32
+
+#define zxi2c			via_i2c
+
+static int zxi2c_fifo_xfer(struct zxi2c *i2c, struct i2c_msg *msg)
+{
+	int err;
+	u16 i, finished;
+	u16 tcr_val = i2c->tcr;
+	void __iomem *base = i2c->base;
+	int read = !!(msg->flags & I2C_M_RD);
+
+	/* reset fifo settings */
+	iowrite8(ZXI2C_HCR_FIFO_RST, base + ZXI2C_HCR);
+	/* select fifo mode */
+	iowrite16(ZXI2C_CR_FIFO_EN, i2c->base + VIAI2C_CR);
+	/* enable fifo interrupt */
+	iowrite8(ZXI2C_IMR_FIFO_MASK, base + VIAI2C_IMR);
+
+	/* sets the transmission length, direction, and the data to be sent */
+	if (read) {
+		tcr_val |= VIAI2C_TCR_READ;
+		iowrite8(msg->len - 1, base + ZXI2C_HRLR);
+	} else {
+
+		iowrite8(msg->len - 1, base + ZXI2C_HTLR);
+		for (i = 0; i < msg->len; i++)
+			iowrite8(msg->buf[i], base + ZXI2C_HTDR);
+	}
+	/* set slave addr */
+	tcr_val |= (msg->addr & VIAI2C_TCR_ADDR_MASK);
+
+	reinit_completion(&i2c->complete);
+
+	/* a write to the TCR triggers the transfer start */
+	iowrite16(tcr_val, i2c->base + VIAI2C_TCR);
+
+	err = via_i2c_wait_event(i2c, ZXI2C_ISR_FIFO);
+	if (err)
+		return err;
+
+	/* Gets the length and data that has been transferred */
+	if (read) {
+		finished = ioread8(base + ZXI2C_HRCNTR);
+		for (i = 0; i < finished; i++)
+			msg->buf[i] = ioread8(base + ZXI2C_HRDR);
+	} else {
+		finished = ioread8(base + ZXI2C_HWCNTR);
+	}
+
+	/* check if data NACK during transmitting */
+	if (finished != msg->len) {
+		dev_err(i2c->dev, "only %s %d/%d bytes\n",
+			read ? "read" : "write", finished, msg->len);
+		return -EAGAIN;
+	}
+
+	return 1;
+}
+
+static int zxi2c_byte_xfer(struct i2c_adapter *adap,
+				struct i2c_msg *msgs, int num)
+{
+	int err;
+	struct zxi2c *i2c = (struct zxi2c *)i2c_get_adapdata(adap);
+
+	/* select byte mode */
+	iowrite16(0, i2c->base + VIAI2C_CR);
+	/* enable interrupt */
+	iowrite8(ZXI2C_IMR_BYTE_MASK, i2c->base + VIAI2C_IMR);
+
+	err = via_i2c_xfer(adap, msgs, num);
+	/* kill unfinished transfer signal */
+	if (err < 0)
+		iowrite16(VIAI2C_CR_END_MASK, i2c->base + VIAI2C_CR);
+
+	return err;
+}
+
+static int zxi2c_master_xfer(struct i2c_adapter *adap,
+				struct i2c_msg *msgs, int num)
+{
+	int err;
+	struct zxi2c *i2c = (struct zxi2c *)i2c_get_adapdata(adap);
+
+	err = via_i2c_wait_bus_ready(i2c);
+	if (err)
+		return err;
+
+	if (num == 1 && msgs->len <= ZXI2C_FIFO_SIZE && msgs->len >= 2)
+		err = zxi2c_fifo_xfer(i2c, msgs);
+	else
+		err = zxi2c_byte_xfer(adap, msgs, num);
+	/*
+	 * fifo or byte interrupts have been enabled inside the xfer functions.
+	 * To make the code more concise, we put the disable action here
+	 */
+	iowrite8(0, i2c->base + VIAI2C_IMR);
+	return err;
+}
+
+static u32 zxi2c_func(struct i2c_adapter *adap)
+{
+	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static const struct i2c_algorithm zxi2c_algorithm = {
+	.master_xfer = zxi2c_master_xfer,
+	.functionality = zxi2c_func,
+};
+
+static const struct i2c_adapter_quirks zxi2c_quirks = {
+	.flags = I2C_AQ_NO_ZERO_LEN | I2C_AQ_COMB_WRITE_THEN_READ,
+};
+
+static const u32 zxi2c_speed_params_table[][3] = {
+	/* speed, ZXI2C_TCR, ZXI2C_FSTP */
+	{ I2C_MAX_STANDARD_MODE_FREQ, 0, ZXI2C_GOLD_FSTP_100K },
+	{ I2C_MAX_FAST_MODE_FREQ, VIAI2C_TCR_FAST, ZXI2C_GOLD_FSTP_400K },
+	{ I2C_MAX_FAST_MODE_PLUS_FREQ, VIAI2C_TCR_FAST, ZXI2C_GOLD_FSTP_1M },
+	{ I2C_MAX_HIGH_SPEED_MODE_FREQ, VIAI2C_TCR_HS_MODE | VIAI2C_TCR_FAST,
+	  ZXI2C_GOLD_FSTP_3400K },
+};
+
+static void zxi2c_set_bus_speed(struct zxi2c *i2c)
+{
+	iowrite16(i2c->tr, i2c->base + VIAI2C_TR);
+	iowrite8(ZXI2C_CS_CLK_50M, i2c->base + ZXI2C_CS);
+	iowrite16(i2c->mcr, i2c->base + VIAI2C_MCR);
+}
+
+static void zxi2c_get_bus_speed(struct zxi2c *i2c)
+{
+	u8 i, count;
+	u8 fstp;
+	const u32 *params;
+	u32 acpi_speed = i2c_acpi_find_bus_speed(i2c->dev);
+
+	count = ARRAY_SIZE(zxi2c_speed_params_table);
+	for (i = 0; i < count; i++)
+		if (acpi_speed == zxi2c_speed_params_table[i][0])
+			break;
+	/* if not found, use 400k as default */
+	i = i < count ? i : 1;
+
+	params = zxi2c_speed_params_table[i];
+	fstp = ioread8(i2c->base + VIAI2C_TR);
+	if (abs(fstp - params[2]) > 0x10) {
+		/*
+		 * if BIOS setting value far from golden value,
+		 * use golden value and warn user
+		 */
+		dev_warn(i2c->dev, "speed:%d, fstp:0x%x, golden:0x%x\n",
+				params[0], fstp, params[2]);
+		i2c->tr = params[2] | 0xff00;
+	} else {
+		i2c->tr = fstp | 0xff00;
+	}
+
+	i2c->tcr = params[1];
+	i2c->mcr = ioread16(i2c->base + VIAI2C_MCR);
+	/* for Hs-mode, use 0000 1000 as master code */
+	if (params[0] == I2C_MAX_HIGH_SPEED_MODE_FREQ)
+		i2c->mcr |= ZXI2C_HS_MASTER_CODE;
+
+	dev_info(i2c->dev, "speed mode is %s\n",
+			i2c_freq_mode_string(params[0]));
+}
+
+static int zxi2c_probe(struct platform_device *pdev)
+{
+	int err = 0;
+	struct zxi2c *i2c;
+	struct pci_dev *pci;
+
+	err = via_i2c_init(pdev, &i2c);
+	if (err)
+		return err;
+
+	i2c->platform = VIAI2C_PLAT_ZHAOXIN;
+	i2c->hrv = ioread8(i2c->base + ZXI2C_REV);
+	i2c->csr = ioread16(i2c->base + VIAI2C_CSR);
+
+	zxi2c_get_bus_speed(i2c);
+	zxi2c_set_bus_speed(i2c);
+
+	i2c->adapter.owner = THIS_MODULE;
+	i2c->adapter.algo = &zxi2c_algorithm;
+	i2c->adapter.retries = 2;
+	i2c->adapter.quirks = &zxi2c_quirks;
+	i2c->adapter.dev.parent = &pdev->dev;
+	ACPI_COMPANION_SET(&i2c->adapter.dev, ACPI_COMPANION(&pdev->dev));
+	pci = to_pci_dev(pdev->dev.parent);
+	snprintf(i2c->adapter.name, sizeof(i2c->adapter.name), "%s-%s-%s",
+			ZXI2C_NAME, dev_name(&pci->dev), dev_name(i2c->dev));
+	i2c_set_adapdata(&i2c->adapter, i2c);
+
+	return devm_i2c_add_adapter(&pdev->dev, &i2c->adapter);
+}
+
+static int zxi2c_resume(struct device *dev)
+{
+	struct zxi2c *i2c = dev_get_drvdata(dev);
+
+	iowrite16(i2c->csr, i2c->base + VIAI2C_CSR);
+	zxi2c_set_bus_speed(i2c);
+
+	return 0;
+}
+
+static const struct dev_pm_ops zxi2c_pm = {
+	SET_SYSTEM_SLEEP_PM_OPS(NULL, zxi2c_resume)
+};
+
+static const struct acpi_device_id zxi2c_acpi_match[] = {
+	{ "IIC1D17", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(acpi, zxi2c_acpi_match);
+
+static struct platform_driver zxi2c_driver = {
+	.probe = zxi2c_probe,
+	.driver = {
+		.name = ZXI2C_NAME,
+		.acpi_match_table = zxi2c_acpi_match,
+		.pm = &zxi2c_pm,
+	},
+};
+module_platform_driver(zxi2c_driver);
+
+MODULE_AUTHOR("HansHu@zhaoxin.com");
+MODULE_DESCRIPTION("Shanghai Zhaoxin IIC driver");
+MODULE_LICENSE("GPL");
-- 
2.34.1


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

* Re: [PATCH v2 1/2] i2c: wmt: split out i2c-viai2c-common.{c,h}
  2023-08-14  8:40 ` [PATCH v2 1/2] i2c: wmt: split out i2c-viai2c-common.{c,h} Hans Hu
@ 2023-08-25 20:02   ` Wolfram Sang
  2023-08-28  2:42     ` Hans Hu
  0 siblings, 1 reply; 10+ messages in thread
From: Wolfram Sang @ 2023-08-25 20:02 UTC (permalink / raw)
  To: Hans Hu; +Cc: andi.shyti, linux-i2c, cobechen, TonyWWang

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

Hi!

first of all, thank you for reworking your driver to share common code
with the WMT driver. This effort is much appreciated and we are close to
go, I think. But some remarks.

> +config I2C_VIAI2C_COMMON
> +	tristate
> +
>  config I2C_WMT
>  	tristate "Wondermedia WM8xxx SoC I2C bus support"
>  	depends on ARCH_VT8500 || COMPILE_TEST
> +	select I2C_VIAI2C_COMMON
>  	help
>  	  Say yes if you want to support the I2C bus on Wondermedia 8xxx-series
>  	  SoCs.
> diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
> index af56fe2c75c0..b7e20c3531b5 100644
> --- a/drivers/i2c/busses/Makefile
> +++ b/drivers/i2c/busses/Makefile
> @@ -120,6 +120,7 @@ obj-$(CONFIG_I2C_TEGRA_BPMP)	+= i2c-tegra-bpmp.o
>  obj-$(CONFIG_I2C_UNIPHIER)	+= i2c-uniphier.o
>  obj-$(CONFIG_I2C_UNIPHIER_F)	+= i2c-uniphier-f.o
>  obj-$(CONFIG_I2C_VERSATILE)	+= i2c-versatile.o
> +obj-$(CONFIG_I2C_VIAI2C_COMMON)	+= i2c-viai2c-common.o
>  obj-$(CONFIG_I2C_WMT)		+= i2c-wmt.o
>  i2c-octeon-objs := i2c-octeon-core.o i2c-octeon-platdrv.o
>  obj-$(CONFIG_I2C_OCTEON)	+= i2c-octeon.o

I'd prefer to link the core object to the driver object like pasemi or
octeon do. Or is there an argument for having a seperate module?

Regards!


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

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

* Re: [PATCH v2 2/2] i2c: add support for Zhaoxin I2C controller
  2023-08-14  8:40 ` [PATCH v2 2/2] i2c: add support for Zhaoxin I2C controller Hans Hu
@ 2023-08-25 20:04   ` Wolfram Sang
  2023-08-28  2:50     ` Hans Hu
  0 siblings, 1 reply; 10+ messages in thread
From: Wolfram Sang @ 2023-08-25 20:04 UTC (permalink / raw)
  To: Hans Hu; +Cc: andi.shyti, linux-i2c, cobechen, TonyWWang

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


> +I2C/SMBUS ZHAOXIN DRIVER
> +M:	Hans Hu <hanshu@zhaoxin.com>
> +L:	linux-i2c@vger.kernel.org
> +S:	Maintained
> +W:	https://www.zhaoxin.com
> +F:	drivers/i2c/busses/i2c-zhaoxin.c

Are you open to maintain the viai2c core as well? If so, then I'd think
adding another "F:"-entry here will do.


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

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

* Re: [PATCH v2 1/2] i2c: wmt: split out i2c-viai2c-common.{c,h}
  2023-08-25 20:02   ` Wolfram Sang
@ 2023-08-28  2:42     ` Hans Hu
  2023-08-28  6:00       ` Wolfram Sang
  0 siblings, 1 reply; 10+ messages in thread
From: Hans Hu @ 2023-08-28  2:42 UTC (permalink / raw)
  To: Wolfram Sang, andi.shyti, linux-i2c, cobechen, TonyWWang


On 2023/8/26 04:02, Wolfram Sang wrote:
> Hi!
>
> first of all, thank you for reworking your driver to share common code
> with the WMT driver. This effort is much appreciated and we are close to
> go, I think. But some remarks.
>
>> +config I2C_VIAI2C_COMMON
>> +	tristate
>> +
>>   config I2C_WMT
>>   	tristate "Wondermedia WM8xxx SoC I2C bus support"
>>   	depends on ARCH_VT8500 || COMPILE_TEST
>> +	select I2C_VIAI2C_COMMON
>>   	help
>>   	  Say yes if you want to support the I2C bus on Wondermedia 8xxx-series
>>   	  SoCs.
>> diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
>> index af56fe2c75c0..b7e20c3531b5 100644
>> --- a/drivers/i2c/busses/Makefile
>> +++ b/drivers/i2c/busses/Makefile
>> @@ -120,6 +120,7 @@ obj-$(CONFIG_I2C_TEGRA_BPMP)	+= i2c-tegra-bpmp.o
>>   obj-$(CONFIG_I2C_UNIPHIER)	+= i2c-uniphier.o
>>   obj-$(CONFIG_I2C_UNIPHIER_F)	+= i2c-uniphier-f.o
>>   obj-$(CONFIG_I2C_VERSATILE)	+= i2c-versatile.o
>> +obj-$(CONFIG_I2C_VIAI2C_COMMON)	+= i2c-viai2c-common.o
>>   obj-$(CONFIG_I2C_WMT)		+= i2c-wmt.o
>>   i2c-octeon-objs := i2c-octeon-core.o i2c-octeon-platdrv.o
>>   obj-$(CONFIG_I2C_OCTEON)	+= i2c-octeon.o
> I'd prefer to link the core object to the driver object like pasemi or
> octeon do. Or is there an argument for having a seperate module?
There are two main reasons:
1. I checked the MAINTAINERS section related to ARM/VT8500,
Found that the entire ARM/VT8500 ARCHITECTURE is currently
unmaintained, maybe one day it will be removed from the kernel.
So I think it might be better as a separate module.
2. In addition, if not separated from wmt, the driver would
become a bit confusing and difficult to maintain.
> Regards!
>

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

* Re: [PATCH v2 2/2] i2c: add support for Zhaoxin I2C controller
  2023-08-25 20:04   ` Wolfram Sang
@ 2023-08-28  2:50     ` Hans Hu
  0 siblings, 0 replies; 10+ messages in thread
From: Hans Hu @ 2023-08-28  2:50 UTC (permalink / raw)
  To: Wolfram Sang, andi.shyti, linux-i2c, cobechen, TonyWWang


On 2023/8/26 04:04, Wolfram Sang wrote:
>> +I2C/SMBUS ZHAOXIN DRIVER
>> +M:	Hans Hu <hanshu@zhaoxin.com>
>> +L:	linux-i2c@vger.kernel.org
>> +S:	Maintained
>> +W:	https://www.zhaoxin.com
>> +F:	drivers/i2c/busses/i2c-zhaoxin.c
> Are you open to maintain the viai2c core as well? If so, then I'd think
> adding another "F:"-entry here will do.
Yes, we can, will add in V3.
>

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

* Re: [PATCH v2 1/2] i2c: wmt: split out i2c-viai2c-common.{c,h}
  2023-08-28  2:42     ` Hans Hu
@ 2023-08-28  6:00       ` Wolfram Sang
  2023-08-28  7:25         ` Hans Hu
  0 siblings, 1 reply; 10+ messages in thread
From: Wolfram Sang @ 2023-08-28  6:00 UTC (permalink / raw)
  To: Hans Hu; +Cc: andi.shyti, linux-i2c, cobechen, TonyWWang

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


> 1. I checked the MAINTAINERS section related to ARM/VT8500,
> Found that the entire ARM/VT8500 ARCHITECTURE is currently
> unmaintained, maybe one day it will be removed from the kernel.
> So I think it might be better as a separate module.

But even if we remove the arch, we will not remove the viai2c core,
right? We just remove the Makefile entry for WMT. And your driver will
still link two object files into one kernel object.

> 2. In addition, if not separated from wmt, the driver would
> become a bit confusing and difficult to maintain.

Ah, maybe this is the misunderstanding. The seperation from WMT is very
good! It is just that the new viai2c core shouldn't be a module itself
but only a seperate object file which gets linked into your driver.
Please check the octeon or pasemi driver for an example.


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

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

* Re: [PATCH v2 1/2] i2c: wmt: split out i2c-viai2c-common.{c,h}
  2023-08-28  6:00       ` Wolfram Sang
@ 2023-08-28  7:25         ` Hans Hu
  2023-08-28  7:37           ` Wolfram Sang
  0 siblings, 1 reply; 10+ messages in thread
From: Hans Hu @ 2023-08-28  7:25 UTC (permalink / raw)
  To: Wolfram Sang, andi.shyti, linux-i2c, cobechen, TonyWWang


On 2023/8/28 14:00, Wolfram Sang wrote:
>> 1. I checked the MAINTAINERS section related to ARM/VT8500,
>> Found that the entire ARM/VT8500 ARCHITECTURE is currently
>> unmaintained, maybe one day it will be removed from the kernel.
>> So I think it might be better as a separate module.
> But even if we remove the arch, we will not remove the viai2c core,
> right? We just remove the Makefile entry for WMT. And your driver will
> still link two object files into one kernel object.
>
>> 2. In addition, if not separated from wmt, the driver would
>> become a bit confusing and difficult to maintain.
> Ah, maybe this is the misunderstanding. The seperation from WMT is very
> good! It is just that the new viai2c core shouldn't be a module itself
> but only a seperate object file which gets linked into your driver.
> Please check the octeon or pasemi driver for an example.
>

Sorry about the misunderstanding, will change to like below:

i2c-zhaoxin-objs:= i2c-viai2c-common.o i2c-zhaoxin-plt.o
obj-$(CONFIG_I2C_ZHAOXIN)       += i2c-zhaoxin.o
i2c-wmt-objs:= i2c-viai2c-common.o i2c-wmt-plt.o
obj-$(CONFIG_I2C_WMT)           += i2c-wmt.o

But I'm not sure which way is more appropriate:
change file name i2c-wmt.c to i2c-wmt-plt.c.(I prefer this)
or
change config name CONFIG_I2C_WMT to CONFIG_I2C_WMT_PLT.

Hans


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

* Re: [PATCH v2 1/2] i2c: wmt: split out i2c-viai2c-common.{c,h}
  2023-08-28  7:25         ` Hans Hu
@ 2023-08-28  7:37           ` Wolfram Sang
  0 siblings, 0 replies; 10+ messages in thread
From: Wolfram Sang @ 2023-08-28  7:37 UTC (permalink / raw)
  To: Hans Hu; +Cc: andi.shyti, linux-i2c, cobechen, TonyWWang

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


> i2c-zhaoxin-objs:= i2c-viai2c-common.o i2c-zhaoxin-plt.o
> obj-$(CONFIG_I2C_ZHAOXIN)       += i2c-zhaoxin.o

Yes, exactly!

> i2c-wmt-objs:= i2c-viai2c-common.o i2c-wmt-plt.o
> obj-$(CONFIG_I2C_WMT)           += i2c-wmt.o
> 
> But I'm not sure which way is more appropriate:
> change file name i2c-wmt.c to i2c-wmt-plt.c.(I prefer this)

I prefer this, too!

Thank you and happy hacking!


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

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

end of thread, other threads:[~2023-08-28  7:38 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-08-14  8:40 [PATCH v2 0/2] i2c: add support for Zhaoxin I2C controller Hans Hu
2023-08-14  8:40 ` [PATCH v2 1/2] i2c: wmt: split out i2c-viai2c-common.{c,h} Hans Hu
2023-08-25 20:02   ` Wolfram Sang
2023-08-28  2:42     ` Hans Hu
2023-08-28  6:00       ` Wolfram Sang
2023-08-28  7:25         ` Hans Hu
2023-08-28  7:37           ` Wolfram Sang
2023-08-14  8:40 ` [PATCH v2 2/2] i2c: add support for Zhaoxin I2C controller Hans Hu
2023-08-25 20:04   ` Wolfram Sang
2023-08-28  2:50     ` Hans Hu

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.