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

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.comSigned-off-by: default avatarAlexandre Belloni <alexandre.belloni@bootlin.com>
parent 317bacf9
...@@ -128,6 +128,9 @@ ...@@ -128,6 +128,9 @@
/* This parameter depends on the implementation and may be tuned */ /* This parameter depends on the implementation and may be tuned */
#define SVC_I3C_FIFO_SIZE 16 #define SVC_I3C_FIFO_SIZE 16
#define SVC_I3C_EVENT_IBI BIT(0)
#define SVC_I3C_EVENT_HOTJOIN BIT(1)
struct svc_i3c_cmd { struct svc_i3c_cmd {
u8 addr; u8 addr;
bool rnw; bool rnw;
...@@ -177,6 +180,7 @@ struct svc_i3c_regs_save { ...@@ -177,6 +180,7 @@ struct svc_i3c_regs_save {
* @ibi.tbq_slot: To be queued IBI slot * @ibi.tbq_slot: To be queued IBI slot
* @ibi.lock: IBI lock * @ibi.lock: IBI lock
* @lock: Transfer lock, protect between IBI work thread and callbacks from master * @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 svc_i3c_master {
struct i3c_master_controller base; struct i3c_master_controller base;
...@@ -206,6 +210,7 @@ struct svc_i3c_master { ...@@ -206,6 +210,7 @@ struct svc_i3c_master {
spinlock_t lock; spinlock_t lock;
} ibi; } ibi;
struct mutex lock; struct mutex lock;
int enabled_events;
}; };
/** /**
...@@ -220,6 +225,11 @@ struct svc_i3c_i2c_dev_data { ...@@ -220,6 +225,11 @@ struct svc_i3c_i2c_dev_data {
struct i3c_generic_ibi_pool *ibi_pool; 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) static bool svc_i3c_master_error(struct svc_i3c_master *master)
{ {
u32 mstatus, merrwarn; u32 mstatus, merrwarn;
...@@ -429,13 +439,16 @@ static void svc_i3c_master_ibi_work(struct work_struct *work) ...@@ -429,13 +439,16 @@ static void svc_i3c_master_ibi_work(struct work_struct *work)
switch (ibitype) { switch (ibitype) {
case SVC_I3C_MSTATUS_IBITYPE_IBI: case SVC_I3C_MSTATUS_IBITYPE_IBI:
dev = svc_i3c_master_dev_from_addr(master, ibiaddr); 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); svc_i3c_master_nack_ibi(master);
else else
svc_i3c_master_handle_ibi(master, dev); svc_i3c_master_handle_ibi(master, dev);
break; break;
case SVC_I3C_MSTATUS_IBITYPE_HOT_JOIN: case SVC_I3C_MSTATUS_IBITYPE_HOT_JOIN:
svc_i3c_master_ack_ibi(master, false); if (is_events_enabled(master, SVC_I3C_EVENT_HOTJOIN))
svc_i3c_master_ack_ibi(master, false);
else
svc_i3c_master_nack_ibi(master);
break; break;
case SVC_I3C_MSTATUS_IBITYPE_MASTER_REQUEST: case SVC_I3C_MSTATUS_IBITYPE_MASTER_REQUEST:
svc_i3c_master_nack_ibi(master); svc_i3c_master_nack_ibi(master);
...@@ -472,7 +485,9 @@ static void svc_i3c_master_ibi_work(struct work_struct *work) ...@@ -472,7 +485,9 @@ static void svc_i3c_master_ibi_work(struct work_struct *work)
svc_i3c_master_emit_stop(master); svc_i3c_master_emit_stop(master);
break; break;
case SVC_I3C_MSTATUS_IBITYPE_HOT_JOIN: case SVC_I3C_MSTATUS_IBITYPE_HOT_JOIN:
queue_work(master->base.wq, &master->hj_work); svc_i3c_master_emit_stop(master);
if (is_events_enabled(master, SVC_I3C_EVENT_HOTJOIN))
queue_work(master->base.wq, &master->hj_work);
break; break;
case SVC_I3C_MSTATUS_IBITYPE_MASTER_REQUEST: case SVC_I3C_MSTATUS_IBITYPE_MASTER_REQUEST:
default: default:
...@@ -1472,6 +1487,7 @@ static int svc_i3c_master_enable_ibi(struct i3c_dev_desc *dev) ...@@ -1472,6 +1487,7 @@ static int svc_i3c_master_enable_ibi(struct i3c_dev_desc *dev)
return ret; return ret;
} }
master->enabled_events |= SVC_I3C_EVENT_IBI;
svc_i3c_master_enable_interrupts(master, SVC_I3C_MINT_SLVSTART); svc_i3c_master_enable_interrupts(master, SVC_I3C_MINT_SLVSTART);
return i3c_master_enec_locked(m, dev->info.dyn_addr, I3C_CCC_EVENT_SIR); return i3c_master_enec_locked(m, dev->info.dyn_addr, I3C_CCC_EVENT_SIR);
...@@ -1483,7 +1499,9 @@ static int svc_i3c_master_disable_ibi(struct i3c_dev_desc *dev) ...@@ -1483,7 +1499,9 @@ static int svc_i3c_master_disable_ibi(struct i3c_dev_desc *dev)
struct svc_i3c_master *master = to_svc_i3c_master(m); struct svc_i3c_master *master = to_svc_i3c_master(m);
int ret; int ret;
svc_i3c_master_disable_interrupts(master); 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); 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) ...@@ -1493,6 +1511,39 @@ static int svc_i3c_master_disable_ibi(struct i3c_dev_desc *dev)
return ret; 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, static void svc_i3c_master_recycle_ibi_slot(struct i3c_dev_desc *dev,
struct i3c_ibi_slot *slot) struct i3c_ibi_slot *slot)
{ {
...@@ -1519,6 +1570,8 @@ static const struct i3c_master_controller_ops svc_i3c_master_ops = { ...@@ -1519,6 +1570,8 @@ static const struct i3c_master_controller_ops svc_i3c_master_ops = {
.recycle_ibi_slot = svc_i3c_master_recycle_ibi_slot, .recycle_ibi_slot = svc_i3c_master_recycle_ibi_slot,
.enable_ibi = svc_i3c_master_enable_ibi, .enable_ibi = svc_i3c_master_enable_ibi,
.disable_ibi = svc_i3c_master_disable_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) static int svc_i3c_master_prepare_clks(struct svc_i3c_master *master)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment