Commit 1d083260 authored by Billy Tsai's avatar Billy Tsai Committed by Alexandre Belloni
Browse files

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: default avatarBilly Tsai <billy_tsai@aspeedtech.com>
Link: https://lore.kernel.org/r/20240429073624.256830-1-billy_tsai@aspeedtech.com


Signed-off-by: default avatarAlexandre Belloni <alexandre.belloni@bootlin.com>
parent 29391d91
Loading
Loading
Loading
Loading
+54 −13
Original line number Diff line number Diff line
@@ -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;
+2 −0
Original line number Diff line number Diff line
@@ -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 {