* [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.