* [PATCH v2 0/2] Add ADI I3C Controller
@ 2025-06-06 13:21 Jorge Marques
2025-06-06 13:21 ` [PATCH v2 1/2] dt-bindings: i3c: Add adi-i3c-master Jorge Marques
2025-06-06 13:21 ` [PATCH v2 2/2] i3c: master: Add driver for Analog Devices I3C Controller IP Jorge Marques
0 siblings, 2 replies; 9+ messages in thread
From: Jorge Marques @ 2025-06-06 13:21 UTC (permalink / raw)
To: Alexandre Belloni, Frank Li, Rob Herring, Krzysztof Kozlowski,
Conor Dooley
Cc: linux-i3c, devicetree, linux-kernel, Jorge Marques
I3C Controller is subset of the I3C-basic specification to interface
peripherals through I3C and I2C. The controller RTL is FPGA
synthesizable and documentation is provided at
https://analogdevicesinc.github.io/hdl/library/i3c_controller
The main target for the I3C Controller IP is low-cost FPGAs.
In this version the driver supports IBI (only the MDB), I3C and I2C
transfers.
Signed-off-by: Jorge Marques <jorge.marques@analog.com>
---
Changes in v2:
Documentation/devicetree/bindings/i3c/adi,i3c-master.yaml:
- Move allof
- Rename clocks to axi, i3c
adi-i3c-master.c:
- Update license year
- Rework regmap to use FIELD_GET, FIELD_PREP
- Reformat regmap to have FIELDS after REG, prefixed by reg name.
- Add overflow safeguards to cmd, tx fifos
- Fix macro related macros (mostly errondeous `| ~BITMASK`
- Use guard macros, remove goto.
- Simplify daa logic
- Replace devm_clk_get with devm_clk_get_enabled
- Solve 64bit->32bit warnings on x86_64 systems by casting to u32
- Link to v1: https://lore.kernel.org/r/20250604-adi-i3c-master-v1-0-0488e80dafcb@analog.com
---
Jorge Marques (2):
dt-bindings: i3c: Add adi-i3c-master
i3c: master: Add driver for Analog Devices I3C Controller IP
.../devicetree/bindings/i3c/adi,i3c-master.yaml | 63 ++
MAINTAINERS | 6 +
drivers/i3c/master/Kconfig | 11 +
drivers/i3c/master/Makefile | 1 +
drivers/i3c/master/adi-i3c-master.c | 1037 ++++++++++++++++++++
5 files changed, 1118 insertions(+)
---
base-commit: 00286d7d643d3c98e48d9cc3a9f471b37154f462
change-id: 20250604-adi-i3c-master-2a5148c58c47
Best regards,
--
Jorge Marques <jorge.marques@analog.com>
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v2 1/2] dt-bindings: i3c: Add adi-i3c-master
2025-06-06 13:21 [PATCH v2 0/2] Add ADI I3C Controller Jorge Marques
@ 2025-06-06 13:21 ` Jorge Marques
2025-06-06 16:08 ` Conor Dooley
2025-06-06 13:21 ` [PATCH v2 2/2] i3c: master: Add driver for Analog Devices I3C Controller IP Jorge Marques
1 sibling, 1 reply; 9+ messages in thread
From: Jorge Marques @ 2025-06-06 13:21 UTC (permalink / raw)
To: Alexandre Belloni, Frank Li, Rob Herring, Krzysztof Kozlowski,
Conor Dooley
Cc: linux-i3c, devicetree, linux-kernel, Jorge Marques
Add bindings doc for ADI I3C Controller IP core, a FPGA synthesizable IP
core that implements the MIPI I3C Basic controller specification.
Signed-off-by: Jorge Marques <jorge.marques@analog.com>
---
.../devicetree/bindings/i3c/adi,i3c-master.yaml | 63 ++++++++++++++++++++++
MAINTAINERS | 5 ++
2 files changed, 68 insertions(+)
diff --git a/Documentation/devicetree/bindings/i3c/adi,i3c-master.yaml b/Documentation/devicetree/bindings/i3c/adi,i3c-master.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..e7fae394ad8fd4143eb1fffb9591a30a2d6cc6ac
--- /dev/null
+++ b/Documentation/devicetree/bindings/i3c/adi,i3c-master.yaml
@@ -0,0 +1,63 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/i3c/adi,i3c-master.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Analog Devices I3C Controller
+
+description: |
+ The ADI I3C controller implements a subset of the I3C-basic specification to
+ interface I3C and I2C peripherals [1].
+
+ [1] https://analogdevicesinc.github.io/hdl/library/i3c_controller
+
+maintainers:
+ - Jorge Marques <jorge.marques@analog.com>
+
+properties:
+ compatible:
+ const: adi,i3c-master
+
+ reg:
+ maxItems: 1
+
+ clocks:
+ minItems: 1
+ items:
+ - description: The AXI interconnect clock.
+ - description: The I3C controller clock.
+
+ clock-names:
+ items:
+ - const: axi
+ - const: i3c
+
+ interrupts:
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+ - clocks
+ - clock-names
+ - interrupts
+
+allOf:
+ - $ref: i3c.yaml#
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ i3c@44a00000 {
+ compatible = "adi,i3c-master";
+ reg = <0x44a00000 0x1000>;
+ interrupts = <0 56 4>;
+ clocks = <&clkc 15>, <&clkc 15>;
+ clock-names = "axi", "i3c";
+ #address-cells = <3>;
+ #size-cells = <0>;
+
+ /* I3C and I2C devices */
+ };
diff --git a/MAINTAINERS b/MAINTAINERS
index 96b82704950184bd71623ff41fc4df31e4c7fe87..6f56e17dcecf902c6812827c1ec3e067c65e9894 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -11243,6 +11243,11 @@ S: Maintained
F: Documentation/devicetree/bindings/i3c/aspeed,ast2600-i3c.yaml
F: drivers/i3c/master/ast2600-i3c-master.c
+I3C DRIVER FOR ANALOG DEVICES I3C CONTROLLER IP
+M: Jorge Marques <jorge.marques@analog.com>
+S: Maintained
+F: Documentation/devicetree/bindings/i3c/adi,i3c-master.yaml
+
I3C DRIVER FOR CADENCE I3C MASTER IP
M: Przemysław Gaj <pgaj@cadence.com>
S: Maintained
--
2.43.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v2 2/2] i3c: master: Add driver for Analog Devices I3C Controller IP
2025-06-06 13:21 [PATCH v2 0/2] Add ADI I3C Controller Jorge Marques
2025-06-06 13:21 ` [PATCH v2 1/2] dt-bindings: i3c: Add adi-i3c-master Jorge Marques
@ 2025-06-06 13:21 ` Jorge Marques
2025-06-06 20:33 ` ALOK TIWARI
1 sibling, 1 reply; 9+ messages in thread
From: Jorge Marques @ 2025-06-06 13:21 UTC (permalink / raw)
To: Alexandre Belloni, Frank Li, Rob Herring, Krzysztof Kozlowski,
Conor Dooley
Cc: linux-i3c, devicetree, linux-kernel, Jorge Marques
Add support for Analog Devices I3C Controller IP, an AXI-interfaced IP
core that supports I3C and I2C devices, multiple speed-grades and
I3C IBIs.
Signed-off-by: Jorge Marques <jorge.marques@analog.com>
---
MAINTAINERS | 1 +
drivers/i3c/master/Kconfig | 11 +
drivers/i3c/master/Makefile | 1 +
drivers/i3c/master/adi-i3c-master.c | 1037 +++++++++++++++++++++++++++++++++++
4 files changed, 1050 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 6f56e17dcecf902c6812827c1ec3e067c65e9894..9eb5b6c327590725d1641fd4b73e48fc1d1a3954 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -11247,6 +11247,7 @@ I3C DRIVER FOR ANALOG DEVICES I3C CONTROLLER IP
M: Jorge Marques <jorge.marques@analog.com>
S: Maintained
F: Documentation/devicetree/bindings/i3c/adi,i3c-master.yaml
+F: drivers/i3c/master/adi-i3c-master.c
I3C DRIVER FOR CADENCE I3C MASTER IP
M: Przemysław Gaj <pgaj@cadence.com>
diff --git a/drivers/i3c/master/Kconfig b/drivers/i3c/master/Kconfig
index 7b30db3253af9d5c6aee6544c060e491bfbeb643..328b7145cdefa20e708ebfa3383e849ce51c5a71 100644
--- a/drivers/i3c/master/Kconfig
+++ b/drivers/i3c/master/Kconfig
@@ -1,4 +1,15 @@
# SPDX-License-Identifier: GPL-2.0-only
+config ADI_I3C_MASTER
+ tristate "Analog Devices I3C master driver"
+ depends on HAS_IOMEM
+ help
+ Support for Analog Devices I3C Controller IP, an AXI-interfaced IP
+ core that supports I3C and I2C devices, multiple speed-grades and
+ I3C IBIs.
+
+ This driver can also be built as a module. If so, the module
+ will be called adi-i3c-master.
+
config CDNS_I3C_MASTER
tristate "Cadence I3C master driver"
depends on HAS_IOMEM
diff --git a/drivers/i3c/master/Makefile b/drivers/i3c/master/Makefile
index 3e97960160bc85e5eaf2966ec0c3fae458c2711e..6cc4f4b73e7bdc206b68c750390f9c3cc2ccb199 100644
--- a/drivers/i3c/master/Makefile
+++ b/drivers/i3c/master/Makefile
@@ -1,4 +1,5 @@
# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_ADI_I3C_MASTER) += adi-i3c-master.o
obj-$(CONFIG_CDNS_I3C_MASTER) += i3c-master-cdns.o
obj-$(CONFIG_DW_I3C_MASTER) += dw-i3c-master.o
obj-$(CONFIG_AST2600_I3C_MASTER) += ast2600-i3c-master.o
diff --git a/drivers/i3c/master/adi-i3c-master.c b/drivers/i3c/master/adi-i3c-master.c
new file mode 100644
index 0000000000000000000000000000000000000000..b3799071573f4066eb14123c94ee599cc6331b3d
--- /dev/null
+++ b/drivers/i3c/master/adi-i3c-master.c
@@ -0,0 +1,1037 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * I3C Controller driver
+ * Copyright 2025 Analog Devices Inc.
+ * Author: Jorge Marques <jorge.marques@analog.com>
+ */
+
+#include <linux/bitops.h>
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/i3c/master.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+
+#define VERSION_MAJOR(x) ((u32)FIELD_GET(GENMASK(23, 16), (x)))
+#define VERSION_MINOR(x) ((u32)FIELD_GET(GENMASK(15, 8), (x)))
+#define VERSION_PATCH(x) ((u32)FIELD_GET(GENMASK(7, 0), (x)))
+
+#define MAX_DEVS 16
+
+#define REG_VERSION 0x000
+#define REG_ENABLE 0x040
+#define REG_IRQ_MASK 0x080
+#define REG_IRQ_PENDING 0x084
+#define REG_IRQ_PEDING_CMDR BIT(5)
+#define REG_IRQ_PEDING_IBI ((u32)BIT(6))
+#define REG_IRQ_PEDING_DAA BIT(7)
+#define REG_CMD_FIFO 0x0d4
+#define REG_CMD_FIFO_0_IS_CCC BIT(22)
+#define REG_CMD_FIFO_0_BCAST BIT(21)
+#define REG_CMD_FIFO_0_SR BIT(20)
+#define REG_CMD_FIFO_0_LEN(l) FIELD_PREP(GENMASK(19, 8), (l))
+#define REG_CMD_FIFO_0_LEN_MAX 4095
+#define REG_CMD_FIFO_0_DEV_ADDR(a) FIELD_PREP(GENMASK(7, 1), a)
+#define REG_CMD_FIFO_0_RNW BIT(0)
+#define REG_CMD_FIFO_1_CCC(id) FIELD_PREP(GENMASK(7, 0), (id))
+#define REG_CMD_FIFO_ROOM 0x0c0
+#define REG_CMDR_FIFO 0x0d8
+#define REG_CMDR_FIFO_NO_ERROR 0
+#define REG_CMDR_FIFO_CE0_ERROR 1
+#define REG_CMDR_FIFO_CE2_ERROR 4
+#define REG_CMDR_FIFO_NACK_RESP 6
+#define REG_CMDR_FIFO_UDA_ERROR 8
+#define REG_CMDR_FIFO_ERROR(x) FIELD_GET(GENMASK(23, 20), (x))
+#define REG_CMDR_FIFO_XFER_BYTES(x) FIELD_GET(GENMASK(19, 8), (x))
+#define REG_SDO_FIFO 0x0dc
+#define REG_SDO_FIFO_ROOM 0x0c8
+#define REG_SDI_FIFO 0x0e0
+#define REG_IBI_FIFO 0x0e4
+#define REG_FIFO_STATUS 0x0e8
+#define REG_FIFO_STATUS_CMDR_EMPTY BIT(0)
+#define REG_FIFO_STATUS_IBI_EMPTY BIT(1)
+#define REG_OPS 0x100
+#define REG_OPS_SET_SG(x) FIELD_PREP(GENMASK(6, 5), (x))
+#define REG_OPS_PP_SG_MASK GENMASK(6, 5)
+#define REG_IBI_CONFIG 0x140
+#define REG_IBI_CONFIG_LISTEN BIT(1)
+#define REG_IBI_CONFIG_ENABLE BIT(0)
+#define REG_DEV_CHAR 0x180
+#define REG_DEV_CHAR_IS_I2C BIT(0)
+#define REG_DEV_CHAR_IS_ATTACHED BIT(1)
+#define REG_DEV_CHAR_BCR_IBI(x) (((x) & GENMASK(2, 1)) << 1)
+#define REG_DEV_CHAR_WEN BIT(8)
+#define REG_DEV_CHAR_ADDR(x) FIELD_PREP(GENMASK(15, 9), (x))
+
+enum speed_grade {PP_SG_UNSET, PP_SG_1MHZ, PP_SG_3MHZ, PP_SG_6MHZ, PP_SG_12MHZ};
+struct adi_i3c_cmd {
+ u32 cmd0;
+ u32 cmd1;
+ u32 tx_len;
+ const void *tx_buf;
+ u32 rx_len;
+ void *rx_buf;
+ u32 error;
+};
+
+struct adi_i3c_xfer {
+ struct list_head node;
+ struct completion comp;
+ int ret;
+ unsigned int ncmds;
+ unsigned int ncmds_comp;
+ struct adi_i3c_cmd cmds[];
+};
+
+struct adi_i3c_master {
+ struct i3c_master_controller base;
+ u32 free_rr_slots;
+ unsigned int maxdevs;
+ struct {
+ unsigned int num_slots;
+ struct i3c_dev_desc **slots;
+ spinlock_t lock; /* Protect IBI slot access */
+ } ibi;
+ struct {
+ struct list_head list;
+ struct adi_i3c_xfer *cur;
+ spinlock_t lock; /* Protect transfer */
+ } xferqueue;
+ void __iomem *regs;
+ struct clk *clk;
+ unsigned long i3c_scl_lim;
+ struct {
+ u8 addrs[MAX_DEVS];
+ u8 index;
+ } daa;
+};
+
+static inline struct adi_i3c_master *to_adi_i3c_master(struct i3c_master_controller *master)
+{
+ return container_of(master, struct adi_i3c_master, base);
+}
+
+static void adi_i3c_master_wr_to_tx_fifo(struct adi_i3c_master *master,
+ const u8 *bytes, unsigned int nbytes)
+{
+ unsigned int n, m;
+ u32 tmp;
+
+ n = readl(master->regs + REG_SDO_FIFO_ROOM);
+ m = min(n, nbytes);
+ writesl(master->regs + REG_SDO_FIFO, bytes, m / 4);
+
+ if (m & 3) {
+ memcpy(&tmp, bytes + (m & ~3), m & 3);
+ writel(tmp, master->regs + REG_SDO_FIFO);
+ }
+}
+
+static void adi_i3c_master_rd_from_rx_fifo(struct adi_i3c_master *master,
+ u8 *bytes, unsigned int nbytes)
+{
+ readsl(master->regs + REG_SDI_FIFO, bytes, nbytes / 4);
+ if (nbytes & 3) {
+ u32 tmp;
+
+ tmp = readl(master->regs + REG_SDI_FIFO);
+ memcpy(bytes + (nbytes & ~3), &tmp, nbytes & 3);
+ }
+}
+
+static bool adi_i3c_master_supports_ccc_cmd(struct i3c_master_controller *m,
+ const struct i3c_ccc_cmd *cmd)
+{
+ if (cmd->ndests > 1)
+ return false;
+
+ switch (cmd->id) {
+ case I3C_CCC_ENEC(true):
+ case I3C_CCC_ENEC(false):
+ case I3C_CCC_DISEC(true):
+ case I3C_CCC_DISEC(false):
+ case I3C_CCC_RSTDAA(true):
+ case I3C_CCC_RSTDAA(false):
+ case I3C_CCC_ENTDAA:
+ case I3C_CCC_SETDASA:
+ case I3C_CCC_SETNEWDA:
+ case I3C_CCC_GETMWL:
+ case I3C_CCC_GETMRL:
+ case I3C_CCC_GETPID:
+ case I3C_CCC_GETBCR:
+ case I3C_CCC_GETDCR:
+ case I3C_CCC_GETSTATUS:
+ case I3C_CCC_GETHDRCAP:
+ return true;
+ default:
+ break;
+ }
+
+ return false;
+}
+
+static int adi_i3c_master_disable(struct adi_i3c_master *master)
+{
+ writel(0, master->regs + REG_IBI_CONFIG);
+
+ return 0;
+}
+
+static struct adi_i3c_xfer *adi_i3c_master_alloc_xfer(struct adi_i3c_master *master,
+ unsigned int ncmds)
+{
+ struct adi_i3c_xfer *xfer;
+
+ xfer = kzalloc(struct_size(xfer, cmds, ncmds), GFP_KERNEL);
+ if (!xfer)
+ return NULL;
+
+ INIT_LIST_HEAD(&xfer->node);
+ xfer->ncmds = ncmds;
+ xfer->ret = -ETIMEDOUT;
+
+ return xfer;
+}
+
+static void adi_i3c_master_start_xfer_locked(struct adi_i3c_master *master)
+{
+ struct adi_i3c_xfer *xfer = master->xferqueue.cur;
+ unsigned int i, n, m;
+
+ if (!xfer)
+ return;
+
+ for (i = 0; i < xfer->ncmds; i++) {
+ struct adi_i3c_cmd *cmd = &xfer->cmds[i];
+
+ if (!(cmd->cmd0 & REG_CMD_FIFO_0_RNW))
+ adi_i3c_master_wr_to_tx_fifo(master, cmd->tx_buf, cmd->tx_len);
+ }
+
+ n = readl(master->regs + REG_CMD_FIFO_ROOM);
+ for (i = 0; i < xfer->ncmds; i++) {
+ struct adi_i3c_cmd *cmd = &xfer->cmds[i];
+
+ m = cmd->cmd0 & REG_CMD_FIFO_0_IS_CCC ? 2 : 1;
+ if (m > n)
+ break;
+ writel(cmd->cmd0, master->regs + REG_CMD_FIFO);
+ if (cmd->cmd0 & REG_CMD_FIFO_0_IS_CCC)
+ writel(cmd->cmd1, master->regs + REG_CMD_FIFO);
+ n -= m;
+ }
+}
+
+static void adi_i3c_master_end_xfer_locked(struct adi_i3c_master *master,
+ u32 pending)
+{
+ struct adi_i3c_xfer *xfer = master->xferqueue.cur;
+ int i, ret = 0;
+ u32 status0;
+
+ if (!xfer)
+ return;
+
+ for (status0 = readl(master->regs + REG_FIFO_STATUS);
+ !(status0 & REG_FIFO_STATUS_CMDR_EMPTY);
+ status0 = readl(master->regs + REG_FIFO_STATUS)) {
+ struct adi_i3c_cmd *cmd;
+ u32 cmdr, rx_len;
+
+ cmdr = readl(master->regs + REG_CMDR_FIFO);
+
+ cmd = &xfer->cmds[xfer->ncmds_comp++];
+ if (cmd->cmd0 & REG_CMD_FIFO_0_RNW) {
+ rx_len = min_t(u32, REG_CMDR_FIFO_XFER_BYTES(cmdr), cmd->rx_len);
+ adi_i3c_master_rd_from_rx_fifo(master, cmd->rx_buf, rx_len);
+ }
+ cmd->error = REG_CMDR_FIFO_ERROR(cmdr);
+ }
+
+ for (i = 0; i < xfer->ncmds; i++) {
+ switch (xfer->cmds[i].error) {
+ case REG_CMDR_FIFO_NO_ERROR:
+ break;
+
+ case REG_CMDR_FIFO_CE0_ERROR:
+ case REG_CMDR_FIFO_CE2_ERROR:
+ case REG_CMDR_FIFO_NACK_RESP:
+ case REG_CMDR_FIFO_UDA_ERROR:
+ ret = -EIO;
+ break;
+
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ }
+
+ xfer->ret = ret;
+
+ if (xfer->ncmds_comp != xfer->ncmds)
+ return;
+
+ complete(&xfer->comp);
+
+ xfer = list_first_entry_or_null(&master->xferqueue.list,
+ struct adi_i3c_xfer, node);
+ if (xfer)
+ list_del_init(&xfer->node);
+
+ master->xferqueue.cur = xfer;
+ adi_i3c_master_start_xfer_locked(master);
+}
+
+static void adi_i3c_master_queue_xfer(struct adi_i3c_master *master,
+ struct adi_i3c_xfer *xfer)
+{
+ init_completion(&xfer->comp);
+ guard(spinlock_irqsave)(&master->xferqueue.lock);
+ if (master->xferqueue.cur) {
+ list_add_tail(&xfer->node, &master->xferqueue.list);
+ } else {
+ master->xferqueue.cur = xfer;
+ adi_i3c_master_start_xfer_locked(master);
+ }
+}
+
+static void adi_i3c_master_unqueue_xfer(struct adi_i3c_master *master,
+ struct adi_i3c_xfer *xfer)
+{
+ guard(spinlock_irqsave)(&master->xferqueue.lock);
+ if (master->xferqueue.cur == xfer)
+ master->xferqueue.cur = NULL;
+ else
+ list_del_init(&xfer->node);
+
+ writel(0x01, master->regs + REG_ENABLE);
+ writel(0x00, master->regs + REG_ENABLE);
+ writel(REG_IRQ_PEDING_CMDR, master->regs + REG_IRQ_MASK);
+}
+
+static enum i3c_error_code adi_i3c_cmd_get_err(struct adi_i3c_cmd *cmd)
+{
+ switch (cmd->error) {
+ case REG_CMDR_FIFO_CE0_ERROR:
+ return I3C_ERROR_M0;
+
+ case REG_CMDR_FIFO_CE2_ERROR:
+ case REG_CMDR_FIFO_NACK_RESP:
+ return I3C_ERROR_M2;
+
+ default:
+ break;
+ }
+
+ return I3C_ERROR_UNKNOWN;
+}
+
+static int adi_i3c_master_send_ccc_cmd(struct i3c_master_controller *m,
+ struct i3c_ccc_cmd *cmd)
+{
+ struct adi_i3c_master *master = to_adi_i3c_master(m);
+ struct adi_i3c_xfer *xfer;
+ struct adi_i3c_cmd *ccmd;
+
+ xfer = adi_i3c_master_alloc_xfer(master, 1);
+ if (!xfer)
+ return -ENOMEM;
+
+ ccmd = xfer->cmds;
+ ccmd->cmd1 = REG_CMD_FIFO_1_CCC(cmd->id);
+ ccmd->cmd0 = REG_CMD_FIFO_0_IS_CCC |
+ REG_CMD_FIFO_0_LEN(cmd->dests[0].payload.len);
+
+ if (cmd->id & I3C_CCC_DIRECT)
+ ccmd->cmd0 |= REG_CMD_FIFO_0_DEV_ADDR(cmd->dests[0].addr);
+
+ if (cmd->rnw) {
+ ccmd->cmd0 |= REG_CMD_FIFO_0_RNW;
+ ccmd->rx_buf = cmd->dests[0].payload.data;
+ ccmd->rx_len = cmd->dests[0].payload.len;
+ } else {
+ ccmd->tx_buf = cmd->dests[0].payload.data;
+ ccmd->tx_len = cmd->dests[0].payload.len;
+ }
+
+ adi_i3c_master_queue_xfer(master, xfer);
+ if (!wait_for_completion_timeout(&xfer->comp, msecs_to_jiffies(1000)))
+ adi_i3c_master_unqueue_xfer(master, xfer);
+
+ cmd->err = adi_i3c_cmd_get_err(&xfer->cmds[0]);
+ kfree(xfer);
+
+ return 0;
+}
+
+static int adi_i3c_master_priv_xfers(struct i3c_dev_desc *dev,
+ struct i3c_priv_xfer *xfers,
+ int nxfers)
+{
+ struct i3c_master_controller *m = i3c_dev_get_master(dev);
+ struct adi_i3c_master *master = to_adi_i3c_master(m);
+ struct adi_i3c_xfer *xfer;
+ int i, ret;
+
+ for (i = 0; i < nxfers; i++) {
+ if (xfers[i].len > REG_CMD_FIFO_0_LEN_MAX)
+ return -EOPNOTSUPP;
+ }
+
+ if (!nxfers)
+ return 0;
+
+ xfer = adi_i3c_master_alloc_xfer(master, nxfers);
+ if (!xfer)
+ return -ENOMEM;
+
+ for (i = 0; i < nxfers; i++) {
+ struct adi_i3c_cmd *ccmd = &xfer->cmds[i];
+
+ ccmd->cmd0 = REG_CMD_FIFO_0_DEV_ADDR(dev->info.dyn_addr);
+
+ if (xfers[i].rnw) {
+ ccmd->cmd0 |= REG_CMD_FIFO_0_RNW;
+ ccmd->rx_buf = xfers[i].data.in;
+ ccmd->rx_len = xfers[i].len;
+ } else {
+ ccmd->tx_buf = xfers[i].data.out;
+ ccmd->tx_len = xfers[i].len;
+ }
+
+ ccmd->cmd0 |= REG_CMD_FIFO_0_LEN(xfers[i].len);
+
+ if (i < nxfers - 1)
+ ccmd->cmd0 |= REG_CMD_FIFO_0_SR;
+
+ if (!i)
+ ccmd->cmd0 |= REG_CMD_FIFO_0_BCAST;
+ }
+
+ adi_i3c_master_queue_xfer(master, xfer);
+ if (!wait_for_completion_timeout(&xfer->comp,
+ msecs_to_jiffies(1000)))
+ adi_i3c_master_unqueue_xfer(master, xfer);
+
+ ret = xfer->ret;
+
+ for (i = 0; i < nxfers; i++)
+ xfers[i].err = adi_i3c_cmd_get_err(&xfer->cmds[i]);
+
+ kfree(xfer);
+
+ return ret;
+}
+
+struct adi_i3c_i2c_dev_data {
+ u16 id;
+ s16 ibi;
+ struct i3c_generic_ibi_pool *ibi_pool;
+};
+
+static int adi_i3c_master_get_rr_slot(struct adi_i3c_master *master,
+ u8 dyn_addr)
+{
+ if (!master->free_rr_slots)
+ return -ENOSPC;
+
+ return ffs(master->free_rr_slots) - 1;
+}
+
+static int adi_i3c_master_reattach_i3c_dev(struct i3c_dev_desc *dev, u8 dyn_addr)
+{
+ struct i3c_master_controller *m = i3c_dev_get_master(dev);
+ struct adi_i3c_master *master = to_adi_i3c_master(m);
+ u8 addr;
+
+ addr = dev->info.dyn_addr ? dev->info.dyn_addr : dev->info.static_addr;
+
+ writel(REG_DEV_CHAR_ADDR(dyn_addr), master->regs + REG_DEV_CHAR);
+ writel((readl(master->regs + REG_DEV_CHAR) &
+ ~REG_DEV_CHAR_IS_ATTACHED) | REG_DEV_CHAR_WEN,
+ master->regs + REG_DEV_CHAR);
+
+ writel(REG_DEV_CHAR_ADDR(addr), master->regs + REG_DEV_CHAR);
+ writel(readl(master->regs + REG_DEV_CHAR) |
+ REG_DEV_CHAR_IS_ATTACHED | REG_DEV_CHAR_WEN,
+ master->regs + REG_DEV_CHAR);
+
+ return 0;
+}
+
+static int adi_i3c_master_attach_i3c_dev(struct i3c_dev_desc *dev)
+{
+ struct i3c_master_controller *m = i3c_dev_get_master(dev);
+ struct adi_i3c_master *master = to_adi_i3c_master(m);
+ struct adi_i3c_i2c_dev_data *data;
+ int slot;
+ u8 addr;
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ slot = adi_i3c_master_get_rr_slot(master, dev->info.dyn_addr);
+ if (slot < 0) {
+ kfree(data);
+ return slot;
+ }
+
+ data->ibi = -1;
+ data->id = slot;
+ i3c_dev_set_master_data(dev, data);
+ master->free_rr_slots &= ~BIT(slot);
+
+ addr = dev->info.dyn_addr ? dev->info.dyn_addr : dev->info.static_addr;
+
+ writel(REG_DEV_CHAR_ADDR(addr), master->regs + REG_DEV_CHAR);
+ writel(readl(master->regs + REG_DEV_CHAR) |
+ REG_DEV_CHAR_IS_ATTACHED | REG_DEV_CHAR_WEN,
+ master->regs + REG_DEV_CHAR);
+
+ return 0;
+}
+
+static void adi_i3c_master_sync_dev_char(struct i3c_master_controller *m)
+{
+ struct adi_i3c_master *master = to_adi_i3c_master(m);
+ struct i3c_dev_desc *i3cdev;
+ u8 addr;
+
+ i3c_bus_for_each_i3cdev(&m->bus, i3cdev) {
+ addr = i3cdev->info.dyn_addr ?
+ i3cdev->info.dyn_addr : i3cdev->info.static_addr;
+ writel(REG_DEV_CHAR_ADDR(addr), master->regs + REG_DEV_CHAR);
+ writel(readl(master->regs + REG_DEV_CHAR) |
+ REG_DEV_CHAR_BCR_IBI(i3cdev->info.bcr) | REG_DEV_CHAR_WEN,
+ master->regs + REG_DEV_CHAR);
+ }
+}
+
+static void adi_i3c_master_detach_i3c_dev(struct i3c_dev_desc *dev)
+{
+ struct i3c_master_controller *m = i3c_dev_get_master(dev);
+ struct adi_i3c_master *master = to_adi_i3c_master(m);
+ struct adi_i3c_i2c_dev_data *data = i3c_dev_get_master_data(dev);
+ u8 addr;
+
+ addr = dev->info.dyn_addr ? dev->info.dyn_addr : dev->info.static_addr;
+
+ writel(REG_DEV_CHAR_ADDR(addr), master->regs + REG_DEV_CHAR);
+ writel((readl(master->regs + REG_DEV_CHAR) &
+ ~REG_DEV_CHAR_IS_ATTACHED) | REG_DEV_CHAR_WEN,
+ master->regs + REG_DEV_CHAR);
+
+ i3c_dev_set_master_data(dev, NULL);
+ master->free_rr_slots |= BIT(data->id);
+ kfree(data);
+}
+
+static int adi_i3c_master_attach_i2c_dev(struct i2c_dev_desc *dev)
+{
+ struct i3c_master_controller *m = i2c_dev_get_master(dev);
+ struct adi_i3c_master *master = to_adi_i3c_master(m);
+ struct adi_i3c_i2c_dev_data *data;
+ int slot;
+
+ slot = adi_i3c_master_get_rr_slot(master, 0);
+ if (slot < 0)
+ return slot;
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->id = slot;
+ master->free_rr_slots &= ~BIT(slot);
+ i2c_dev_set_master_data(dev, data);
+
+ writel(REG_DEV_CHAR_ADDR(dev->addr) |
+ REG_DEV_CHAR_IS_I2C | REG_DEV_CHAR_IS_ATTACHED | REG_DEV_CHAR_WEN,
+ master->regs + REG_DEV_CHAR);
+
+ return 0;
+}
+
+static void adi_i3c_master_detach_i2c_dev(struct i2c_dev_desc *dev)
+{
+ struct i3c_master_controller *m = i2c_dev_get_master(dev);
+ struct adi_i3c_master *master = to_adi_i3c_master(m);
+ struct adi_i3c_i2c_dev_data *data = i2c_dev_get_master_data(dev);
+
+ writel(REG_DEV_CHAR_ADDR(dev->addr) |
+ REG_DEV_CHAR_IS_I2C | REG_DEV_CHAR_WEN,
+ master->regs + REG_DEV_CHAR);
+
+ i2c_dev_set_master_data(dev, NULL);
+ master->free_rr_slots |= BIT(data->id);
+ kfree(data);
+}
+
+static void adi_i3c_master_bus_cleanup(struct i3c_master_controller *m)
+{
+ struct adi_i3c_master *master = to_adi_i3c_master(m);
+
+ adi_i3c_master_disable(master);
+}
+
+static void adi_i3c_master_upd_i3c_scl_lim(struct adi_i3c_master *master)
+{
+ struct i3c_master_controller *m = &master->base;
+ struct i3c_bus *bus = i3c_master_get_bus(m);
+ u8 i3c_scl_lim = 0;
+ struct i3c_dev_desc *dev;
+ u8 pp_sg;
+
+ i3c_bus_for_each_i3cdev(bus, dev) {
+ u8 max_fscl;
+
+ max_fscl = max(I3C_CCC_MAX_SDR_FSCL(dev->info.max_read_ds),
+ I3C_CCC_MAX_SDR_FSCL(dev->info.max_write_ds));
+
+ switch (max_fscl) {
+ case I3C_SDR1_FSCL_8MHZ:
+ max_fscl = PP_SG_6MHZ;
+ break;
+ case I3C_SDR2_FSCL_6MHZ:
+ max_fscl = PP_SG_3MHZ;
+ break;
+ case I3C_SDR3_FSCL_4MHZ:
+ max_fscl = PP_SG_3MHZ;
+ break;
+ case I3C_SDR4_FSCL_2MHZ:
+ max_fscl = PP_SG_1MHZ;
+ break;
+ case I3C_SDR0_FSCL_MAX:
+ default:
+ max_fscl = PP_SG_12MHZ;
+ break;
+ }
+
+ if (max_fscl &&
+ (i3c_scl_lim > max_fscl || !i3c_scl_lim))
+ i3c_scl_lim = max_fscl;
+ }
+
+ if (!i3c_scl_lim)
+ return;
+
+ master->i3c_scl_lim = i3c_scl_lim - 1;
+
+ pp_sg = readl(master->regs + REG_OPS) & ~REG_OPS_PP_SG_MASK;
+ pp_sg |= REG_OPS_SET_SG(master->i3c_scl_lim);
+
+ writel(pp_sg, master->regs + REG_OPS);
+}
+
+static void adi_i3c_master_get_features(struct adi_i3c_master *master,
+ unsigned int slot,
+ struct i3c_device_info *info)
+{
+ memset(info, 0, sizeof(*info));
+
+ info->dyn_addr = 0x31;
+ info->dcr = 0x00;
+ info->bcr = 0x40;
+ info->pid = 0;
+}
+
+static int adi_i3c_master_do_daa(struct i3c_master_controller *m)
+{
+ struct adi_i3c_master *master = to_adi_i3c_master(m);
+ u32 irq_mask;
+ int ret, addr;
+
+ addr = 0x8;
+ for (u8 i = 0; i < MAX_DEVS; i++) {
+ addr = i3c_master_get_free_addr(m, addr);
+ if (addr < 0)
+ return addr;
+ master->daa.addrs[i] = addr;
+ }
+
+ irq_mask = readl(master->regs + REG_IRQ_MASK);
+ writel(irq_mask | REG_IRQ_PEDING_DAA,
+ master->regs + REG_IRQ_MASK);
+
+ master->daa.index = 0;
+ ret = i3c_master_entdaa_locked(&master->base);
+
+ writel(irq_mask, master->regs + REG_IRQ_MASK);
+
+ /* DAA always finishes with CE2_ERROR or NACK_RESP */
+ if (ret && ret != I3C_ERROR_M2)
+ return ret;
+
+ /* Add I3C devices discovered */
+ for (u8 i = 0; i < master->daa.index; i++)
+ i3c_master_add_i3c_dev_locked(m, master->daa.addrs[i]);
+ /* Sync retrieved devs info with the IP */
+ adi_i3c_master_sync_dev_char(m);
+
+ i3c_master_defslvs_locked(&master->base);
+
+ adi_i3c_master_upd_i3c_scl_lim(master);
+
+ return 0;
+}
+
+static int adi_i3c_master_bus_init(struct i3c_master_controller *m)
+{
+ struct adi_i3c_master *master = to_adi_i3c_master(m);
+ struct i3c_device_info info = { };
+ int ret;
+
+ ret = i3c_master_get_free_addr(m, 0);
+ if (ret < 0)
+ return ret;
+
+ adi_i3c_master_get_features(master, 0, &info);
+ ret = i3c_master_set_info(&master->base, &info);
+ if (ret)
+ return ret;
+
+ writel(REG_IBI_CONFIG_LISTEN,
+ master->regs + REG_IBI_CONFIG);
+
+ return 0;
+}
+
+static void adi_i3c_master_handle_ibi(struct adi_i3c_master *master,
+ u32 ibi)
+{
+ struct adi_i3c_i2c_dev_data *data;
+ struct i3c_ibi_slot *slot;
+ struct i3c_dev_desc *dev;
+ u8 da, id;
+ u8 *mdb;
+
+ da = (ibi >> 17) & GENMASK(6, 0);
+ for (id = 0; id < master->ibi.num_slots; id++) {
+ if (master->ibi.slots[id] &&
+ master->ibi.slots[id]->info.dyn_addr == da)
+ break;
+ }
+
+ if (id == master->ibi.num_slots)
+ return;
+
+ dev = master->ibi.slots[id];
+ guard(spinlock)(&master->ibi.lock);
+
+ data = i3c_dev_get_master_data(dev);
+ slot = i3c_generic_ibi_get_free_slot(data->ibi_pool);
+ if (!slot)
+ return;
+
+ mdb = slot->data;
+ mdb[0] = (ibi >> 8) & GENMASK(7, 0);
+
+ slot->len = 1;
+ i3c_master_queue_ibi(dev, slot);
+}
+
+static void adi_i3c_master_demux_ibis(struct adi_i3c_master *master)
+{
+ u32 status0;
+
+ for (status0 = readl(master->regs + REG_FIFO_STATUS);
+ !(status0 & REG_FIFO_STATUS_IBI_EMPTY);
+ status0 = readl(master->regs + REG_FIFO_STATUS)) {
+ u32 ibi = readl(master->regs + REG_IBI_FIFO);
+
+ adi_i3c_master_handle_ibi(master, ibi);
+ }
+}
+
+static void adi_i3c_master_handle_da_req(struct adi_i3c_master *master)
+{
+ u8 payload0[8];
+ u32 addr;
+
+ /* Clear device characteristics */
+ adi_i3c_master_rd_from_rx_fifo(master, payload0, 6);
+ addr = master->daa.addrs[master->daa.index++];
+ addr = (addr << 1) | (parity8(addr) ? 0 : 1);
+
+ writel(addr, master->regs + REG_SDO_FIFO);
+}
+
+static irqreturn_t adi_i3c_master_irq(int irq, void *data)
+{
+ struct adi_i3c_master *master = data;
+ u32 pending;
+
+ pending = readl_relaxed(master->regs + REG_IRQ_PENDING);
+ writel_relaxed(pending, master->regs + REG_IRQ_PENDING);
+ if (pending & REG_IRQ_PEDING_CMDR) {
+ spin_lock(&master->xferqueue.lock);
+ adi_i3c_master_end_xfer_locked(master, pending);
+ spin_unlock(&master->xferqueue.lock);
+ }
+ if (pending & REG_IRQ_PEDING_IBI)
+ adi_i3c_master_demux_ibis(master);
+ if (pending & REG_IRQ_PEDING_DAA)
+ adi_i3c_master_handle_da_req(master);
+
+ return IRQ_HANDLED;
+}
+
+static int adi_i3c_master_i2c_xfers(struct i2c_dev_desc *dev,
+ struct i2c_msg *xfers,
+ int nxfers)
+{
+ struct i3c_master_controller *m = i2c_dev_get_master(dev);
+ struct adi_i3c_master *master = to_adi_i3c_master(m);
+ struct adi_i3c_xfer *xfer;
+ int i, ret;
+
+ for (i = 0; i < nxfers; i++) {
+ if (xfers[i].len > REG_CMD_FIFO_0_LEN_MAX)
+ return -EOPNOTSUPP;
+ if (xfers[i].flags & I2C_M_TEN)
+ return -EOPNOTSUPP;
+ }
+
+ if (!nxfers)
+ return 0;
+
+ xfer = adi_i3c_master_alloc_xfer(master, nxfers);
+ if (!xfer)
+ return -ENOMEM;
+
+ for (i = 0; i < nxfers; i++) {
+ struct adi_i3c_cmd *ccmd = &xfer->cmds[i];
+
+ ccmd->cmd0 = REG_CMD_FIFO_0_DEV_ADDR(xfers[i].addr);
+
+ if (xfers[i].flags & I2C_M_RD) {
+ ccmd->cmd0 |= REG_CMD_FIFO_0_RNW;
+ ccmd->rx_buf = xfers[i].buf;
+ ccmd->rx_len = xfers[i].len;
+ } else {
+ ccmd->tx_buf = xfers[i].buf;
+ ccmd->tx_len = xfers[i].len;
+ }
+
+ ccmd->cmd0 |= REG_CMD_FIFO_0_LEN(xfers[i].len);
+ }
+
+ adi_i3c_master_queue_xfer(master, xfer);
+ if (!wait_for_completion_timeout(&xfer->comp,
+ msecs_to_jiffies(1000)))
+ adi_i3c_master_unqueue_xfer(master, xfer);
+
+ ret = xfer->ret;
+ kfree(xfer);
+ return ret;
+}
+
+static int adi_i3c_master_disable_ibi(struct i3c_dev_desc *dev)
+{
+ struct i3c_master_controller *m = i3c_dev_get_master(dev);
+ struct adi_i3c_master *master = to_adi_i3c_master(m);
+ struct i3c_dev_desc *i3cdev;
+ u32 enabled = 0;
+ int ret;
+
+ ret = i3c_master_disec_locked(m, dev->info.dyn_addr,
+ I3C_CCC_EVENT_SIR);
+
+ i3c_bus_for_each_i3cdev(&m->bus, i3cdev) {
+ if (dev != i3cdev && i3cdev->ibi)
+ enabled |= i3cdev->ibi->enabled;
+ }
+ if (!enabled) {
+ writel(REG_IBI_CONFIG_LISTEN,
+ master->regs + REG_IBI_CONFIG);
+ writel(readl(master->regs + REG_IRQ_MASK) & ~REG_IRQ_PEDING_IBI,
+ master->regs + REG_IRQ_MASK);
+ }
+
+ return ret;
+}
+
+static int adi_i3c_master_enable_ibi(struct i3c_dev_desc *dev)
+{
+ struct i3c_master_controller *m = i3c_dev_get_master(dev);
+ struct adi_i3c_master *master = to_adi_i3c_master(m);
+
+ writel(REG_IBI_CONFIG_LISTEN | REG_IBI_CONFIG_ENABLE,
+ master->regs + REG_IBI_CONFIG);
+
+ writel(readl(master->regs + REG_IRQ_MASK) | REG_IRQ_PEDING_IBI,
+ master->regs + REG_IRQ_MASK);
+
+ return i3c_master_enec_locked(m, dev->info.dyn_addr,
+ I3C_CCC_EVENT_SIR);
+}
+
+static int adi_i3c_master_request_ibi(struct i3c_dev_desc *dev,
+ const struct i3c_ibi_setup *req)
+{
+ struct i3c_master_controller *m = i3c_dev_get_master(dev);
+ struct adi_i3c_master *master = to_adi_i3c_master(m);
+ struct adi_i3c_i2c_dev_data *data;
+ unsigned long flags;
+ unsigned int i;
+
+ data = i3c_dev_get_master_data(dev);
+ data->ibi_pool = i3c_generic_ibi_alloc_pool(dev, req);
+ if (IS_ERR(data->ibi_pool))
+ return PTR_ERR(data->ibi_pool);
+
+ spin_lock_irqsave(&master->ibi.lock, flags);
+ for (i = 0; i < master->ibi.num_slots; i++) {
+ if (!master->ibi.slots[i]) {
+ data->ibi = i;
+ master->ibi.slots[i] = dev;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&master->ibi.lock, flags);
+
+ if (i < master->ibi.num_slots)
+ return 0;
+
+ i3c_generic_ibi_free_pool(data->ibi_pool);
+ data->ibi_pool = NULL;
+
+ return -ENOSPC;
+}
+
+static void adi_i3c_master_free_ibi(struct i3c_dev_desc *dev)
+{
+ struct i3c_master_controller *m = i3c_dev_get_master(dev);
+ struct adi_i3c_master *master = to_adi_i3c_master(m);
+ struct adi_i3c_i2c_dev_data *data = i3c_dev_get_master_data(dev);
+ unsigned long flags;
+
+ spin_lock_irqsave(&master->ibi.lock, flags);
+ master->ibi.slots[data->ibi] = NULL;
+ data->ibi = -1;
+ spin_unlock_irqrestore(&master->ibi.lock, flags);
+
+ i3c_generic_ibi_free_pool(data->ibi_pool);
+}
+
+static void adi_i3c_master_recycle_ibi_slot(struct i3c_dev_desc *dev,
+ struct i3c_ibi_slot *slot)
+{
+ struct adi_i3c_i2c_dev_data *data = i3c_dev_get_master_data(dev);
+
+ i3c_generic_ibi_recycle_slot(data->ibi_pool, slot);
+}
+
+static const struct i3c_master_controller_ops adi_i3c_master_ops = {
+ .bus_init = adi_i3c_master_bus_init,
+ .bus_cleanup = adi_i3c_master_bus_cleanup,
+ .attach_i3c_dev = adi_i3c_master_attach_i3c_dev,
+ .reattach_i3c_dev = adi_i3c_master_reattach_i3c_dev,
+ .detach_i3c_dev = adi_i3c_master_detach_i3c_dev,
+ .attach_i2c_dev = adi_i3c_master_attach_i2c_dev,
+ .detach_i2c_dev = adi_i3c_master_detach_i2c_dev,
+ .do_daa = adi_i3c_master_do_daa,
+ .supports_ccc_cmd = adi_i3c_master_supports_ccc_cmd,
+ .send_ccc_cmd = adi_i3c_master_send_ccc_cmd,
+ .priv_xfers = adi_i3c_master_priv_xfers,
+ .i2c_xfers = adi_i3c_master_i2c_xfers,
+ .request_ibi = adi_i3c_master_request_ibi,
+ .enable_ibi = adi_i3c_master_enable_ibi,
+ .disable_ibi = adi_i3c_master_disable_ibi,
+ .free_ibi = adi_i3c_master_free_ibi,
+ .recycle_ibi_slot = adi_i3c_master_recycle_ibi_slot,
+};
+
+static const struct of_device_id adi_i3c_master_of_match[] = {
+ { .compatible = "adi,i3c-master" },
+ {}
+};
+
+static int adi_i3c_master_probe(struct platform_device *pdev)
+{
+ struct adi_i3c_master *master;
+ unsigned int version;
+ int ret, irq;
+
+ master = devm_kzalloc(&pdev->dev, sizeof(*master), GFP_KERNEL);
+ if (!master)
+ return -ENOMEM;
+
+ master->regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(master->regs))
+ return PTR_ERR(master->regs);
+
+ master->clk = devm_clk_get_enabled(&pdev->dev, "axi");
+ if (IS_ERR(master->clk))
+ return PTR_ERR(master->clk);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ version = readl(master->regs + REG_VERSION);
+ if (VERSION_MAJOR(version) != 0) {
+ dev_err(&pdev->dev, "Unsupported IP version %u.%u.%c\n",
+ VERSION_MAJOR(version),
+ VERSION_MINOR(version),
+ VERSION_PATCH(version));
+ return -EINVAL;
+ }
+
+ writel(0x00, master->regs + REG_ENABLE);
+ writel(0x00, master->regs + REG_IRQ_MASK);
+
+ ret = devm_request_irq(&pdev->dev, irq, adi_i3c_master_irq, 0,
+ dev_name(&pdev->dev), master);
+ if (ret)
+ return ret;
+
+ platform_set_drvdata(pdev, master);
+
+ master->maxdevs = MAX_DEVS;
+ master->free_rr_slots = GENMASK(master->maxdevs, 1);
+
+ writel(REG_IRQ_PEDING_CMDR, master->regs + REG_IRQ_MASK);
+
+ spin_lock_init(&master->ibi.lock);
+ master->ibi.num_slots = 15;
+ master->ibi.slots = devm_kcalloc(&pdev->dev, master->ibi.num_slots,
+ sizeof(*master->ibi.slots),
+ GFP_KERNEL);
+ if (!master->ibi.slots)
+ return -ENOMEM;
+
+ return i3c_master_register(&master->base, &pdev->dev,
+ &adi_i3c_master_ops, false);
+}
+
+static void adi_i3c_master_remove(struct platform_device *pdev)
+{
+ struct adi_i3c_master *master = platform_get_drvdata(pdev);
+
+ writel(0xff, master->regs + REG_IRQ_PENDING);
+ writel(0x00, master->regs + REG_IRQ_MASK);
+ writel(0x01, master->regs + REG_ENABLE);
+
+ i3c_master_unregister(&master->base);
+}
+
+static struct platform_driver adi_i3c_master = {
+ .probe = adi_i3c_master_probe,
+ .remove = adi_i3c_master_remove,
+ .driver = {
+ .name = "adi-i3c-master",
+ .of_match_table = adi_i3c_master_of_match,
+ },
+};
+module_platform_driver(adi_i3c_master);
+
+MODULE_AUTHOR("Jorge Marques <jorge.marques@analog.com>");
+MODULE_DESCRIPTION("Analog Devices I3C master driver");
+MODULE_LICENSE("GPL");
--
2.43.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH v2 1/2] dt-bindings: i3c: Add adi-i3c-master
2025-06-06 13:21 ` [PATCH v2 1/2] dt-bindings: i3c: Add adi-i3c-master Jorge Marques
@ 2025-06-06 16:08 ` Conor Dooley
2025-06-17 14:50 ` Jorge Marques
0 siblings, 1 reply; 9+ messages in thread
From: Conor Dooley @ 2025-06-06 16:08 UTC (permalink / raw)
To: Jorge Marques
Cc: Alexandre Belloni, Frank Li, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, linux-i3c, devicetree, linux-kernel
[-- Attachment #1: Type: text/plain, Size: 3180 bytes --]
On Fri, Jun 06, 2025 at 03:21:02PM +0200, Jorge Marques wrote:
> Add bindings doc for ADI I3C Controller IP core, a FPGA synthesizable IP
> core that implements the MIPI I3C Basic controller specification.
>
> Signed-off-by: Jorge Marques <jorge.marques@analog.com>
> ---
> .../devicetree/bindings/i3c/adi,i3c-master.yaml | 63 ++++++++++++++++++++++
> MAINTAINERS | 5 ++
> 2 files changed, 68 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/i3c/adi,i3c-master.yaml b/Documentation/devicetree/bindings/i3c/adi,i3c-master.yaml
> new file mode 100644
> index 0000000000000000000000000000000000000000..e7fae394ad8fd4143eb1fffb9591a30a2d6cc6ac
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/i3c/adi,i3c-master.yaml
> @@ -0,0 +1,63 @@
> +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/i3c/adi,i3c-master.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Analog Devices I3C Controller
> +
> +description: |
> + The ADI I3C controller implements a subset of the I3C-basic specification to
> + interface I3C and I2C peripherals [1].
> +
> + [1] https://analogdevicesinc.github.io/hdl/library/i3c_controller
> +
> +maintainers:
> + - Jorge Marques <jorge.marques@analog.com>
> +
> +properties:
> + compatible:
> + const: adi,i3c-master
Seeing this with no versioning info is pretty suspect.
The adi spi, pwm, axi-dmac etc all have versioning info, please try to
emulate that.
Otherwise, I think this is okay.
Cheers,
Conor.
> +
> + reg:
> + maxItems: 1
> +
> + clocks:
> + minItems: 1
> + items:
> + - description: The AXI interconnect clock.
> + - description: The I3C controller clock.
> +
> + clock-names:
> + items:
> + - const: axi
> + - const: i3c
> +
> + interrupts:
> + maxItems: 1
> +
> +required:
> + - compatible
> + - reg
> + - clocks
> + - clock-names
> + - interrupts
> +
> +allOf:
> + - $ref: i3c.yaml#
> +
> +unevaluatedProperties: false
> +
> +examples:
> + - |
> + i3c@44a00000 {
> + compatible = "adi,i3c-master";
> + reg = <0x44a00000 0x1000>;
> + interrupts = <0 56 4>;
> + clocks = <&clkc 15>, <&clkc 15>;
> + clock-names = "axi", "i3c";
> + #address-cells = <3>;
> + #size-cells = <0>;
> +
> + /* I3C and I2C devices */
> + };
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 96b82704950184bd71623ff41fc4df31e4c7fe87..6f56e17dcecf902c6812827c1ec3e067c65e9894 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -11243,6 +11243,11 @@ S: Maintained
> F: Documentation/devicetree/bindings/i3c/aspeed,ast2600-i3c.yaml
> F: drivers/i3c/master/ast2600-i3c-master.c
>
> +I3C DRIVER FOR ANALOG DEVICES I3C CONTROLLER IP
> +M: Jorge Marques <jorge.marques@analog.com>
> +S: Maintained
> +F: Documentation/devicetree/bindings/i3c/adi,i3c-master.yaml
> +
> I3C DRIVER FOR CADENCE I3C MASTER IP
> M: Przemysław Gaj <pgaj@cadence.com>
> S: Maintained
>
> --
> 2.43.0
>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v2 2/2] i3c: master: Add driver for Analog Devices I3C Controller IP
2025-06-06 13:21 ` [PATCH v2 2/2] i3c: master: Add driver for Analog Devices I3C Controller IP Jorge Marques
@ 2025-06-06 20:33 ` ALOK TIWARI
2025-06-17 14:54 ` Jorge Marques
0 siblings, 1 reply; 9+ messages in thread
From: ALOK TIWARI @ 2025-06-06 20:33 UTC (permalink / raw)
To: Jorge Marques, Alexandre Belloni, Frank Li, Rob Herring,
Krzysztof Kozlowski, Conor Dooley
Cc: linux-i3c, devicetree, linux-kernel
> new file mode 100644
> index 0000000000000000000000000000000000000000..b3799071573f4066eb14123c94ee599cc6331b3d
> --- /dev/null
> +++ b/drivers/i3c/master/adi-i3c-master.c
> @@ -0,0 +1,1037 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * I3C Controller driver
> + * Copyright 2025 Analog Devices Inc.
> + * Author: Jorge Marques <jorge.marques@analog.com>
> + */
> +
> +#include <linux/bitops.h>
> +#include <linux/bitfield.h>
> +#include <linux/clk.h>
> +#include <linux/err.h>
> +#include <linux/errno.h>
> +#include <linux/i3c/master.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +
> +#define VERSION_MAJOR(x) ((u32)FIELD_GET(GENMASK(23, 16), (x)))
> +#define VERSION_MINOR(x) ((u32)FIELD_GET(GENMASK(15, 8), (x)))
> +#define VERSION_PATCH(x) ((u32)FIELD_GET(GENMASK(7, 0), (x)))
> +
> +#define MAX_DEVS 16
> +
> +#define REG_VERSION 0x000
> +#define REG_ENABLE 0x040
> +#define REG_IRQ_MASK 0x080
> +#define REG_IRQ_PENDING 0x084
> +#define REG_IRQ_PEDING_CMDR BIT(5)
> +#define REG_IRQ_PEDING_IBI ((u32)BIT(6))
> +#define REG_IRQ_PEDING_DAA BIT(7)
typo PEDING -> PENDING
> +#define REG_CMD_FIFO 0x0d4
> +#define REG_CMD_FIFO_0_IS_CCC BIT(22)
> +#define REG_CMD_FIFO_0_BCAST BIT(21)
> +#define REG_CMD_FIFO_0_SR BIT(20)
> +#define REG_CMD_FIFO_0_LEN(l) FIELD_PREP(GENMASK(19, 8), (l))
> +#define REG_CMD_FIFO_0_LEN_MAX 4095
> +#define REG_CMD_FIFO_0_DEV_ADDR(a) FIELD_PREP(GENMASK(7, 1), a)
> +#define REG_CMD_FIFO_0_RNW BIT(0)
> +#define REG_CMD_FIFO_1_CCC(id) FIELD_PREP(GENMASK(7, 0), (id))
[snip]
> +
> +static int adi_i3c_master_priv_xfers(struct i3c_dev_desc *dev,
> + struct i3c_priv_xfer *xfers,
> + int nxfers)
> +{
> + struct i3c_master_controller *m = i3c_dev_get_master(dev);
> + struct adi_i3c_master *master = to_adi_i3c_master(m);
> + struct adi_i3c_xfer *xfer;
> + int i, ret;
> +
> + for (i = 0; i < nxfers; i++) {
> + if (xfers[i].len > REG_CMD_FIFO_0_LEN_MAX)
> + return -EOPNOTSUPP;
> + }
> +
> + if (!nxfers)
> + return 0;
this can move before for
> +
> + xfer = adi_i3c_master_alloc_xfer(master, nxfers);
> + if (!xfer)
> + return -ENOMEM;
Thanks
alok
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v2 1/2] dt-bindings: i3c: Add adi-i3c-master
2025-06-06 16:08 ` Conor Dooley
@ 2025-06-17 14:50 ` Jorge Marques
2025-06-17 15:47 ` Conor Dooley
0 siblings, 1 reply; 9+ messages in thread
From: Jorge Marques @ 2025-06-17 14:50 UTC (permalink / raw)
To: Conor Dooley
Cc: Jorge Marques, Alexandre Belloni, Frank Li, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, linux-i3c, devicetree,
linux-kernel
On Fri, Jun 06, 2025 at 05:08:45PM +0100, Conor Dooley wrote:
> On Fri, Jun 06, 2025 at 03:21:02PM +0200, Jorge Marques wrote:
> > Add bindings doc for ADI I3C Controller IP core, a FPGA synthesizable IP
> > core that implements the MIPI I3C Basic controller specification.
> >
> > Signed-off-by: Jorge Marques <jorge.marques@analog.com>
> > ---
> > .../devicetree/bindings/i3c/adi,i3c-master.yaml | 63 ++++++++++++++++++++++
> > MAINTAINERS | 5 ++
> > 2 files changed, 68 insertions(+)
> >
> > diff --git a/Documentation/devicetree/bindings/i3c/adi,i3c-master.yaml b/Documentation/devicetree/bindings/i3c/adi,i3c-master.yaml
> > new file mode 100644
> > index 0000000000000000000000000000000000000000..e7fae394ad8fd4143eb1fffb9591a30a2d6cc6ac
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/i3c/adi,i3c-master.yaml
> > @@ -0,0 +1,63 @@
> > +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
> > +%YAML 1.2
> > +---
> > +$id: http://devicetree.org/schemas/i3c/adi,i3c-master.yaml#
> > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > +
> > +title: Analog Devices I3C Controller
> > +
> > +description: |
> > + The ADI I3C controller implements a subset of the I3C-basic specification to
> > + interface I3C and I2C peripherals [1].
> > +
> > + [1] https://analogdevicesinc.github.io/hdl/library/i3c_controller
> > +
> > +maintainers:
> > + - Jorge Marques <jorge.marques@analog.com>
> > +
> > +properties:
> > + compatible:
> > + const: adi,i3c-master
>
Hi Conor,
> Seeing this with no versioning info is pretty suspect.
> The adi spi, pwm, axi-dmac etc all have versioning info, please try to
> emulate that.
>
> Otherwise, I think this is okay.
>
> Cheers,
> Conor.
>
The driver checks the IP version via register access, for the other adi
ips, the -x.00.a suffix was never updated. For example, the axi-dmac
current IP version is v4.5.64 while the compatible still is 1.00.a and
there is a mention of v4.3.a in the binding. It is unclear whether
suffixing it with "-1.00.a" would be useful, or if the time comes call
the new "adi,i3c-master-2".
Do you still think it is necessary?
Best regards,
Jorge
> > +
> > + reg:
> > + maxItems: 1
> > +
> > + clocks:
> > + minItems: 1
> > + items:
> > + - description: The AXI interconnect clock.
> > + - description: The I3C controller clock.
> > +
> > + clock-names:
> > + items:
> > + - const: axi
> > + - const: i3c
> > +
> > + interrupts:
> > + maxItems: 1
> > +
> > +required:
> > + - compatible
> > + - reg
> > + - clocks
> > + - clock-names
> > + - interrupts
> > +
> > +allOf:
> > + - $ref: i3c.yaml#
> > +
> > +unevaluatedProperties: false
> > +
> > +examples:
> > + - |
> > + i3c@44a00000 {
> > + compatible = "adi,i3c-master";
> > + reg = <0x44a00000 0x1000>;
> > + interrupts = <0 56 4>;
> > + clocks = <&clkc 15>, <&clkc 15>;
> > + clock-names = "axi", "i3c";
> > + #address-cells = <3>;
> > + #size-cells = <0>;
> > +
> > + /* I3C and I2C devices */
> > + };
> > diff --git a/MAINTAINERS b/MAINTAINERS
> > index 96b82704950184bd71623ff41fc4df31e4c7fe87..6f56e17dcecf902c6812827c1ec3e067c65e9894 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -11243,6 +11243,11 @@ S: Maintained
> > F: Documentation/devicetree/bindings/i3c/aspeed,ast2600-i3c.yaml
> > F: drivers/i3c/master/ast2600-i3c-master.c
> >
> > +I3C DRIVER FOR ANALOG DEVICES I3C CONTROLLER IP
> > +M: Jorge Marques <jorge.marques@analog.com>
> > +S: Maintained
> > +F: Documentation/devicetree/bindings/i3c/adi,i3c-master.yaml
> > +
> > I3C DRIVER FOR CADENCE I3C MASTER IP
> > M: Przemysław Gaj <pgaj@cadence.com>
> > S: Maintained
> >
> > --
> > 2.43.0
> >
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v2 2/2] i3c: master: Add driver for Analog Devices I3C Controller IP
2025-06-06 20:33 ` ALOK TIWARI
@ 2025-06-17 14:54 ` Jorge Marques
0 siblings, 0 replies; 9+ messages in thread
From: Jorge Marques @ 2025-06-17 14:54 UTC (permalink / raw)
To: ALOK TIWARI
Cc: Jorge Marques, Alexandre Belloni, Frank Li, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, linux-i3c, devicetree,
linux-kernel
Hi Alok,
On Sat, Jun 07, 2025 at 02:03:42AM +0530, ALOK TIWARI wrote:
> > new file mode 100644
> > index 0000000000000000000000000000000000000000..b3799071573f4066eb14123c94ee599cc6331b3d
> > --- /dev/null
> > +++ b/drivers/i3c/master/adi-i3c-master.c
> > @@ -0,0 +1,1037 @@
> > +// SPDX-License-Identifier: GPL-2.0-only
> > +/*
> > + * I3C Controller driver
> > + * Copyright 2025 Analog Devices Inc.
> > + * Author: Jorge Marques <jorge.marques@analog.com>
> > + */
> > +
> > +#include <linux/bitops.h>
> > +#include <linux/bitfield.h>
> > +#include <linux/clk.h>
> > +#include <linux/err.h>
> > +#include <linux/errno.h>
> > +#include <linux/i3c/master.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/io.h>
> > +#include <linux/module.h>
> > +#include <linux/of.h>
> > +#include <linux/platform_device.h>
> > +
> > +#define VERSION_MAJOR(x) ((u32)FIELD_GET(GENMASK(23, 16), (x)))
> > +#define VERSION_MINOR(x) ((u32)FIELD_GET(GENMASK(15, 8), (x)))
> > +#define VERSION_PATCH(x) ((u32)FIELD_GET(GENMASK(7, 0), (x)))
> > +
> > +#define MAX_DEVS 16
> > +
> > +#define REG_VERSION 0x000
> > +#define REG_ENABLE 0x040
> > +#define REG_IRQ_MASK 0x080
> > +#define REG_IRQ_PENDING 0x084
> > +#define REG_IRQ_PEDING_CMDR BIT(5)
> > +#define REG_IRQ_PEDING_IBI ((u32)BIT(6))
> > +#define REG_IRQ_PEDING_DAA BIT(7)
>
> typo PEDING -> PENDING
>
Haaa ups, thank you!
> > +#define REG_CMD_FIFO 0x0d4
> > +#define REG_CMD_FIFO_0_IS_CCC BIT(22)
> > +#define REG_CMD_FIFO_0_BCAST BIT(21)
> > +#define REG_CMD_FIFO_0_SR BIT(20)
> > +#define REG_CMD_FIFO_0_LEN(l) FIELD_PREP(GENMASK(19, 8), (l))
> > +#define REG_CMD_FIFO_0_LEN_MAX 4095
> > +#define REG_CMD_FIFO_0_DEV_ADDR(a) FIELD_PREP(GENMASK(7, 1), a)
> > +#define REG_CMD_FIFO_0_RNW BIT(0)
> > +#define REG_CMD_FIFO_1_CCC(id) FIELD_PREP(GENMASK(7, 0), (id))
> [snip]
> > +
> > +static int adi_i3c_master_priv_xfers(struct i3c_dev_desc *dev,
> > + struct i3c_priv_xfer *xfers,
> > + int nxfers)
> > +{
> > + struct i3c_master_controller *m = i3c_dev_get_master(dev);
> > + struct adi_i3c_master *master = to_adi_i3c_master(m);
> > + struct adi_i3c_xfer *xfer;
> > + int i, ret;
> > +
> > + for (i = 0; i < nxfers; i++) {
> > + if (xfers[i].len > REG_CMD_FIFO_0_LEN_MAX)
> > + return -EOPNOTSUPP;
> > + }
> > +
> > + if (!nxfers)
> > + return 0;
>
> this can move before for
>
Oh well, I will actually remove the LEN_MAX define since the depth is
actually a HDL parameter. Instead, the FIFO ROOM will be checked before
writing new SDOs and CMDs.
> > +
> > + xfer = adi_i3c_master_alloc_xfer(master, nxfers);
> > + if (!xfer)
> > + return -ENOMEM;
>
> Thanks
> alok
Best regards,
Jorge
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v2 1/2] dt-bindings: i3c: Add adi-i3c-master
2025-06-17 14:50 ` Jorge Marques
@ 2025-06-17 15:47 ` Conor Dooley
2025-06-18 7:10 ` Jorge Marques
0 siblings, 1 reply; 9+ messages in thread
From: Conor Dooley @ 2025-06-17 15:47 UTC (permalink / raw)
To: Jorge Marques
Cc: Jorge Marques, Alexandre Belloni, Frank Li, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, linux-i3c, devicetree,
linux-kernel
[-- Attachment #1: Type: text/plain, Size: 2589 bytes --]
On Tue, Jun 17, 2025 at 04:50:43PM +0200, Jorge Marques wrote:
> On Fri, Jun 06, 2025 at 05:08:45PM +0100, Conor Dooley wrote:
> > On Fri, Jun 06, 2025 at 03:21:02PM +0200, Jorge Marques wrote:
> > > Add bindings doc for ADI I3C Controller IP core, a FPGA synthesizable IP
> > > core that implements the MIPI I3C Basic controller specification.
> > >
> > > Signed-off-by: Jorge Marques <jorge.marques@analog.com>
> > > ---
> > > .../devicetree/bindings/i3c/adi,i3c-master.yaml | 63 ++++++++++++++++++++++
> > > MAINTAINERS | 5 ++
> > > 2 files changed, 68 insertions(+)
> > >
> > > diff --git a/Documentation/devicetree/bindings/i3c/adi,i3c-master.yaml b/Documentation/devicetree/bindings/i3c/adi,i3c-master.yaml
> > > new file mode 100644
> > > index 0000000000000000000000000000000000000000..e7fae394ad8fd4143eb1fffb9591a30a2d6cc6ac
> > > --- /dev/null
> > > +++ b/Documentation/devicetree/bindings/i3c/adi,i3c-master.yaml
> > > @@ -0,0 +1,63 @@
> > > +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
> > > +%YAML 1.2
> > > +---
> > > +$id: http://devicetree.org/schemas/i3c/adi,i3c-master.yaml#
> > > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > > +
> > > +title: Analog Devices I3C Controller
> > > +
> > > +description: |
> > > + The ADI I3C controller implements a subset of the I3C-basic specification to
> > > + interface I3C and I2C peripherals [1].
> > > +
> > > + [1] https://analogdevicesinc.github.io/hdl/library/i3c_controller
> > > +
> > > +maintainers:
> > > + - Jorge Marques <jorge.marques@analog.com>
> > > +
> > > +properties:
> > > + compatible:
> > > + const: adi,i3c-master
> >
> Hi Conor,
> > Seeing this with no versioning info is pretty suspect.
> > The adi spi, pwm, axi-dmac etc all have versioning info, please try to
> > emulate that.
> >
> > Otherwise, I think this is okay.
> >
> > Cheers,
> > Conor.
> >
> The driver checks the IP version via register access, for the other adi
> ips, the -x.00.a suffix was never updated. For example, the axi-dmac
> current IP version is v4.5.64 while the compatible still is 1.00.a and
> there is a mention of v4.3.a in the binding. It is unclear whether
> suffixing it with "-1.00.a" would be useful, or if the time comes call
> the new "adi,i3c-master-2".
>
> Do you still think it is necessary?
I do, yes. In the other cases, if a new version comes out that has some
sort of meaningful difference a new compatible can be added. Until then,
using the -1.00.a one is okay.
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v2 1/2] dt-bindings: i3c: Add adi-i3c-master
2025-06-17 15:47 ` Conor Dooley
@ 2025-06-18 7:10 ` Jorge Marques
0 siblings, 0 replies; 9+ messages in thread
From: Jorge Marques @ 2025-06-18 7:10 UTC (permalink / raw)
To: Conor Dooley
Cc: Jorge Marques, Alexandre Belloni, Frank Li, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, linux-i3c, devicetree,
linux-kernel
On Tue, Jun 17, 2025 at 04:47:58PM +0100, Conor Dooley wrote:
> On Tue, Jun 17, 2025 at 04:50:43PM +0200, Jorge Marques wrote:
> > On Fri, Jun 06, 2025 at 05:08:45PM +0100, Conor Dooley wrote:
> > > On Fri, Jun 06, 2025 at 03:21:02PM +0200, Jorge Marques wrote:
> > > > Add bindings doc for ADI I3C Controller IP core, a FPGA synthesizable IP
> > > > core that implements the MIPI I3C Basic controller specification.
> > > >
> > > > Signed-off-by: Jorge Marques <jorge.marques@analog.com>
> > > > ---
> > > > .../devicetree/bindings/i3c/adi,i3c-master.yaml | 63 ++++++++++++++++++++++
> > > > MAINTAINERS | 5 ++
> > > > 2 files changed, 68 insertions(+)
> > > >
> > > > diff --git a/Documentation/devicetree/bindings/i3c/adi,i3c-master.yaml b/Documentation/devicetree/bindings/i3c/adi,i3c-master.yaml
> > > > new file mode 100644
> > > > index 0000000000000000000000000000000000000000..e7fae394ad8fd4143eb1fffb9591a30a2d6cc6ac
> > > > --- /dev/null
> > > > +++ b/Documentation/devicetree/bindings/i3c/adi,i3c-master.yaml
> > > > @@ -0,0 +1,63 @@
> > > > +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
> > > > +%YAML 1.2
> > > > +---
> > > > +$id: http://devicetree.org/schemas/i3c/adi,i3c-master.yaml#
> > > > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > > > +
> > > > +title: Analog Devices I3C Controller
> > > > +
> > > > +description: |
> > > > + The ADI I3C controller implements a subset of the I3C-basic specification to
> > > > + interface I3C and I2C peripherals [1].
> > > > +
> > > > + [1] https://analogdevicesinc.github.io/hdl/library/i3c_controller
> > > > +
> > > > +maintainers:
> > > > + - Jorge Marques <jorge.marques@analog.com>
> > > > +
> > > > +properties:
> > > > + compatible:
> > > > + const: adi,i3c-master
> > >
> > Hi Conor,
> > > Seeing this with no versioning info is pretty suspect.
> > > The adi spi, pwm, axi-dmac etc all have versioning info, please try to
> > > emulate that.
> > >
> > > Otherwise, I think this is okay.
> > >
> > > Cheers,
> > > Conor.
> > >
> > The driver checks the IP version via register access, for the other adi
> > ips, the -x.00.a suffix was never updated. For example, the axi-dmac
> > current IP version is v4.5.64 while the compatible still is 1.00.a and
> > there is a mention of v4.3.a in the binding. It is unclear whether
> > suffixing it with "-1.00.a" would be useful, or if the time comes call
> > the new "adi,i3c-master-2".
> >
> > Do you still think it is necessary?
>
Hi Conor,
> I do, yes. In the other cases, if a new version comes out that has some
> sort of meaningful difference a new compatible can be added. Until then,
> using the -1.00.a one is okay.
Ok, I will add the -1.00.a suffix,
Best regards,
Jorge
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2025-06-18 7:10 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-06-06 13:21 [PATCH v2 0/2] Add ADI I3C Controller Jorge Marques
2025-06-06 13:21 ` [PATCH v2 1/2] dt-bindings: i3c: Add adi-i3c-master Jorge Marques
2025-06-06 16:08 ` Conor Dooley
2025-06-17 14:50 ` Jorge Marques
2025-06-17 15:47 ` Conor Dooley
2025-06-18 7:10 ` Jorge Marques
2025-06-06 13:21 ` [PATCH v2 2/2] i3c: master: Add driver for Analog Devices I3C Controller IP Jorge Marques
2025-06-06 20:33 ` ALOK TIWARI
2025-06-17 14:54 ` Jorge Marques
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).