Commit 05b26c31 authored by Frank Li's avatar Frank Li Committed by Alexandre Belloni
Browse files

i3c: master: svc: add hot join support



Add hot join support for svc master controller. Disable hot join by
default.
User can use sysfs entry to enable hot join.

Signed-off-by: default avatarFrank Li <Frank.Li@nxp.com>
Link: https://lore.kernel.org/r/20231201222532.2431484-3-Frank.Li@nxp.com


Signed-off-by: default avatarAlexandre Belloni <alexandre.belloni@bootlin.com>
parent 317bacf9
Loading
Loading
Loading
Loading
+57 −4
Original line number Diff line number Diff line
@@ -128,6 +128,9 @@
/* This parameter depends on the implementation and may be tuned */
#define SVC_I3C_FIFO_SIZE 16

#define SVC_I3C_EVENT_IBI	BIT(0)
#define SVC_I3C_EVENT_HOTJOIN	BIT(1)

struct svc_i3c_cmd {
	u8 addr;
	bool rnw;
@@ -177,6 +180,7 @@ struct svc_i3c_regs_save {
 * @ibi.tbq_slot: To be queued IBI slot
 * @ibi.lock: IBI lock
 * @lock: Transfer lock, protect between IBI work thread and callbacks from master
 * @enabled_events: Bit masks for enable events (IBI, HotJoin).
 */
struct svc_i3c_master {
	struct i3c_master_controller base;
@@ -206,6 +210,7 @@ struct svc_i3c_master {
		spinlock_t lock;
	} ibi;
	struct mutex lock;
	int enabled_events;
};

/**
@@ -220,6 +225,11 @@ struct svc_i3c_i2c_dev_data {
	struct i3c_generic_ibi_pool *ibi_pool;
};

static inline bool is_events_enabled(struct svc_i3c_master *master, u32 mask)
{
	return !!(master->enabled_events & mask);
}

static bool svc_i3c_master_error(struct svc_i3c_master *master)
{
	u32 mstatus, merrwarn;
@@ -429,13 +439,16 @@ static void svc_i3c_master_ibi_work(struct work_struct *work)
	switch (ibitype) {
	case SVC_I3C_MSTATUS_IBITYPE_IBI:
		dev = svc_i3c_master_dev_from_addr(master, ibiaddr);
		if (!dev)
		if (!dev || !is_events_enabled(master, SVC_I3C_EVENT_IBI))
			svc_i3c_master_nack_ibi(master);
		else
			svc_i3c_master_handle_ibi(master, dev);
		break;
	case SVC_I3C_MSTATUS_IBITYPE_HOT_JOIN:
		if (is_events_enabled(master, SVC_I3C_EVENT_HOTJOIN))
			svc_i3c_master_ack_ibi(master, false);
		else
			svc_i3c_master_nack_ibi(master);
		break;
	case SVC_I3C_MSTATUS_IBITYPE_MASTER_REQUEST:
		svc_i3c_master_nack_ibi(master);
@@ -472,6 +485,8 @@ static void svc_i3c_master_ibi_work(struct work_struct *work)
		svc_i3c_master_emit_stop(master);
		break;
	case SVC_I3C_MSTATUS_IBITYPE_HOT_JOIN:
		svc_i3c_master_emit_stop(master);
		if (is_events_enabled(master, SVC_I3C_EVENT_HOTJOIN))
			queue_work(master->base.wq, &master->hj_work);
		break;
	case SVC_I3C_MSTATUS_IBITYPE_MASTER_REQUEST:
@@ -1472,6 +1487,7 @@ static int svc_i3c_master_enable_ibi(struct i3c_dev_desc *dev)
		return ret;
	}

	master->enabled_events |= SVC_I3C_EVENT_IBI;
	svc_i3c_master_enable_interrupts(master, SVC_I3C_MINT_SLVSTART);

	return i3c_master_enec_locked(m, dev->info.dyn_addr, I3C_CCC_EVENT_SIR);
@@ -1483,6 +1499,8 @@ static int svc_i3c_master_disable_ibi(struct i3c_dev_desc *dev)
	struct svc_i3c_master *master = to_svc_i3c_master(m);
	int ret;

	master->enabled_events &= ~SVC_I3C_EVENT_IBI;
	if (!master->enabled_events)
		svc_i3c_master_disable_interrupts(master);

	ret = i3c_master_disec_locked(m, dev->info.dyn_addr, I3C_CCC_EVENT_SIR);
@@ -1493,6 +1511,39 @@ static int svc_i3c_master_disable_ibi(struct i3c_dev_desc *dev)
	return ret;
}

static int svc_i3c_master_enable_hotjoin(struct i3c_master_controller *m)
{
	struct svc_i3c_master *master = to_svc_i3c_master(m);
	int ret;

	ret = pm_runtime_resume_and_get(master->dev);
	if (ret < 0) {
		dev_err(master->dev, "<%s> Cannot get runtime PM.\n", __func__);
		return ret;
	}

	master->enabled_events |= SVC_I3C_EVENT_HOTJOIN;

	svc_i3c_master_enable_interrupts(master, SVC_I3C_MINT_SLVSTART);

	return 0;
}

static int svc_i3c_master_disable_hotjoin(struct i3c_master_controller *m)
{
	struct svc_i3c_master *master = to_svc_i3c_master(m);

	master->enabled_events &= ~SVC_I3C_EVENT_HOTJOIN;

	if (!master->enabled_events)
		svc_i3c_master_disable_interrupts(master);

	pm_runtime_mark_last_busy(master->dev);
	pm_runtime_put_autosuspend(master->dev);

	return 0;
}

static void svc_i3c_master_recycle_ibi_slot(struct i3c_dev_desc *dev,
					    struct i3c_ibi_slot *slot)
{
@@ -1519,6 +1570,8 @@ static const struct i3c_master_controller_ops svc_i3c_master_ops = {
	.recycle_ibi_slot = svc_i3c_master_recycle_ibi_slot,
	.enable_ibi = svc_i3c_master_enable_ibi,
	.disable_ibi = svc_i3c_master_disable_ibi,
	.enable_hotjoin = svc_i3c_master_enable_hotjoin,
	.disable_hotjoin = svc_i3c_master_disable_hotjoin,
};

static int svc_i3c_master_prepare_clks(struct svc_i3c_master *master)