Add Silvaco I3C target controller support
Signed-off-by: Frank Li <Frank.Li@nxp.com>
---
drivers/i3c/Kconfig | 3 +
drivers/i3c/Makefile | 1 +
drivers/i3c/target/Kconfig | 9 +
drivers/i3c/target/Makefile | 4 +
drivers/i3c/target/svc-i3c-target.c | 795 ++++++++++++++++++++++++++++
5 files changed, 812 insertions(+)
create mode 100644 drivers/i3c/target/Kconfig
create mode 100644 drivers/i3c/target/Makefile
create mode 100644 drivers/i3c/target/svc-i3c-target.c
diff --git a/drivers/i3c/Kconfig b/drivers/i3c/Kconfig
index d59a7eb83d13a..08ceef313f831 100644
--- a/drivers/i3c/Kconfig
+++ b/drivers/i3c/Kconfig
@@ -48,3 +48,6 @@ config I3C_TARGET_CONFIGFS
the target function and used to bind the function with a target
controller.
+if I3C_TARGET
+source "drivers/i3c/target/Kconfig"
+endif # I3C_TARGET
diff --git a/drivers/i3c/Makefile b/drivers/i3c/Makefile
index 475afb40429e0..c36455cb852ea 100644
--- a/drivers/i3c/Makefile
+++ b/drivers/i3c/Makefile
@@ -4,3 +4,4 @@ obj-$(CONFIG_I3C) += i3c.o
obj-$(CONFIG_I3C) += master/
obj-$(CONFIG_I3C_TARGET) += target.o
obj-$(CONFIG_I3C_TARGET_CONFIGFS) += i3c-cfs.o
+obj-$(CONFIG_I3C_TARGET) += target/
diff --git a/drivers/i3c/target/Kconfig b/drivers/i3c/target/Kconfig
new file mode 100644
index 0000000000000..6b6307117caab
--- /dev/null
+++ b/drivers/i3c/target/Kconfig
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0
+
+config I3C_TARGET_CTRL_SVC
+ tristate "Silvaco I3C Dual-Role Target driver"
+ depends on I3C
+ depends on HAS_IOMEM
+ depends on !(ALPHA || PARISC)
+ help
+ Support for Silvaco I3C Dual-Role Target Controller.
diff --git a/drivers/i3c/target/Makefile b/drivers/i3c/target/Makefile
new file mode 100644
index 0000000000000..10e160f66e3dc
--- /dev/null
+++ b/drivers/i3c/target/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-${CONFIG_I3C_TARGET_CTRL_SVC} += svc-i3c-target.o
+
diff --git a/drivers/i3c/target/svc-i3c-target.c b/drivers/i3c/target/svc-i3c-target.c
new file mode 100644
index 0000000000000..1bedf840525a5
--- /dev/null
+++ b/drivers/i3c/target/svc-i3c-target.c
@@ -0,0 +1,795 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2023 NXP.
+ *
+ * Author: Frank Li <Frank.Li@nxp.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/errno.h>
+#include <linux/i3c/target.h>
+#include <linux/i3c/target.h>
+#include <linux/interrupt.h>
+#include <linux/iopoll.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/clk.h>
+#include <linux/console.h>
+#include <linux/i3c/device.h>
+#include <linux/kernel.h>
+#include <linux/mod_devicetable.h>
+
+enum i3c_clks {
+ PCLK,
+ FCLK,
+ SCLK,
+ MAXCLK,
+};
+
+struct svc_i3c_target {
+ struct device *dev;
+ void __iomem *regs;
+ int irq;
+ struct clk_bulk_data clks[MAXCLK];
+
+ struct list_head txq;
+ spinlock_t txq_lock; /* protect tx queue */
+ struct list_head rxq;
+ spinlock_t rxq_lock; /* protect rx queue */
+ struct list_head cq;
+ spinlock_t cq_lock; /* protect complete queue */
+
+ struct work_struct work;
+ struct workqueue_struct *workqueue;
+
+ struct completion dacomplete;
+ struct i3c_target_ctrl_features features;
+
+ spinlock_t ctrl_lock; /* protext access SCTRL register */
+};
+
+#define I3C_SCONFIG 0x4
+#define I3C_SCONFIG_SLVENA_MASK BIT(0)
+#define I3C_SCONFIG_OFFLINE_MASK BIT(9)
+#define I3C_SCONFIG_SADDR_MASK GENMASK(31, 25)
+
+#define I3C_SSTATUS 0x8
+#define I3C_SSTATUS_STNOTSTOP_MASK BIT(0)
+#define I3C_SSTATUS_STOP_MASK BIT(10)
+#define I3C_SSTATUS_RX_PEND_MASK BIT(11)
+#define I3C_SSTATUS_TXNOTFULL_MASK BIT(12)
+#define I3C_SSTATUS_DACHG_MASK BIT(13)
+#define I3C_SSTATUS_EVDET_MASK GENMASK(21, 20)
+#define I3C_SSTATUS_EVDET_ACKED 0x3
+#define I3C_SSTATUS_IBIDIS_MASK BIT(24)
+#define I3C_SSTATUS_HJDIS_MASK BIT(27)
+
+#define I3C_SCTRL 0xc
+#define I3C_SCTRL_EVENT_MASK GENMASK(1, 0)
+#define I3C_SCTRL_EVENT_IBI 0x1
+#define I3C_SCTRL_EVENT_HOTJOIN 0x3
+#define I3C_SCTRL_EXTDATA_MASK BIT(3)
+#define I3C_SCTRL_IBIDATA_MASK GENMASK(15, 8)
+
+#define I3C_SINTSET 0x10
+#define I3C_SINTCLR 0x14
+#define I3C_SINT_START BIT(8)
+#define I3C_SINT_MATCHED BIT(9)
+#define I3C_SINT_STOP BIT(10)
+#define I3C_SINT_RXPEND BIT(11)
+#define I3C_SINT_TXSEND BIT(12)
+#define I3C_SINT_DACHG BIT(13)
+#define I3C_SINT_CCC BIT(14)
+#define I3C_SINT_ERRWARN BIT(15)
+#define I3C_SINT_DDRMAATCHED BIT(16)
+#define I3C_SINT_CHANDLED BIT(17)
+#define I3C_SINT_EVENT BIT(18)
+#define I3C_SINT_SLVRST BIT(19)
+
+#define I3C_SDATACTRL 0x2c
+#define I3C_SDATACTRL_RXEMPTY_MASK BIT(31)
+#define I3C_SDATACTRL_TXFULL_MASK BIT(30)
+#define I3C_SDATACTRL_RXCOUNT_MASK GENMASK(28, 24)
+#define I3C_SDATACTRL_TXCOUNT_MASK GENMASK(20, 16)
+#define I3C_SDATACTRL_FLUSHFB_MASK BIT(1)
+#define I3C_SDATACTRL_FLUSHTB_MASK BIT(0)
+
+#define I3C_SWDATAB 0x30
+#define I3C_SWDATAB_END_ALSO_MASK BIT(16)
+#define I3C_SWDATAB_END_MASK BIT(8)
+
+#define I3C_SWDATAE 0x34
+#define I3C_SRDATAB 0x40
+
+#define I3C_SCAPABILITIES 0x60
+#define I3C_SCAPABILITIES_FIFOTX_MASK GENMASK(27, 26)
+#define I3C_SCAPABILITIES_FIFORX_MASK GENMASK(29, 28)
+
+#define I3C_SMAXLIMITS 0x68
+#define I3C_SMAXLIMITS_MAXRD_MASK GENMASK(11, 0)
+#define I3C_SMAXLIMITS_MAXWR_MASK GENMASK(27, 16)
+
+#define I3C_SIDPARTNO 0x6c
+
+#define I3C_SIDEXT 0x70
+#define I3C_SIDEXT_BCR_MASK GENMASK(23, 16)
+#define I3C_SIDEXT_DCR_MASK GENMASK(15, 8)
+#define I3C_SVENDORID 0x74
+
+#define I3C_SMAPCTRL0 0x11c
+#define I3C_SMAPCTRL0_ENA_MASK BIT(0)
+#define I3C_SMAPCTRL0_DA_MASK GENMASK(7, 1)
+
+#define I3C_IBIEXT1 0x140
+#define I3C_IBIEXT1_CNT_MASK GEN_MASK(2, 0)
+#define I3C_IBIEXT1_MAX_MASK GEN_MASK(4, 6)
+#define I3C_IBIEXT1_EXT1_SHIFT 8
+#define I3C_IBIEXT1_EXT2_SHIFT 16
+#define I3C_IBIEXT1_EXT3_SHIFT 24
+
+#define I3C_IBIEXT2 0x144
+#define I3C_IBIEXT2_EXT4_SHIFT 0
+#define I3C_IBIEXT2_EXT5_SHIFT 8
+#define I3C_IBIEXT2_EXT6_SHIFT 16
+#define I3C_IBIEXT2_EXT7_SHIFT 24
+
+static int svc_i3c_target_enable(struct i3c_target_ctrl *ctrl)
+{
+ struct svc_i3c_target *svc;
+ u32 val;
+
+ svc = dev_get_drvdata(&ctrl->dev);
+
+ val = readl_relaxed(svc->regs + I3C_SCONFIG);
+ val |= I3C_SCONFIG_SLVENA_MASK;
+ writel_relaxed(val, svc->regs + I3C_SCONFIG);
+
+ return 0;
+}
+
+static int svc_i3c_target_disable(struct i3c_target_ctrl *ctrl)
+{
+ struct svc_i3c_target *svc;
+ u32 val;
+
+ svc = dev_get_drvdata(&ctrl->dev);
+
+ val = readl_relaxed(svc->regs + I3C_SCONFIG);
+ val &= ~I3C_SCONFIG_SLVENA_MASK;
+ writel_relaxed(val, svc->regs + I3C_SCONFIG);
+
+ return 0;
+}
+
+static int svc_i3c_target_set_config(struct i3c_target_ctrl *ctrl, struct i3c_target_func *func)
+{
+ struct svc_i3c_target *svc;
+ u32 val;
+ u32 wm, rm;
+
+ svc = dev_get_drvdata(&ctrl->dev);
+
+ if (func->static_addr > 0x7F)
+ return -EINVAL;
+
+ val = readl_relaxed(svc->regs + I3C_SCONFIG);
+ val &= ~I3C_SCONFIG_SLVENA_MASK;
+ val |= FIELD_PREP(I3C_SCONFIG_SADDR_MASK, func->static_addr);
+ writel_relaxed(val, svc->regs + I3C_SCONFIG);
+
+ if (func->part_id)
+ writel_relaxed((func->part_id << 16) |
+ ((func->instance_id << 12) & GENMASK(15, 12)) |
+ (func->ext_id & GENMASK(11, 0)), svc->regs + I3C_SIDPARTNO);
+
+ writel_relaxed(FIELD_PREP(I3C_SIDEXT_BCR_MASK, func->bcr) |
+ FIELD_PREP(I3C_SIDEXT_DCR_MASK, func->dcr),
+ svc->regs + I3C_SIDEXT);
+
+ wm = func->max_write_len == 0 ?
+ FIELD_GET(I3C_SMAXLIMITS_MAXWR_MASK, I3C_SMAXLIMITS_MAXWR_MASK) : func->max_write_len;
+
+ wm = max_t(u32, val, 8);
+
+ rm = func->max_read_len == 0 ?
+ FIELD_GET(I3C_SMAXLIMITS_MAXRD_MASK, I3C_SMAXLIMITS_MAXRD_MASK) : func->max_read_len;
+ rm = max_t(u32, val, 16);
+
+ val = FIELD_PREP(I3C_SMAXLIMITS_MAXRD_MASK, rm) | FIELD_PREP(I3C_SMAXLIMITS_MAXWR_MASK, wm);
+ writel_relaxed(val, svc->regs + I3C_SMAXLIMITS);
+
+ writel_relaxed(func->vendor_id, svc->regs + I3C_SVENDORID);
+ return 0;
+}
+
+const struct i3c_target_ctrl_features *svc_i3c_get_features(struct i3c_target_ctrl *ctrl)
+{
+ struct svc_i3c_target *svc;
+
+ svc = dev_get_drvdata(&ctrl->dev);
+
+ if (!svc)
+ return NULL;
+
+ return &svc->features;
+}
+
+static void svc_i3c_queue_complete(struct svc_i3c_target *svc, struct i3c_request *complete)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&svc->cq_lock, flags);
+ list_add_tail(&complete->list, &svc->cq);
+ spin_unlock_irqrestore(&svc->cq_lock, flags);
+ queue_work(svc->workqueue, &svc->work);
+}
+
+static void svc_i3c_fill_txfifo(struct svc_i3c_target *svc)
+{
+ struct i3c_request *req, *complete = NULL;
+ unsigned long flags;
+ int val;
+
+ spin_lock_irqsave(&svc->txq_lock, flags);
+ while ((!!(req = list_first_entry_or_null(&svc->txq, struct i3c_request, list))) &&
+ !((readl_relaxed(svc->regs + I3C_SDATACTRL) & I3C_SDATACTRL_TXFULL_MASK))) {
+ while (!(readl_relaxed(svc->regs + I3C_SDATACTRL)
+ & I3C_SDATACTRL_TXFULL_MASK)) {
+ val = *(u8 *)(req->buf + req->actual);
+
+ if (req->actual + 1 == req->length)
+ writel_relaxed(val, svc->regs + I3C_SWDATAE);
+ else
+ writel_relaxed(val, svc->regs + I3C_SWDATAB);
+
+ req->actual++;
+
+ if (req->actual == req->length) {
+ list_del(&req->list);
+ complete = req;
+ spin_unlock_irqrestore(&svc->txq_lock, flags);
+
+ svc_i3c_queue_complete(svc, complete);
+
+ spin_lock_irqsave(&svc->txq_lock, flags);
+ break;
+ }
+ }
+ }
+ spin_unlock_irqrestore(&svc->txq_lock, flags);
+}
+
+static int svc_i3c_target_queue(struct i3c_request *req, gfp_t)
+{
+ struct svc_i3c_target *svc;
+ struct list_head *q;
+ unsigned long flags;
+ spinlock_t *lk;
+
+ svc = dev_get_drvdata(&req->ctrl->dev);
+ if (!svc)
+ return -EINVAL;
+
+ if (req->tx) {
+ q = &svc->txq;
+ lk = &svc->txq_lock;
+ } else {
+ q = &svc->rxq;
+ lk = &svc->rxq_lock;
+ }
+
+ spin_lock_irqsave(lk, flags);
+ list_add_tail(&req->list, q);
+ spin_unlock_irqrestore(lk, flags);
+
+ if (req->tx)
+ svc_i3c_fill_txfifo(svc);
+
+ if (req->tx)
+ writel_relaxed(I3C_SINT_TXSEND, svc->regs + I3C_SINTSET);
+ else
+ writel_relaxed(I3C_SINT_RXPEND, svc->regs + I3C_SINTSET);
+
+ return 0;
+}
+
+static int svc_i3c_dequeue(struct i3c_request *req)
+{
+ struct svc_i3c_target *svc;
+ unsigned long flags;
+ spinlock_t *lk;
+
+ svc = dev_get_drvdata(&req->ctrl->dev);
+ if (!svc)
+ return -EINVAL;
+
+ if (req->tx)
+ lk = &svc->txq_lock;
+ else
+ lk = &svc->rxq_lock;
+
+ spin_lock_irqsave(lk, flags);
+ list_del(&req->list);
+ spin_unlock_irqrestore(lk, flags);
+
+ return 0;
+}
+
+static void svc_i3c_target_fifo_flush(struct i3c_target_ctrl *ctrl, bool tx)
+{
+ struct svc_i3c_target *svc;
+ u32 val;
+
+ svc = dev_get_drvdata(&ctrl->dev);
+
+ val = readl_relaxed(svc->regs + I3C_SDATACTRL);
+
+ val |= tx ? I3C_SDATACTRL_FLUSHTB_MASK : I3C_SDATACTRL_FLUSHFB_MASK;
+
+ writel_relaxed(val, svc->regs + I3C_SDATACTRL);
+}
+
+static int
+svc_i3c_target_raise_ibi(struct i3c_target_ctrl *ctrl, void *p, u8 size)
+{
+ struct svc_i3c_target *svc;
+ unsigned long flags;
+ u8 *ibidata = p;
+ u32 ext1 = 0, ext2 = 0;
+ u32 val;
+ int ret;
+
+ svc = dev_get_drvdata(&ctrl->dev);
+
+ if (size && !p)
+ return -EINVAL;
+
+ if (size > 8)
+ return -EINVAL;
+
+ val = readl_relaxed(svc->regs + I3C_SSTATUS);
+ if (val & I3C_SSTATUS_IBIDIS_MASK)
+ return -EINVAL;
+
+ ret = readl_relaxed_poll_timeout(svc->regs + I3C_SCTRL, val,
+ !(val & I3C_SCTRL_EVENT_MASK), 0, 10000);
+ if (ret) {
+ dev_err(&ctrl->dev, "Timeout when polling for NO event pending");
+ val &= ~I3C_SCTRL_EVENT_MASK;
+ writel_relaxed(val, svc->regs + I3C_SCTRL);
+ return -ENAVAIL;
+ }
+
+ spin_lock_irqsave(&svc->ctrl_lock, flags);
+
+ val = readl_relaxed(svc->regs + I3C_SCTRL);
+
+ val &= ~I3C_SCTRL_EVENT_MASK | I3C_SCTRL_IBIDATA_MASK;
+ val |= FIELD_PREP(I3C_SCTRL_EVENT_MASK, I3C_SCTRL_EVENT_IBI);
+
+ if (size) {
+ val |= FIELD_PREP(I3C_SCTRL_IBIDATA_MASK, *ibidata);
+ ibidata++;
+
+ if (size > 1)
+ val |= I3C_SCTRL_EXTDATA_MASK;
+
+ size--;
+ if (size > 0) {
+ ext1 |= (size + 2);
+ ext1 |= (*ibidata++) << I3C_IBIEXT1_EXT1_SHIFT;
+ size--;
+ }
+
+ if (size > 0) {
+ ext1 |= (*ibidata++) << I3C_IBIEXT1_EXT2_SHIFT;
+ size--;
+ }
+
+ if (size > 0) {
+ ext1 |= (*ibidata++) << I3C_IBIEXT1_EXT3_SHIFT;
+ size--;
+ }
+
+ writel_relaxed(ext1, svc->regs + I3C_IBIEXT1);
+
+ if (size > 0) {
+ ext2 |= (*ibidata++) << I3C_IBIEXT2_EXT4_SHIFT;
+ size--;
+ }
+
+ if (size > 0) {
+ ext2 |= (*ibidata++) << I3C_IBIEXT2_EXT5_SHIFT;
+ size--;
+ }
+
+ if (size > 0) {
+ ext2 |= (*ibidata++) << I3C_IBIEXT2_EXT6_SHIFT;
+ size--;
+ }
+
+ if (size > 0) {
+ ext2 |= (*ibidata++) << I3C_IBIEXT2_EXT7_SHIFT;
+ size--;
+ }
+
+ writeb_relaxed(ext2, svc->regs + I3C_IBIEXT2);
+ }
+
+ /* Issue IBI*/
+ writel_relaxed(val, svc->regs + I3C_SCTRL);
+ spin_unlock_irqrestore(&svc->ctrl_lock, flags);
+
+ ret = readl_relaxed_poll_timeout(svc->regs + I3C_SCTRL, val,
+ !(val & I3C_SCTRL_EVENT_MASK), 0, 1000000);
+ if (ret) {
+ dev_err(&ctrl->dev, "Timeout when polling for IBI finish\n");
+
+ //clear event to above hang bus
+ spin_lock_irqsave(&svc->ctrl_lock, flags);
+ val = readl_relaxed(svc->regs + I3C_SCTRL);
+ val &= ~I3C_SCTRL_EVENT_MASK;
+ writel_relaxed(val, svc->regs + I3C_SCTRL);
+ spin_unlock_irqrestore(&svc->ctrl_lock, flags);
+
+ return -ENAVAIL;
+ }
+
+ return 0;
+}
+
+static void svc_i3c_target_complete(struct work_struct *work)
+{
+ struct svc_i3c_target *svc = container_of(work, struct svc_i3c_target, work);
+ struct i3c_request *req;
+ unsigned long flags;
+
+ spin_lock_irqsave(&svc->cq_lock, flags);
+ while (!list_empty(&svc->cq)) {
+ req = list_first_entry(&svc->cq, struct i3c_request, list);
+ list_del(&req->list);
+ spin_unlock_irqrestore(&svc->cq_lock, flags);
+ req->complete(req);
+
+ spin_lock_irqsave(&svc->cq_lock, flags);
+ }
+ spin_unlock_irqrestore(&svc->cq_lock, flags);
+}
+
+static irqreturn_t svc_i3c_target_irq_handler(int irq, void *dev_id)
+{
+ struct i3c_request *req, *complete = NULL;
+ struct svc_i3c_target *svc = dev_id;
+ unsigned long flags;
+ u32 statusFlags;
+
+ statusFlags = readl(svc->regs + I3C_SSTATUS);
+ writel(statusFlags, svc->regs + I3C_SSTATUS);
+
+ if (statusFlags & I3C_SSTATUS_DACHG_MASK)
+ complete_all(&svc->dacomplete);
+
+ if (statusFlags & I3C_SSTATUS_RX_PEND_MASK) {
+ spin_lock_irqsave(&svc->rxq_lock, flags);
+ req = list_first_entry_or_null(&svc->rxq, struct i3c_request, list);
+
+ if (!req) {
+ writel_relaxed(I3C_SINT_RXPEND, svc->regs + I3C_SINTCLR);
+ } else {
+ while (!(readl_relaxed(svc->regs + I3C_SDATACTRL) &
+ I3C_SDATACTRL_RXEMPTY_MASK)) {
+ *(u8 *)(req->buf + req->actual) =
+ readl_relaxed(svc->regs + I3C_SRDATAB);
+ req->actual++;
+
+ if (req->actual == req->length) {
+ complete = req;
+ list_del(&req->list);
+ break;
+ }
+ }
+
+ if (req->actual != req->length && (statusFlags & I3C_SSTATUS_STOP_MASK)) {
+ complete = req;
+ list_del(&req->list);
+ }
+ }
+ spin_unlock_irqrestore(&svc->rxq_lock, flags);
+
+ if (complete) {
+ spin_lock_irqsave(&svc->cq_lock, flags);
+ list_add_tail(&complete->list, &svc->cq);
+ spin_unlock_irqrestore(&svc->cq_lock, flags);
+ queue_work(svc->workqueue, &svc->work);
+ complete = NULL;
+ }
+ }
+
+ if (statusFlags & I3C_SSTATUS_TXNOTFULL_MASK) {
+ svc_i3c_fill_txfifo(svc);
+
+ spin_lock_irqsave(&svc->txq_lock, flags);
+ if (list_empty(&svc->txq))
+ writel_relaxed(I3C_SINT_TXSEND, svc->regs + I3C_SINTCLR);
+ spin_unlock_irqrestore(&svc->txq_lock, flags);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static void svc_i3c_cancel_all_reqs(struct i3c_target_ctrl *ctrl, bool tx)
+{
+ struct svc_i3c_target *svc;
+ struct i3c_request *req;
+ struct list_head *q;
+ unsigned long flags;
+ spinlock_t *lk;
+
+ svc = dev_get_drvdata(&ctrl->dev);
+ if (!svc)
+ return;
+
+ if (tx) {
+ q = &svc->txq;
+ lk = &svc->txq_lock;
+ } else {
+ q = &svc->rxq;
+ lk = &svc->rxq_lock;
+ }
+
+ spin_lock_irqsave(lk, flags);
+ while (!list_empty(q)) {
+ req = list_first_entry(q, struct i3c_request, list);
+ list_del(&req->list);
+ spin_unlock_irqrestore(lk, flags);
+
+ req->status = I3C_REQUEST_CANCEL;
+ req->complete(req);
+ spin_lock_irqsave(lk, flags);
+ }
+ spin_unlock_irqrestore(lk, flags);
+}
+
+static int svc_i3c_hotjoin(struct i3c_target_ctrl *ctrl)
+{
+ struct svc_i3c_target *svc;
+ int ret;
+ u32 val;
+ u32 cfg;
+
+ svc = dev_get_drvdata(&ctrl->dev);
+ if (!svc)
+ return -EINVAL;
+
+ reinit_completion(&svc->dacomplete);
+
+ val = readl_relaxed(svc->regs + I3C_SSTATUS);
+ if (val & I3C_SSTATUS_HJDIS_MASK) {
+ dev_err(&ctrl->dev, "Hotjoin disabled by i3c master\n");
+ return -EINVAL;
+ }
+
+ ret = readl_relaxed_poll_timeout(svc->regs + I3C_SCTRL, val,
+ !(val & I3C_SCTRL_EVENT_MASK), 0, 10000);
+ if (ret) {
+ dev_err(&ctrl->dev, "Timeout when polling for none event pending");
+ return -ENAVAIL;
+ }
+
+ cfg = readl_relaxed(svc->regs + I3C_SCONFIG);
+ cfg |= I3C_SCONFIG_OFFLINE_MASK;
+ writel_relaxed(cfg, svc->regs + I3C_SCONFIG);
+
+ val &= ~(I3C_SCTRL_EVENT_MASK | I3C_SCTRL_IBIDATA_MASK);
+ val |= FIELD_PREP(I3C_SCTRL_EVENT_MASK, I3C_SCTRL_EVENT_HOTJOIN);
+ /* Issue hotjoin*/
+ writel_relaxed(val, svc->regs + I3C_SCTRL);
+
+ ret = readl_relaxed_poll_timeout(svc->regs + I3C_SCTRL, val,
+ !(val & I3C_SCTRL_EVENT_MASK), 0, 100000);
+ if (ret) {
+ val &= ~FIELD_PREP(I3C_SCTRL_EVENT_MASK, I3C_SCTRL_EVENT_MASK);
+ writel_relaxed(val, svc->regs + I3C_SCTRL);
+ dev_err(&ctrl->dev, "Timeout when polling for HOTJOIN finish\n");
+ return -EINVAL;
+ }
+
+ val = readl_relaxed(svc->regs + I3C_SSTATUS);
+ val = FIELD_GET(I3C_SSTATUS_EVDET_MASK, val);
+ if (val != I3C_SSTATUS_EVDET_ACKED) {
+ dev_err(&ctrl->dev, "Master NACKED hotjoin request\n");
+ return -EINVAL;
+ }
+
+ writel_relaxed(I3C_SINT_DACHG, svc->regs + I3C_SINTSET);
+ ret = wait_for_completion_timeout(&svc->dacomplete, msecs_to_jiffies(100));
+ writel_relaxed(I3C_SINT_DACHG, svc->regs + I3C_SINTCLR);
+ if (!ret) {
+ dev_err(&ctrl->dev, "wait for da assignment timeout\n");
+ return -EIO;
+ }
+
+ val = readl_relaxed(svc->regs + I3C_SMAPCTRL0);
+ val = FIELD_GET(I3C_SMAPCTRL0_DA_MASK, val);
+ dev_info(&ctrl->dev, "Get dynamtic address 0x%x\n", val);
+ return 0;
+}
+
+static int svc_i3c_set_status_format1(struct i3c_target_ctrl *ctrl, u16 status)
+{
+ struct svc_i3c_target *svc;
+ unsigned long flags;
+ u32 val;
+
+ svc = dev_get_drvdata(&ctrl->dev);
+
+ spin_lock_irqsave(&svc->ctrl_lock, flags);
+ val = readl_relaxed(svc->regs + I3C_SCTRL);
+ val &= 0xFFFF;
+ val |= status << 16;
+ writel_relaxed(val, svc->regs + I3C_SCTRL);
+ spin_unlock_irqrestore(&svc->ctrl_lock, flags);
+
+ return 0;
+}
+
+static u16 svc_i3c_get_status_format1(struct i3c_target_ctrl *ctrl)
+{
+ struct svc_i3c_target *svc;
+
+ svc = dev_get_drvdata(&ctrl->dev);
+
+ return readl_relaxed(svc->regs + I3C_SCTRL) >> 16;
+}
+
+static u8 svc_i3c_get_addr(struct i3c_target_ctrl *ctrl)
+{
+ struct svc_i3c_target *svc;
+ int val;
+
+ svc = dev_get_drvdata(&ctrl->dev);
+
+ val = readl_relaxed(svc->regs + I3C_SMAPCTRL0);
+
+ if (val & I3C_SMAPCTRL0_ENA_MASK)
+ return FIELD_GET(I3C_SMAPCTRL0_DA_MASK, val);
+
+ return 0;
+}
+
+int svc_i3c_fifo_status(struct i3c_target_ctrl *ctrl, bool tx)
+{
+ struct svc_i3c_target *svc;
+ int val;
+
+ svc = dev_get_drvdata(&ctrl->dev);
+
+ val = readl_relaxed(svc->regs + I3C_SDATACTRL);
+
+ if (tx)
+ return FIELD_GET(I3C_SDATACTRL_TXCOUNT_MASK, val);
+ else
+ return FIELD_GET(I3C_SDATACTRL_RXCOUNT_MASK, val);
+}
+
+static struct i3c_target_ctrl_ops svc_i3c_target_ops = {
+ .set_config = svc_i3c_target_set_config,
+ .enable = svc_i3c_target_enable,
+ .disable = svc_i3c_target_disable,
+ .queue = svc_i3c_target_queue,
+ .dequeue = svc_i3c_dequeue,
+ .raise_ibi = svc_i3c_target_raise_ibi,
+ .fifo_flush = svc_i3c_target_fifo_flush,
+ .cancel_all_reqs = svc_i3c_cancel_all_reqs,
+ .get_features = svc_i3c_get_features,
+ .hotjoin = svc_i3c_hotjoin,
+ .fifo_status = svc_i3c_fifo_status,
+ .set_status_format1 = svc_i3c_set_status_format1,
+ .get_status_format1 = svc_i3c_get_status_format1,
+ .get_addr = svc_i3c_get_addr,
+};
+
+static int svc_i3c_target_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct i3c_target_ctrl *target;
+ struct svc_i3c_target *svc;
+ int ret;
+ u32 val;
+
+ svc = devm_kzalloc(dev, sizeof(*svc), GFP_KERNEL);
+ if (!svc)
+ return -ENOMEM;
+
+ svc->regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(svc->regs))
+ return PTR_ERR(svc->regs);
+
+ svc->clks[PCLK].id = "pclk";
+ svc->clks[FCLK].id = "fast_clk";
+ svc->clks[SCLK].id = "slow_clk";
+
+ ret = devm_clk_bulk_get(dev, MAXCLK, svc->clks);
+ if (ret < 0) {
+ dev_err(dev, "fail get clks: %d\n", ret);
+ return ret;
+ }
+
+ ret = clk_bulk_prepare_enable(MAXCLK, svc->clks);
+ if (ret < 0) {
+ dev_err(dev, "fail enable clks: %d\n", ret);
+ return ret;
+ }
+
+ svc->irq = platform_get_irq(pdev, 0);
+ if (svc->irq < 0)
+ return svc->irq;
+
+ INIT_LIST_HEAD(&svc->txq);
+ INIT_LIST_HEAD(&svc->rxq);
+ INIT_LIST_HEAD(&svc->cq);
+ spin_lock_init(&svc->txq_lock);
+ spin_lock_init(&svc->rxq_lock);
+ spin_lock_init(&svc->cq_lock);
+ spin_lock_init(&svc->ctrl_lock);
+
+ init_completion(&svc->dacomplete);
+
+ INIT_WORK(&svc->work, svc_i3c_target_complete);
+ svc->workqueue = alloc_workqueue("%s-cq", 0, 0, dev_name(dev));
+ if (!svc->workqueue)
+ return -ENOMEM;
+
+ /* Disable all IRQ */
+ writel_relaxed(0xFFFFFFFF, svc->regs + I3C_SINTCLR);
+
+ val = readl_relaxed(svc->regs + I3C_SCAPABILITIES);
+ svc->features.tx_fifo_sz = FIELD_GET(I3C_SCAPABILITIES_FIFOTX_MASK, val);
+ svc->features.tx_fifo_sz = 2 << svc->features.tx_fifo_sz;
+
+ svc->features.rx_fifo_sz = FIELD_GET(I3C_SCAPABILITIES_FIFORX_MASK, val);
+ svc->features.rx_fifo_sz = 2 << svc->features.rx_fifo_sz;
+
+ ret = devm_request_irq(dev, svc->irq, svc_i3c_target_irq_handler, 0, "svc-i3c-irq", svc);
+ if (ret)
+ return -ENOENT;
+
+ target = devm_i3c_target_ctrl_create(dev, &svc_i3c_target_ops);
+ if (!target)
+ return -ENOMEM;
+
+ dev_set_drvdata(&target->dev, svc);
+
+ return 0;
+}
+
+static int svc_i3c_target_remove(struct platform_device *pdev)
+{
+ return 0;
+}
+
+static const struct of_device_id svc_i3c_target_of_match_tbl[] = {
+ { .compatible = "silvaco,i3c-target-v1" },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, svc_i3c_target_of_match_tbl);
+
+static struct platform_driver svc_i3c_target = {
+ .probe = svc_i3c_target_probe,
+ .remove = svc_i3c_target_remove,
+ .driver = {
+ .name = "silvaco-i3c-target",
+ .of_match_table = svc_i3c_target_of_match_tbl,
+ //.pm = &svc_i3c_pm_ops,
+ },
+};
+module_platform_driver(svc_i3c_target);
+
+MODULE_DESCRIPTION("Silvaco dual-role I3C target driver");
+MODULE_LICENSE("GPL");
--
2.34.1
Hi Frank,
kernel test robot noticed the following build warnings:
[auto build test WARNING on tty/tty-testing]
[also build test WARNING on tty/tty-next tty/tty-linus robh/for-next linus/master v6.7 next-20240111]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Frank-Li/i3c-add-target-mode-support/20240111-015711
base: https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty.git tty-testing
patch link: https://lore.kernel.org/r/20240110175221.2335480-5-Frank.Li%40nxp.com
patch subject: [PATCH v2 4/7] i3c: target: add svc target controller support
config: x86_64-allmodconfig (https://download.01.org/0day-ci/archive/20240111/202401112149.TbWJpRfM-lkp@intel.com/config)
compiler: ClangBuiltLinux clang version 17.0.6 (https://github.com/llvm/llvm-project 6009708b4367171ccdbf4b5905cb6a803753fe18)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20240111/202401112149.TbWJpRfM-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202401112149.TbWJpRfM-lkp@intel.com/
All warnings (new ones prefixed by >>):
>> drivers/i3c/target/svc-i3c-target.c:211:40: warning: no previous prototype for function 'svc_i3c_get_features' [-Wmissing-prototypes]
211 | const struct i3c_target_ctrl_features *svc_i3c_get_features(struct i3c_target_ctrl *ctrl)
| ^
drivers/i3c/target/svc-i3c-target.c:211:7: note: declare 'static' if the function is not intended to be used outside of this translation unit
211 | const struct i3c_target_ctrl_features *svc_i3c_get_features(struct i3c_target_ctrl *ctrl)
| ^
| static
>> drivers/i3c/target/svc-i3c-target.c:268:63: warning: omitting the parameter name in a function definition is a C2x extension [-Wc2x-extensions]
268 | static int svc_i3c_target_queue(struct i3c_request *req, gfp_t)
| ^
>> drivers/i3c/target/svc-i3c-target.c:666:5: warning: no previous prototype for function 'svc_i3c_fifo_status' [-Wmissing-prototypes]
666 | int svc_i3c_fifo_status(struct i3c_target_ctrl *ctrl, bool tx)
| ^
drivers/i3c/target/svc-i3c-target.c:666:1: note: declare 'static' if the function is not intended to be used outside of this translation unit
666 | int svc_i3c_fifo_status(struct i3c_target_ctrl *ctrl, bool tx)
| ^
| static
3 warnings generated.
vim +/svc_i3c_get_features +211 drivers/i3c/target/svc-i3c-target.c
210
> 211 const struct i3c_target_ctrl_features *svc_i3c_get_features(struct i3c_target_ctrl *ctrl)
212 {
213 struct svc_i3c_target *svc;
214
215 svc = dev_get_drvdata(&ctrl->dev);
216
217 if (!svc)
218 return NULL;
219
220 return &svc->features;
221 }
222
223 static void svc_i3c_queue_complete(struct svc_i3c_target *svc, struct i3c_request *complete)
224 {
225 unsigned long flags;
226
227 spin_lock_irqsave(&svc->cq_lock, flags);
228 list_add_tail(&complete->list, &svc->cq);
229 spin_unlock_irqrestore(&svc->cq_lock, flags);
230 queue_work(svc->workqueue, &svc->work);
231 }
232
233 static void svc_i3c_fill_txfifo(struct svc_i3c_target *svc)
234 {
235 struct i3c_request *req, *complete = NULL;
236 unsigned long flags;
237 int val;
238
239 spin_lock_irqsave(&svc->txq_lock, flags);
240 while ((!!(req = list_first_entry_or_null(&svc->txq, struct i3c_request, list))) &&
241 !((readl_relaxed(svc->regs + I3C_SDATACTRL) & I3C_SDATACTRL_TXFULL_MASK))) {
242 while (!(readl_relaxed(svc->regs + I3C_SDATACTRL)
243 & I3C_SDATACTRL_TXFULL_MASK)) {
244 val = *(u8 *)(req->buf + req->actual);
245
246 if (req->actual + 1 == req->length)
247 writel_relaxed(val, svc->regs + I3C_SWDATAE);
248 else
249 writel_relaxed(val, svc->regs + I3C_SWDATAB);
250
251 req->actual++;
252
253 if (req->actual == req->length) {
254 list_del(&req->list);
255 complete = req;
256 spin_unlock_irqrestore(&svc->txq_lock, flags);
257
258 svc_i3c_queue_complete(svc, complete);
259
260 spin_lock_irqsave(&svc->txq_lock, flags);
261 break;
262 }
263 }
264 }
265 spin_unlock_irqrestore(&svc->txq_lock, flags);
266 }
267
> 268 static int svc_i3c_target_queue(struct i3c_request *req, gfp_t)
269 {
270 struct svc_i3c_target *svc;
271 struct list_head *q;
272 unsigned long flags;
273 spinlock_t *lk;
274
275 svc = dev_get_drvdata(&req->ctrl->dev);
276 if (!svc)
277 return -EINVAL;
278
279 if (req->tx) {
280 q = &svc->txq;
281 lk = &svc->txq_lock;
282 } else {
283 q = &svc->rxq;
284 lk = &svc->rxq_lock;
285 }
286
287 spin_lock_irqsave(lk, flags);
288 list_add_tail(&req->list, q);
289 spin_unlock_irqrestore(lk, flags);
290
291 if (req->tx)
292 svc_i3c_fill_txfifo(svc);
293
294 if (req->tx)
295 writel_relaxed(I3C_SINT_TXSEND, svc->regs + I3C_SINTSET);
296 else
297 writel_relaxed(I3C_SINT_RXPEND, svc->regs + I3C_SINTSET);
298
299 return 0;
300 }
301
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
Hi Frank,
kernel test robot noticed the following build warnings:
[auto build test WARNING on tty/tty-testing]
[also build test WARNING on tty/tty-next tty/tty-linus robh/for-next linus/master v6.7 next-20240111]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Frank-Li/i3c-add-target-mode-support/20240111-015711
base: https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty.git tty-testing
patch link: https://lore.kernel.org/r/20240110175221.2335480-5-Frank.Li%40nxp.com
patch subject: [PATCH v2 4/7] i3c: target: add svc target controller support
config: powerpc-allmodconfig (https://download.01.org/0day-ci/archive/20240111/202401111724.LiPgx2yU-lkp@intel.com/config)
compiler: clang version 18.0.0git (https://github.com/llvm/llvm-project 9bde5becb44ea071f5e1fa1f5d4071dc8788b18c)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20240111/202401111724.LiPgx2yU-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202401111724.LiPgx2yU-lkp@intel.com/
All warnings (new ones prefixed by >>):
In file included from include/linux/io.h:13:
In file included from arch/powerpc/include/asm/io.h:24:
In file included from include/linux/mm.h:1084:
In file included from include/linux/huge_mm.h:8:
In file included from include/linux/fs.h:33:
In file included from include/linux/percpu-rwsem.h:7:
In file included from include/linux/rcuwait.h:6:
In file included from include/linux/sched/signal.h:6:
include/linux/signal.h:186:1: warning: array index 1 is past the end of the array (that has type 'unsigned long[1]') [-Warray-bounds]
186 | _SIG_SET_OP(signotset, _sig_not)
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/signal.h:176:10: note: expanded from macro '_SIG_SET_OP'
176 | case 2: set->sig[1] = op(set->sig[1]); \
| ^ ~
arch/powerpc/include/uapi/asm/signal.h:18:2: note: array 'sig' declared here
18 | unsigned long sig[_NSIG_WORDS];
| ^
In file included from drivers/i3c/target/svc-i3c-target.c:14:
In file included from include/linux/interrupt.h:11:
In file included from include/linux/hardirq.h:11:
In file included from arch/powerpc/include/asm/hardirq.h:6:
In file included from include/linux/irq.h:20:
In file included from include/linux/io.h:13:
In file included from arch/powerpc/include/asm/io.h:24:
In file included from include/linux/mm.h:1084:
In file included from include/linux/huge_mm.h:8:
In file included from include/linux/fs.h:33:
In file included from include/linux/percpu-rwsem.h:7:
In file included from include/linux/rcuwait.h:6:
In file included from include/linux/sched/signal.h:6:
include/linux/signal.h:197:10: warning: array index 1 is past the end of the array (that has type 'unsigned long[1]') [-Warray-bounds]
197 | case 2: set->sig[1] = 0;
| ^ ~
arch/powerpc/include/uapi/asm/signal.h:18:2: note: array 'sig' declared here
18 | unsigned long sig[_NSIG_WORDS];
| ^
In file included from drivers/i3c/target/svc-i3c-target.c:14:
In file included from include/linux/interrupt.h:11:
In file included from include/linux/hardirq.h:11:
In file included from arch/powerpc/include/asm/hardirq.h:6:
In file included from include/linux/irq.h:20:
In file included from include/linux/io.h:13:
In file included from arch/powerpc/include/asm/io.h:24:
In file included from include/linux/mm.h:1084:
In file included from include/linux/huge_mm.h:8:
In file included from include/linux/fs.h:33:
In file included from include/linux/percpu-rwsem.h:7:
In file included from include/linux/rcuwait.h:6:
In file included from include/linux/sched/signal.h:6:
include/linux/signal.h:210:10: warning: array index 1 is past the end of the array (that has type 'unsigned long[1]') [-Warray-bounds]
210 | case 2: set->sig[1] = -1;
| ^ ~
arch/powerpc/include/uapi/asm/signal.h:18:2: note: array 'sig' declared here
18 | unsigned long sig[_NSIG_WORDS];
| ^
In file included from drivers/i3c/target/svc-i3c-target.c:14:
In file included from include/linux/interrupt.h:11:
In file included from include/linux/hardirq.h:11:
In file included from arch/powerpc/include/asm/hardirq.h:6:
In file included from include/linux/irq.h:20:
In file included from include/linux/io.h:13:
In file included from arch/powerpc/include/asm/io.h:24:
In file included from include/linux/mm.h:1084:
In file included from include/linux/huge_mm.h:8:
In file included from include/linux/fs.h:33:
In file included from include/linux/percpu-rwsem.h:7:
In file included from include/linux/rcuwait.h:6:
In file included from include/linux/sched/signal.h:6:
include/linux/signal.h:241:10: warning: array index 1 is past the end of the array (that has type 'unsigned long[1]') [-Warray-bounds]
241 | case 2: set->sig[1] = 0;
| ^ ~
arch/powerpc/include/uapi/asm/signal.h:18:2: note: array 'sig' declared here
18 | unsigned long sig[_NSIG_WORDS];
| ^
In file included from drivers/i3c/target/svc-i3c-target.c:14:
In file included from include/linux/interrupt.h:11:
In file included from include/linux/hardirq.h:11:
In file included from arch/powerpc/include/asm/hardirq.h:6:
In file included from include/linux/irq.h:20:
In file included from include/linux/io.h:13:
In file included from arch/powerpc/include/asm/io.h:24:
In file included from include/linux/mm.h:1084:
In file included from include/linux/huge_mm.h:8:
In file included from include/linux/fs.h:33:
In file included from include/linux/percpu-rwsem.h:7:
In file included from include/linux/rcuwait.h:6:
In file included from include/linux/sched/signal.h:6:
include/linux/signal.h:254:10: warning: array index 1 is past the end of the array (that has type 'unsigned long[1]') [-Warray-bounds]
254 | case 2: set->sig[1] = -1;
| ^ ~
arch/powerpc/include/uapi/asm/signal.h:18:2: note: array 'sig' declared here
18 | unsigned long sig[_NSIG_WORDS];
| ^
drivers/i3c/target/svc-i3c-target.c:211:40: warning: no previous prototype for function 'svc_i3c_get_features' [-Wmissing-prototypes]
211 | const struct i3c_target_ctrl_features *svc_i3c_get_features(struct i3c_target_ctrl *ctrl)
| ^
drivers/i3c/target/svc-i3c-target.c:211:7: note: declare 'static' if the function is not intended to be used outside of this translation unit
211 | const struct i3c_target_ctrl_features *svc_i3c_get_features(struct i3c_target_ctrl *ctrl)
| ^
| static
>> drivers/i3c/target/svc-i3c-target.c:268:63: warning: omitting the parameter name in a function definition is a C23 extension [-Wc23-extensions]
268 | static int svc_i3c_target_queue(struct i3c_request *req, gfp_t)
| ^
drivers/i3c/target/svc-i3c-target.c:666:5: warning: no previous prototype for function 'svc_i3c_fifo_status' [-Wmissing-prototypes]
666 | int svc_i3c_fifo_status(struct i3c_target_ctrl *ctrl, bool tx)
| ^
drivers/i3c/target/svc-i3c-target.c:666:1: note: declare 'static' if the function is not intended to be used outside of this translation unit
666 | int svc_i3c_fifo_status(struct i3c_target_ctrl *ctrl, bool tx)
| ^
| static
52 warnings and 5 errors generated.
vim +268 drivers/i3c/target/svc-i3c-target.c
267
> 268 static int svc_i3c_target_queue(struct i3c_request *req, gfp_t)
269 {
270 struct svc_i3c_target *svc;
271 struct list_head *q;
272 unsigned long flags;
273 spinlock_t *lk;
274
275 svc = dev_get_drvdata(&req->ctrl->dev);
276 if (!svc)
277 return -EINVAL;
278
279 if (req->tx) {
280 q = &svc->txq;
281 lk = &svc->txq_lock;
282 } else {
283 q = &svc->rxq;
284 lk = &svc->rxq_lock;
285 }
286
287 spin_lock_irqsave(lk, flags);
288 list_add_tail(&req->list, q);
289 spin_unlock_irqrestore(lk, flags);
290
291 if (req->tx)
292 svc_i3c_fill_txfifo(svc);
293
294 if (req->tx)
295 writel_relaxed(I3C_SINT_TXSEND, svc->regs + I3C_SINTSET);
296 else
297 writel_relaxed(I3C_SINT_RXPEND, svc->regs + I3C_SINTSET);
298
299 return 0;
300 }
301
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
© 2016 - 2025 Red Hat, Inc.