i3c: dw: Add hot-join support.

Add hot-join support for dw i3c master controller.
By default, the hot-join acknowledgment is disabled, and the hardware will
automatically send the DISEC CCC when it receives the hot-join request.
Users can use the sys entry to enable it.

Signed-off-by: Billy Tsai <billy_tsai@aspeedtech.com>
Link: https://lore.kernel.org/r/20240429073624.256830-1-billy_tsai@aspeedtech.com
Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
This commit is contained in:
Billy Tsai 2024-04-29 15:36:24 +08:00 committed by Alexandre Belloni
parent 29391d9142
commit 1d08326020
2 changed files with 55 additions and 12 deletions

View file

@ -1136,6 +1136,23 @@ static void dw_i3c_master_free_ibi(struct i3c_dev_desc *dev)
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)
@ -1170,23 +1187,34 @@ static void dw_i3c_master_set_sir_enabled(struct dw_i3c_master *master,
}
writel(reg, master->regs + IBI_SIR_REQ_REJECT);
if (global) {
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);
if (global)
dw_i3c_master_enable_sir_signal(master, enable);
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);
}
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);
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);
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);
@ -1326,6 +1354,8 @@ static void dw_i3c_master_irq_handle_ibis(struct dw_i3c_master *master)
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,
@ -1393,6 +1423,8 @@ static const struct i3c_master_controller_ops dw_mipi_i3c_ibi_ops = {
.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,
};
/* default platform ops implementations */
@ -1412,6 +1444,14 @@ static const struct dw_i3c_platform_ops dw_i3c_platform_ops_default = {
.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)
{
@ -1469,6 +1509,7 @@ int dw_i3c_common_probe(struct dw_i3c_master *master,
if (master->ibi_capable)
ops = &dw_mipi_i3c_ibi_ops;
INIT_WORK(&master->hj_work, dw_i3c_hj_work);
ret = i3c_master_register(&master->base, &pdev->dev, ops, false);
if (ret)
goto err_assert_rst;

View file

@ -57,6 +57,8 @@ struct dw_i3c_master {
/* platform-specific data */
const struct dw_i3c_platform_ops *platform_ops;
struct work_struct hj_work;
};
struct dw_i3c_platform_ops {