summaryrefslogtreecommitdiff
path: root/drivers/i3c/master/dw-i3c-master.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/i3c/master/dw-i3c-master.c')
-rw-r--r--drivers/i3c/master/dw-i3c-master.c848
1 files changed, 724 insertions, 124 deletions
diff --git a/drivers/i3c/master/dw-i3c-master.c b/drivers/i3c/master/dw-i3c-master.c
index 51a8608203de..276592a8222e 100644
--- a/drivers/i3c/master/dw-i3c-master.c
+++ b/drivers/i3c/master/dw-i3c-master.c
@@ -17,10 +17,15 @@
#include <linux/list.h>
#include <linux/module.h>
#include <linux/of.h>
+#include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
#include <linux/reset.h>
#include <linux/slab.h>
+#include "../internals.h"
+#include "dw-i3c-master.h"
+
#define DEVICE_CTRL 0x0
#define DEV_CTRL_ENABLE BIT(31)
#define DEV_CTRL_RESUME BIT(30)
@@ -74,7 +79,22 @@
#define RX_TX_DATA_PORT 0x14
#define IBI_QUEUE_STATUS 0x18
+#define IBI_QUEUE_STATUS_IBI_ID(x) (((x) & GENMASK(15, 8)) >> 8)
+#define IBI_QUEUE_STATUS_DATA_LEN(x) ((x) & GENMASK(7, 0))
+#define IBI_QUEUE_IBI_ADDR(x) (IBI_QUEUE_STATUS_IBI_ID(x) >> 1)
+#define IBI_QUEUE_IBI_RNW(x) (IBI_QUEUE_STATUS_IBI_ID(x) & BIT(0))
+#define IBI_TYPE_MR(x) \
+ ((IBI_QUEUE_IBI_ADDR(x) != I3C_HOT_JOIN_ADDR) && !IBI_QUEUE_IBI_RNW(x))
+#define IBI_TYPE_HJ(x) \
+ ((IBI_QUEUE_IBI_ADDR(x) == I3C_HOT_JOIN_ADDR) && !IBI_QUEUE_IBI_RNW(x))
+#define IBI_TYPE_SIRQ(x) \
+ ((IBI_QUEUE_IBI_ADDR(x) != I3C_HOT_JOIN_ADDR) && IBI_QUEUE_IBI_RNW(x))
+
#define QUEUE_THLD_CTRL 0x1c
+#define QUEUE_THLD_CTRL_IBI_STAT_MASK GENMASK(31, 24)
+#define QUEUE_THLD_CTRL_IBI_STAT(x) (((x) - 1) << 24)
+#define QUEUE_THLD_CTRL_IBI_DATA_MASK GENMASK(20, 16)
+#define QUEUE_THLD_CTRL_IBI_DATA(x) ((x) << 16)
#define QUEUE_THLD_CTRL_RESP_BUF_MASK GENMASK(15, 8)
#define QUEUE_THLD_CTRL_RESP_BUF(x) (((x) - 1) << 8)
@@ -184,13 +204,13 @@
#define EXTENDED_CAPABILITY 0xe8
#define SLAVE_CONFIG 0xec
+#define DEV_ADDR_TABLE_IBI_MDB BIT(12)
+#define DEV_ADDR_TABLE_SIR_REJECT BIT(13)
#define DEV_ADDR_TABLE_LEGACY_I2C_DEV BIT(31)
#define DEV_ADDR_TABLE_DYNAMIC_ADDR(x) (((x) << 16) & GENMASK(23, 16))
#define DEV_ADDR_TABLE_STATIC_ADDR(x) ((x) & GENMASK(6, 0))
#define DEV_ADDR_TABLE_LOC(start, idx) ((start) + ((idx) << 2))
-#define MAX_DEVS 32
-
#define I3C_BUS_SDR1_SCL_RATE 8000000
#define I3C_BUS_SDR2_SCL_RATE 6000000
#define I3C_BUS_SDR3_SCL_RATE 4000000
@@ -200,11 +220,15 @@
#define I3C_BUS_THIGH_MAX_NS 41
#define XFER_TIMEOUT (msecs_to_jiffies(1000))
+#define RPM_AUTOSUSPEND_TIMEOUT 1000 /* ms */
-struct dw_i3c_master_caps {
- u8 cmdfifodepth;
- u8 datafifodepth;
-};
+/* Timing values to configure 12.5MHz frequency */
+#define AMD_I3C_OD_TIMING 0x4C007C
+#define AMD_I3C_PP_TIMING 0x8001A
+
+/* List of quirks */
+#define AMD_I3C_OD_PP_TIMING BIT(1)
+#define DW_I3C_DISABLE_RUNTIME_PM_QUIRK BIT(2)
struct dw_i3c_cmd {
u32 cmd_lo;
@@ -221,39 +245,17 @@ struct dw_i3c_xfer {
struct completion comp;
int ret;
unsigned int ncmds;
- struct dw_i3c_cmd cmds[];
-};
-
-struct dw_i3c_master {
- struct i3c_master_controller base;
- u16 maxdevs;
- u16 datstartaddr;
- u32 free_pos;
- struct {
- struct list_head list;
- struct dw_i3c_xfer *cur;
- spinlock_t lock;
- } xferqueue;
- struct dw_i3c_master_caps caps;
- void __iomem *regs;
- struct reset_control *core_rst;
- struct clk *core_clk;
- char version[5];
- char type[5];
- u8 addrs[MAX_DEVS];
+ struct dw_i3c_cmd cmds[] __counted_by(ncmds);
};
struct dw_i3c_i2c_dev_data {
u8 index;
+ struct i3c_generic_ibi_pool *ibi_pool;
};
-static u8 even_parity(u8 p)
-{
- p ^= p >> 4;
- p &= 0xf;
-
- return (0x9669 >> p) & 1;
-}
+struct dw_i3c_drvdata {
+ u32 flags;
+};
static bool dw_i3c_master_supports_ccc_cmd(struct i3c_master_controller *m,
const struct i3c_ccc_cmd *cmd)
@@ -306,7 +308,14 @@ static void dw_i3c_master_disable(struct dw_i3c_master *master)
static void dw_i3c_master_enable(struct dw_i3c_master *master)
{
- writel(readl(master->regs + DEVICE_CTRL) | DEV_CTRL_ENABLE,
+ u32 dev_ctrl;
+
+ dev_ctrl = readl(master->regs + DEVICE_CTRL);
+ /* For now don't support Hot-Join */
+ dev_ctrl |= DEV_CTRL_HOT_JOIN_NACK;
+ if (master->i2c_slv_prsnt)
+ dev_ctrl |= DEV_CTRL_I2C_SLAVE_PRESENT;
+ writel(dev_ctrl | DEV_CTRL_ENABLE,
master->regs + DEVICE_CTRL);
}
@@ -315,7 +324,7 @@ static int dw_i3c_master_get_addr_pos(struct dw_i3c_master *master, u8 addr)
int pos;
for (pos = 0; pos < master->maxdevs; pos++) {
- if (addr == master->addrs[pos])
+ if (addr == master->devs[pos].addr)
return pos;
}
@@ -333,25 +342,19 @@ static int dw_i3c_master_get_free_pos(struct dw_i3c_master *master)
static void dw_i3c_master_wr_tx_fifo(struct dw_i3c_master *master,
const u8 *bytes, int nbytes)
{
- writesl(master->regs + RX_TX_DATA_PORT, bytes, nbytes / 4);
- if (nbytes & 3) {
- u32 tmp = 0;
-
- memcpy(&tmp, bytes + (nbytes & ~3), nbytes & 3);
- writesl(master->regs + RX_TX_DATA_PORT, &tmp, 1);
- }
+ i3c_writel_fifo(master->regs + RX_TX_DATA_PORT, bytes, nbytes);
}
static void dw_i3c_master_read_rx_fifo(struct dw_i3c_master *master,
u8 *bytes, int nbytes)
{
- readsl(master->regs + RX_TX_DATA_PORT, bytes, nbytes / 4);
- if (nbytes & 3) {
- u32 tmp;
+ i3c_readl_fifo(master->regs + RX_TX_DATA_PORT, bytes, nbytes);
+}
- readsl(master->regs + RX_TX_DATA_PORT, &tmp, 1);
- memcpy(bytes + (nbytes & ~3), &tmp, nbytes & 3);
- }
+static void dw_i3c_master_read_ibi_fifo(struct dw_i3c_master *master,
+ u8 *bytes, int nbytes)
+{
+ i3c_readl_fifo(master->regs + IBI_QUEUE_STATUS, bytes, nbytes);
}
static struct dw_i3c_xfer *
@@ -515,6 +518,32 @@ static void dw_i3c_master_end_xfer_locked(struct dw_i3c_master *master, u32 isr)
dw_i3c_master_start_xfer_locked(master);
}
+static void dw_i3c_master_set_intr_regs(struct dw_i3c_master *master)
+{
+ u32 thld_ctrl;
+
+ thld_ctrl = readl(master->regs + QUEUE_THLD_CTRL);
+ thld_ctrl &= ~(QUEUE_THLD_CTRL_RESP_BUF_MASK |
+ QUEUE_THLD_CTRL_IBI_STAT_MASK |
+ QUEUE_THLD_CTRL_IBI_DATA_MASK);
+ thld_ctrl |= QUEUE_THLD_CTRL_IBI_STAT(1) |
+ QUEUE_THLD_CTRL_IBI_DATA(31);
+ writel(thld_ctrl, master->regs + QUEUE_THLD_CTRL);
+
+ thld_ctrl = readl(master->regs + DATA_BUFFER_THLD_CTRL);
+ thld_ctrl &= ~DATA_BUFFER_THLD_CTRL_RX_BUF;
+ writel(thld_ctrl, master->regs + DATA_BUFFER_THLD_CTRL);
+
+ writel(INTR_ALL, master->regs + INTR_STATUS);
+ writel(INTR_MASTER_MASK, master->regs + INTR_STATUS_EN);
+ writel(INTR_MASTER_MASK, master->regs + INTR_SIGNAL_EN);
+
+ master->sir_rej_mask = IBI_REQ_REJECT_ALL;
+ writel(master->sir_rej_mask, master->regs + IBI_SIR_REQ_REJECT);
+
+ writel(IBI_REQ_REJECT_ALL, master->regs + IBI_MR_REQ_REJECT);
+}
+
static int dw_i3c_clk_cfg(struct dw_i3c_master *master)
{
unsigned long core_rate, core_period;
@@ -531,19 +560,28 @@ static int dw_i3c_clk_cfg(struct dw_i3c_master *master)
if (hcnt < SCL_I3C_TIMING_CNT_MIN)
hcnt = SCL_I3C_TIMING_CNT_MIN;
- lcnt = DIV_ROUND_UP(core_rate, I3C_BUS_TYP_I3C_SCL_RATE) - hcnt;
+ lcnt = DIV_ROUND_UP(core_rate, master->base.bus.scl_rate.i3c) - hcnt;
if (lcnt < SCL_I3C_TIMING_CNT_MIN)
lcnt = SCL_I3C_TIMING_CNT_MIN;
scl_timing = SCL_I3C_TIMING_HCNT(hcnt) | SCL_I3C_TIMING_LCNT(lcnt);
writel(scl_timing, master->regs + SCL_I3C_PP_TIMING);
+ master->i3c_pp_timing = scl_timing;
- if (!(readl(master->regs + DEVICE_CTRL) & DEV_CTRL_I2C_SLAVE_PRESENT))
+ /*
+ * In pure i3c mode, MST_FREE represents tCAS. In shared mode, this
+ * will be set up by dw_i2c_clk_cfg as tLOW.
+ */
+ if (master->base.bus.mode == I3C_BUS_MODE_PURE) {
writel(BUS_I3C_MST_FREE(lcnt), master->regs + BUS_FREE_TIMING);
+ master->bus_free_timing = BUS_I3C_MST_FREE(lcnt);
+ }
- lcnt = DIV_ROUND_UP(I3C_BUS_TLOW_OD_MIN_NS, core_period);
+ lcnt = max_t(u8,
+ DIV_ROUND_UP(I3C_BUS_TLOW_OD_MIN_NS, core_period), lcnt);
scl_timing = SCL_I3C_TIMING_HCNT(hcnt) | SCL_I3C_TIMING_LCNT(lcnt);
writel(scl_timing, master->regs + SCL_I3C_OD_TIMING);
+ master->i3c_od_timing = scl_timing;
lcnt = DIV_ROUND_UP(core_rate, I3C_BUS_SDR1_SCL_RATE) - hcnt;
scl_timing = SCL_EXT_LCNT_1(lcnt);
@@ -554,6 +592,7 @@ static int dw_i3c_clk_cfg(struct dw_i3c_master *master)
lcnt = DIV_ROUND_UP(core_rate, I3C_BUS_SDR4_SCL_RATE) - hcnt;
scl_timing |= SCL_EXT_LCNT_4(lcnt);
writel(scl_timing, master->regs + SCL_EXT_LCNT_TIMING);
+ master->ext_lcnt_timing = scl_timing;
return 0;
}
@@ -571,20 +610,25 @@ static int dw_i2c_clk_cfg(struct dw_i3c_master *master)
core_period = DIV_ROUND_UP(1000000000, core_rate);
lcnt = DIV_ROUND_UP(I3C_BUS_I2C_FMP_TLOW_MIN_NS, core_period);
- hcnt = DIV_ROUND_UP(core_rate, I3C_BUS_I2C_FM_PLUS_SCL_RATE) - lcnt;
+ hcnt = DIV_ROUND_UP(core_rate, I3C_BUS_I2C_FM_PLUS_SCL_MAX_RATE) - lcnt;
scl_timing = SCL_I2C_FMP_TIMING_HCNT(hcnt) |
SCL_I2C_FMP_TIMING_LCNT(lcnt);
writel(scl_timing, master->regs + SCL_I2C_FMP_TIMING);
+ master->i2c_fmp_timing = scl_timing;
lcnt = DIV_ROUND_UP(I3C_BUS_I2C_FM_TLOW_MIN_NS, core_period);
- hcnt = DIV_ROUND_UP(core_rate, I3C_BUS_I2C_FM_SCL_RATE) - lcnt;
+ hcnt = DIV_ROUND_UP(core_rate, I3C_BUS_I2C_FM_SCL_MAX_RATE) - lcnt;
scl_timing = SCL_I2C_FM_TIMING_HCNT(hcnt) |
SCL_I2C_FM_TIMING_LCNT(lcnt);
writel(scl_timing, master->regs + SCL_I2C_FM_TIMING);
+ master->i2c_fm_timing = scl_timing;
writel(BUS_I3C_MST_FREE(lcnt), master->regs + BUS_FREE_TIMING);
+ master->bus_free_timing = BUS_I3C_MST_FREE(lcnt);
+
writel(readl(master->regs + DEVICE_CTRL) | DEV_CTRL_I2C_SLAVE_PRESENT,
master->regs + DEVICE_CTRL);
+ master->i2c_slv_prsnt = true;
return 0;
}
@@ -594,61 +638,57 @@ static int dw_i3c_master_bus_init(struct i3c_master_controller *m)
struct dw_i3c_master *master = to_dw_i3c_master(m);
struct i3c_bus *bus = i3c_master_get_bus(m);
struct i3c_device_info info = { };
- u32 thld_ctrl;
int ret;
+ ret = pm_runtime_resume_and_get(master->dev);
+ if (ret < 0) {
+ dev_err(master->dev,
+ "<%s> cannot resume i3c bus master, err: %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = master->platform_ops->init(master);
+ if (ret)
+ goto rpm_out;
+
switch (bus->mode) {
case I3C_BUS_MODE_MIXED_FAST:
case I3C_BUS_MODE_MIXED_LIMITED:
ret = dw_i2c_clk_cfg(master);
if (ret)
- return ret;
+ goto rpm_out;
fallthrough;
case I3C_BUS_MODE_PURE:
ret = dw_i3c_clk_cfg(master);
if (ret)
- return ret;
+ goto rpm_out;
break;
default:
- return -EINVAL;
+ ret = -EINVAL;
+ goto rpm_out;
}
- thld_ctrl = readl(master->regs + QUEUE_THLD_CTRL);
- thld_ctrl &= ~QUEUE_THLD_CTRL_RESP_BUF_MASK;
- writel(thld_ctrl, master->regs + QUEUE_THLD_CTRL);
-
- thld_ctrl = readl(master->regs + DATA_BUFFER_THLD_CTRL);
- thld_ctrl &= ~DATA_BUFFER_THLD_CTRL_RX_BUF;
- writel(thld_ctrl, master->regs + DATA_BUFFER_THLD_CTRL);
-
- writel(INTR_ALL, master->regs + INTR_STATUS);
- writel(INTR_MASTER_MASK, master->regs + INTR_STATUS_EN);
- writel(INTR_MASTER_MASK, master->regs + INTR_SIGNAL_EN);
-
ret = i3c_master_get_free_addr(m, 0);
if (ret < 0)
- return ret;
+ goto rpm_out;
writel(DEV_ADDR_DYNAMIC_ADDR_VALID | DEV_ADDR_DYNAMIC(ret),
master->regs + DEVICE_ADDR);
-
+ master->dev_addr = ret;
memset(&info, 0, sizeof(info));
info.dyn_addr = ret;
ret = i3c_master_set_info(&master->base, &info);
if (ret)
- return ret;
-
- writel(IBI_REQ_REJECT_ALL, master->regs + IBI_SIR_REQ_REJECT);
- writel(IBI_REQ_REJECT_ALL, master->regs + IBI_MR_REQ_REJECT);
-
- /* For now don't support Hot-Join */
- writel(readl(master->regs + DEVICE_CTRL) | DEV_CTRL_HOT_JOIN_NACK,
- master->regs + DEVICE_CTRL);
+ goto rpm_out;
+ dw_i3c_master_set_intr_regs(master);
dw_i3c_master_enable(master);
- return 0;
+rpm_out:
+ pm_runtime_put_autosuspend(master->dev);
+ return ret;
}
static void dw_i3c_master_bus_cleanup(struct i3c_master_controller *m)
@@ -741,6 +781,12 @@ static int dw_i3c_ccc_get(struct dw_i3c_master *master, struct i3c_ccc_cmd *ccc)
return ret;
}
+static void amd_configure_od_pp_quirk(struct dw_i3c_master *master)
+{
+ master->i3c_od_timing = AMD_I3C_OD_TIMING;
+ master->i3c_pp_timing = AMD_I3C_PP_TIMING;
+}
+
static int dw_i3c_master_send_ccc_cmd(struct i3c_master_controller *m,
struct i3c_ccc_cmd *ccc)
{
@@ -750,11 +796,27 @@ static int dw_i3c_master_send_ccc_cmd(struct i3c_master_controller *m,
if (ccc->id == I3C_CCC_ENTDAA)
return -EINVAL;
+ /* AMD platform specific OD and PP timings */
+ if (master->quirks & AMD_I3C_OD_PP_TIMING) {
+ amd_configure_od_pp_quirk(master);
+ writel(master->i3c_pp_timing, master->regs + SCL_I3C_PP_TIMING);
+ writel(master->i3c_od_timing, master->regs + SCL_I3C_OD_TIMING);
+ }
+
+ ret = pm_runtime_resume_and_get(master->dev);
+ if (ret < 0) {
+ dev_err(master->dev,
+ "<%s> cannot resume i3c bus master, err: %d\n",
+ __func__, ret);
+ return ret;
+ }
+
if (ccc->rnw)
ret = dw_i3c_ccc_get(master, ccc);
else
ret = dw_i3c_ccc_set(master, ccc);
+ pm_runtime_put_autosuspend(master->dev);
return ret;
}
@@ -764,9 +826,17 @@ static int dw_i3c_master_daa(struct i3c_master_controller *m)
struct dw_i3c_xfer *xfer;
struct dw_i3c_cmd *cmd;
u32 olddevs, newdevs;
- u8 p, last_addr = 0;
+ u8 last_addr = 0;
int ret, pos;
+ ret = pm_runtime_resume_and_get(master->dev);
+ if (ret < 0) {
+ dev_err(master->dev,
+ "<%s> cannot resume i3c bus master, err: %d\n",
+ __func__, ret);
+ return ret;
+ }
+
olddevs = ~(master->free_pos);
/* Prepare DAT before launching DAA. */
@@ -775,27 +845,34 @@ static int dw_i3c_master_daa(struct i3c_master_controller *m)
continue;
ret = i3c_master_get_free_addr(m, last_addr + 1);
- if (ret < 0)
- return -ENOSPC;
+ if (ret < 0) {
+ ret = -ENOSPC;
+ goto rpm_out;
+ }
- master->addrs[pos] = ret;
- p = even_parity(ret);
+ master->devs[pos].addr = ret;
last_addr = ret;
- ret |= (p << 7);
+
+ ret |= parity8(ret) ? 0 : BIT(7);
writel(DEV_ADDR_TABLE_DYNAMIC_ADDR(ret),
master->regs +
DEV_ADDR_TABLE_LOC(master->datstartaddr, pos));
+
+ ret = 0;
}
xfer = dw_i3c_master_alloc_xfer(master, 1);
- if (!xfer)
- return -ENOMEM;
+ if (!xfer) {
+ ret = -ENOMEM;
+ goto rpm_out;
+ }
pos = dw_i3c_master_get_free_pos(master);
if (pos < 0) {
dw_i3c_master_free_xfer(xfer);
- return pos;
+ ret = pos;
+ goto rpm_out;
}
cmd = &xfer->cmds[0];
cmd->cmd_hi = 0x1;
@@ -815,12 +892,14 @@ static int dw_i3c_master_daa(struct i3c_master_controller *m)
for (pos = 0; pos < master->maxdevs; pos++) {
if (newdevs & BIT(pos))
- i3c_master_add_i3c_dev_locked(m, master->addrs[pos]);
+ i3c_master_add_i3c_dev_locked(m, master->devs[pos].addr);
}
dw_i3c_master_free_xfer(xfer);
- return 0;
+rpm_out:
+ pm_runtime_put_autosuspend(master->dev);
+ return ret;
}
static int dw_i3c_master_priv_xfers(struct i3c_dev_desc *dev,
@@ -838,7 +917,7 @@ static int dw_i3c_master_priv_xfers(struct i3c_dev_desc *dev,
return 0;
if (i3c_nxfers > master->caps.cmdfifodepth)
- return -ENOTSUPP;
+ return -EOPNOTSUPP;
for (i = 0; i < i3c_nxfers; i++) {
if (i3c_xfers[i].rnw)
@@ -849,12 +928,20 @@ static int dw_i3c_master_priv_xfers(struct i3c_dev_desc *dev,
if (ntxwords > master->caps.datafifodepth ||
nrxwords > master->caps.datafifodepth)
- return -ENOTSUPP;
+ return -EOPNOTSUPP;
xfer = dw_i3c_master_alloc_xfer(master, i3c_nxfers);
if (!xfer)
return -ENOMEM;
+ ret = pm_runtime_resume_and_get(master->dev);
+ if (ret < 0) {
+ dev_err(master->dev,
+ "<%s> cannot resume i3c bus master, err: %d\n",
+ __func__, ret);
+ return ret;
+ }
+
for (i = 0; i < i3c_nxfers; i++) {
struct dw_i3c_cmd *cmd = &xfer->cmds[i];
@@ -886,9 +973,17 @@ static int dw_i3c_master_priv_xfers(struct i3c_dev_desc *dev,
if (!wait_for_completion_timeout(&xfer->comp, XFER_TIMEOUT))
dw_i3c_master_dequeue_xfer(master, xfer);
+ for (i = 0; i < i3c_nxfers; i++) {
+ struct dw_i3c_cmd *cmd = &xfer->cmds[i];
+
+ if (i3c_xfers[i].rnw)
+ i3c_xfers[i].len = cmd->rx_len;
+ }
+
ret = xfer->ret;
dw_i3c_master_free_xfer(xfer);
+ pm_runtime_put_autosuspend(master->dev);
return ret;
}
@@ -907,11 +1002,11 @@ static int dw_i3c_master_reattach_i3c_dev(struct i3c_dev_desc *dev,
master->regs +
DEV_ADDR_TABLE_LOC(master->datstartaddr, data->index));
- master->addrs[data->index] = 0;
+ master->devs[data->index].addr = 0;
master->free_pos |= BIT(data->index);
data->index = pos;
- master->addrs[pos] = dev->info.dyn_addr;
+ master->devs[pos].addr = dev->info.dyn_addr;
master->free_pos &= ~BIT(pos);
}
@@ -919,7 +1014,7 @@ static int dw_i3c_master_reattach_i3c_dev(struct i3c_dev_desc *dev,
master->regs +
DEV_ADDR_TABLE_LOC(master->datstartaddr, data->index));
- master->addrs[data->index] = dev->info.dyn_addr;
+ master->devs[data->index].addr = dev->info.dyn_addr;
return 0;
}
@@ -940,11 +1035,11 @@ static int dw_i3c_master_attach_i3c_dev(struct i3c_dev_desc *dev)
return -ENOMEM;
data->index = pos;
- master->addrs[pos] = dev->info.dyn_addr ? : dev->info.static_addr;
+ master->devs[pos].addr = dev->info.dyn_addr ? : dev->info.static_addr;
master->free_pos &= ~BIT(pos);
i3c_dev_set_master_data(dev, data);
- writel(DEV_ADDR_TABLE_DYNAMIC_ADDR(master->addrs[pos]),
+ writel(DEV_ADDR_TABLE_DYNAMIC_ADDR(master->devs[pos].addr),
master->regs +
DEV_ADDR_TABLE_LOC(master->datstartaddr, data->index));
@@ -962,13 +1057,13 @@ static void dw_i3c_master_detach_i3c_dev(struct i3c_dev_desc *dev)
DEV_ADDR_TABLE_LOC(master->datstartaddr, data->index));
i3c_dev_set_master_data(dev, NULL);
- master->addrs[data->index] = 0;
+ master->devs[data->index].addr = 0;
master->free_pos |= BIT(data->index);
kfree(data);
}
static int dw_i3c_master_i2c_xfers(struct i2c_dev_desc *dev,
- const struct i2c_msg *i2c_xfers,
+ struct i2c_msg *i2c_xfers,
int i2c_nxfers)
{
struct dw_i3c_i2c_dev_data *data = i2c_dev_get_master_data(dev);
@@ -982,7 +1077,7 @@ static int dw_i3c_master_i2c_xfers(struct i2c_dev_desc *dev,
return 0;
if (i2c_nxfers > master->caps.cmdfifodepth)
- return -ENOTSUPP;
+ return -EOPNOTSUPP;
for (i = 0; i < i2c_nxfers; i++) {
if (i2c_xfers[i].flags & I2C_M_RD)
@@ -993,12 +1088,20 @@ static int dw_i3c_master_i2c_xfers(struct i2c_dev_desc *dev,
if (ntxwords > master->caps.datafifodepth ||
nrxwords > master->caps.datafifodepth)
- return -ENOTSUPP;
+ return -EOPNOTSUPP;
xfer = dw_i3c_master_alloc_xfer(master, i2c_nxfers);
if (!xfer)
return -ENOMEM;
+ ret = pm_runtime_resume_and_get(master->dev);
+ if (ret < 0) {
+ dev_err(master->dev,
+ "<%s> cannot resume i3c bus master, err: %d\n",
+ __func__, ret);
+ return ret;
+ }
+
for (i = 0; i < i2c_nxfers; i++) {
struct dw_i3c_cmd *cmd = &xfer->cmds[i];
@@ -1023,12 +1126,13 @@ static int dw_i3c_master_i2c_xfers(struct i2c_dev_desc *dev,
}
dw_i3c_master_enqueue_xfer(master, xfer);
- if (!wait_for_completion_timeout(&xfer->comp, XFER_TIMEOUT))
+ if (!wait_for_completion_timeout(&xfer->comp, m->i2c.timeout))
dw_i3c_master_dequeue_xfer(master, xfer);
ret = xfer->ret;
dw_i3c_master_free_xfer(xfer);
+ pm_runtime_put_autosuspend(master->dev);
return ret;
}
@@ -1048,7 +1152,8 @@ static int dw_i3c_master_attach_i2c_dev(struct i2c_dev_desc *dev)
return -ENOMEM;
data->index = pos;
- master->addrs[pos] = dev->addr;
+ master->devs[pos].addr = dev->addr;
+ master->devs[pos].is_i2c_addr = true;
master->free_pos &= ~BIT(pos);
i2c_dev_set_master_data(dev, data);
@@ -1071,11 +1176,295 @@ static void dw_i3c_master_detach_i2c_dev(struct i2c_dev_desc *dev)
DEV_ADDR_TABLE_LOC(master->datstartaddr, data->index));
i2c_dev_set_master_data(dev, NULL);
- master->addrs[data->index] = 0;
+ master->devs[data->index].addr = 0;
master->free_pos |= BIT(data->index);
kfree(data);
}
+static int dw_i3c_master_request_ibi(struct i3c_dev_desc *dev,
+ const struct i3c_ibi_setup *req)
+{
+ struct dw_i3c_i2c_dev_data *data = i3c_dev_get_master_data(dev);
+ struct i3c_master_controller *m = i3c_dev_get_master(dev);
+ struct dw_i3c_master *master = to_dw_i3c_master(m);
+ unsigned long flags;
+
+ data->ibi_pool = i3c_generic_ibi_alloc_pool(dev, req);
+ if (IS_ERR(data->ibi_pool))
+ return PTR_ERR(data->ibi_pool);
+
+ spin_lock_irqsave(&master->devs_lock, flags);
+ master->devs[data->index].ibi_dev = dev;
+ spin_unlock_irqrestore(&master->devs_lock, flags);
+
+ return 0;
+}
+
+static void dw_i3c_master_free_ibi(struct i3c_dev_desc *dev)
+{
+ struct dw_i3c_i2c_dev_data *data = i3c_dev_get_master_data(dev);
+ struct i3c_master_controller *m = i3c_dev_get_master(dev);
+ struct dw_i3c_master *master = to_dw_i3c_master(m);
+ unsigned long flags;
+
+ spin_lock_irqsave(&master->devs_lock, flags);
+ master->devs[data->index].ibi_dev = NULL;
+ spin_unlock_irqrestore(&master->devs_lock, flags);
+
+ i3c_generic_ibi_free_pool(data->ibi_pool);
+ data->ibi_pool = NULL;
+}
+
+static void dw_i3c_master_enable_sir_signal(struct dw_i3c_master *master, bool enable)
+{
+ u32 reg;
+
+ reg = readl(master->regs + INTR_STATUS_EN);
+ reg &= ~INTR_IBI_THLD_STAT;
+ if (enable)
+ reg |= INTR_IBI_THLD_STAT;
+ writel(reg, master->regs + INTR_STATUS_EN);
+
+ reg = readl(master->regs + INTR_SIGNAL_EN);
+ reg &= ~INTR_IBI_THLD_STAT;
+ if (enable)
+ reg |= INTR_IBI_THLD_STAT;
+ writel(reg, master->regs + INTR_SIGNAL_EN);
+}
+
+static void dw_i3c_master_set_sir_enabled(struct dw_i3c_master *master,
+ struct i3c_dev_desc *dev,
+ u8 idx, bool enable)
+{
+ unsigned long flags;
+ u32 dat_entry, reg;
+ bool global;
+
+ dat_entry = DEV_ADDR_TABLE_LOC(master->datstartaddr, idx);
+
+ spin_lock_irqsave(&master->devs_lock, flags);
+ reg = readl(master->regs + dat_entry);
+ if (enable) {
+ reg &= ~DEV_ADDR_TABLE_SIR_REJECT;
+ if (dev->info.bcr & I3C_BCR_IBI_PAYLOAD)
+ reg |= DEV_ADDR_TABLE_IBI_MDB;
+ } else {
+ reg |= DEV_ADDR_TABLE_SIR_REJECT;
+ }
+ master->platform_ops->set_dat_ibi(master, dev, enable, &reg);
+ writel(reg, master->regs + dat_entry);
+
+ if (enable) {
+ global = (master->sir_rej_mask == IBI_REQ_REJECT_ALL);
+ master->sir_rej_mask &= ~BIT(idx);
+ } else {
+ bool hj_rejected = !!(readl(master->regs + DEVICE_CTRL) & DEV_CTRL_HOT_JOIN_NACK);
+
+ master->sir_rej_mask |= BIT(idx);
+ global = (master->sir_rej_mask == IBI_REQ_REJECT_ALL) && hj_rejected;
+ }
+ writel(master->sir_rej_mask, master->regs + IBI_SIR_REQ_REJECT);
+
+ if (global)
+ dw_i3c_master_enable_sir_signal(master, enable);
+
+
+ spin_unlock_irqrestore(&master->devs_lock, flags);
+}
+
+static int dw_i3c_master_enable_hotjoin(struct i3c_master_controller *m)
+{
+ struct dw_i3c_master *master = to_dw_i3c_master(m);
+ int ret;
+
+ ret = pm_runtime_resume_and_get(master->dev);
+ if (ret < 0) {
+ dev_err(master->dev,
+ "<%s> cannot resume i3c bus master, err: %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ dw_i3c_master_enable_sir_signal(master, true);
+ writel(readl(master->regs + DEVICE_CTRL) & ~DEV_CTRL_HOT_JOIN_NACK,
+ master->regs + DEVICE_CTRL);
+
+ return 0;
+}
+
+static int dw_i3c_master_disable_hotjoin(struct i3c_master_controller *m)
+{
+ struct dw_i3c_master *master = to_dw_i3c_master(m);
+
+ writel(readl(master->regs + DEVICE_CTRL) | DEV_CTRL_HOT_JOIN_NACK,
+ master->regs + DEVICE_CTRL);
+
+ pm_runtime_put_autosuspend(master->dev);
+ return 0;
+}
+
+static int dw_i3c_master_enable_ibi(struct i3c_dev_desc *dev)
+{
+ struct dw_i3c_i2c_dev_data *data = i3c_dev_get_master_data(dev);
+ struct i3c_master_controller *m = i3c_dev_get_master(dev);
+ struct dw_i3c_master *master = to_dw_i3c_master(m);
+ int rc;
+
+ rc = pm_runtime_resume_and_get(master->dev);
+ if (rc < 0) {
+ dev_err(master->dev,
+ "<%s> cannot resume i3c bus master, err: %d\n",
+ __func__, rc);
+ return rc;
+ }
+
+ dw_i3c_master_set_sir_enabled(master, dev, data->index, true);
+
+ rc = i3c_master_enec_locked(m, dev->info.dyn_addr, I3C_CCC_EVENT_SIR);
+
+ if (rc) {
+ dw_i3c_master_set_sir_enabled(master, dev, data->index, false);
+ pm_runtime_put_autosuspend(master->dev);
+ }
+
+ return rc;
+}
+
+static int dw_i3c_master_disable_ibi(struct i3c_dev_desc *dev)
+{
+ struct dw_i3c_i2c_dev_data *data = i3c_dev_get_master_data(dev);
+ struct i3c_master_controller *m = i3c_dev_get_master(dev);
+ struct dw_i3c_master *master = to_dw_i3c_master(m);
+ int rc;
+
+ rc = i3c_master_disec_locked(m, dev->info.dyn_addr, I3C_CCC_EVENT_SIR);
+ if (rc)
+ return rc;
+
+ dw_i3c_master_set_sir_enabled(master, dev, data->index, false);
+
+ pm_runtime_put_autosuspend(master->dev);
+ return 0;
+}
+
+static void dw_i3c_master_recycle_ibi_slot(struct i3c_dev_desc *dev,
+ struct i3c_ibi_slot *slot)
+{
+ struct dw_i3c_i2c_dev_data *data = i3c_dev_get_master_data(dev);
+
+ i3c_generic_ibi_recycle_slot(data->ibi_pool, slot);
+}
+
+static void dw_i3c_master_drain_ibi_queue(struct dw_i3c_master *master,
+ int len)
+{
+ int i;
+
+ for (i = 0; i < DIV_ROUND_UP(len, 4); i++)
+ readl(master->regs + IBI_QUEUE_STATUS);
+}
+
+static void dw_i3c_master_handle_ibi_sir(struct dw_i3c_master *master,
+ u32 status)
+{
+ struct dw_i3c_i2c_dev_data *data;
+ struct i3c_ibi_slot *slot;
+ struct i3c_dev_desc *dev;
+ unsigned long flags;
+ u8 addr, len;
+ int idx;
+
+ addr = IBI_QUEUE_IBI_ADDR(status);
+ len = IBI_QUEUE_STATUS_DATA_LEN(status);
+
+ /*
+ * We be tempted to check the error status in bit 30; however, due
+ * to the PEC errata workaround on some platform implementations (see
+ * ast2600_i3c_set_dat_ibi()), those will almost always have a PEC
+ * error on IBI payload data, as well as losing the last byte of
+ * payload.
+ *
+ * If we implement error status checking on that bit, we may need
+ * a new platform op to validate it.
+ */
+
+ spin_lock_irqsave(&master->devs_lock, flags);
+ idx = dw_i3c_master_get_addr_pos(master, addr);
+ if (idx < 0) {
+ dev_dbg_ratelimited(&master->base.dev,
+ "IBI from unknown addr 0x%x\n", addr);
+ goto err_drain;
+ }
+
+ dev = master->devs[idx].ibi_dev;
+ if (!dev || !dev->ibi) {
+ dev_dbg_ratelimited(&master->base.dev,
+ "IBI from non-requested dev idx %d\n", idx);
+ goto err_drain;
+ }
+
+ data = i3c_dev_get_master_data(dev);
+ slot = i3c_generic_ibi_get_free_slot(data->ibi_pool);
+ if (!slot) {
+ dev_dbg_ratelimited(&master->base.dev,
+ "No IBI slots available\n");
+ goto err_drain;
+ }
+
+ if (dev->ibi->max_payload_len < len) {
+ dev_dbg_ratelimited(&master->base.dev,
+ "IBI payload len %d greater than max %d\n",
+ len, dev->ibi->max_payload_len);
+ goto err_drain;
+ }
+
+ if (len) {
+ dw_i3c_master_read_ibi_fifo(master, slot->data, len);
+ slot->len = len;
+ }
+ i3c_master_queue_ibi(dev, slot);
+
+ spin_unlock_irqrestore(&master->devs_lock, flags);
+
+ return;
+
+err_drain:
+ dw_i3c_master_drain_ibi_queue(master, len);
+
+ spin_unlock_irqrestore(&master->devs_lock, flags);
+}
+
+/* "ibis": referring to In-Band Interrupts, and not
+ * https://en.wikipedia.org/wiki/Australian_white_ibis. The latter should
+ * not be handled.
+ */
+static void dw_i3c_master_irq_handle_ibis(struct dw_i3c_master *master)
+{
+ unsigned int i, len, n_ibis;
+ u32 reg;
+
+ reg = readl(master->regs + QUEUE_STATUS_LEVEL);
+ n_ibis = QUEUE_STATUS_IBI_STATUS_CNT(reg);
+ if (!n_ibis)
+ return;
+
+ for (i = 0; i < n_ibis; i++) {
+ reg = readl(master->regs + IBI_QUEUE_STATUS);
+
+ if (IBI_TYPE_SIRQ(reg)) {
+ dw_i3c_master_handle_ibi_sir(master, reg);
+ } else if (IBI_TYPE_HJ(reg)) {
+ queue_work(master->base.wq, &master->hj_work);
+ } else {
+ len = IBI_QUEUE_STATUS_DATA_LEN(reg);
+ dev_info(&master->base.dev,
+ "unsupported IBI type 0x%lx len %d\n",
+ IBI_QUEUE_STATUS_IBI_ID(reg), len);
+ dw_i3c_master_drain_ibi_queue(master, len);
+ }
+ }
+}
+
static irqreturn_t dw_i3c_master_irq_handler(int irq, void *dev_id)
{
struct dw_i3c_master *master = dev_id;
@@ -1094,6 +1483,9 @@ static irqreturn_t dw_i3c_master_irq_handler(int irq, void *dev_id)
writel(INTR_TRANSFER_ERR_STAT, master->regs + INTR_STATUS);
spin_unlock(&master->xferqueue.lock);
+ if (status & INTR_IBI_THLD_STAT)
+ dw_i3c_master_irq_handle_ibis(master);
+
return IRQ_HANDLED;
}
@@ -1110,34 +1502,69 @@ static const struct i3c_master_controller_ops dw_mipi_i3c_ops = {
.attach_i2c_dev = dw_i3c_master_attach_i2c_dev,
.detach_i2c_dev = dw_i3c_master_detach_i2c_dev,
.i2c_xfers = dw_i3c_master_i2c_xfers,
+ .request_ibi = dw_i3c_master_request_ibi,
+ .free_ibi = dw_i3c_master_free_ibi,
+ .enable_ibi = dw_i3c_master_enable_ibi,
+ .disable_ibi = dw_i3c_master_disable_ibi,
+ .recycle_ibi_slot = dw_i3c_master_recycle_ibi_slot,
+ .enable_hotjoin = dw_i3c_master_enable_hotjoin,
+ .disable_hotjoin = dw_i3c_master_disable_hotjoin,
};
-static int dw_i3c_probe(struct platform_device *pdev)
+/* default platform ops implementations */
+static int dw_i3c_platform_init_nop(struct dw_i3c_master *i3c)
+{
+ return 0;
+}
+
+static void dw_i3c_platform_set_dat_ibi_nop(struct dw_i3c_master *i3c,
+ struct i3c_dev_desc *dev,
+ bool enable, u32 *dat)
+{
+}
+
+static const struct dw_i3c_platform_ops dw_i3c_platform_ops_default = {
+ .init = dw_i3c_platform_init_nop,
+ .set_dat_ibi = dw_i3c_platform_set_dat_ibi_nop,
+};
+
+static void dw_i3c_hj_work(struct work_struct *work)
+{
+ struct dw_i3c_master *master =
+ container_of(work, typeof(*master), hj_work);
+
+ i3c_master_do_daa(&master->base);
+}
+
+int dw_i3c_common_probe(struct dw_i3c_master *master,
+ struct platform_device *pdev)
{
- struct dw_i3c_master *master;
int ret, irq;
+ const struct dw_i3c_drvdata *drvdata;
+ unsigned long quirks = 0;
- master = devm_kzalloc(&pdev->dev, sizeof(*master), GFP_KERNEL);
- if (!master)
- return -ENOMEM;
+ if (!master->platform_ops)
+ master->platform_ops = &dw_i3c_platform_ops_default;
+
+ master->dev = &pdev->dev;
master->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(master->regs))
return PTR_ERR(master->regs);
- master->core_clk = devm_clk_get(&pdev->dev, NULL);
+ master->core_clk = devm_clk_get_enabled(&pdev->dev, NULL);
if (IS_ERR(master->core_clk))
return PTR_ERR(master->core_clk);
+ master->pclk = devm_clk_get_optional_enabled(&pdev->dev, "pclk");
+ if (IS_ERR(master->pclk))
+ return PTR_ERR(master->pclk);
+
master->core_rst = devm_reset_control_get_optional_exclusive(&pdev->dev,
"core_rst");
if (IS_ERR(master->core_rst))
return PTR_ERR(master->core_rst);
- ret = clk_prepare_enable(master->core_clk);
- if (ret)
- goto err_disable_core_clk;
-
reset_control_deassert(master->core_rst);
spin_lock_init(&master->xferqueue.lock);
@@ -1153,6 +1580,11 @@ static int dw_i3c_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, master);
+ pm_runtime_set_autosuspend_delay(&pdev->dev, RPM_AUTOSUSPEND_TIMEOUT);
+ pm_runtime_use_autosuspend(&pdev->dev);
+ pm_runtime_set_active(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+
/* Information regarding the FIFOs/QUEUEs depth */
ret = readl(master->regs + QUEUE_STATUS_LEVEL);
master->caps.cmdfifodepth = QUEUE_STATUS_LEVEL_CMD(ret);
@@ -1165,50 +1597,218 @@ static int dw_i3c_probe(struct platform_device *pdev)
master->maxdevs = ret >> 16;
master->free_pos = GENMASK(master->maxdevs - 1, 0);
+ if (has_acpi_companion(&pdev->dev)) {
+ quirks = (unsigned long)device_get_match_data(&pdev->dev);
+ } else if (pdev->dev.of_node) {
+ drvdata = device_get_match_data(&pdev->dev);
+ if (drvdata)
+ quirks = drvdata->flags;
+ }
+ master->quirks = quirks;
+
+ /* Keep controller enabled by preventing runtime suspend */
+ if (master->quirks & DW_I3C_DISABLE_RUNTIME_PM_QUIRK)
+ pm_runtime_get_noresume(&pdev->dev);
+
+ INIT_WORK(&master->hj_work, dw_i3c_hj_work);
ret = i3c_master_register(&master->base, &pdev->dev,
&dw_mipi_i3c_ops, false);
if (ret)
- goto err_assert_rst;
+ goto err_disable_pm;
return 0;
+err_disable_pm:
+ pm_runtime_disable(&pdev->dev);
+ pm_runtime_set_suspended(&pdev->dev);
+ pm_runtime_dont_use_autosuspend(&pdev->dev);
+
err_assert_rst:
reset_control_assert(master->core_rst);
-err_disable_core_clk:
- clk_disable_unprepare(master->core_clk);
-
return ret;
}
+EXPORT_SYMBOL_GPL(dw_i3c_common_probe);
+
+void dw_i3c_common_remove(struct dw_i3c_master *master)
+{
+ cancel_work_sync(&master->hj_work);
+ i3c_master_unregister(&master->base);
-static int dw_i3c_remove(struct platform_device *pdev)
+ /* Balance pm_runtime_get_noresume() from probe() */
+ if (master->quirks & DW_I3C_DISABLE_RUNTIME_PM_QUIRK)
+ pm_runtime_put_noidle(master->dev);
+
+ pm_runtime_disable(master->dev);
+ pm_runtime_set_suspended(master->dev);
+ pm_runtime_dont_use_autosuspend(master->dev);
+}
+EXPORT_SYMBOL_GPL(dw_i3c_common_remove);
+
+/* base platform implementation */
+
+static int dw_i3c_probe(struct platform_device *pdev)
+{
+ struct dw_i3c_master *master;
+
+ master = devm_kzalloc(&pdev->dev, sizeof(*master), GFP_KERNEL);
+ if (!master)
+ return -ENOMEM;
+
+ return dw_i3c_common_probe(master, pdev);
+}
+
+static void dw_i3c_remove(struct platform_device *pdev)
{
struct dw_i3c_master *master = platform_get_drvdata(pdev);
- int ret;
- ret = i3c_master_unregister(&master->base);
+ dw_i3c_common_remove(master);
+}
+
+static void dw_i3c_master_restore_addrs(struct dw_i3c_master *master)
+{
+ u32 pos, reg_val;
+
+ writel(DEV_ADDR_DYNAMIC_ADDR_VALID | DEV_ADDR_DYNAMIC(master->dev_addr),
+ master->regs + DEVICE_ADDR);
+
+ for (pos = 0; pos < master->maxdevs; pos++) {
+ if (master->free_pos & BIT(pos))
+ continue;
+
+ if (master->devs[pos].is_i2c_addr)
+ reg_val = DEV_ADDR_TABLE_LEGACY_I2C_DEV |
+ DEV_ADDR_TABLE_STATIC_ADDR(master->devs[pos].addr);
+ else
+ reg_val = DEV_ADDR_TABLE_DYNAMIC_ADDR(master->devs[pos].addr);
+
+ writel(reg_val, master->regs + DEV_ADDR_TABLE_LOC(master->datstartaddr, pos));
+ }
+}
+
+static void dw_i3c_master_restore_timing_regs(struct dw_i3c_master *master)
+{
+ /* AMD platform specific OD and PP timings */
+ if (master->quirks & AMD_I3C_OD_PP_TIMING)
+ amd_configure_od_pp_quirk(master);
+
+ writel(master->i3c_pp_timing, master->regs + SCL_I3C_PP_TIMING);
+ writel(master->bus_free_timing, master->regs + BUS_FREE_TIMING);
+ writel(master->i3c_od_timing, master->regs + SCL_I3C_OD_TIMING);
+ writel(master->ext_lcnt_timing, master->regs + SCL_EXT_LCNT_TIMING);
+
+ if (master->i2c_slv_prsnt) {
+ writel(master->i2c_fmp_timing, master->regs + SCL_I2C_FMP_TIMING);
+ writel(master->i2c_fm_timing, master->regs + SCL_I2C_FM_TIMING);
+ }
+}
+
+static int dw_i3c_master_enable_clks(struct dw_i3c_master *master)
+{
+ int ret = 0;
+
+ ret = clk_prepare_enable(master->core_clk);
if (ret)
return ret;
- reset_control_assert(master->core_rst);
+ ret = clk_prepare_enable(master->pclk);
+ if (ret) {
+ clk_disable_unprepare(master->core_clk);
+ return ret;
+ }
+ return 0;
+}
+
+static inline void dw_i3c_master_disable_clks(struct dw_i3c_master *master)
+{
+ clk_disable_unprepare(master->pclk);
clk_disable_unprepare(master->core_clk);
+}
+
+static int __maybe_unused dw_i3c_master_runtime_suspend(struct device *dev)
+{
+ struct dw_i3c_master *master = dev_get_drvdata(dev);
+
+ dw_i3c_master_disable(master);
+
+ reset_control_assert(master->core_rst);
+ dw_i3c_master_disable_clks(master);
+ pinctrl_pm_select_sleep_state(dev);
+ return 0;
+}
+
+static int __maybe_unused dw_i3c_master_runtime_resume(struct device *dev)
+{
+ struct dw_i3c_master *master = dev_get_drvdata(dev);
+
+ pinctrl_pm_select_default_state(dev);
+ dw_i3c_master_enable_clks(master);
+ reset_control_deassert(master->core_rst);
+ dw_i3c_master_set_intr_regs(master);
+ dw_i3c_master_restore_timing_regs(master);
+ dw_i3c_master_restore_addrs(master);
+
+ dw_i3c_master_enable(master);
return 0;
}
+static const struct dev_pm_ops dw_i3c_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
+ SET_RUNTIME_PM_OPS(dw_i3c_master_runtime_suspend, dw_i3c_master_runtime_resume, NULL)
+};
+
+static void dw_i3c_shutdown(struct platform_device *pdev)
+{
+ struct dw_i3c_master *master = platform_get_drvdata(pdev);
+ int ret;
+
+ ret = pm_runtime_resume_and_get(master->dev);
+ if (ret < 0) {
+ dev_err(master->dev,
+ "<%s> cannot resume i3c bus master, err: %d\n",
+ __func__, ret);
+ return;
+ }
+
+ cancel_work_sync(&master->hj_work);
+
+ /* Disable interrupts */
+ writel((u32)~INTR_ALL, master->regs + INTR_STATUS_EN);
+ writel((u32)~INTR_ALL, master->regs + INTR_SIGNAL_EN);
+
+ pm_runtime_put_autosuspend(master->dev);
+}
+
+static const struct dw_i3c_drvdata altr_agilex5_drvdata = {
+ .flags = DW_I3C_DISABLE_RUNTIME_PM_QUIRK,
+};
+
static const struct of_device_id dw_i3c_master_of_match[] = {
{ .compatible = "snps,dw-i3c-master-1.00a", },
+ { .compatible = "altr,agilex5-dw-i3c-master",
+ .data = &altr_agilex5_drvdata,
+ },
{},
};
MODULE_DEVICE_TABLE(of, dw_i3c_master_of_match);
+static const struct acpi_device_id amd_i3c_device_match[] = {
+ { "AMDI0015", AMD_I3C_OD_PP_TIMING },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, amd_i3c_device_match);
+
static struct platform_driver dw_i3c_driver = {
.probe = dw_i3c_probe,
.remove = dw_i3c_remove,
+ .shutdown = dw_i3c_shutdown,
.driver = {
.name = "dw-i3c-master",
- .of_match_table = of_match_ptr(dw_i3c_master_of_match),
+ .of_match_table = dw_i3c_master_of_match,
+ .acpi_match_table = amd_i3c_device_match,
+ .pm = &dw_i3c_pm_ops,
},
};
module_platform_driver(dw_i3c_driver);