On Tue, Jul 22, 2025 at 09:07:46PM +0200, Wolfram Sang wrote:
> Add a basic driver for the I3C controller found in Renesas RZ/G3S and
> G3E SoCs. Support I3C pure busses (tested with two targets) and mixed
> busses (two I3C devices plus various I2C targets). DAA and communication
> with temperature sensors worked reliably at various speeds.
>
> Missing features such as IBI, HotJoin, and target mode will be added
> incrementally.
>
> Signed-off-by: Wolfram Sang <wsa+rene...@sang-engineering.com>

Reviewed-by: Frank Li <frank...@nxp.com>

> ---
> Changes since v3:
> * added 'const' to renesas_i3c_irq_desc pointers
>
>  MAINTAINERS                      |    7 +
>  drivers/i3c/master/Kconfig       |   10 +
>  drivers/i3c/master/Makefile      |    1 +
>  drivers/i3c/master/renesas-i3c.c | 1404 ++++++++++++++++++++++++++++++
>  4 files changed, 1422 insertions(+)
>  create mode 100644 drivers/i3c/master/renesas-i3c.c
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 60bba48f5479..6c841aa52113 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -11462,6 +11462,13 @@ S:   Maintained
>  F:   Documentation/devicetree/bindings/i3c/cdns,i3c-master.yaml
>  F:   drivers/i3c/master/i3c-master-cdns.c
>
> +I3C DRIVER FOR RENESAS
> +M:   Wolfram Sang <wsa+rene...@sang-engineering.com>
> +M:   Tommaso Merciai <tommaso.merciai...@bp.renesas.com>
> +S:   Supported
> +F:   Documentation/devicetree/bindings/i3c/renesas,i3c.yaml
> +F:   drivers/i3c/master/renesas-i3c.c
> +
>  I3C DRIVER FOR SYNOPSYS DESIGNWARE
>  S:   Orphan
>  F:   Documentation/devicetree/bindings/i3c/snps,dw-i3c-master.yaml
> diff --git a/drivers/i3c/master/Kconfig b/drivers/i3c/master/Kconfig
> index 7b30db3253af..13df2944f2ec 100644
> --- a/drivers/i3c/master/Kconfig
> +++ b/drivers/i3c/master/Kconfig
> @@ -64,3 +64,13 @@ config MIPI_I3C_HCI_PCI
>
>         This driver can also be built as a module. If so, the module will be
>         called mipi-i3c-hci-pci.
> +
> +config RENESAS_I3C
> +     tristate "Renesas I3C controller driver"
> +     depends on HAS_IOMEM
> +     depends on ARCH_RENESAS || COMPILE_TEST
> +     help
> +       Support the Renesas I3C controller as found in some RZ variants.
> +
> +       This driver can also be built as a module. If so, the module will be
> +       called renesas-i3c.
> diff --git a/drivers/i3c/master/Makefile b/drivers/i3c/master/Makefile
> index 3e97960160bc..aac74f3e3851 100644
> --- a/drivers/i3c/master/Makefile
> +++ b/drivers/i3c/master/Makefile
> @@ -4,3 +4,4 @@ obj-$(CONFIG_DW_I3C_MASTER)           += dw-i3c-master.o
>  obj-$(CONFIG_AST2600_I3C_MASTER)     += ast2600-i3c-master.o
>  obj-$(CONFIG_SVC_I3C_MASTER)         += svc-i3c-master.o
>  obj-$(CONFIG_MIPI_I3C_HCI)           += mipi-i3c-hci/
> +obj-$(CONFIG_RENESAS_I3C)            += renesas-i3c.o
> diff --git a/drivers/i3c/master/renesas-i3c.c 
> b/drivers/i3c/master/renesas-i3c.c
> new file mode 100644
> index 000000000000..964359659fd9
> --- /dev/null
> +++ b/drivers/i3c/master/renesas-i3c.c
> @@ -0,0 +1,1404 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Renesas I3C Controller driver
> + * Copyright (C) 2023-25 Renesas Electronics Corp.
> + *
> + * TODO: IBI support, HotJoin support, Target support
> + */
> +
> +#include <linux/bitfield.h>
> +#include <linux/bitops.h>
> +#include <linux/clk.h>
> +#include <linux/completion.h>
> +#include <linux/err.h>
> +#include <linux/errno.h>
> +#include <linux/i2c.h>
> +#include <linux/i3c/master.h>
> +#include <linux/interrupt.h>
> +#include <linux/ioport.h>
> +#include <linux/iopoll.h>
> +#include <linux/list.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/reset.h>
> +#include <linux/slab.h>
> +#include "../internals.h"
> +
> +#define PRTS                 0x00
> +#define  PRTS_PRTMD          BIT(0)
> +
> +#define BCTL                 0x14
> +#define  BCTL_INCBA          BIT(0)
> +#define  BCTL_HJACKCTL               BIT(8)
> +#define  BCTL_ABT            BIT(29)
> +#define  BCTL_BUSE           BIT(31)
> +
> +#define MSDVAD                       0x18
> +#define  MSDVAD_MDYAD(x)     FIELD_PREP(GENMASK(21, 16), x)
> +#define  MSDVAD_MDYADV               BIT(31)
> +
> +#define RSTCTL                       0x20
> +#define  RSTCTL_RI3CRST              BIT(0)
> +#define  RSTCTL_INTLRST              BIT(16)
> +
> +#define INST                 0x30
> +
> +#define IBINCTL                      0x58
> +#define  IBINCTL_NRHJCTL     BIT(0)
> +#define  IBINCTL_NRMRCTL     BIT(1)
> +#define  IBINCTL_NRSIRCTL    BIT(3)
> +
> +#define SVCTL                        0x64
> +
> +#define REFCKCTL             0x70
> +#define  REFCKCTL_IREFCKS(x) FIELD_PREP(GENMASK(2, 0), x)
> +
> +#define STDBR                        0x74
> +#define  STDBR_SBRLO(cond, x)        FIELD_PREP(GENMASK(7, 0), (x) >> (cond))
> +#define  STDBR_SBRHO(cond, x)        FIELD_PREP(GENMASK(15, 8), (x) >> 
> (cond))
> +#define  STDBR_SBRLP(x)              FIELD_PREP(GENMASK(21, 16), x)
> +#define  STDBR_SBRHP(x)              FIELD_PREP(GENMASK(29, 24), x)
> +#define  STDBR_DSBRPO                BIT(31)
> +
> +#define EXTBR                        0x78
> +#define  EXTBR_EBRLO(x)              FIELD_PREP(GENMASK(7, 0), x)
> +#define  EXTBR_EBRHO(x)              FIELD_PREP(GENMASK(15, 8), x)
> +#define  EXTBR_EBRLP(x)              FIELD_PREP(GENMASK(21, 16), x)
> +#define  EXTBR_EBRHP(x)              FIELD_PREP(GENMASK(29, 24), x)
> +
> +#define BFRECDT                      0x7c
> +#define  BFRECDT_FRECYC(x)   FIELD_PREP(GENMASK(8, 0), x)
> +
> +#define BAVLCDT                      0x80
> +#define  BAVLCDT_AVLCYC(x)   FIELD_PREP(GENMASK(8, 0), x)
> +
> +#define BIDLCDT                      0x84
> +#define  BIDLCDT_IDLCYC(x)   FIELD_PREP(GENMASK(17, 0), x)
> +
> +#define ACKCTL                       0xa0
> +#define  ACKCTL_ACKT         BIT(1)
> +#define  ACKCTL_ACKTWP               BIT(2)
> +
> +#define SCSTRCTL             0xa4
> +#define  SCSTRCTL_ACKTWE     BIT(0)
> +#define  SCSTRCTL_RWE                BIT(1)
> +
> +#define SCSTLCTL             0xb0
> +
> +#define CNDCTL                       0x140
> +#define  CNDCTL_STCND                BIT(0)
> +#define  CNDCTL_SRCND                BIT(1)
> +#define  CNDCTL_SPCND                BIT(2)
> +
> +#define NCMDQP                       0x150 /* Normal Command Queue */
> +#define  NCMDQP_CMD_ATTR(x)  FIELD_PREP(GENMASK(2, 0), x)
> +#define  NCMDQP_IMMED_XFER   0x01
> +#define  NCMDQP_ADDR_ASSGN   0x02
> +#define  NCMDQP_TID(x)               FIELD_PREP(GENMASK(6, 3), x)
> +#define  NCMDQP_CMD(x)               FIELD_PREP(GENMASK(14, 7), x)
> +#define  NCMDQP_CP           BIT(15)
> +#define  NCMDQP_DEV_INDEX(x) FIELD_PREP(GENMASK(20, 16), x)
> +#define  NCMDQP_BYTE_CNT(x)  FIELD_PREP(GENMASK(25, 23), x)
> +#define  NCMDQP_DEV_COUNT(x) FIELD_PREP(GENMASK(29, 26), x)
> +#define  NCMDQP_MODE(x)              FIELD_PREP(GENMASK(28, 26), x)
> +#define  NCMDQP_RNW(x)               FIELD_PREP(GENMASK(29, 29), x)
> +#define  NCMDQP_ROC          BIT(30)
> +#define  NCMDQP_TOC          BIT(31)
> +#define  NCMDQP_DATA_LENGTH(x)       FIELD_PREP(GENMASK(31, 16), x)
> +
> +#define NRSPQP                       0x154 /* Normal Respone Queue */
> +#define  NRSPQP_NO_ERROR                     0
> +#define  NRSPQP_ERROR_CRC            1
> +#define  NRSPQP_ERROR_PARITY         2
> +#define  NRSPQP_ERROR_FRAME          3
> +#define  NRSPQP_ERROR_IBA_NACK               4
> +#define  NRSPQP_ERROR_ADDRESS_NACK   5
> +#define  NRSPQP_ERROR_OVER_UNDER_FLOW        6
> +#define  NRSPQP_ERROR_TRANSF_ABORT   8
> +#define  NRSPQP_ERROR_I2C_W_NACK_ERR 9
> +#define  NRSPQP_ERROR_UNSUPPORTED    10
> +#define  NRSPQP_DATA_LEN(x)  FIELD_GET(GENMASK(15, 0), x)
> +#define  NRSPQP_ERR_STATUS(x)        FIELD_GET(GENMASK(31, 28), x)
> +
> +#define NTDTBP0                      0x158 /* Normal Transfer Data Buffer */
> +#define  NTDTBP0_DEPTH               16
> +
> +#define NQTHCTL                      0x190
> +#define  NQTHCTL_CMDQTH(x)   FIELD_PREP(GENMASK(1, 0), x)
> +#define  NQTHCTL_IBIDSSZ(x)  FIELD_PREP(GENMASK(23, 16), x)
> +
> +#define NTBTHCTL0            0x194
> +
> +#define NRQTHCTL             0x1c0
> +
> +#define BST                  0x1d0
> +#define  BST_STCNDDF         BIT(0)
> +#define  BST_SPCNDDF         BIT(1)
> +#define  BST_NACKDF          BIT(4)
> +#define  BST_TENDF           BIT(8)
> +
> +#define BSTE                 0x1d4
> +#define  BSTE_STCNDDE                BIT(0)
> +#define  BSTE_SPCNDDE                BIT(1)
> +#define  BSTE_NACKDE         BIT(4)
> +#define  BSTE_TENDE          BIT(8)
> +#define  BSTE_ALE            BIT(16)
> +#define  BSTE_TODE           BIT(20)
> +#define  BSTE_WUCNDDE                BIT(24)
> +#define  BSTE_ALL_FLAG               (BSTE_STCNDDE | BSTE_SPCNDDE |\
> +                             BSTE_NACKDE | BSTE_TENDE |\
> +                             BSTE_ALE | BSTE_TODE | BSTE_WUCNDDE)
> +
> +#define BIE                  0x1d8
> +#define  BIE_STCNDDIE                BIT(0)
> +#define  BIE_SPCNDDIE                BIT(1)
> +#define  BIE_NACKDIE         BIT(4)
> +#define  BIE_TENDIE          BIT(8)
> +
> +#define NTST                 0x1e0
> +#define  NTST_TDBEF0         BIT(0)
> +#define  NTST_RDBFF0         BIT(1)
> +#define  NTST_CMDQEF         BIT(3)
> +#define  NTST_RSPQFF         BIT(4)
> +#define  NTST_TABTF          BIT(5)
> +#define  NTST_TEF            BIT(9)
> +
> +#define NTSTE                        0x1e4
> +#define  NTSTE_TDBEE0                BIT(0)
> +#define  NTSTE_RDBFE0                BIT(1)
> +#define  NTSTE_IBIQEFE               BIT(2)
> +#define  NTSTE_CMDQEE                BIT(3)
> +#define  NTSTE_RSPQFE                BIT(4)
> +#define  NTSTE_TABTE         BIT(5)
> +#define  NTSTE_TEE           BIT(9)
> +#define  NTSTE_RSQFE         BIT(20)
> +#define  NTSTE_ALL_FLAG              (NTSTE_TDBEE0 | NTSTE_RDBFE0 |\
> +                             NTSTE_IBIQEFE | NTSTE_CMDQEE |\
> +                             NTSTE_RSPQFE | NTSTE_TABTE |\
> +                             NTSTE_TEE | NTSTE_RSQFE)
> +
> +#define NTIE                 0x1e8
> +#define  NTIE_TDBEIE0                BIT(0)
> +#define  NTIE_RDBFIE0                BIT(1)
> +#define  NTIE_IBIQEFIE               BIT(2)
> +#define  NTIE_RSPQFIE                BIT(4)
> +#define  NTIE_RSQFIE         BIT(20)
> +
> +#define BCST                 0x210
> +#define  BCST_BFREF          BIT(0)
> +
> +#define DATBAS(x)            (0x224 + 0x8 * (x))
> +#define  DATBAS_DVSTAD(x)    FIELD_PREP(GENMASK(6, 0), x)
> +#define  DATBAS_DVDYAD(x)    FIELD_PREP(GENMASK(23, 16), x)
> +
> +#define NDBSTLV0             0x398
> +#define  NDBSTLV0_RDBLV(x)   FIELD_GET(GENMASK(15, 8), x)
> +
> +#define RENESAS_I3C_MAX_DEVS 8
> +#define I2C_INIT_MSG         -1
> +
> +enum i3c_internal_state {
> +     I3C_INTERNAL_STATE_DISABLED,
> +     I3C_INTERNAL_STATE_CONTROLLER_IDLE,
> +     I3C_INTERNAL_STATE_CONTROLLER_ENTDAA,
> +     I3C_INTERNAL_STATE_CONTROLLER_SETDASA,
> +     I3C_INTERNAL_STATE_CONTROLLER_WRITE,
> +     I3C_INTERNAL_STATE_CONTROLLER_READ,
> +     I3C_INTERNAL_STATE_CONTROLLER_COMMAND_WRITE,
> +     I3C_INTERNAL_STATE_CONTROLLER_COMMAND_READ,
> +};
> +
> +enum renesas_i3c_event {
> +     I3C_COMMAND_ADDRESS_ASSIGNMENT,
> +     I3C_WRITE,
> +     I3C_READ,
> +     I3C_COMMAND_WRITE,
> +     I3C_COMMAND_READ,
> +};
> +
> +struct renesas_i3c_cmd {
> +     u32 cmd0;
> +     u32 len;
> +     const void *tx_buf;
> +     u32 tx_count;
> +     void *rx_buf;
> +     u32 rx_count;
> +     u32 err;
> +     u8 rnw;
> +     /* i2c xfer */
> +     int i2c_bytes_left;
> +     int i2c_is_last;
> +     u8 *i2c_buf;
> +     const struct i2c_msg *msg;
> +};
> +
> +struct renesas_i3c_xfer {
> +     struct list_head node;
> +     struct completion comp;
> +     int ret;
> +     bool is_i2c_xfer;
> +     unsigned int ncmds;
> +     struct renesas_i3c_cmd cmds[] __counted_by(ncmds);
> +};
> +
> +struct renesas_i3c_xferqueue {
> +     struct list_head list;
> +     struct renesas_i3c_xfer *cur;
> +     /* Lock for accessing the xfer queue */
> +     spinlock_t lock;
> +};
> +
> +struct renesas_i3c {
> +     struct i3c_master_controller base;
> +     enum i3c_internal_state internal_state;
> +     u16 maxdevs;
> +     u32 free_pos;
> +     u32 i2c_STDBR;
> +     u32 i3c_STDBR;
> +     u8 addrs[RENESAS_I3C_MAX_DEVS];
> +     struct renesas_i3c_xferqueue xferqueue;
> +     void __iomem *regs;
> +     struct clk *tclk;
> +};
> +
> +struct renesas_i3c_i2c_dev_data {
> +     u8 index;
> +};
> +
> +struct renesas_i3c_irq_desc {
> +     const char *name;
> +     irq_handler_t isr;
> +     const char *desc;
> +};
> +
> +struct renesas_i3c_config {
> +     unsigned int has_pclkrw:1;
> +};
> +
> +static inline void renesas_i3c_reg_update(void __iomem *reg, u32 mask, u32 
> val)
> +{
> +     u32 data = readl(reg);
> +
> +     data &= ~mask;
> +     data |= (val & mask);
> +     writel(data, reg);
> +}
> +
> +static inline u32 renesas_readl(void __iomem *base, u32 reg)
> +{
> +     return readl(base + reg);
> +}
> +
> +static inline void renesas_writel(void __iomem *base, u32 reg, u32 val)
> +{
> +     writel(val, base + reg);
> +}
> +
> +static void renesas_set_bit(void __iomem *base, u32 reg, u32 val)
> +{
> +     renesas_i3c_reg_update(base + reg, val, val);
> +}
> +
> +static void renesas_clear_bit(void __iomem *base, u32 reg, u32 val)
> +{
> +     renesas_i3c_reg_update(base + reg, val, 0);
> +}
> +
> +static inline struct renesas_i3c *to_renesas_i3c(struct 
> i3c_master_controller *m)
> +{
> +     return container_of(m, struct renesas_i3c, base);
> +}
> +
> +static inline u32 datbas_dvdyad_with_parity(u8 addr)
> +{
> +     return DATBAS_DVDYAD(addr | (parity8(addr) ? 0 : BIT(7)));
> +}
> +
> +static int renesas_i3c_get_free_pos(struct renesas_i3c *i3c)
> +{
> +     if (!(i3c->free_pos & GENMASK(i3c->maxdevs - 1, 0)))
> +             return -ENOSPC;
> +
> +     return ffs(i3c->free_pos) - 1;
> +}
> +
> +static int renesas_i3c_get_addr_pos(struct renesas_i3c *i3c, u8 addr)
> +{
> +     int pos;
> +
> +     for (pos = 0; pos < i3c->maxdevs; pos++) {
> +             if (addr == i3c->addrs[pos])
> +                     return pos;
> +     }
> +
> +     return -EINVAL;
> +}
> +
> +static struct renesas_i3c_xfer *renesas_i3c_alloc_xfer(struct renesas_i3c 
> *i3c,
> +                                                    unsigned int ncmds)
> +{
> +     struct renesas_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 renesas_i3c_start_xfer_locked(struct renesas_i3c *i3c)
> +{
> +     struct renesas_i3c_xfer *xfer = i3c->xferqueue.cur;
> +     struct renesas_i3c_cmd *cmd;
> +     u32 cmd1;
> +
> +     if (!xfer)
> +             return;
> +
> +     cmd = xfer->cmds;
> +
> +     switch (i3c->internal_state) {
> +     case I3C_INTERNAL_STATE_CONTROLLER_ENTDAA:
> +     case I3C_INTERNAL_STATE_CONTROLLER_SETDASA:
> +             renesas_set_bit(i3c->regs, NTIE, NTIE_RSPQFIE);
> +             renesas_writel(i3c->regs, NCMDQP, cmd->cmd0);
> +             renesas_writel(i3c->regs, NCMDQP, 0);
> +             break;
> +     case I3C_INTERNAL_STATE_CONTROLLER_WRITE:
> +     case I3C_INTERNAL_STATE_CONTROLLER_COMMAND_WRITE:
> +             renesas_set_bit(i3c->regs, NTIE, NTIE_RSPQFIE);
> +             if (cmd->len <= 4) {
> +                     cmd->cmd0 |= NCMDQP_CMD_ATTR(NCMDQP_IMMED_XFER);
> +                     cmd->cmd0 |= NCMDQP_BYTE_CNT(cmd->len);
> +                     cmd->tx_count = cmd->len;
> +                     cmd1 = cmd->len == 0 ? 0 : *(u32 *)cmd->tx_buf;
> +             } else {
> +                     cmd1 = NCMDQP_DATA_LENGTH(cmd->len);
> +             }
> +             renesas_writel(i3c->regs, NCMDQP, cmd->cmd0);
> +             renesas_writel(i3c->regs, NCMDQP, cmd1);
> +             break;
> +     case I3C_INTERNAL_STATE_CONTROLLER_READ:
> +     case I3C_INTERNAL_STATE_CONTROLLER_COMMAND_READ:
> +             renesas_set_bit(i3c->regs, NTIE, NTIE_RDBFIE0);
> +             cmd1 = NCMDQP_DATA_LENGTH(cmd->len);
> +             renesas_writel(i3c->regs, NCMDQP, cmd->cmd0);
> +             renesas_writel(i3c->regs, NCMDQP, cmd1);
> +             break;
> +     default:
> +             break;
> +     }
> +
> +     /* Clear the command queue empty flag */
> +     renesas_clear_bit(i3c->regs, NTST, NTST_CMDQEF);
> +}
> +
> +static void renesas_i3c_dequeue_xfer_locked(struct renesas_i3c *i3c,
> +                                         struct renesas_i3c_xfer *xfer)
> +{
> +     if (i3c->xferqueue.cur == xfer)
> +             i3c->xferqueue.cur = NULL;
> +     else
> +             list_del_init(&xfer->node);
> +}
> +
> +static void renesas_i3c_dequeue_xfer(struct renesas_i3c *i3c, struct 
> renesas_i3c_xfer *xfer)
> +{
> +     scoped_guard(spinlock_irqsave, &i3c->xferqueue.lock)
> +             renesas_i3c_dequeue_xfer_locked(i3c, xfer);
> +}
> +
> +static void renesas_i3c_enqueue_xfer(struct renesas_i3c *i3c, struct 
> renesas_i3c_xfer *xfer)
> +{
> +     reinit_completion(&xfer->comp);
> +     scoped_guard(spinlock_irqsave, &i3c->xferqueue.lock) {
> +             if (i3c->xferqueue.cur) {
> +                     list_add_tail(&xfer->node, &i3c->xferqueue.list);
> +             } else {
> +                     i3c->xferqueue.cur = xfer;
> +                     if (!xfer->is_i2c_xfer)
> +                             renesas_i3c_start_xfer_locked(i3c);
> +             }
> +     }
> +}
> +
> +static void renesas_i3c_wait_xfer(struct renesas_i3c *i3c, struct 
> renesas_i3c_xfer *xfer)
> +{
> +     unsigned long time_left;
> +
> +     renesas_i3c_enqueue_xfer(i3c, xfer);
> +
> +     time_left = wait_for_completion_timeout(&xfer->comp, 
> I3C_BUS_XFER_TIMEOUT_TYP_JIF);
> +     if (!time_left)
> +             renesas_i3c_dequeue_xfer(i3c, xfer);
> +}
> +
> +static void renesas_i3c_set_prts(struct renesas_i3c *i3c, u32 val)
> +{
> +     /* Required sequence according to tnrza0140ae */
> +     renesas_set_bit(i3c->regs, RSTCTL, RSTCTL_INTLRST);
> +     renesas_writel(i3c->regs, PRTS, val);
> +     renesas_clear_bit(i3c->regs, RSTCTL, RSTCTL_INTLRST);
> +}
> +
> +static void renesas_i3c_bus_enable(struct i3c_master_controller *m, bool 
> i3c_mode)
> +{
> +     struct renesas_i3c *i3c = to_renesas_i3c(m);
> +
> +     /* Setup either I3C or I2C protocol */
> +     if (i3c_mode) {
> +             renesas_i3c_set_prts(i3c, 0);
> +             /* Revisit: INCBA handling, especially after I2C transfers */
> +             renesas_set_bit(i3c->regs, BCTL, BCTL_HJACKCTL | BCTL_INCBA);
> +             renesas_set_bit(i3c->regs, MSDVAD, MSDVAD_MDYADV);
> +             renesas_writel(i3c->regs, STDBR, i3c->i3c_STDBR);
> +     } else {
> +             renesas_i3c_set_prts(i3c, PRTS_PRTMD);
> +             renesas_writel(i3c->regs, STDBR, i3c->i2c_STDBR);
> +     }
> +
> +     /* Enable I3C bus */
> +     renesas_set_bit(i3c->regs, BCTL, BCTL_BUSE);
> +}
> +
> +static int renesas_i3c_reset(struct renesas_i3c *i3c)
> +{
> +     u32 val;
> +
> +     renesas_writel(i3c->regs, BCTL, 0);
> +     renesas_set_bit(i3c->regs, RSTCTL, RSTCTL_RI3CRST);
> +
> +     return read_poll_timeout(renesas_readl, val, !(val & RSTCTL_RI3CRST),
> +                              0, 1000, false, i3c->regs, RSTCTL);
> +}
> +
> +static int renesas_i3c_bus_init(struct i3c_master_controller *m)
> +{
> +     struct renesas_i3c *i3c = to_renesas_i3c(m);
> +     struct i3c_bus *bus = i3c_master_get_bus(m);
> +     struct i3c_device_info info = {};
> +     struct i2c_timings t;
> +     unsigned long rate;
> +     u32 double_SBR, val;
> +     int cks, pp_high_ticks, pp_low_ticks, i3c_total_ticks;
> +     int od_high_ticks, od_low_ticks, i2c_total_ticks;
> +     int ret;
> +
> +     rate = clk_get_rate(i3c->tclk);
> +     if (!rate)
> +             return -EINVAL;
> +
> +     ret = renesas_i3c_reset(i3c);
> +     if (ret)
> +             return ret;
> +
> +     i2c_total_ticks = DIV_ROUND_UP(rate, bus->scl_rate.i2c);
> +     i3c_total_ticks = DIV_ROUND_UP(rate, bus->scl_rate.i3c);
> +
> +     i2c_parse_fw_timings(&m->dev, &t, true);
> +
> +     for (cks = 0; cks < 7; cks++) {
> +             /* SCL low-period calculation in Open-drain mode */
> +             od_low_ticks = ((i2c_total_ticks * 6) / 10);
> +
> +             /* SCL clock calculation in Push-Pull mode */
> +             if (bus->mode == I3C_BUS_MODE_PURE)
> +                     pp_high_ticks = ((i3c_total_ticks * 5) / 10);
> +             else
> +                     pp_high_ticks = DIV_ROUND_UP(I3C_BUS_THIGH_MIXED_MAX_NS,
> +                                                  NSEC_PER_SEC / rate);
> +             pp_low_ticks = i3c_total_ticks - pp_high_ticks;
> +
> +             if ((od_low_ticks / 2) <= 0xFF && pp_low_ticks < 0x3F)
> +                     break;
> +
> +             i2c_total_ticks /= 2;
> +             i3c_total_ticks /= 2;
> +             rate /= 2;
> +     }
> +
> +     /* SCL clock period calculation in Open-drain mode */
> +     if ((od_low_ticks / 2) > 0xFF || pp_low_ticks > 0x3F) {
> +             dev_err(&m->dev, "invalid speed (i2c-scl = %lu Hz, i3c-scl = 
> %lu Hz). Too slow.\n",
> +                     (unsigned long)bus->scl_rate.i2c, (unsigned 
> long)bus->scl_rate.i3c);
> +             return -EINVAL;
> +     }
> +
> +     /* SCL high-period calculation in Open-drain mode */
> +     od_high_ticks = i2c_total_ticks - od_low_ticks;
> +
> +     /* Standard Bit Rate setting */
> +     double_SBR = od_low_ticks > 0xFF ? 1 : 0;
> +     i3c->i3c_STDBR = (double_SBR ? STDBR_DSBRPO : 0) |
> +                     STDBR_SBRLO(double_SBR, od_low_ticks) |
> +                     STDBR_SBRHO(double_SBR, od_high_ticks) |
> +                     STDBR_SBRLP(pp_low_ticks) |
> +                     STDBR_SBRHP(pp_high_ticks);
> +
> +     od_low_ticks -= t.scl_fall_ns / (NSEC_PER_SEC / rate) + 1;
> +     od_high_ticks -= t.scl_rise_ns / (NSEC_PER_SEC / rate) + 1;
> +     i3c->i2c_STDBR = (double_SBR ? STDBR_DSBRPO : 0) |
> +                     STDBR_SBRLO(double_SBR, od_low_ticks) |
> +                     STDBR_SBRHO(double_SBR, od_high_ticks) |
> +                     STDBR_SBRLP(pp_low_ticks) |
> +                     STDBR_SBRHP(pp_high_ticks);
> +     renesas_writel(i3c->regs, STDBR, i3c->i3c_STDBR);
> +
> +     /* Extended Bit Rate setting */
> +     renesas_writel(i3c->regs, EXTBR, EXTBR_EBRLO(od_low_ticks) |
> +                                        EXTBR_EBRHO(od_high_ticks) |
> +                                        EXTBR_EBRLP(pp_low_ticks) |
> +                                        EXTBR_EBRHP(pp_high_ticks));
> +
> +     renesas_writel(i3c->regs, REFCKCTL, REFCKCTL_IREFCKS(cks));
> +
> +     /* Disable Slave Mode */
> +     renesas_writel(i3c->regs, SVCTL, 0);
> +
> +     /* Initialize Queue/Buffer threshold */
> +     renesas_writel(i3c->regs, NQTHCTL, NQTHCTL_IBIDSSZ(6) |
> +                                          NQTHCTL_CMDQTH(1));
> +
> +     /* The only supported configuration is two entries*/
> +     renesas_writel(i3c->regs, NTBTHCTL0, 0);
> +     /* Interrupt when there is one entry in the queue */
> +     renesas_writel(i3c->regs, NRQTHCTL, 0);
> +
> +     /* Enable all Bus/Transfer Status Flags */
> +     renesas_writel(i3c->regs, BSTE, BSTE_ALL_FLAG);
> +     renesas_writel(i3c->regs, NTSTE, NTSTE_ALL_FLAG);
> +
> +     /* Interrupt enable settings */
> +     renesas_writel(i3c->regs, BIE, BIE_NACKDIE | BIE_TENDIE);
> +     renesas_writel(i3c->regs, NTIE, 0);
> +
> +     /* Clear Status register */
> +     renesas_writel(i3c->regs, NTST, 0);
> +     renesas_writel(i3c->regs, INST, 0);
> +     renesas_writel(i3c->regs, BST, 0);
> +
> +     /* Hot-Join Acknowlege setting. */
> +     renesas_set_bit(i3c->regs, BCTL, BCTL_HJACKCTL);
> +
> +     renesas_writel(i3c->regs, IBINCTL, IBINCTL_NRHJCTL | IBINCTL_NRMRCTL |
> +                                          IBINCTL_NRSIRCTL);
> +
> +     renesas_writel(i3c->regs, SCSTLCTL, 0);
> +     renesas_set_bit(i3c->regs, SCSTRCTL, SCSTRCTL_ACKTWE);
> +
> +     /* Bus condition timing */
> +     val = DIV_ROUND_UP(I3C_BUS_TBUF_MIXED_FM_MIN_NS, NSEC_PER_SEC / rate);
> +     renesas_writel(i3c->regs, BFRECDT, BFRECDT_FRECYC(val));
> +
> +     val = DIV_ROUND_UP(I3C_BUS_TAVAL_MIN_NS, NSEC_PER_SEC / rate);
> +     renesas_writel(i3c->regs, BAVLCDT, BAVLCDT_AVLCYC(val));
> +
> +     val = DIV_ROUND_UP(I3C_BUS_TIDLE_MIN_NS, NSEC_PER_SEC / rate);
> +     renesas_writel(i3c->regs, BIDLCDT, BIDLCDT_IDLCYC(val));
> +
> +     ret = i3c_master_get_free_addr(m, 0);
> +     if (ret < 0)
> +             return ret;
> +
> +     renesas_writel(i3c->regs, MSDVAD, MSDVAD_MDYAD(ret) | MSDVAD_MDYADV);
> +
> +     memset(&info, 0, sizeof(info));
> +     info.dyn_addr = ret;
> +     return i3c_master_set_info(&i3c->base, &info);
> +}
> +
> +static void renesas_i3c_bus_cleanup(struct i3c_master_controller *m)
> +{
> +     struct renesas_i3c *i3c = to_renesas_i3c(m);
> +
> +     renesas_i3c_reset(i3c);
> +}
> +
> +static int renesas_i3c_daa(struct i3c_master_controller *m)
> +{
> +     struct renesas_i3c *i3c = to_renesas_i3c(m);
> +     struct renesas_i3c_cmd *cmd;
> +     u32 olddevs, newdevs;
> +     u8 last_addr = 0, pos;
> +     int ret;
> +
> +     struct renesas_i3c_xfer *xfer __free(kfree) = 
> renesas_i3c_alloc_xfer(i3c, 1);
> +     if (!xfer)
> +             return -ENOMEM;
> +
> +     /* Enable I3C bus. */
> +     renesas_i3c_bus_enable(m, true);
> +
> +     olddevs = ~(i3c->free_pos);
> +     i3c->internal_state = I3C_INTERNAL_STATE_CONTROLLER_ENTDAA;
> +
> +     /* Setting DATBASn registers for target devices. */
> +     for (pos = 0; pos < i3c->maxdevs; pos++) {
> +             if (olddevs & BIT(pos))
> +                     continue;
> +
> +             ret = i3c_master_get_free_addr(m, last_addr + 1);
> +             if (ret < 0)
> +                     return -ENOSPC;
> +
> +             i3c->addrs[pos] = ret;
> +             last_addr = ret;
> +
> +             renesas_writel(i3c->regs, DATBAS(pos), 
> datbas_dvdyad_with_parity(ret));
> +     }
> +
> +     init_completion(&xfer->comp);
> +     cmd = xfer->cmds;
> +     cmd->rx_count = 0;
> +
> +     ret = renesas_i3c_get_free_pos(i3c);
> +     if (ret < 0)
> +             return ret;
> +
> +     /*
> +      * Setup the command descriptor to start the ENTDAA command
> +      * and starting at the selected device index.
> +      */
> +     cmd->cmd0 = NCMDQP_CMD_ATTR(NCMDQP_ADDR_ASSGN) | NCMDQP_ROC |
> +                 NCMDQP_TID(I3C_COMMAND_ADDRESS_ASSIGNMENT) |
> +                 NCMDQP_CMD(I3C_CCC_ENTDAA) | NCMDQP_DEV_INDEX(ret) |
> +                 NCMDQP_DEV_COUNT(i3c->maxdevs - ret) | NCMDQP_TOC;
> +
> +     renesas_i3c_wait_xfer(i3c, xfer);
> +
> +     newdevs = GENMASK(i3c->maxdevs - cmd->rx_count - 1, 0);
> +     newdevs &= ~olddevs;
> +
> +     for (pos = 0; pos < i3c->maxdevs; pos++) {
> +             if (newdevs & BIT(pos))
> +                     i3c_master_add_i3c_dev_locked(m, i3c->addrs[pos]);
> +     }
> +
> +     return ret < 0 ? ret : 0;
> +}
> +
> +static bool renesas_i3c_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_ENTAS(0, true):
> +     case I3C_CCC_ENTAS(1, true):
> +     case I3C_CCC_ENTAS(2, true):
> +     case I3C_CCC_ENTAS(3, true):
> +     case I3C_CCC_ENTAS(0, false):
> +     case I3C_CCC_ENTAS(1, false):
> +     case I3C_CCC_ENTAS(2, false):
> +     case I3C_CCC_ENTAS(3, false):
> +     case I3C_CCC_RSTDAA(true):
> +     case I3C_CCC_RSTDAA(false):
> +     case I3C_CCC_ENTDAA:
> +     case I3C_CCC_DEFSLVS:
> +     case I3C_CCC_SETMWL(true):
> +     case I3C_CCC_SETMWL(false):
> +     case I3C_CCC_SETMRL(true):
> +     case I3C_CCC_SETMRL(false):
> +     case I3C_CCC_ENTTM:
> +     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_GETACCMST:
> +     case I3C_CCC_GETMXDS:
> +             return true;
> +     default:
> +             return false;
> +     }
> +}
> +
> +static int renesas_i3c_send_ccc_cmd(struct i3c_master_controller *m,
> +                                        struct i3c_ccc_cmd *ccc)
> +{
> +     struct renesas_i3c *i3c = to_renesas_i3c(m);
> +     struct renesas_i3c_xfer *xfer;
> +     struct renesas_i3c_cmd *cmd;
> +     int ret, pos = 0;
> +
> +     if (ccc->id & I3C_CCC_DIRECT) {
> +             pos = renesas_i3c_get_addr_pos(i3c, ccc->dests[0].addr);
> +             if (pos < 0)
> +                     return pos;
> +     }
> +
> +     xfer = renesas_i3c_alloc_xfer(i3c, 1);
> +     if (!xfer)
> +             return -ENOMEM;
> +
> +     renesas_i3c_bus_enable(m, true);
> +
> +     init_completion(&xfer->comp);
> +     cmd = xfer->cmds;
> +     cmd->rnw = ccc->rnw;
> +     cmd->cmd0 = 0;
> +
> +     /* Calculate the command descriptor. */
> +     switch (ccc->id) {
> +     case I3C_CCC_SETDASA:
> +             renesas_writel(i3c->regs, DATBAS(pos),
> +                     DATBAS_DVSTAD(ccc->dests[0].addr) |
> +                     DATBAS_DVDYAD(*(u8 *)ccc->dests[0].payload.data >> 1));
> +             cmd->cmd0 = NCMDQP_CMD_ATTR(NCMDQP_ADDR_ASSGN) | NCMDQP_ROC |
> +                     NCMDQP_TID(I3C_COMMAND_ADDRESS_ASSIGNMENT) |
> +                     NCMDQP_CMD(I3C_CCC_SETDASA) | NCMDQP_DEV_INDEX(pos) |
> +                     NCMDQP_DEV_COUNT(0) | NCMDQP_TOC;
> +             i3c->internal_state = I3C_INTERNAL_STATE_CONTROLLER_SETDASA;
> +             break;
> +     default:
> +             /* Calculate the command descriptor. */
> +             cmd->cmd0 = NCMDQP_TID(I3C_COMMAND_WRITE) | NCMDQP_MODE(0) |
> +                             NCMDQP_RNW(ccc->rnw) | NCMDQP_CMD(ccc->id) |
> +                             NCMDQP_ROC | NCMDQP_TOC | NCMDQP_CP |
> +                             NCMDQP_DEV_INDEX(pos);
> +
> +             if (ccc->rnw) {
> +                     cmd->rx_buf = ccc->dests[0].payload.data;
> +                     cmd->len = ccc->dests[0].payload.len;
> +                     cmd->rx_count = 0;
> +                     i3c->internal_state = 
> I3C_INTERNAL_STATE_CONTROLLER_COMMAND_READ;
> +             } else {
> +                     cmd->tx_buf = ccc->dests[0].payload.data;
> +                     cmd->len = ccc->dests[0].payload.len;
> +                     cmd->tx_count = 0;
> +                     i3c->internal_state = 
> I3C_INTERNAL_STATE_CONTROLLER_COMMAND_WRITE;
> +             }
> +     }
> +
> +     renesas_i3c_wait_xfer(i3c, xfer);
> +
> +     ret = xfer->ret;
> +     if (ret)
> +             ccc->err = I3C_ERROR_M2;
> +
> +     kfree(xfer);
> +
> +     return ret;
> +}
> +
> +static int renesas_i3c_priv_xfers(struct i3c_dev_desc *dev, struct 
> i3c_priv_xfer *i3c_xfers,
> +                                      int i3c_nxfers)
> +{
> +     struct i3c_master_controller *m = i3c_dev_get_master(dev);
> +     struct renesas_i3c *i3c = to_renesas_i3c(m);
> +     struct renesas_i3c_i2c_dev_data *data = i3c_dev_get_master_data(dev);
> +     struct renesas_i3c_xfer *xfer;
> +     int i;
> +
> +     /* Enable I3C bus. */
> +     renesas_i3c_bus_enable(m, true);
> +
> +     xfer = renesas_i3c_alloc_xfer(i3c, 1);
> +     if (!xfer)
> +             return -ENOMEM;
> +
> +     init_completion(&xfer->comp);
> +
> +     for (i = 0; i < i3c_nxfers; i++) {
> +             struct renesas_i3c_cmd *cmd = xfer->cmds;
> +
> +             /* Calculate the Transfer Command Descriptor */
> +             cmd->rnw = i3c_xfers[i].rnw;
> +             cmd->cmd0 = NCMDQP_DEV_INDEX(data->index) | NCMDQP_MODE(0) |
> +                         NCMDQP_RNW(cmd->rnw) | NCMDQP_ROC | NCMDQP_TOC;
> +
> +             if (i3c_xfers[i].rnw) {
> +                     cmd->rx_count = 0;
> +                     cmd->cmd0 |= NCMDQP_TID(I3C_READ);
> +                     cmd->rx_buf = i3c_xfers[i].data.in;
> +                     cmd->len = i3c_xfers[i].len;
> +                     i3c->internal_state = 
> I3C_INTERNAL_STATE_CONTROLLER_READ;
> +             } else {
> +                     cmd->tx_count = 0;
> +                     cmd->cmd0 |= NCMDQP_TID(I3C_WRITE);
> +                     cmd->tx_buf = i3c_xfers[i].data.out;
> +                     cmd->len = i3c_xfers[i].len;
> +                     i3c->internal_state = 
> I3C_INTERNAL_STATE_CONTROLLER_WRITE;
> +             }
> +
> +             if (!i3c_xfers[i].rnw && i3c_xfers[i].len > 4) {
> +                     i3c_writel_fifo(i3c->regs + NTDTBP0, cmd->tx_buf, 
> cmd->len);
> +                     if (cmd->len > NTDTBP0_DEPTH * sizeof(u32))
> +                             renesas_set_bit(i3c->regs, NTIE, NTIE_TDBEIE0);
> +             }
> +
> +             renesas_i3c_wait_xfer(i3c, xfer);
> +     }
> +
> +     return 0;
> +}
> +
> +static int renesas_i3c_attach_i3c_dev(struct i3c_dev_desc *dev)
> +{
> +     struct i3c_master_controller *m = i3c_dev_get_master(dev);
> +     struct renesas_i3c *i3c = to_renesas_i3c(m);
> +     struct renesas_i3c_i2c_dev_data *data;
> +     int pos;
> +
> +     pos = renesas_i3c_get_free_pos(i3c);
> +     if (pos < 0)
> +             return pos;
> +
> +     data = kzalloc(sizeof(*data), GFP_KERNEL);
> +     if (!data)
> +             return -ENOMEM;
> +
> +     data->index = pos;
> +     i3c->addrs[pos] = dev->info.dyn_addr ? : dev->info.static_addr;
> +     i3c->free_pos &= ~BIT(pos);
> +
> +     renesas_writel(i3c->regs, DATBAS(pos), 
> DATBAS_DVSTAD(dev->info.static_addr) |
> +                                 datbas_dvdyad_with_parity(i3c->addrs[pos]));
> +     i3c_dev_set_master_data(dev, data);
> +
> +     return 0;
> +}
> +
> +static int renesas_i3c_reattach_i3c_dev(struct i3c_dev_desc *dev,
> +                                            u8 old_dyn_addr)
> +{
> +     struct i3c_master_controller *m = i3c_dev_get_master(dev);
> +     struct renesas_i3c *i3c = to_renesas_i3c(m);
> +     struct renesas_i3c_i2c_dev_data *data = i3c_dev_get_master_data(dev);
> +
> +     i3c->addrs[data->index] = dev->info.dyn_addr ? dev->info.dyn_addr :
> +                                                     dev->info.static_addr;
> +
> +     return 0;
> +}
> +
> +static void renesas_i3c_detach_i3c_dev(struct i3c_dev_desc *dev)
> +{
> +     struct renesas_i3c_i2c_dev_data *data = i3c_dev_get_master_data(dev);
> +     struct i3c_master_controller *m = i3c_dev_get_master(dev);
> +     struct renesas_i3c *i3c = to_renesas_i3c(m);
> +
> +     i3c_dev_set_master_data(dev, NULL);
> +     i3c->addrs[data->index] = 0;
> +     i3c->free_pos |= BIT(data->index);
> +     kfree(data);
> +}
> +
> +static int renesas_i3c_i2c_xfers(struct i2c_dev_desc *dev,
> +                                     struct i2c_msg *i2c_xfers,
> +                                     int i2c_nxfers)
> +{
> +     struct i3c_master_controller *m = i2c_dev_get_master(dev);
> +     struct renesas_i3c *i3c = to_renesas_i3c(m);
> +     struct renesas_i3c_cmd *cmd;
> +     u8 start_bit = CNDCTL_STCND;
> +     int i;
> +
> +     struct renesas_i3c_xfer *xfer __free(kfree) = 
> renesas_i3c_alloc_xfer(i3c, 1);
> +     if (!xfer)
> +             return -ENOMEM;
> +
> +     if (!i2c_nxfers)
> +             return 0;
> +
> +     renesas_i3c_bus_enable(m, false);
> +
> +     init_completion(&xfer->comp);
> +     xfer->is_i2c_xfer = true;
> +     cmd = xfer->cmds;
> +
> +     if (!(renesas_readl(i3c->regs, BCST) & BCST_BFREF)) {
> +             cmd->err = -EBUSY;
> +             return cmd->err;
> +     }
> +
> +     renesas_writel(i3c->regs, BST, 0);
> +
> +     renesas_i3c_enqueue_xfer(i3c, xfer);
> +
> +     for (i = 0; i < i2c_nxfers; i++) {
> +             cmd->i2c_bytes_left = I2C_INIT_MSG;
> +             cmd->i2c_buf = i2c_xfers[i].buf;
> +             cmd->msg = &i2c_xfers[i];
> +             cmd->i2c_is_last = (i == i2c_nxfers - 1);
> +
> +             renesas_set_bit(i3c->regs, BIE, BIE_NACKDIE);
> +             renesas_set_bit(i3c->regs, NTIE, NTIE_TDBEIE0);
> +             renesas_set_bit(i3c->regs, BIE, BIE_STCNDDIE);
> +
> +             /* Issue Start condition */
> +             renesas_set_bit(i3c->regs, CNDCTL, start_bit);
> +
> +             renesas_set_bit(i3c->regs, NTSTE, NTSTE_TDBEE0);
> +
> +             wait_for_completion_timeout(&xfer->comp, m->i2c.timeout);
> +
> +             if (cmd->err)
> +                     break;
> +
> +             start_bit = CNDCTL_SRCND;
> +     }
> +
> +     renesas_i3c_dequeue_xfer(i3c, xfer);
> +     return cmd->err;
> +}
> +
> +static int renesas_i3c_attach_i2c_dev(struct i2c_dev_desc *dev)
> +{
> +     struct i3c_master_controller *m = i2c_dev_get_master(dev);
> +     struct renesas_i3c *i3c = to_renesas_i3c(m);
> +     struct renesas_i3c_i2c_dev_data *data;
> +     int pos;
> +
> +     pos = renesas_i3c_get_free_pos(i3c);
> +     if (pos < 0)
> +             return pos;
> +
> +     data = kzalloc(sizeof(*data), GFP_KERNEL);
> +     if (!data)
> +             return -ENOMEM;
> +
> +     data->index = pos;
> +     i3c->addrs[pos] = dev->addr;
> +     i3c->free_pos &= ~BIT(pos);
> +     i2c_dev_set_master_data(dev, data);
> +
> +     return 0;
> +}
> +
> +static void renesas_i3c_detach_i2c_dev(struct i2c_dev_desc *dev)
> +{
> +     struct renesas_i3c_i2c_dev_data *data = i2c_dev_get_master_data(dev);
> +     struct i3c_master_controller *m = i2c_dev_get_master(dev);
> +     struct renesas_i3c *i3c = to_renesas_i3c(m);
> +
> +     i2c_dev_set_master_data(dev, NULL);
> +     i3c->addrs[data->index] = 0;
> +     i3c->free_pos |= BIT(data->index);
> +     kfree(data);
> +}
> +
> +static irqreturn_t renesas_i3c_tx_isr(int irq, void *data)
> +{
> +     struct renesas_i3c *i3c = data;
> +     struct renesas_i3c_xfer *xfer;
> +     struct renesas_i3c_cmd *cmd;
> +     u8 val;
> +
> +     scoped_guard(spinlock, &i3c->xferqueue.lock) {
> +             xfer = i3c->xferqueue.cur;
> +             cmd = xfer->cmds;
> +
> +             if (xfer->is_i2c_xfer) {
> +                     if (!cmd->i2c_bytes_left)
> +                             return IRQ_NONE;
> +
> +                     if (cmd->i2c_bytes_left != I2C_INIT_MSG) {
> +                             val = *cmd->i2c_buf;
> +                             cmd->i2c_buf++;
> +                             cmd->i2c_bytes_left--;
> +                             renesas_writel(i3c->regs, NTDTBP0, val);
> +                     }
> +
> +                     if (cmd->i2c_bytes_left == 0) {
> +                             renesas_clear_bit(i3c->regs, NTIE, 
> NTIE_TDBEIE0);
> +                             renesas_set_bit(i3c->regs, BIE, BIE_TENDIE);
> +                     }
> +
> +                     /* Clear the Transmit Buffer Empty status flag. */
> +                     renesas_clear_bit(i3c->regs, NTST, NTST_TDBEF0);
> +             } else {
> +                     i3c_writel_fifo(i3c->regs + NTDTBP0, cmd->tx_buf, 
> cmd->len);
> +             }
> +     }
> +
> +     return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t renesas_i3c_resp_isr(int irq, void *data)
> +{
> +     struct renesas_i3c *i3c = data;
> +     struct renesas_i3c_xfer *xfer;
> +     struct renesas_i3c_cmd *cmd;
> +     u32 resp_descriptor = renesas_readl(i3c->regs, NRSPQP);
> +     u32 bytes_remaining = 0;
> +     u32 ntst, data_len;
> +     int ret = 0;
> +
> +     scoped_guard(spinlock, &i3c->xferqueue.lock) {
> +             xfer = i3c->xferqueue.cur;
> +             cmd = xfer->cmds;
> +
> +             /* Clear the Respone Queue Full status flag*/
> +             renesas_clear_bit(i3c->regs, NTST, NTST_RSPQFF);
> +
> +             data_len = NRSPQP_DATA_LEN(resp_descriptor);
> +
> +             switch (i3c->internal_state) {
> +             case I3C_INTERNAL_STATE_CONTROLLER_ENTDAA:
> +                     cmd->rx_count = data_len;
> +                     break;
> +             case I3C_INTERNAL_STATE_CONTROLLER_WRITE:
> +             case I3C_INTERNAL_STATE_CONTROLLER_COMMAND_WRITE:
> +                     /* Disable the transmit IRQ if it hasn't been disabled 
> already. */
> +                     renesas_clear_bit(i3c->regs, NTIE, NTIE_TDBEIE0);
> +                     break;
> +             case I3C_INTERNAL_STATE_CONTROLLER_READ:
> +             case I3C_INTERNAL_STATE_CONTROLLER_COMMAND_READ:
> +                     if (NDBSTLV0_RDBLV(renesas_readl(i3c->regs, NDBSTLV0)) 
> && !cmd->err)
> +                             bytes_remaining = data_len - cmd->rx_count;
> +
> +                     i3c_readl_fifo(i3c->regs + NTDTBP0, cmd->rx_buf, 
> bytes_remaining);
> +                     renesas_clear_bit(i3c->regs, NTIE, NTIE_RDBFIE0);
> +                     break;
> +             default:
> +                     break;
> +             }
> +
> +             switch (NRSPQP_ERR_STATUS(resp_descriptor)) {
> +             case NRSPQP_NO_ERROR:
> +                     break;
> +             case NRSPQP_ERROR_PARITY:
> +             case NRSPQP_ERROR_IBA_NACK:
> +             case NRSPQP_ERROR_TRANSF_ABORT:
> +             case NRSPQP_ERROR_CRC:
> +             case NRSPQP_ERROR_FRAME:
> +                     ret = -EIO;
> +                     break;
> +             case NRSPQP_ERROR_OVER_UNDER_FLOW:
> +                     ret = -ENOSPC;
> +                     break;
> +             case NRSPQP_ERROR_UNSUPPORTED:
> +                     ret = -EOPNOTSUPP;
> +                     break;
> +             case NRSPQP_ERROR_I2C_W_NACK_ERR:
> +             case NRSPQP_ERROR_ADDRESS_NACK:
> +             default:
> +                     ret = -EINVAL;
> +                     break;
> +             }
> +
> +             /*
> +              * If the transfer was aborted, then the abort flag must be 
> cleared
> +              * before notifying the application that a transfer has 
> completed.
> +              */
> +             ntst = renesas_readl(i3c->regs, NTST);
> +             if (ntst & NTST_TABTF)
> +                     renesas_clear_bit(i3c->regs, BCTL, BCTL_ABT);
> +
> +             /* Clear error status flags. */
> +             renesas_clear_bit(i3c->regs, NTST, NTST_TEF | NTST_TABTF);
> +
> +             xfer->ret = ret;
> +             complete(&xfer->comp);
> +
> +             xfer = list_first_entry_or_null(&i3c->xferqueue.list,
> +                                             struct renesas_i3c_xfer, node);
> +             if (xfer)
> +                     list_del_init(&xfer->node);
> +
> +             i3c->xferqueue.cur = xfer;
> +     }
> +
> +     return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t renesas_i3c_tend_isr(int irq, void *data)
> +{
> +     struct renesas_i3c *i3c = data;
> +     struct renesas_i3c_xfer *xfer;
> +     struct renesas_i3c_cmd *cmd;
> +
> +     scoped_guard(spinlock, &i3c->xferqueue.lock) {
> +             xfer = i3c->xferqueue.cur;
> +             cmd = xfer->cmds;
> +
> +             if (xfer->is_i2c_xfer) {
> +                     if (renesas_readl(i3c->regs, BST) & BST_NACKDF) {
> +                             /* We got a NACKIE */
> +                             renesas_readl(i3c->regs, NTDTBP0); /* dummy 
> read */
> +                             renesas_clear_bit(i3c->regs, BST, BST_NACKDF);
> +                             cmd->err = -ENXIO;
> +                     } else if (cmd->i2c_bytes_left) {
> +                             renesas_set_bit(i3c->regs, NTIE, NTIE_TDBEIE0);
> +                             return IRQ_NONE;
> +                     }
> +
> +                     if (cmd->i2c_is_last || cmd->err) {
> +                             renesas_clear_bit(i3c->regs, BIE, BIE_TENDIE);
> +                             renesas_set_bit(i3c->regs, BIE, BIE_SPCNDDIE);
> +                             renesas_set_bit(i3c->regs, CNDCTL, 
> CNDCTL_SPCND);
> +                     } else {
> +                             /* Transfer is complete, but do not send STOP */
> +                             renesas_clear_bit(i3c->regs, NTSTE, 
> NTSTE_TDBEE0);
> +                             renesas_clear_bit(i3c->regs, BIE, BIE_TENDIE);
> +                             xfer->ret = 0;
> +                             complete(&xfer->comp);
> +                     }
> +             }
> +
> +             /* Clear the Transmit Buffer Empty status flag. */
> +             renesas_clear_bit(i3c->regs, BST, BST_TENDF);
> +     }
> +
> +     return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t renesas_i3c_rx_isr(int irq, void *data)
> +{
> +     struct renesas_i3c *i3c = data;
> +     struct renesas_i3c_xfer *xfer;
> +     struct renesas_i3c_cmd *cmd;
> +     int read_bytes;
> +
> +     /* If resp_isr already read the data and updated 'xfer', we can just 
> leave */
> +     if (!(renesas_readl(i3c->regs, NTIE) & NTIE_RDBFIE0))
> +             return IRQ_NONE;
> +
> +     scoped_guard(spinlock, &i3c->xferqueue.lock) {
> +             xfer = i3c->xferqueue.cur;
> +             cmd = xfer->cmds;
> +
> +             if (xfer->is_i2c_xfer) {
> +                     if (!cmd->i2c_bytes_left)
> +                             return IRQ_NONE;
> +
> +                     if (cmd->i2c_bytes_left == I2C_INIT_MSG) {
> +                             cmd->i2c_bytes_left = cmd->msg->len;
> +                             renesas_set_bit(i3c->regs, SCSTRCTL, 
> SCSTRCTL_RWE);
> +                             renesas_readl(i3c->regs, NTDTBP0); /* dummy 
> read */
> +                             if (cmd->i2c_bytes_left == 1)
> +                                     renesas_writel(i3c->regs, ACKCTL, 
> ACKCTL_ACKT | ACKCTL_ACKTWP);
> +                             return IRQ_HANDLED;
> +                     }
> +
> +                     if (cmd->i2c_bytes_left == 1) {
> +                             /* STOP must come before we set ACKCTL! */
> +                             if (cmd->i2c_is_last) {
> +                                     renesas_set_bit(i3c->regs, BIE, 
> BIE_SPCNDDIE);
> +                                     renesas_clear_bit(i3c->regs, BST, 
> BST_SPCNDDF);
> +                                     renesas_set_bit(i3c->regs, CNDCTL, 
> CNDCTL_SPCND);
> +                             }
> +                             renesas_writel(i3c->regs, ACKCTL, ACKCTL_ACKT | 
> ACKCTL_ACKTWP);
> +                     } else {
> +                             renesas_writel(i3c->regs, ACKCTL, 
> ACKCTL_ACKTWP);
> +                     }
> +
> +                     /* Reading acks the RIE interrupt */
> +                     *cmd->i2c_buf = renesas_readl(i3c->regs, NTDTBP0);
> +                     cmd->i2c_buf++;
> +                     cmd->i2c_bytes_left--;
> +             } else {
> +                     read_bytes = NDBSTLV0_RDBLV(renesas_readl(i3c->regs, 
> NDBSTLV0)) * sizeof(u32);
> +                     i3c_readl_fifo(i3c->regs + NTDTBP0, cmd->rx_buf, 
> read_bytes);
> +                     cmd->rx_count = read_bytes;
> +             }
> +
> +             /* Clear the Read Buffer Full status flag. */
> +             renesas_clear_bit(i3c->regs, NTST, NTST_RDBFF0);
> +     }
> +
> +     return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t renesas_i3c_stop_isr(int irq, void *data)
> +{
> +     struct renesas_i3c *i3c = data;
> +     struct renesas_i3c_xfer *xfer;
> +
> +     scoped_guard(spinlock, &i3c->xferqueue.lock) {
> +             xfer = i3c->xferqueue.cur;
> +
> +             /* read back registers to confirm writes have fully propagated 
> */
> +             renesas_writel(i3c->regs, BST, 0);
> +             renesas_readl(i3c->regs, BST);
> +             renesas_writel(i3c->regs, BIE, 0);
> +             renesas_clear_bit(i3c->regs, NTST, NTST_TDBEF0 | NTST_RDBFF0);
> +             renesas_clear_bit(i3c->regs, SCSTRCTL, SCSTRCTL_RWE);
> +
> +             xfer->ret = 0;
> +             complete(&xfer->comp);
> +     }
> +
> +     return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t renesas_i3c_start_isr(int irq, void *data)
> +{
> +     struct renesas_i3c *i3c = data;
> +     struct renesas_i3c_xfer *xfer;
> +     struct renesas_i3c_cmd *cmd;
> +     u8 val;
> +
> +     scoped_guard(spinlock, &i3c->xferqueue.lock) {
> +             xfer = i3c->xferqueue.cur;
> +             cmd = xfer->cmds;
> +
> +             if (xfer->is_i2c_xfer) {
> +                     if (!cmd->i2c_bytes_left)
> +                             return IRQ_NONE;
> +
> +                     if (cmd->i2c_bytes_left == I2C_INIT_MSG) {
> +                             if (cmd->msg->flags & I2C_M_RD) {
> +                                     /* On read, switch over to receive 
> interrupt */
> +                                     renesas_clear_bit(i3c->regs, NTIE, 
> NTIE_TDBEIE0);
> +                                     renesas_set_bit(i3c->regs, NTIE, 
> NTIE_RDBFIE0);
> +                             } else {
> +                                     /* On write, initialize length */
> +                                     cmd->i2c_bytes_left = cmd->msg->len;
> +                             }
> +
> +                             val = i2c_8bit_addr_from_msg(cmd->msg);
> +                             renesas_writel(i3c->regs, NTDTBP0, val);
> +                     }
> +             }
> +
> +             renesas_clear_bit(i3c->regs, BIE, BIE_STCNDDIE);
> +             renesas_clear_bit(i3c->regs, BST, BST_STCNDDF);
> +     }
> +
> +     return IRQ_HANDLED;
> +}
> +
> +static const struct i3c_master_controller_ops renesas_i3c_ops = {
> +     .bus_init = renesas_i3c_bus_init,
> +     .bus_cleanup = renesas_i3c_bus_cleanup,
> +     .attach_i3c_dev = renesas_i3c_attach_i3c_dev,
> +     .reattach_i3c_dev = renesas_i3c_reattach_i3c_dev,
> +     .detach_i3c_dev = renesas_i3c_detach_i3c_dev,
> +     .do_daa = renesas_i3c_daa,
> +     .supports_ccc_cmd = renesas_i3c_supports_ccc_cmd,
> +     .send_ccc_cmd = renesas_i3c_send_ccc_cmd,
> +     .priv_xfers = renesas_i3c_priv_xfers,
> +     .attach_i2c_dev = renesas_i3c_attach_i2c_dev,
> +     .detach_i2c_dev = renesas_i3c_detach_i2c_dev,
> +     .i2c_xfers = renesas_i3c_i2c_xfers,
> +};
> +
> +static const struct renesas_i3c_irq_desc renesas_i3c_irqs[] = {
> +     { .name = "resp", .isr = renesas_i3c_resp_isr, .desc = "i3c-resp" },
> +     { .name = "rx", .isr = renesas_i3c_rx_isr, .desc = "i3c-rx" },
> +     { .name = "tx", .isr = renesas_i3c_tx_isr, .desc = "i3c-tx" },
> +     { .name = "st", .isr = renesas_i3c_start_isr, .desc = "i3c-start" },
> +     { .name = "sp", .isr = renesas_i3c_stop_isr, .desc = "i3c-stop" },
> +     { .name = "tend", .isr = renesas_i3c_tend_isr, .desc = "i3c-tend" },
> +     { .name = "nack", .isr = renesas_i3c_tend_isr, .desc = "i3c-nack" },
> +};
> +
> +static int renesas_i3c_probe(struct platform_device *pdev)
> +{
> +     struct renesas_i3c *i3c;
> +     struct reset_control *reset;
> +     struct clk *clk;
> +     const struct renesas_i3c_config *config = 
> of_device_get_match_data(&pdev->dev);
> +     int ret, i;
> +
> +     if (!config)
> +             return -ENODATA;
> +
> +     i3c = devm_kzalloc(&pdev->dev, sizeof(*i3c), GFP_KERNEL);
> +     if (!i3c)
> +             return -ENOMEM;
> +
> +     i3c->regs = devm_platform_ioremap_resource(pdev, 0);
> +     if (IS_ERR(i3c->regs))
> +             return PTR_ERR(i3c->regs);
> +
> +     clk = devm_clk_get_enabled(&pdev->dev, "pclk");
> +     if (IS_ERR(clk))
> +             return PTR_ERR(clk);
> +
> +     if (config->has_pclkrw) {
> +             clk = devm_clk_get_enabled(&pdev->dev, "pclkrw");
> +             if (IS_ERR(clk))
> +                     return PTR_ERR(clk);
> +     }
> +
> +     i3c->tclk = devm_clk_get_enabled(&pdev->dev, "tclk");
> +     if (IS_ERR(i3c->tclk))
> +             return PTR_ERR(i3c->tclk);
> +
> +     reset = 
> devm_reset_control_get_optional_exclusive_deasserted(&pdev->dev, "tresetn");
> +     if (IS_ERR(reset))
> +             return dev_err_probe(&pdev->dev, PTR_ERR(reset),
> +                                  "Error: missing tresetn ctrl\n");
> +
> +     reset = 
> devm_reset_control_get_optional_exclusive_deasserted(&pdev->dev, "presetn");
> +     if (IS_ERR(reset))
> +             return dev_err_probe(&pdev->dev, PTR_ERR(reset),
> +                                  "Error: missing presetn ctrl\n");
> +
> +     spin_lock_init(&i3c->xferqueue.lock);
> +     INIT_LIST_HEAD(&i3c->xferqueue.list);
> +
> +     ret = renesas_i3c_reset(i3c);
> +     if (ret)
> +             return ret;
> +
> +     for (i = 0; i < ARRAY_SIZE(renesas_i3c_irqs); i++) {
> +             ret = platform_get_irq_byname(pdev, renesas_i3c_irqs[i].name);
> +             if (ret < 0)
> +                     return ret;
> +
> +             ret = devm_request_irq(&pdev->dev, ret, renesas_i3c_irqs[i].isr,
> +                                    0, renesas_i3c_irqs[i].desc, i3c);
> +             if (ret)
> +                     return ret;
> +     }
> +
> +     platform_set_drvdata(pdev, i3c);
> +
> +     i3c->maxdevs = RENESAS_I3C_MAX_DEVS;
> +     i3c->free_pos = GENMASK(i3c->maxdevs - 1, 0);
> +
> +     return i3c_master_register(&i3c->base, &pdev->dev, &renesas_i3c_ops, 
> false);
> +}
> +
> +static void renesas_i3c_remove(struct platform_device *pdev)
> +{
> +     struct renesas_i3c *i3c = platform_get_drvdata(pdev);
> +
> +     i3c_master_unregister(&i3c->base);
> +}
> +
> +static const struct renesas_i3c_config empty_i3c_config = {
> +};
> +
> +static const struct renesas_i3c_config r9a09g047_i3c_config = {
> +     .has_pclkrw = 1,
> +};
> +
> +static const struct of_device_id renesas_i3c_of_ids[] = {
> +     { .compatible = "renesas,r9a08g045-i3c", .data = &empty_i3c_config },
> +     { .compatible = "renesas,r9a09g047-i3c", .data = &r9a09g047_i3c_config 
> },
> +     { /* sentinel */ },
> +};
> +MODULE_DEVICE_TABLE(of, renesas_i3c_of_ids);
> +
> +static struct platform_driver renesas_i3c = {
> +     .probe = renesas_i3c_probe,
> +     .remove = renesas_i3c_remove,
> +     .driver = {
> +             .name = "renesas-i3c",
> +             .of_match_table = renesas_i3c_of_ids,
> +     },
> +};
> +module_platform_driver(renesas_i3c);
> +
> +MODULE_AUTHOR("Wolfram Sang <wsa+rene...@sang-engineering.com>");
> +MODULE_AUTHOR("Renesas BSP teams");
> +MODULE_DESCRIPTION("Renesas I3C controller driver");
> +MODULE_LICENSE("GPL");
> --
> 2.47.2
>

Reply via email to