mirror of https://github.com/torvalds/linux.git
1405 lines
37 KiB
C
1405 lines
37 KiB
C
// 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 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+renesas@sang-engineering.com>");
|
|
MODULE_AUTHOR("Renesas BSP teams");
|
|
MODULE_DESCRIPTION("Renesas I3C controller driver");
|
|
MODULE_LICENSE("GPL");
|