| // 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, msecs_to_jiffies(1000)); |
| 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 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+renesas@sang-engineering.com>"); |
| MODULE_AUTHOR("Renesas BSP teams"); |
| MODULE_DESCRIPTION("Renesas I3C controller driver"); |
| MODULE_LICENSE("GPL"); |