Skip to contentSkip to conversation

i3c: mipi-i3c-hci-pci: Enable IBI while runtime suspended for Intel controllers

PATCH Superseded Adrian Hunter 4 participants 26 messages Jan 29 – Feb 3 I3C

v1 of 5 Superseded Latest is v5 →

lore
Share & tools
lore
#0 lore raw

cover i3c: mipi-i3c-hci-pci: Enable IBI while runtime suspended for Intel controllers

Hi

Here are patches related to enabling IBI while runtime suspended for Intel controllers.

Intel LPSS I3C controllers can wake from runtime suspend to receive in-band interrupts (IBIs).

It is non-trivial to implement because the parent PCI device has 2 I3C bus instances (MIPI I3C HCI Multi-Bus Instance capability) represented by platform devices with a separate driver, but the IBI-wakeup is shared by both, which means runtime PM has to be managed by the parent PCI driver.

To make that work, the PCI driver handles runtime PM, but leverages the mipi-i3c-hci platform driver's functionality for saving and restoring controller state.

Adrian Hunter (7):
i3c: mipi-i3c-hci-pci: Set d3hot_delay to 0 for Intel controllers
i3c: master: Allow controller drivers to select runtime PM device
i3c: master: Mark last_busy on IBI when runtime PM is allowed
i3c: mipi-i3c-hci: Add quirk to allow IBI while runtime suspended
i3c: mipi-i3c-hci: Allow parent to manage runtime PM
i3c: mipi-i3c-hci-pci: Add optional ability to manage child runtime PM
i3c: mipi-i3c-hci-pci: Enable IBI while runtime suspended for Intel controllers

 drivers/i3c/master.c                               |  14 +-
 drivers/i3c/master/mipi-i3c-hci/core.c             |  30 ++--
 drivers/i3c/master/mipi-i3c-hci/hci.h              |   7 +
 drivers/i3c/master/mipi-i3c-hci/mipi-i3c-hci-pci.c | 158 ++++++++++++++++++++-
 include/linux/i3c/master.h                         |   2 +
 5 files changed, 194 insertions(+), 17 deletions(-)

Regards
Adrian

#1 lore raw

PATCH 1/7 i3c: mipi-i3c-hci-pci: Set d3hot_delay to 0 for Intel controllers

Set d3hot_delay to 0 for Intel controllers because a delay is not needed.

---
 drivers/i3c/master/mipi-i3c-hci/mipi-i3c-hci-pci.c | 1 +
 1 file changed, 1 insertion(+)
drivers/i3c/master/mipi-i3c-hci/mipi-i3c-hci-pci.c +1 -0
index 0f05a15c14c7..bc83caad4197 100644
@@ -164,6 +164,7 @@ static int intel_i3c_init(struct mipi_i3c_hci_pci *hci)
 	dma_set_mask_and_coherent(&hci->pci->dev, DMA_BIT_MASK(64));
 
 	hci->pci->d3cold_delay = 0;
+	hci->pci->d3hot_delay = 0;
 
 	hci->private = host;
 	host->priv = priv;
Signature · 1 line
2.51.0
reviewer Reviewed-by on v1 1/7
#2 lore raw

Re: [PATCH 1/7] i3c: mipi-i3c-hci-pci: Set d3hot_delay to 0 for Intel controllers

Quoted 4 lines · Adrian Hunter

Set d3hot_delay to 0 for Intel controllers because a delay is not needed.

---

Quoted 18 lines
 drivers/i3c/master/mipi-i3c-hci/mipi-i3c-hci-pci.c | 1 +
 1 file changed, 1 insertion(+)
drivers/i3c/master/mipi-i3c-hci/mipi-i3c-hci-pci.c +1 -1
index 0f05a15c14c7..bc83caad4197 100644
@@ -164,6 +164,7 @@ static int intel_i3c_init(struct mipi_i3c_hci_pci *hci)
 	dma_set_mask_and_coherent(&hci->pci->dev, DMA_BIT_MASK(64));
 
 	hci->pci->d3cold_delay = 0;
+	hci->pci->d3hot_delay = 0;
 
 	hci->private = host;
 	host->priv = priv;
--

2.51.0

#3 lore raw

PATCH 2/7 i3c: master: Allow controller drivers to select runtime PM device

In reply to Adrian Hunter

Some I3C controller drivers need runtime PM to operate on a device other than the parent device. To support that, add an rpm_dev pointer to struct i3c_master_controller so drivers can specify which device should be used for runtime power management.

If a driver does not set rpm_dev explicitly, default to using the parent device to maintain existing behaviour.

Update the runtime PM helpers to use rpm_dev instead of dev.parent.

---
 drivers/i3c/master.c       | 9 ++++++---
 include/linux/i3c/master.h | 2 ++
 2 files changed, 8 insertions(+), 3 deletions(-)
drivers/i3c/master.c +6 -3
index 49fb6e30a68e..bcc493dc9d04 100644
@@ -108,10 +108,10 @@ static struct i3c_master_controller *dev_to_i3cmaster(struct device *dev)
 
 static int __must_check i3c_master_rpm_get(struct i3c_master_controller *master)
 {
-	int ret = master->rpm_allowed ? pm_runtime_resume_and_get(master->dev.parent) : 0;
+	int ret = master->rpm_allowed ? pm_runtime_resume_and_get(master->rpm_dev) : 0;
 
 	if (ret < 0) {
-		dev_err(master->dev.parent, "runtime resume failed, error %d\n", ret);
+		dev_err(master->rpm_dev, "runtime resume failed, error %d\n", ret);
 		return ret;
 	}
 	return 0;
@@ -120,7 +120,7 @@ static int __must_check i3c_master_rpm_get(struct i3c_master_controller *master)
 static void i3c_master_rpm_put(struct i3c_master_controller *master)
 {
 	if (master->rpm_allowed)
-		pm_runtime_put_autosuspend(master->dev.parent);
+		pm_runtime_put_autosuspend(master->rpm_dev);
 }
 
 int i3c_bus_rpm_get(struct i3c_bus *bus)
@@ -2975,6 +2975,9 @@ int i3c_master_register(struct i3c_master_controller *master,
 	INIT_LIST_HEAD(&master->boardinfo.i2c);
 	INIT_LIST_HEAD(&master->boardinfo.i3c);
 
+	if (!master->rpm_dev)
+		master->rpm_dev = parent;
+
 	ret = i3c_master_rpm_get(master);
 	if (ret)
 		return ret;
include/linux/i3c/master.h +2 -0
index af2bb48363ba..4be67a902dd8 100644
@@ -501,6 +501,7 @@ struct i3c_master_controller_ops {
  *	 registered to the I2C subsystem to be as transparent as possible to
  *	 existing I2C drivers
  * @ops: master operations. See &struct i3c_master_controller_ops
+ * @rpm_dev: Runtime PM device
  * @secondary: true if the master is a secondary master
  * @init_done: true when the bus initialization is done
  * @hotjoin: true if the master support hotjoin
@@ -526,6 +527,7 @@ struct i3c_master_controller {
 	struct i3c_dev_desc *this;
 	struct i2c_adapter i2c;
 	const struct i3c_master_controller_ops *ops;
+	struct device *rpm_dev;
 	unsigned int secondary : 1;
 	unsigned int init_done : 1;
 	unsigned int hotjoin: 1;
Signature · 1 line
2.51.0
#4 lore raw

PATCH 3/7 i3c: master: Mark last_busy on IBI when runtime PM is allowed

In reply to Adrian Hunter

When an IBI can be received after the controller is pm_runtime_put_autosuspend()'ed, the interrupt may occur just before the device is auto‑suspended. In such cases, the runtime PM core may not see any recent activity and may suspend the device earlier than intended.

Mark the controller as last busy whenever an IBI is queued (when rpm_ibi_allowed is set) so that the auto-suspend delay correctly reflects recent bus activity and avoids premature suspension.

---
 drivers/i3c/master.c | 5 +++++
 1 file changed, 5 insertions(+)
drivers/i3c/master.c +5 -0
index bcc493dc9d04..dcc07ebc50a2 100644
@@ -2721,9 +2721,14 @@ static void i3c_master_unregister_i3c_devs(struct i3c_master_controller *master)
  */
 void i3c_master_queue_ibi(struct i3c_dev_desc *dev, struct i3c_ibi_slot *slot)
 {
+	struct i3c_master_controller *master = i3c_dev_get_master(dev);
+
 	if (!dev->ibi || !slot)
 		return;
 
+	if (master->rpm_ibi_allowed)
+		pm_runtime_mark_last_busy(master->rpm_dev);
+
 	atomic_inc(&dev->ibi->pending_ibis);
 	queue_work(dev->ibi->wq, &slot->work);
 }
Signature · 1 line
2.51.0
reviewer
#5 lore raw

Re: [PATCH 3/7] i3c: master: Mark last_busy on IBI when runtime PM is allowed

Quoted 8 lines · Adrian Hunter

When an IBI can be received after the controller is pm_runtime_put_autosuspend()'ed, the interrupt may occur just before the device is auto‑suspended. In such cases, the runtime PM core may not see any recent activity and may suspend the device earlier than intended.

Mark the controller as last busy whenever an IBI is queued (when rpm_ibi_allowed is set) so that the auto-suspend delay correctly reflects recent bus activity and avoids premature suspension.

look like this can't resolve problem. pm_runtime_mark_last_busy() just change dev->power.last_busy. If suspend before it, nothing happen.

irq use thread irq, in irq thread call pm_runtime_resume() if needs.

And this function call by irq handle, just put to work queue, what's impact if do nothing here?

Frank

Quoted 28 lines
---
 drivers/i3c/master.c | 5 +++++
 1 file changed, 5 insertions(+)
drivers/i3c/master.c +5 -1
index bcc493dc9d04..dcc07ebc50a2 100644
@@ -2721,9 +2721,14 @@ static void i3c_master_unregister_i3c_devs(struct i3c_master_controller *master)
  */
 void i3c_master_queue_ibi(struct i3c_dev_desc *dev, struct i3c_ibi_slot *slot)
 {
+	struct i3c_master_controller *master = i3c_dev_get_master(dev);
+
 	if (!dev->ibi || !slot)
 		return;
 
+	if (master->rpm_ibi_allowed)
+		pm_runtime_mark_last_busy(master->rpm_dev);
+
 	atomic_inc(&dev->ibi->pending_ibis);
 	queue_work(dev->ibi->wq, &slot->work);
 }
--

2.51.0

#6 lore raw

Re: [PATCH 3/7] i3c: master: Mark last_busy on IBI when runtime PM is allowed

Quoted 6 lines · Frank Li
Quoted 5 lines · Adrian Hunter

When an IBI can be received after the controller is pm_runtime_put_autosuspend()'ed, the interrupt may occur just before the device is auto‑suspended. In such cases, the runtime PM core may not see any recent activity and may suspend the device earlier than intended.

Quoted 6 lines
Quoted 3 lines

Mark the controller as last busy whenever an IBI is queued (when rpm_ibi_allowed is set) so that the auto-suspend delay correctly reflects recent bus activity and avoids premature suspension.

look like this can't resolve problem. pm_runtime_mark_last_busy() just change dev->power.last_busy. If suspend before it, nothing happen.

It should be effective.

rpm_suspend() recalculates the autosuspend expiry time based on last_busy (see pm_runtime_autosuspend_expiration()) and restarts the timer is it is in the future.

Quoted 5 lines

irq use thread irq, in irq thread call pm_runtime_resume() if needs.

And this function call by irq handle, just put to work queue, what's impact if do nothing here?

Just premature runtime suspension inconsistent with autosuspend_delay.

Quoted 31 lines

Frank

Quoted 28 lines
---
 drivers/i3c/master.c | 5 +++++
 1 file changed, 5 insertions(+)
drivers/i3c/master.c +5 -1
index bcc493dc9d04..dcc07ebc50a2 100644
@@ -2721,9 +2721,14 @@ static void i3c_master_unregister_i3c_devs(struct i3c_master_controller *master)
  */
 void i3c_master_queue_ibi(struct i3c_dev_desc *dev, struct i3c_ibi_slot *slot)
 {
+	struct i3c_master_controller *master = i3c_dev_get_master(dev);
+
 	if (!dev->ibi || !slot)
 		return;
 
+	if (master->rpm_ibi_allowed)
+		pm_runtime_mark_last_busy(master->rpm_dev);
+
 	atomic_inc(&dev->ibi->pending_ibis);
 	queue_work(dev->ibi->wq, &slot->work);
 }
--

2.51.0

reviewer
#7 lore raw

Re: [PATCH 3/7] i3c: master: Mark last_busy on IBI when runtime PM is allowed

Quoted 21 lines · Adrian Hunter
Quoted 6 lines · Frank Li
Quoted 5 lines · Adrian Hunter

When an IBI can be received after the controller is pm_runtime_put_autosuspend()'ed, the interrupt may occur just before the device is auto‑suspended. In such cases, the runtime PM core may not see any recent activity and may suspend the device earlier than intended.

Quoted 6 lines
Quoted 3 lines

Mark the controller as last busy whenever an IBI is queued (when rpm_ibi_allowed is set) so that the auto-suspend delay correctly reflects recent bus activity and avoids premature suspension.

look like this can't resolve problem. pm_runtime_mark_last_busy() just change dev->power.last_busy. If suspend before it, nothing happen.

It should be effective.

rpm_suspend() recalculates the autosuspend expiry time based on last_busy (see pm_runtime_autosuspend_expiration()) and restarts the timer is it is in the future.

Quoted 1 line
Quoted 6 lines
Quoted 4 lines

irq use thread irq, in irq thread call pm_runtime_resume() if needs.

And this function call by irq handle, just put to work queue, what's impact if do nothing here?

Just premature runtime suspension inconsistent with autosuspend_delay.

CPU 0 CPU 1
1. rpm_suspend() 2. pm_runtime_mark_last_busy(master->rpm_dev)

if 2 happen before 1, it can extend suspend. 2 happen after 1, it should do nothing.

Frank

Quoted 33 lines
Quoted 32 lines

Frank

Quoted 28 lines
---
 drivers/i3c/master.c | 5 +++++
 1 file changed, 5 insertions(+)
drivers/i3c/master.c +5 -1
index bcc493dc9d04..dcc07ebc50a2 100644
@@ -2721,9 +2721,14 @@ static void i3c_master_unregister_i3c_devs(struct i3c_master_controller *master)
  */
 void i3c_master_queue_ibi(struct i3c_dev_desc *dev, struct i3c_ibi_slot *slot)
 {
+	struct i3c_master_controller *master = i3c_dev_get_master(dev);
+
 	if (!dev->ibi || !slot)
 		return;
 
+	if (master->rpm_ibi_allowed)
+		pm_runtime_mark_last_busy(master->rpm_dev);
+
 	atomic_inc(&dev->ibi->pending_ibis);
 	queue_work(dev->ibi->wq, &slot->work);
 }
--

2.51.0

#8 lore raw

Re: [PATCH 3/7] i3c: master: Mark last_busy on IBI when runtime PM is allowed

Quoted 27 lines · Frank Li
Quoted 26 lines · Adrian Hunter
Quoted 6 lines · Frank Li
Quoted 5 lines · Adrian Hunter

When an IBI can be received after the controller is pm_runtime_put_autosuspend()'ed, the interrupt may occur just before the device is auto‑suspended. In such cases, the runtime PM core may not see any recent activity and may suspend the device earlier than intended.

Quoted 6 lines
Quoted 3 lines

Mark the controller as last busy whenever an IBI is queued (when rpm_ibi_allowed is set) so that the auto-suspend delay correctly reflects recent bus activity and avoids premature suspension.

look like this can't resolve problem. pm_runtime_mark_last_busy() just change dev->power.last_busy. If suspend before it, nothing happen.

It should be effective.

rpm_suspend() recalculates the autosuspend expiry time based on last_busy (see pm_runtime_autosuspend_expiration()) and restarts the timer is it is in the future.

Quoted 5 lines

irq use thread irq, in irq thread call pm_runtime_resume() if needs.

And this function call by irq handle, just put to work queue, what's impact if do nothing here?

Quoted 8 lines
Quoted 1 line

Just premature runtime suspension inconsistent with autosuspend_delay.

CPU 0 CPU 1
1. rpm_suspend() 2. pm_runtime_mark_last_busy(master->rpm_dev)

if 2 happen before 1, it can extend suspend. 2 happen after 1, it should do nothing.

2 happening after 1 is a separate issue. It will never happen in the wakeup case because the wakeup does a runtime resume:

	pm_runtime_put_autosuspend()
	IBI -> pm_runtime_mark_last_busy()
	another IBI -> pm_runtime_mark_last_busy() and so on
	<autosuspend_delay finally elapses>
	rpm_suspend() -> device suspended, PME activated
	IBI START -> PME -> pm_request_resume()
	IBI is delivered after controller runtime resumes
Quoted 35 lines

Frank

Quoted 33 lines
Quoted 32 lines

Frank

Quoted 28 lines
---
 drivers/i3c/master.c | 5 +++++
 1 file changed, 5 insertions(+)
drivers/i3c/master.c +5 -1
index bcc493dc9d04..dcc07ebc50a2 100644
@@ -2721,9 +2721,14 @@ static void i3c_master_unregister_i3c_devs(struct i3c_master_controller *master)
  */
 void i3c_master_queue_ibi(struct i3c_dev_desc *dev, struct i3c_ibi_slot *slot)
 {
+	struct i3c_master_controller *master = i3c_dev_get_master(dev);
+
 	if (!dev->ibi || !slot)
 		return;
 
+	if (master->rpm_ibi_allowed)
+		pm_runtime_mark_last_busy(master->rpm_dev);
+
 	atomic_inc(&dev->ibi->pending_ibis);
 	queue_work(dev->ibi->wq, &slot->work);
 }
--

2.51.0

#9 lore raw

PATCH 4/7 i3c: mipi-i3c-hci: Add quirk to allow IBI while runtime suspended

In reply to Adrian Hunter

Some I3C controllers can be automatically runtime-resumed in order to handle in-band interrupts (IBIs), meaning that runtime suspend does not need to be blocked when IBIs are enabled.

For example, a PCI-attached controller in a low-power state may generate a Power Management Event (PME) when the SDA line is pulled low to signal the START condition of an IBI. The PCI subsystem will then runtime-resume the device, allowing the IBI to be received without requiring the controller to remain active.

Introduce a new quirk, HCI_QUIRK_RPM_IBI_ALLOWED, so that drivers can opt-in to this capability via driver data.

---
 drivers/i3c/master/mipi-i3c-hci/core.c | 3 +++
 drivers/i3c/master/mipi-i3c-hci/hci.h  | 1 +
 2 files changed, 4 insertions(+)
drivers/i3c/master/mipi-i3c-hci/core.c +3 -0
index e925584113d1..ec4dbe64c35e 100644
@@ -959,6 +959,9 @@ static int i3c_hci_probe(struct platform_device *pdev)
 	if (hci->quirks & HCI_QUIRK_RPM_ALLOWED)
 		i3c_hci_rpm_enable(&pdev->dev);
 
+	if (hci->quirks & HCI_QUIRK_RPM_IBI_ALLOWED)
+		hci->master.rpm_ibi_allowed = true;
+
 	return i3c_master_register(&hci->master, &pdev->dev, &i3c_hci_ops, false);
 }
 
drivers/i3c/master/mipi-i3c-hci/hci.h +1 -0
index 6035f74212db..819328a85b84 100644
@@ -146,6 +146,7 @@ struct i3c_hci_dev_data {
 #define HCI_QUIRK_OD_PP_TIMING		BIT(3)  /* Set OD and PP timings for AMD platforms */
 #define HCI_QUIRK_RESP_BUF_THLD		BIT(4)  /* Set resp buf thld to 0 for AMD platforms */
 #define HCI_QUIRK_RPM_ALLOWED		BIT(5)  /* Runtime PM allowed */
+#define HCI_QUIRK_RPM_IBI_ALLOWED	BIT(6)  /* IBI and Hot-Join allowed while runtime suspended */
 
 /* global functions */
 void mipi_i3c_hci_resume(struct i3c_hci *hci);
Signature · 1 line
2.51.0
#10 lore raw

PATCH 5/7 i3c: mipi-i3c-hci: Allow parent to manage runtime PM

In reply to Adrian Hunter

Some platforms implement the MIPI I3C HCI Multi-Bus Instance capability, where a single parent device hosts multiple I3C controller instances. In such designs, the parent - not the individual child instances - may need to coordinate runtime PM so that all controllers enter low-power states together, and all runtime suspend callbacks are invoked in a controlled and synchronized manner.

For example, if the parent enables IBI-wakeup when transitioning into a low-power state, every bus instance must remain able to receive IBIs up until that point. This requires deferring the individual controllers’ runtime suspend callbacks (which disable bus activity) until the parent decides it is safe for all instances to suspend together.

To support this usage model:

* Export the controller's runtime PM suspend/resume callbacks so that the parent can invoke them directly.

* Add a new quirk, HCI_QUIRK_RPM_PARENT_MANAGED, which designates the parent device as the controller’s runtime PM device (rpm_dev). When used without HCI_QUIRK_RPM_ALLOWED, this also prevents the child instance’s system-suspend callbacks from using pm_runtime_force_suspend()/pm_runtime_force_resume(), since runtime PM is managed entirely by the parent.

* Move DEFAULT_AUTOSUSPEND_DELAY_MS into the header so it can be shared by parent-managed PM implementations.

The new quirk allows platforms with multi-bus parent-managed PM infrastructure to correctly coordinate runtime PM across all I3C HCI instances.

---
 drivers/i3c/master/mipi-i3c-hci/core.c | 25 ++++++++++++++++---------
 drivers/i3c/master/mipi-i3c-hci/hci.h  |  6 ++++++
 2 files changed, 22 insertions(+), 9 deletions(-)
drivers/i3c/master/mipi-i3c-hci/core.c +16 -9
index ec4dbe64c35e..cb974b0f9e17 100644
@@ -733,7 +733,7 @@ static int i3c_hci_reset_and_init(struct i3c_hci *hci)
 	return 0;
 }
 
-static int i3c_hci_runtime_suspend(struct device *dev)
+int i3c_hci_runtime_suspend(struct device *dev)
 {
 	struct i3c_hci *hci = dev_get_drvdata(dev);
 	int ret;
@@ -746,8 +746,9 @@ static int i3c_hci_runtime_suspend(struct device *dev)
 
 	return 0;
 }
+EXPORT_SYMBOL_GPL(i3c_hci_runtime_suspend);
 
-static int i3c_hci_runtime_resume(struct device *dev)
+int i3c_hci_runtime_resume(struct device *dev)
 {
 	struct i3c_hci *hci = dev_get_drvdata(dev);
 	int ret;
@@ -768,6 +769,7 @@ static int i3c_hci_runtime_resume(struct device *dev)
 
 	return 0;
 }
+EXPORT_SYMBOL_GPL(i3c_hci_runtime_resume);
 
 static int i3c_hci_suspend(struct device *dev)
 {
@@ -784,12 +786,14 @@ static int i3c_hci_resume_common(struct device *dev, bool rstdaa)
 	struct i3c_hci *hci = dev_get_drvdata(dev);
 	int ret;
 
-	if (!(hci->quirks & HCI_QUIRK_RPM_ALLOWED))
-		return 0;
+	if (!(hci->quirks & HCI_QUIRK_RPM_PARENT_MANAGED)) {
+		if (!(hci->quirks & HCI_QUIRK_RPM_ALLOWED))
+			return 0;
 
-	ret = pm_runtime_force_resume(dev);
-	if (ret)
-		return ret;
+		ret = pm_runtime_force_resume(dev);
+		if (ret)
+			return ret;
+	}
 
 	ret = i3c_master_do_daa_ext(&hci->master, rstdaa);
 	if (ret)
@@ -812,8 +816,6 @@ static int i3c_hci_restore(struct device *dev)
 	return i3c_hci_resume_common(dev, true);
 }
 
-#define DEFAULT_AUTOSUSPEND_DELAY_MS 1000
-
 static void i3c_hci_rpm_enable(struct device *dev)
 {
 	struct i3c_hci *hci = dev_get_drvdata(dev);
@@ -962,6 +964,11 @@ static int i3c_hci_probe(struct platform_device *pdev)
 	if (hci->quirks & HCI_QUIRK_RPM_IBI_ALLOWED)
 		hci->master.rpm_ibi_allowed = true;
 
+	if (hci->quirks & HCI_QUIRK_RPM_PARENT_MANAGED) {
+		hci->master.rpm_dev = pdev->dev.parent;
+		hci->master.rpm_allowed = true;
+	}
+
 	return i3c_master_register(&hci->master, &pdev->dev, &i3c_hci_ops, false);
 }
 
drivers/i3c/master/mipi-i3c-hci/hci.h +6 -0
index 819328a85b84..d0e7ad58ac15 100644
@@ -147,6 +147,7 @@ struct i3c_hci_dev_data {
 #define HCI_QUIRK_RESP_BUF_THLD		BIT(4)  /* Set resp buf thld to 0 for AMD platforms */
 #define HCI_QUIRK_RPM_ALLOWED		BIT(5)  /* Runtime PM allowed */
 #define HCI_QUIRK_RPM_IBI_ALLOWED	BIT(6)  /* IBI and Hot-Join allowed while runtime suspended */
+#define HCI_QUIRK_RPM_PARENT_MANAGED	BIT(7)  /* Runtime PM managed by parent device */
 
 /* global functions */
 void mipi_i3c_hci_resume(struct i3c_hci *hci);
@@ -156,4 +157,9 @@ void amd_set_od_pp_timing(struct i3c_hci *hci);
 void amd_set_resp_buf_thld(struct i3c_hci *hci);
 void i3c_hci_sync_irq_inactive(struct i3c_hci *hci);
 
+#define DEFAULT_AUTOSUSPEND_DELAY_MS 1000
+
+int i3c_hci_runtime_suspend(struct device *dev);
+int i3c_hci_runtime_resume(struct device *dev);
+
 #endif
Signature · 1 line
2.51.0
reviewer
#11 lore raw

Re: [PATCH 5/7] i3c: mipi-i3c-hci: Allow parent to manage runtime PM

Quoted 9 lines · Adrian Hunter

Some platforms implement the MIPI I3C HCI Multi-Bus Instance capability, where a single parent device hosts multiple I3C controller instances. In such designs, the parent - not the individual child instances - may need to coordinate runtime PM so that all controllers enter low-power states together, and all runtime suspend callbacks are invoked in a controlled and synchronized manner.

For example, if the parent enables IBI-wakeup when transitioning into a low-power state,

Does your hardware support recieve IBI when runtime suspend?

Frank

Quoted 128 lines

every bus instance must remain able to receive IBIs up
until that point. This requires deferring the individual controllers’ runtime suspend callbacks (which disable bus activity) until the parent decides it is safe for all instances to suspend together.

To support this usage model:

* Export the controller's runtime PM suspend/resume callbacks so that the parent can invoke them directly.

* Add a new quirk, HCI_QUIRK_RPM_PARENT_MANAGED, which designates the parent device as the controller’s runtime PM device (rpm_dev). When used without HCI_QUIRK_RPM_ALLOWED, this also prevents the child instance’s system-suspend callbacks from using pm_runtime_force_suspend()/pm_runtime_force_resume(), since runtime PM is managed entirely by the parent.

* Move DEFAULT_AUTOSUSPEND_DELAY_MS into the header so it can be shared by parent-managed PM implementations.

The new quirk allows platforms with multi-bus parent-managed PM infrastructure to correctly coordinate runtime PM across all I3C HCI instances.

---
 drivers/i3c/master/mipi-i3c-hci/core.c | 25 ++++++++++++++++---------
 drivers/i3c/master/mipi-i3c-hci/hci.h  |  6 ++++++
 2 files changed, 22 insertions(+), 9 deletions(-)
drivers/i3c/master/mipi-i3c-hci/core.c +16 -9
index ec4dbe64c35e..cb974b0f9e17 100644
@@ -733,7 +733,7 @@ static int i3c_hci_reset_and_init(struct i3c_hci *hci)
 	return 0;
 }
 
-static int i3c_hci_runtime_suspend(struct device *dev)
+int i3c_hci_runtime_suspend(struct device *dev)
 {
 	struct i3c_hci *hci = dev_get_drvdata(dev);
 	int ret;
@@ -746,8 +746,9 @@ static int i3c_hci_runtime_suspend(struct device *dev)
 
 	return 0;
 }
+EXPORT_SYMBOL_GPL(i3c_hci_runtime_suspend);
 
-static int i3c_hci_runtime_resume(struct device *dev)
+int i3c_hci_runtime_resume(struct device *dev)
 {
 	struct i3c_hci *hci = dev_get_drvdata(dev);
 	int ret;
@@ -768,6 +769,7 @@ static int i3c_hci_runtime_resume(struct device *dev)
 
 	return 0;
 }
+EXPORT_SYMBOL_GPL(i3c_hci_runtime_resume);
 
 static int i3c_hci_suspend(struct device *dev)
 {
@@ -784,12 +786,14 @@ static int i3c_hci_resume_common(struct device *dev, bool rstdaa)
 	struct i3c_hci *hci = dev_get_drvdata(dev);
 	int ret;
 
-	if (!(hci->quirks & HCI_QUIRK_RPM_ALLOWED))
-		return 0;
+	if (!(hci->quirks & HCI_QUIRK_RPM_PARENT_MANAGED)) {
+		if (!(hci->quirks & HCI_QUIRK_RPM_ALLOWED))
+			return 0;
 
-	ret = pm_runtime_force_resume(dev);
-	if (ret)
-		return ret;
+		ret = pm_runtime_force_resume(dev);
+		if (ret)
+			return ret;
+	}
 
 	ret = i3c_master_do_daa_ext(&hci->master, rstdaa);
 	if (ret)
@@ -812,8 +816,6 @@ static int i3c_hci_restore(struct device *dev)
 	return i3c_hci_resume_common(dev, true);
 }
 
-#define DEFAULT_AUTOSUSPEND_DELAY_MS 1000
-
 static void i3c_hci_rpm_enable(struct device *dev)
 {
 	struct i3c_hci *hci = dev_get_drvdata(dev);
@@ -962,6 +964,11 @@ static int i3c_hci_probe(struct platform_device *pdev)
 	if (hci->quirks & HCI_QUIRK_RPM_IBI_ALLOWED)
 		hci->master.rpm_ibi_allowed = true;
 
+	if (hci->quirks & HCI_QUIRK_RPM_PARENT_MANAGED) {
+		hci->master.rpm_dev = pdev->dev.parent;
+		hci->master.rpm_allowed = true;
+	}
+
 	return i3c_master_register(&hci->master, &pdev->dev, &i3c_hci_ops, false);
 }
 
drivers/i3c/master/mipi-i3c-hci/hci.h +6 -1
index 819328a85b84..d0e7ad58ac15 100644
@@ -147,6 +147,7 @@ struct i3c_hci_dev_data {
 #define HCI_QUIRK_RESP_BUF_THLD		BIT(4)  /* Set resp buf thld to 0 for AMD platforms */
 #define HCI_QUIRK_RPM_ALLOWED		BIT(5)  /* Runtime PM allowed */
 #define HCI_QUIRK_RPM_IBI_ALLOWED	BIT(6)  /* IBI and Hot-Join allowed while runtime suspended */
+#define HCI_QUIRK_RPM_PARENT_MANAGED	BIT(7)  /* Runtime PM managed by parent device */
 
 /* global functions */
 void mipi_i3c_hci_resume(struct i3c_hci *hci);
@@ -156,4 +157,9 @@ void amd_set_od_pp_timing(struct i3c_hci *hci);
 void amd_set_resp_buf_thld(struct i3c_hci *hci);
 void i3c_hci_sync_irq_inactive(struct i3c_hci *hci);
 
+#define DEFAULT_AUTOSUSPEND_DELAY_MS 1000
+
+int i3c_hci_runtime_suspend(struct device *dev);
+int i3c_hci_runtime_resume(struct device *dev);
+
 #endif
--

2.51.0

#12 lore raw

Re: [PATCH 5/7] i3c: mipi-i3c-hci: Allow parent to manage runtime PM

Quoted 8 lines · Frank Li
Quoted 7 lines · Adrian Hunter

Some platforms implement the MIPI I3C HCI Multi-Bus Instance capability, where a single parent device hosts multiple I3C controller instances. In such designs, the parent - not the individual child instances - may need to coordinate runtime PM so that all controllers enter low-power states together, and all runtime suspend callbacks are invoked in a controlled and synchronized manner.

Quoted 4 lines
Quoted 2 lines

For example, if the parent enables IBI-wakeup when transitioning into a low-power state,

Does your hardware support recieve IBI when runtime suspend?

When runtime suspended (in D3), the hardware first triggers a Power Management Event (PME) when the SDA line is pulled low to signal the START condition of an IBI. The PCI subsystem will then runtime-resume the device. When the bus is enabled, the clock is started and the IBI is received.

Quoted 131 lines

Frank

Quoted 128 lines

every bus instance must remain able to receive IBIs up
until that point. This requires deferring the individual controllers’ runtime suspend callbacks (which disable bus activity) until the parent decides it is safe for all instances to suspend together.

To support this usage model:

* Export the controller's runtime PM suspend/resume callbacks so that the parent can invoke them directly.

* Add a new quirk, HCI_QUIRK_RPM_PARENT_MANAGED, which designates the parent device as the controller’s runtime PM device (rpm_dev). When used without HCI_QUIRK_RPM_ALLOWED, this also prevents the child instance’s system-suspend callbacks from using pm_runtime_force_suspend()/pm_runtime_force_resume(), since runtime PM is managed entirely by the parent.

* Move DEFAULT_AUTOSUSPEND_DELAY_MS into the header so it can be shared by parent-managed PM implementations.

The new quirk allows platforms with multi-bus parent-managed PM infrastructure to correctly coordinate runtime PM across all I3C HCI instances.

---
 drivers/i3c/master/mipi-i3c-hci/core.c | 25 ++++++++++++++++---------
 drivers/i3c/master/mipi-i3c-hci/hci.h  |  6 ++++++
 2 files changed, 22 insertions(+), 9 deletions(-)
drivers/i3c/master/mipi-i3c-hci/core.c +16 -9
index ec4dbe64c35e..cb974b0f9e17 100644
@@ -733,7 +733,7 @@ static int i3c_hci_reset_and_init(struct i3c_hci *hci)
 	return 0;
 }
 
-static int i3c_hci_runtime_suspend(struct device *dev)
+int i3c_hci_runtime_suspend(struct device *dev)
 {
 	struct i3c_hci *hci = dev_get_drvdata(dev);
 	int ret;
@@ -746,8 +746,9 @@ static int i3c_hci_runtime_suspend(struct device *dev)
 
 	return 0;
 }
+EXPORT_SYMBOL_GPL(i3c_hci_runtime_suspend);
 
-static int i3c_hci_runtime_resume(struct device *dev)
+int i3c_hci_runtime_resume(struct device *dev)
 {
 	struct i3c_hci *hci = dev_get_drvdata(dev);
 	int ret;
@@ -768,6 +769,7 @@ static int i3c_hci_runtime_resume(struct device *dev)
 
 	return 0;
 }
+EXPORT_SYMBOL_GPL(i3c_hci_runtime_resume);
 
 static int i3c_hci_suspend(struct device *dev)
 {
@@ -784,12 +786,14 @@ static int i3c_hci_resume_common(struct device *dev, bool rstdaa)
 	struct i3c_hci *hci = dev_get_drvdata(dev);
 	int ret;
 
-	if (!(hci->quirks & HCI_QUIRK_RPM_ALLOWED))
-		return 0;
+	if (!(hci->quirks & HCI_QUIRK_RPM_PARENT_MANAGED)) {
+		if (!(hci->quirks & HCI_QUIRK_RPM_ALLOWED))
+			return 0;
 
-	ret = pm_runtime_force_resume(dev);
-	if (ret)
-		return ret;
+		ret = pm_runtime_force_resume(dev);
+		if (ret)
+			return ret;
+	}
 
 	ret = i3c_master_do_daa_ext(&hci->master, rstdaa);
 	if (ret)
@@ -812,8 +816,6 @@ static int i3c_hci_restore(struct device *dev)
 	return i3c_hci_resume_common(dev, true);
 }
 
-#define DEFAULT_AUTOSUSPEND_DELAY_MS 1000
-
 static void i3c_hci_rpm_enable(struct device *dev)
 {
 	struct i3c_hci *hci = dev_get_drvdata(dev);
@@ -962,6 +964,11 @@ static int i3c_hci_probe(struct platform_device *pdev)
 	if (hci->quirks & HCI_QUIRK_RPM_IBI_ALLOWED)
 		hci->master.rpm_ibi_allowed = true;
 
+	if (hci->quirks & HCI_QUIRK_RPM_PARENT_MANAGED) {
+		hci->master.rpm_dev = pdev->dev.parent;
+		hci->master.rpm_allowed = true;
+	}
+
 	return i3c_master_register(&hci->master, &pdev->dev, &i3c_hci_ops, false);
 }
 
drivers/i3c/master/mipi-i3c-hci/hci.h +6 -1
index 819328a85b84..d0e7ad58ac15 100644
@@ -147,6 +147,7 @@ struct i3c_hci_dev_data {
 #define HCI_QUIRK_RESP_BUF_THLD		BIT(4)  /* Set resp buf thld to 0 for AMD platforms */
 #define HCI_QUIRK_RPM_ALLOWED		BIT(5)  /* Runtime PM allowed */
 #define HCI_QUIRK_RPM_IBI_ALLOWED	BIT(6)  /* IBI and Hot-Join allowed while runtime suspended */
+#define HCI_QUIRK_RPM_PARENT_MANAGED	BIT(7)  /* Runtime PM managed by parent device */
 
 /* global functions */
 void mipi_i3c_hci_resume(struct i3c_hci *hci);
@@ -156,4 +157,9 @@ void amd_set_od_pp_timing(struct i3c_hci *hci);
 void amd_set_resp_buf_thld(struct i3c_hci *hci);
 void i3c_hci_sync_irq_inactive(struct i3c_hci *hci);
 
+#define DEFAULT_AUTOSUSPEND_DELAY_MS 1000
+
+int i3c_hci_runtime_suspend(struct device *dev);
+int i3c_hci_runtime_resume(struct device *dev);
+
 #endif
--

2.51.0

reviewer
#13 lore raw

Re: [PATCH 5/7] i3c: mipi-i3c-hci: Allow parent to manage runtime PM

Quoted 12 lines · Adrian Hunter
Quoted 11 lines · Frank Li
Quoted 9 lines · Adrian Hunter

Some platforms implement the MIPI I3C HCI Multi-Bus Instance capability, where a single parent device hosts multiple I3C controller instances. In such designs, the parent - not the individual child instances - may need to coordinate runtime PM so that all controllers enter low-power states together, and all runtime suspend callbacks are invoked in a controlled and synchronized manner.

For example, if the parent enables IBI-wakeup when transitioning into a low-power state,

Quoted 6 lines
Quoted 1 line

Does your hardware support recieve IBI when runtime suspend?

When runtime suspended (in D3), the hardware first triggers a Power Management Event (PME) when the SDA line is pulled low to signal the START condition of an IBI. The PCI subsystem will then runtime-resume the device. When the bus is enabled, the clock is started and the IBI is received.

It align my assumption, why need complex solution.

SDA->PME->IRQ should handle by hardware, so irq handle queue IBI to working queue.

IBI work will try do transfer, which will call runtime resume(), then transfer data.

What's issue?

Frank

Quoted 133 lines
Quoted 132 lines

Frank

Quoted 128 lines

every bus instance must remain able to receive IBIs up
until that point. This requires deferring the individual controllers’ runtime suspend callbacks (which disable bus activity) until the parent decides it is safe for all instances to suspend together.

To support this usage model:

* Export the controller's runtime PM suspend/resume callbacks so that the parent can invoke them directly.

* Add a new quirk, HCI_QUIRK_RPM_PARENT_MANAGED, which designates the parent device as the controller’s runtime PM device (rpm_dev). When used without HCI_QUIRK_RPM_ALLOWED, this also prevents the child instance’s system-suspend callbacks from using pm_runtime_force_suspend()/pm_runtime_force_resume(), since runtime PM is managed entirely by the parent.

* Move DEFAULT_AUTOSUSPEND_DELAY_MS into the header so it can be shared by parent-managed PM implementations.

The new quirk allows platforms with multi-bus parent-managed PM infrastructure to correctly coordinate runtime PM across all I3C HCI instances.

---
 drivers/i3c/master/mipi-i3c-hci/core.c | 25 ++++++++++++++++---------
 drivers/i3c/master/mipi-i3c-hci/hci.h  |  6 ++++++
 2 files changed, 22 insertions(+), 9 deletions(-)
drivers/i3c/master/mipi-i3c-hci/core.c +16 -9
index ec4dbe64c35e..cb974b0f9e17 100644
@@ -733,7 +733,7 @@ static int i3c_hci_reset_and_init(struct i3c_hci *hci)
 	return 0;
 }
 
-static int i3c_hci_runtime_suspend(struct device *dev)
+int i3c_hci_runtime_suspend(struct device *dev)
 {
 	struct i3c_hci *hci = dev_get_drvdata(dev);
 	int ret;
@@ -746,8 +746,9 @@ static int i3c_hci_runtime_suspend(struct device *dev)
 
 	return 0;
 }
+EXPORT_SYMBOL_GPL(i3c_hci_runtime_suspend);
 
-static int i3c_hci_runtime_resume(struct device *dev)
+int i3c_hci_runtime_resume(struct device *dev)
 {
 	struct i3c_hci *hci = dev_get_drvdata(dev);
 	int ret;
@@ -768,6 +769,7 @@ static int i3c_hci_runtime_resume(struct device *dev)
 
 	return 0;
 }
+EXPORT_SYMBOL_GPL(i3c_hci_runtime_resume);
 
 static int i3c_hci_suspend(struct device *dev)
 {
@@ -784,12 +786,14 @@ static int i3c_hci_resume_common(struct device *dev, bool rstdaa)
 	struct i3c_hci *hci = dev_get_drvdata(dev);
 	int ret;
 
-	if (!(hci->quirks & HCI_QUIRK_RPM_ALLOWED))
-		return 0;
+	if (!(hci->quirks & HCI_QUIRK_RPM_PARENT_MANAGED)) {
+		if (!(hci->quirks & HCI_QUIRK_RPM_ALLOWED))
+			return 0;
 
-	ret = pm_runtime_force_resume(dev);
-	if (ret)
-		return ret;
+		ret = pm_runtime_force_resume(dev);
+		if (ret)
+			return ret;
+	}
 
 	ret = i3c_master_do_daa_ext(&hci->master, rstdaa);
 	if (ret)
@@ -812,8 +816,6 @@ static int i3c_hci_restore(struct device *dev)
 	return i3c_hci_resume_common(dev, true);
 }
 
-#define DEFAULT_AUTOSUSPEND_DELAY_MS 1000
-
 static void i3c_hci_rpm_enable(struct device *dev)
 {
 	struct i3c_hci *hci = dev_get_drvdata(dev);
@@ -962,6 +964,11 @@ static int i3c_hci_probe(struct platform_device *pdev)
 	if (hci->quirks & HCI_QUIRK_RPM_IBI_ALLOWED)
 		hci->master.rpm_ibi_allowed = true;
 
+	if (hci->quirks & HCI_QUIRK_RPM_PARENT_MANAGED) {
+		hci->master.rpm_dev = pdev->dev.parent;
+		hci->master.rpm_allowed = true;
+	}
+
 	return i3c_master_register(&hci->master, &pdev->dev, &i3c_hci_ops, false);
 }
 
drivers/i3c/master/mipi-i3c-hci/hci.h +6 -1
index 819328a85b84..d0e7ad58ac15 100644
@@ -147,6 +147,7 @@ struct i3c_hci_dev_data {
 #define HCI_QUIRK_RESP_BUF_THLD		BIT(4)  /* Set resp buf thld to 0 for AMD platforms */
 #define HCI_QUIRK_RPM_ALLOWED		BIT(5)  /* Runtime PM allowed */
 #define HCI_QUIRK_RPM_IBI_ALLOWED	BIT(6)  /* IBI and Hot-Join allowed while runtime suspended */
+#define HCI_QUIRK_RPM_PARENT_MANAGED	BIT(7)  /* Runtime PM managed by parent device */
 
 /* global functions */
 void mipi_i3c_hci_resume(struct i3c_hci *hci);
@@ -156,4 +157,9 @@ void amd_set_od_pp_timing(struct i3c_hci *hci);
 void amd_set_resp_buf_thld(struct i3c_hci *hci);
 void i3c_hci_sync_irq_inactive(struct i3c_hci *hci);
 
+#define DEFAULT_AUTOSUSPEND_DELAY_MS 1000
+
+int i3c_hci_runtime_suspend(struct device *dev);
+int i3c_hci_runtime_resume(struct device *dev);
+
 #endif
--

2.51.0

#14 lore raw

Re: [PATCH 5/7] i3c: mipi-i3c-hci: Allow parent to manage runtime PM

Quoted 22 lines · Frank Li
Quoted 12 lines · Adrian Hunter
Quoted 11 lines · Frank Li
Quoted 9 lines · Adrian Hunter

Some platforms implement the MIPI I3C HCI Multi-Bus Instance capability, where a single parent device hosts multiple I3C controller instances. In such designs, the parent - not the individual child instances - may need to coordinate runtime PM so that all controllers enter low-power states together, and all runtime suspend callbacks are invoked in a controlled and synchronized manner.

For example, if the parent enables IBI-wakeup when transitioning into a low-power state,

Quoted 6 lines
Quoted 1 line

Does your hardware support recieve IBI when runtime suspend?

When runtime suspended (in D3), the hardware first triggers a Power Management Event (PME) when the SDA line is pulled low to signal the START condition of an IBI. The PCI subsystem will then runtime-resume the device. When the bus is enabled, the clock is started and the IBI is received.

It align my assumption, why need complex solution.

Quoted 7 lines

SDA->PME->IRQ should handle by hardware, so irq handle queue IBI to working queue.

IBI work will try do transfer, which will call runtime resume(), then transfer data.

What's issue?

The PME indicates I3C START (SDA line pulled low). The controller is in a low power state unable to operate the bus. At this point it is not known what I3C device has pulled down the SDA line, or even if it is an IBI since it is indistinguishable from hot-join at this point.

The PCI PME IRQ is not the device's IRQ. It is handled by acpi_irq() which ultimately informs the PCI subsystem to wake the PCI device. The PCI subsystem performs pm_request_resume(), refer pci_acpi_wake_dev().

When the controller is resumed, it enables the I3C bus and the IBI is finally delivered normally.

However, none of that is related to this patch.

This patch is because the PCI device has 2 I3C bus instances and only 1 PME wakeup. The PME becomes active when the PCI device is put to a low power state. Both I3C bus instances must be runtime suspended then. Similarly, upon resume the PME is no longer active, so both I3C bus instances must make their buses operational - we don't know which may have received an IBI. And there may be further IBIs which can't be received unless the associated bus is operational. The PCI device is no longer in a low power state, so there will be no PME in that case.

Quoted 136 lines

Frank

Quoted 133 lines
Quoted 132 lines

Frank

Quoted 128 lines

every bus instance must remain able to receive IBIs up
until that point. This requires deferring the individual controllers’ runtime suspend callbacks (which disable bus activity) until the parent decides it is safe for all instances to suspend together.

To support this usage model:

* Export the controller's runtime PM suspend/resume callbacks so that the parent can invoke them directly.

* Add a new quirk, HCI_QUIRK_RPM_PARENT_MANAGED, which designates the parent device as the controller’s runtime PM device (rpm_dev). When used without HCI_QUIRK_RPM_ALLOWED, this also prevents the child instance’s system-suspend callbacks from using pm_runtime_force_suspend()/pm_runtime_force_resume(), since runtime PM is managed entirely by the parent.

* Move DEFAULT_AUTOSUSPEND_DELAY_MS into the header so it can be shared by parent-managed PM implementations.

The new quirk allows platforms with multi-bus parent-managed PM infrastructure to correctly coordinate runtime PM across all I3C HCI instances.

---
 drivers/i3c/master/mipi-i3c-hci/core.c | 25 ++++++++++++++++---------
 drivers/i3c/master/mipi-i3c-hci/hci.h  |  6 ++++++
 2 files changed, 22 insertions(+), 9 deletions(-)
drivers/i3c/master/mipi-i3c-hci/core.c +16 -9
index ec4dbe64c35e..cb974b0f9e17 100644
@@ -733,7 +733,7 @@ static int i3c_hci_reset_and_init(struct i3c_hci *hci)
 	return 0;
 }
 
-static int i3c_hci_runtime_suspend(struct device *dev)
+int i3c_hci_runtime_suspend(struct device *dev)
 {
 	struct i3c_hci *hci = dev_get_drvdata(dev);
 	int ret;
@@ -746,8 +746,9 @@ static int i3c_hci_runtime_suspend(struct device *dev)
 
 	return 0;
 }
+EXPORT_SYMBOL_GPL(i3c_hci_runtime_suspend);
 
-static int i3c_hci_runtime_resume(struct device *dev)
+int i3c_hci_runtime_resume(struct device *dev)
 {
 	struct i3c_hci *hci = dev_get_drvdata(dev);
 	int ret;
@@ -768,6 +769,7 @@ static int i3c_hci_runtime_resume(struct device *dev)
 
 	return 0;
 }
+EXPORT_SYMBOL_GPL(i3c_hci_runtime_resume);
 
 static int i3c_hci_suspend(struct device *dev)
 {
@@ -784,12 +786,14 @@ static int i3c_hci_resume_common(struct device *dev, bool rstdaa)
 	struct i3c_hci *hci = dev_get_drvdata(dev);
 	int ret;
 
-	if (!(hci->quirks & HCI_QUIRK_RPM_ALLOWED))
-		return 0;
+	if (!(hci->quirks & HCI_QUIRK_RPM_PARENT_MANAGED)) {
+		if (!(hci->quirks & HCI_QUIRK_RPM_ALLOWED))
+			return 0;
 
-	ret = pm_runtime_force_resume(dev);
-	if (ret)
-		return ret;
+		ret = pm_runtime_force_resume(dev);
+		if (ret)
+			return ret;
+	}
 
 	ret = i3c_master_do_daa_ext(&hci->master, rstdaa);
 	if (ret)
@@ -812,8 +816,6 @@ static int i3c_hci_restore(struct device *dev)
 	return i3c_hci_resume_common(dev, true);
 }
 
-#define DEFAULT_AUTOSUSPEND_DELAY_MS 1000
-
 static void i3c_hci_rpm_enable(struct device *dev)
 {
 	struct i3c_hci *hci = dev_get_drvdata(dev);
@@ -962,6 +964,11 @@ static int i3c_hci_probe(struct platform_device *pdev)
 	if (hci->quirks & HCI_QUIRK_RPM_IBI_ALLOWED)
 		hci->master.rpm_ibi_allowed = true;
 
+	if (hci->quirks & HCI_QUIRK_RPM_PARENT_MANAGED) {
+		hci->master.rpm_dev = pdev->dev.parent;
+		hci->master.rpm_allowed = true;
+	}
+
 	return i3c_master_register(&hci->master, &pdev->dev, &i3c_hci_ops, false);
 }
 
drivers/i3c/master/mipi-i3c-hci/hci.h +6 -1
index 819328a85b84..d0e7ad58ac15 100644
@@ -147,6 +147,7 @@ struct i3c_hci_dev_data {
 #define HCI_QUIRK_RESP_BUF_THLD		BIT(4)  /* Set resp buf thld to 0 for AMD platforms */
 #define HCI_QUIRK_RPM_ALLOWED		BIT(5)  /* Runtime PM allowed */
 #define HCI_QUIRK_RPM_IBI_ALLOWED	BIT(6)  /* IBI and Hot-Join allowed while runtime suspended */
+#define HCI_QUIRK_RPM_PARENT_MANAGED	BIT(7)  /* Runtime PM managed by parent device */
 
 /* global functions */
 void mipi_i3c_hci_resume(struct i3c_hci *hci);
@@ -156,4 +157,9 @@ void amd_set_od_pp_timing(struct i3c_hci *hci);
 void amd_set_resp_buf_thld(struct i3c_hci *hci);
 void i3c_hci_sync_irq_inactive(struct i3c_hci *hci);
 
+#define DEFAULT_AUTOSUSPEND_DELAY_MS 1000
+
+int i3c_hci_runtime_suspend(struct device *dev);
+int i3c_hci_runtime_resume(struct device *dev);
+
 #endif
--

2.51.0

reviewer
#15 lore raw

Re: [PATCH 5/7] i3c: mipi-i3c-hci: Allow parent to manage runtime PM

Quoted 40 lines · Adrian Hunter
Quoted 22 lines · Frank Li
Quoted 12 lines · Adrian Hunter
Quoted 11 lines · Frank Li
Quoted 9 lines · Adrian Hunter

Some platforms implement the MIPI I3C HCI Multi-Bus Instance capability, where a single parent device hosts multiple I3C controller instances. In such designs, the parent - not the individual child instances - may need to coordinate runtime PM so that all controllers enter low-power states together, and all runtime suspend callbacks are invoked in a controlled and synchronized manner.

For example, if the parent enables IBI-wakeup when transitioning into a low-power state,

Quoted 6 lines
Quoted 1 line

Does your hardware support recieve IBI when runtime suspend?

When runtime suspended (in D3), the hardware first triggers a Power Management Event (PME) when the SDA line is pulled low to signal the START condition of an IBI. The PCI subsystem will then runtime-resume the device. When the bus is enabled, the clock is started and the IBI is received.

It align my assumption, why need complex solution.

Quoted 7 lines

SDA->PME->IRQ should handle by hardware, so irq handle queue IBI to working queue.

IBI work will try do transfer, which will call runtime resume(), then transfer data.

What's issue?

The PME indicates I3C START (SDA line pulled low). The controller is in a low power state unable to operate the bus. At this point it is not known what I3C device has pulled down the SDA line, or even if it is an IBI since it is indistinguishable from hot-join at this point.

The PCI PME IRQ is not the device's IRQ. It is handled by acpi_irq() which ultimately informs the PCI subsystem to wake the PCI device. The PCI subsystem performs pm_request_resume(), refer pci_acpi_wake_dev().

Quoted 8 lines

When the controller is resumed, it enables the I3C bus and the IBI is finally delivered normally.

However, none of that is related to this patch.

This patch is because the PCI device has 2 I3C bus instances and only 1 PME wakeup. The PME becomes active when the PCI device is put to a low power state.

One instance 1 suspend, instance 2 running, PME is inactive, what's happen if instance 1 request IBI?

IBI will be missed?

Quoted 4 lines

Both I3C bus instances must be runtime suspended then.
Similarly, upon resume the PME is no longer active, so both I3C bus instances must make their buses operational - we don't know which may have received an IBI.

Does PME active auto by hardware or need software config?

Frank

Quoted 141 lines

And there may be further IBIs which can't be received unless the associated bus is operational. The PCI device is no longer in a low power state, so there will be no PME in that case.

Quoted 136 lines

Frank

Quoted 133 lines
Quoted 132 lines

Frank

Quoted 128 lines

every bus instance must remain able to receive IBIs up
until that point. This requires deferring the individual controllers’ runtime suspend callbacks (which disable bus activity) until the parent decides it is safe for all instances to suspend together.

To support this usage model:

* Export the controller's runtime PM suspend/resume callbacks so that the parent can invoke them directly.

* Add a new quirk, HCI_QUIRK_RPM_PARENT_MANAGED, which designates the parent device as the controller’s runtime PM device (rpm_dev). When used without HCI_QUIRK_RPM_ALLOWED, this also prevents the child instance’s system-suspend callbacks from using pm_runtime_force_suspend()/pm_runtime_force_resume(), since runtime PM is managed entirely by the parent.

* Move DEFAULT_AUTOSUSPEND_DELAY_MS into the header so it can be shared by parent-managed PM implementations.

The new quirk allows platforms with multi-bus parent-managed PM infrastructure to correctly coordinate runtime PM across all I3C HCI instances.

---
 drivers/i3c/master/mipi-i3c-hci/core.c | 25 ++++++++++++++++---------
 drivers/i3c/master/mipi-i3c-hci/hci.h  |  6 ++++++
 2 files changed, 22 insertions(+), 9 deletions(-)
drivers/i3c/master/mipi-i3c-hci/core.c +16 -9
index ec4dbe64c35e..cb974b0f9e17 100644
@@ -733,7 +733,7 @@ static int i3c_hci_reset_and_init(struct i3c_hci *hci)
 	return 0;
 }
 
-static int i3c_hci_runtime_suspend(struct device *dev)
+int i3c_hci_runtime_suspend(struct device *dev)
 {
 	struct i3c_hci *hci = dev_get_drvdata(dev);
 	int ret;
@@ -746,8 +746,9 @@ static int i3c_hci_runtime_suspend(struct device *dev)
 
 	return 0;
 }
+EXPORT_SYMBOL_GPL(i3c_hci_runtime_suspend);
 
-static int i3c_hci_runtime_resume(struct device *dev)
+int i3c_hci_runtime_resume(struct device *dev)
 {
 	struct i3c_hci *hci = dev_get_drvdata(dev);
 	int ret;
@@ -768,6 +769,7 @@ static int i3c_hci_runtime_resume(struct device *dev)
 
 	return 0;
 }
+EXPORT_SYMBOL_GPL(i3c_hci_runtime_resume);
 
 static int i3c_hci_suspend(struct device *dev)
 {
@@ -784,12 +786,14 @@ static int i3c_hci_resume_common(struct device *dev, bool rstdaa)
 	struct i3c_hci *hci = dev_get_drvdata(dev);
 	int ret;
 
-	if (!(hci->quirks & HCI_QUIRK_RPM_ALLOWED))
-		return 0;
+	if (!(hci->quirks & HCI_QUIRK_RPM_PARENT_MANAGED)) {
+		if (!(hci->quirks & HCI_QUIRK_RPM_ALLOWED))
+			return 0;
 
-	ret = pm_runtime_force_resume(dev);
-	if (ret)
-		return ret;
+		ret = pm_runtime_force_resume(dev);
+		if (ret)
+			return ret;
+	}
 
 	ret = i3c_master_do_daa_ext(&hci->master, rstdaa);
 	if (ret)
@@ -812,8 +816,6 @@ static int i3c_hci_restore(struct device *dev)
 	return i3c_hci_resume_common(dev, true);
 }
 
-#define DEFAULT_AUTOSUSPEND_DELAY_MS 1000
-
 static void i3c_hci_rpm_enable(struct device *dev)
 {
 	struct i3c_hci *hci = dev_get_drvdata(dev);
@@ -962,6 +964,11 @@ static int i3c_hci_probe(struct platform_device *pdev)
 	if (hci->quirks & HCI_QUIRK_RPM_IBI_ALLOWED)
 		hci->master.rpm_ibi_allowed = true;
 
+	if (hci->quirks & HCI_QUIRK_RPM_PARENT_MANAGED) {
+		hci->master.rpm_dev = pdev->dev.parent;
+		hci->master.rpm_allowed = true;
+	}
+
 	return i3c_master_register(&hci->master, &pdev->dev, &i3c_hci_ops, false);
 }
 
drivers/i3c/master/mipi-i3c-hci/hci.h +6 -1
index 819328a85b84..d0e7ad58ac15 100644
@@ -147,6 +147,7 @@ struct i3c_hci_dev_data {
 #define HCI_QUIRK_RESP_BUF_THLD		BIT(4)  /* Set resp buf thld to 0 for AMD platforms */
 #define HCI_QUIRK_RPM_ALLOWED		BIT(5)  /* Runtime PM allowed */
 #define HCI_QUIRK_RPM_IBI_ALLOWED	BIT(6)  /* IBI and Hot-Join allowed while runtime suspended */
+#define HCI_QUIRK_RPM_PARENT_MANAGED	BIT(7)  /* Runtime PM managed by parent device */
 
 /* global functions */
 void mipi_i3c_hci_resume(struct i3c_hci *hci);
@@ -156,4 +157,9 @@ void amd_set_od_pp_timing(struct i3c_hci *hci);
 void amd_set_resp_buf_thld(struct i3c_hci *hci);
 void i3c_hci_sync_irq_inactive(struct i3c_hci *hci);
 
+#define DEFAULT_AUTOSUSPEND_DELAY_MS 1000
+
+int i3c_hci_runtime_suspend(struct device *dev);
+int i3c_hci_runtime_resume(struct device *dev);
+
 #endif
--

2.51.0

#16 lore raw

Re: [PATCH 5/7] i3c: mipi-i3c-hci: Allow parent to manage runtime PM

Quoted 44 lines · Frank Li
Quoted 43 lines · Adrian Hunter
Quoted 22 lines · Frank Li
Quoted 12 lines · Adrian Hunter
Quoted 11 lines · Frank Li
Quoted 9 lines · Adrian Hunter

Some platforms implement the MIPI I3C HCI Multi-Bus Instance capability, where a single parent device hosts multiple I3C controller instances. In such designs, the parent - not the individual child instances - may need to coordinate runtime PM so that all controllers enter low-power states together, and all runtime suspend callbacks are invoked in a controlled and synchronized manner.

For example, if the parent enables IBI-wakeup when transitioning into a low-power state,

Quoted 6 lines
Quoted 1 line

Does your hardware support recieve IBI when runtime suspend?

When runtime suspended (in D3), the hardware first triggers a Power Management Event (PME) when the SDA line is pulled low to signal the START condition of an IBI. The PCI subsystem will then runtime-resume the device. When the bus is enabled, the clock is started and the IBI is received.

It align my assumption, why need complex solution.

Quoted 7 lines

SDA->PME->IRQ should handle by hardware, so irq handle queue IBI to working queue.

IBI work will try do transfer, which will call runtime resume(), then transfer data.

What's issue?

The PME indicates I3C START (SDA line pulled low). The controller is in a low power state unable to operate the bus. At this point it is not known what I3C device has pulled down the SDA line, or even if it is an IBI since it is indistinguishable from hot-join at this point.

The PCI PME IRQ is not the device's IRQ. It is handled by acpi_irq() which ultimately informs the PCI subsystem to wake the PCI device. The PCI subsystem performs pm_request_resume(), refer pci_acpi_wake_dev().

When the controller is resumed, it enables the I3C bus and the IBI is finally delivered normally.

Quoted 8 lines
Quoted 5 lines

However, none of that is related to this patch.

This patch is because the PCI device has 2 I3C bus instances and only 1 PME wakeup. The PME becomes active when the PCI device is put to a low power state.

One instance 1 suspend, instance 2 running, PME is inactive, what's happen if instance 1 request IBI?

Nothing will happen. Instance 1 I3C bus is not operational and there can be no PME when the PCI device is not in a low power state (D3hot)

Quoted 2 lines

IBI will be missed?

Possibly not if instance 1 is eventually resumed and the I3C device requesting the IBI has not yet given up.

Quoted 7 lines
Quoted 5 lines

Both I3C bus instances must be runtime suspended then.
Similarly, upon resume the PME is no longer active, so both I3C bus instances must make their buses operational - we don't know which may have received an IBI.

Does PME active auto by hardware or need software config?

PCI devices (hardware) advertise their PME capability in terms of which states are capable of PMEs. Currently the Intel LPSS I3C device lists only D3hot.

The PCI subsystem (software) automatically enables the PME before runtime suspend if the target power state allows it.

Quoted 143 lines

Frank

Quoted 141 lines

And there may be further IBIs which can't be received unless the associated bus is operational. The PCI device is no longer in a low power state, so there will be no PME in that case.

Quoted 136 lines

Frank

Quoted 133 lines
Quoted 132 lines

Frank

Quoted 128 lines

every bus instance must remain able to receive IBIs up
until that point. This requires deferring the individual controllers’ runtime suspend callbacks (which disable bus activity) until the parent decides it is safe for all instances to suspend together.

To support this usage model:

* Export the controller's runtime PM suspend/resume callbacks so that the parent can invoke them directly.

* Add a new quirk, HCI_QUIRK_RPM_PARENT_MANAGED, which designates the parent device as the controller’s runtime PM device (rpm_dev). When used without HCI_QUIRK_RPM_ALLOWED, this also prevents the child instance’s system-suspend callbacks from using pm_runtime_force_suspend()/pm_runtime_force_resume(), since runtime PM is managed entirely by the parent.

* Move DEFAULT_AUTOSUSPEND_DELAY_MS into the header so it can be shared by parent-managed PM implementations.

The new quirk allows platforms with multi-bus parent-managed PM infrastructure to correctly coordinate runtime PM across all I3C HCI instances.

---
 drivers/i3c/master/mipi-i3c-hci/core.c | 25 ++++++++++++++++---------
 drivers/i3c/master/mipi-i3c-hci/hci.h  |  6 ++++++
 2 files changed, 22 insertions(+), 9 deletions(-)
drivers/i3c/master/mipi-i3c-hci/core.c +16 -9
index ec4dbe64c35e..cb974b0f9e17 100644
@@ -733,7 +733,7 @@ static int i3c_hci_reset_and_init(struct i3c_hci *hci)
 	return 0;
 }
 
-static int i3c_hci_runtime_suspend(struct device *dev)
+int i3c_hci_runtime_suspend(struct device *dev)
 {
 	struct i3c_hci *hci = dev_get_drvdata(dev);
 	int ret;
@@ -746,8 +746,9 @@ static int i3c_hci_runtime_suspend(struct device *dev)
 
 	return 0;
 }
+EXPORT_SYMBOL_GPL(i3c_hci_runtime_suspend);
 
-static int i3c_hci_runtime_resume(struct device *dev)
+int i3c_hci_runtime_resume(struct device *dev)
 {
 	struct i3c_hci *hci = dev_get_drvdata(dev);
 	int ret;
@@ -768,6 +769,7 @@ static int i3c_hci_runtime_resume(struct device *dev)
 
 	return 0;
 }
+EXPORT_SYMBOL_GPL(i3c_hci_runtime_resume);
 
 static int i3c_hci_suspend(struct device *dev)
 {
@@ -784,12 +786,14 @@ static int i3c_hci_resume_common(struct device *dev, bool rstdaa)
 	struct i3c_hci *hci = dev_get_drvdata(dev);
 	int ret;
 
-	if (!(hci->quirks & HCI_QUIRK_RPM_ALLOWED))
-		return 0;
+	if (!(hci->quirks & HCI_QUIRK_RPM_PARENT_MANAGED)) {
+		if (!(hci->quirks & HCI_QUIRK_RPM_ALLOWED))
+			return 0;
 
-	ret = pm_runtime_force_resume(dev);
-	if (ret)
-		return ret;
+		ret = pm_runtime_force_resume(dev);
+		if (ret)
+			return ret;
+	}
 
 	ret = i3c_master_do_daa_ext(&hci->master, rstdaa);
 	if (ret)
@@ -812,8 +816,6 @@ static int i3c_hci_restore(struct device *dev)
 	return i3c_hci_resume_common(dev, true);
 }
 
-#define DEFAULT_AUTOSUSPEND_DELAY_MS 1000
-
 static void i3c_hci_rpm_enable(struct device *dev)
 {
 	struct i3c_hci *hci = dev_get_drvdata(dev);
@@ -962,6 +964,11 @@ static int i3c_hci_probe(struct platform_device *pdev)
 	if (hci->quirks & HCI_QUIRK_RPM_IBI_ALLOWED)
 		hci->master.rpm_ibi_allowed = true;
 
+	if (hci->quirks & HCI_QUIRK_RPM_PARENT_MANAGED) {
+		hci->master.rpm_dev = pdev->dev.parent;
+		hci->master.rpm_allowed = true;
+	}
+
 	return i3c_master_register(&hci->master, &pdev->dev, &i3c_hci_ops, false);
 }
 
drivers/i3c/master/mipi-i3c-hci/hci.h +6 -1
index 819328a85b84..d0e7ad58ac15 100644
@@ -147,6 +147,7 @@ struct i3c_hci_dev_data {
 #define HCI_QUIRK_RESP_BUF_THLD		BIT(4)  /* Set resp buf thld to 0 for AMD platforms */
 #define HCI_QUIRK_RPM_ALLOWED		BIT(5)  /* Runtime PM allowed */
 #define HCI_QUIRK_RPM_IBI_ALLOWED	BIT(6)  /* IBI and Hot-Join allowed while runtime suspended */
+#define HCI_QUIRK_RPM_PARENT_MANAGED	BIT(7)  /* Runtime PM managed by parent device */
 
 /* global functions */
 void mipi_i3c_hci_resume(struct i3c_hci *hci);
@@ -156,4 +157,9 @@ void amd_set_od_pp_timing(struct i3c_hci *hci);
 void amd_set_resp_buf_thld(struct i3c_hci *hci);
 void i3c_hci_sync_irq_inactive(struct i3c_hci *hci);
 
+#define DEFAULT_AUTOSUSPEND_DELAY_MS 1000
+
+int i3c_hci_runtime_suspend(struct device *dev);
+int i3c_hci_runtime_resume(struct device *dev);
+
 #endif
--

2.51.0

reviewer
#17 lore raw

Re: [PATCH 5/7] i3c: mipi-i3c-hci: Allow parent to manage runtime PM

Quoted 69 lines · Adrian Hunter
Quoted 44 lines · Frank Li
Quoted 43 lines · Adrian Hunter
Quoted 22 lines · Frank Li
Quoted 12 lines · Adrian Hunter
Quoted 11 lines · Frank Li
Quoted 9 lines · Adrian Hunter

Some platforms implement the MIPI I3C HCI Multi-Bus Instance capability, where a single parent device hosts multiple I3C controller instances. In such designs, the parent - not the individual child instances - may need to coordinate runtime PM so that all controllers enter low-power states together, and all runtime suspend callbacks are invoked in a controlled and synchronized manner.

For example, if the parent enables IBI-wakeup when transitioning into a low-power state,

Quoted 6 lines
Quoted 1 line

Does your hardware support recieve IBI when runtime suspend?

When runtime suspended (in D3), the hardware first triggers a Power Management Event (PME) when the SDA line is pulled low to signal the START condition of an IBI. The PCI subsystem will then runtime-resume the device. When the bus is enabled, the clock is started and the IBI is received.

It align my assumption, why need complex solution.

Quoted 7 lines

SDA->PME->IRQ should handle by hardware, so irq handle queue IBI to working queue.

IBI work will try do transfer, which will call runtime resume(), then transfer data.

What's issue?

The PME indicates I3C START (SDA line pulled low). The controller is in a low power state unable to operate the bus. At this point it is not known what I3C device has pulled down the SDA line, or even if it is an IBI since it is indistinguishable from hot-join at this point.

The PCI PME IRQ is not the device's IRQ. It is handled by acpi_irq() which ultimately informs the PCI subsystem to wake the PCI device. The PCI subsystem performs pm_request_resume(), refer pci_acpi_wake_dev().

When the controller is resumed, it enables the I3C bus and the IBI is finally delivered normally.

Quoted 8 lines
Quoted 5 lines

However, none of that is related to this patch.

This patch is because the PCI device has 2 I3C bus instances and only 1 PME wakeup. The PME becomes active when the PCI device is put to a low power state.

One instance 1 suspend, instance 2 running, PME is inactive, what's happen if instance 1 request IBI?

Nothing will happen. Instance 1 I3C bus is not operational and there can be no PME when the PCI device is not in a low power state (D3hot)

Quoted 2 lines

IBI will be missed?

Possibly not if instance 1 is eventually resumed and the I3C device requesting the IBI has not yet given up.

Quoted 6 lines
Quoted 5 lines

Both I3C bus instances must be runtime suspended then.
Similarly, upon resume the PME is no longer active, so both I3C bus instances must make their buses operational - we don't know which may have received an IBI.

Quoted 8 lines
Quoted 1 line

Does PME active auto by hardware or need software config?

PCI devices (hardware) advertise their PME capability in terms of which states are capable of PMEs. Currently the Intel LPSS I3C device lists only D3hot.

The PCI subsystem (software) automatically enables the PME before runtime suspend if the target power state allows it.

Okay, I think I understand your situation, let me check patch again.

Frank

Quoted 145 lines
Quoted 144 lines

Frank

Quoted 141 lines

And there may be further IBIs which can't be received unless the associated bus is operational. The PCI device is no longer in a low power state, so there will be no PME in that case.

Quoted 136 lines

Frank

Quoted 133 lines
Quoted 132 lines

Frank

Quoted 128 lines

every bus instance must remain able to receive IBIs up
until that point. This requires deferring the individual controllers’ runtime suspend callbacks (which disable bus activity) until the parent decides it is safe for all instances to suspend together.

To support this usage model:

* Export the controller's runtime PM suspend/resume callbacks so that the parent can invoke them directly.

* Add a new quirk, HCI_QUIRK_RPM_PARENT_MANAGED, which designates the parent device as the controller’s runtime PM device (rpm_dev). When used without HCI_QUIRK_RPM_ALLOWED, this also prevents the child instance’s system-suspend callbacks from using pm_runtime_force_suspend()/pm_runtime_force_resume(), since runtime PM is managed entirely by the parent.

* Move DEFAULT_AUTOSUSPEND_DELAY_MS into the header so it can be shared by parent-managed PM implementations.

The new quirk allows platforms with multi-bus parent-managed PM infrastructure to correctly coordinate runtime PM across all I3C HCI instances.

---
 drivers/i3c/master/mipi-i3c-hci/core.c | 25 ++++++++++++++++---------
 drivers/i3c/master/mipi-i3c-hci/hci.h  |  6 ++++++
 2 files changed, 22 insertions(+), 9 deletions(-)
drivers/i3c/master/mipi-i3c-hci/core.c +16 -9
index ec4dbe64c35e..cb974b0f9e17 100644
@@ -733,7 +733,7 @@ static int i3c_hci_reset_and_init(struct i3c_hci *hci)
 	return 0;
 }
 
-static int i3c_hci_runtime_suspend(struct device *dev)
+int i3c_hci_runtime_suspend(struct device *dev)
 {
 	struct i3c_hci *hci = dev_get_drvdata(dev);
 	int ret;
@@ -746,8 +746,9 @@ static int i3c_hci_runtime_suspend(struct device *dev)
 
 	return 0;
 }
+EXPORT_SYMBOL_GPL(i3c_hci_runtime_suspend);
 
-static int i3c_hci_runtime_resume(struct device *dev)
+int i3c_hci_runtime_resume(struct device *dev)
 {
 	struct i3c_hci *hci = dev_get_drvdata(dev);
 	int ret;
@@ -768,6 +769,7 @@ static int i3c_hci_runtime_resume(struct device *dev)
 
 	return 0;
 }
+EXPORT_SYMBOL_GPL(i3c_hci_runtime_resume);
 
 static int i3c_hci_suspend(struct device *dev)
 {
@@ -784,12 +786,14 @@ static int i3c_hci_resume_common(struct device *dev, bool rstdaa)
 	struct i3c_hci *hci = dev_get_drvdata(dev);
 	int ret;
 
-	if (!(hci->quirks & HCI_QUIRK_RPM_ALLOWED))
-		return 0;
+	if (!(hci->quirks & HCI_QUIRK_RPM_PARENT_MANAGED)) {
+		if (!(hci->quirks & HCI_QUIRK_RPM_ALLOWED))
+			return 0;
 
-	ret = pm_runtime_force_resume(dev);
-	if (ret)
-		return ret;
+		ret = pm_runtime_force_resume(dev);
+		if (ret)
+			return ret;
+	}
 
 	ret = i3c_master_do_daa_ext(&hci->master, rstdaa);
 	if (ret)
@@ -812,8 +816,6 @@ static int i3c_hci_restore(struct device *dev)
 	return i3c_hci_resume_common(dev, true);
 }
 
-#define DEFAULT_AUTOSUSPEND_DELAY_MS 1000
-
 static void i3c_hci_rpm_enable(struct device *dev)
 {
 	struct i3c_hci *hci = dev_get_drvdata(dev);
@@ -962,6 +964,11 @@ static int i3c_hci_probe(struct platform_device *pdev)
 	if (hci->quirks & HCI_QUIRK_RPM_IBI_ALLOWED)
 		hci->master.rpm_ibi_allowed = true;
 
+	if (hci->quirks & HCI_QUIRK_RPM_PARENT_MANAGED) {
+		hci->master.rpm_dev = pdev->dev.parent;
+		hci->master.rpm_allowed = true;
+	}
+
 	return i3c_master_register(&hci->master, &pdev->dev, &i3c_hci_ops, false);
 }
 
drivers/i3c/master/mipi-i3c-hci/hci.h +6 -1
index 819328a85b84..d0e7ad58ac15 100644
@@ -147,6 +147,7 @@ struct i3c_hci_dev_data {
 #define HCI_QUIRK_RESP_BUF_THLD		BIT(4)  /* Set resp buf thld to 0 for AMD platforms */
 #define HCI_QUIRK_RPM_ALLOWED		BIT(5)  /* Runtime PM allowed */
 #define HCI_QUIRK_RPM_IBI_ALLOWED	BIT(6)  /* IBI and Hot-Join allowed while runtime suspended */
+#define HCI_QUIRK_RPM_PARENT_MANAGED	BIT(7)  /* Runtime PM managed by parent device */
 
 /* global functions */
 void mipi_i3c_hci_resume(struct i3c_hci *hci);
@@ -156,4 +157,9 @@ void amd_set_od_pp_timing(struct i3c_hci *hci);
 void amd_set_resp_buf_thld(struct i3c_hci *hci);
 void i3c_hci_sync_irq_inactive(struct i3c_hci *hci);
 
+#define DEFAULT_AUTOSUSPEND_DELAY_MS 1000
+
+int i3c_hci_runtime_suspend(struct device *dev);
+int i3c_hci_runtime_resume(struct device *dev);
+
 #endif
--

2.51.0

reviewer
#18 lore raw

Re: [PATCH 5/7] i3c: mipi-i3c-hci: Allow parent to manage runtime PM

In reply to Adrian Hunter
Quoted 69 lines · Adrian Hunter
Quoted 44 lines · Frank Li
Quoted 43 lines · Adrian Hunter
Quoted 22 lines · Frank Li
Quoted 12 lines · Adrian Hunter
Quoted 11 lines · Frank Li
Quoted 9 lines · Adrian Hunter

Some platforms implement the MIPI I3C HCI Multi-Bus Instance capability, where a single parent device hosts multiple I3C controller instances. In such designs, the parent - not the individual child instances - may need to coordinate runtime PM so that all controllers enter low-power states together, and all runtime suspend callbacks are invoked in a controlled and synchronized manner.

For example, if the parent enables IBI-wakeup when transitioning into a low-power state,

Quoted 6 lines
Quoted 1 line

Does your hardware support recieve IBI when runtime suspend?

When runtime suspended (in D3), the hardware first triggers a Power Management Event (PME) when the SDA line is pulled low to signal the START condition of an IBI. The PCI subsystem will then runtime-resume the device. When the bus is enabled, the clock is started and the IBI is received.

It align my assumption, why need complex solution.

Quoted 7 lines

SDA->PME->IRQ should handle by hardware, so irq handle queue IBI to working queue.

IBI work will try do transfer, which will call runtime resume(), then transfer data.

What's issue?

The PME indicates I3C START (SDA line pulled low). The controller is in a low power state unable to operate the bus. At this point it is not known what I3C device has pulled down the SDA line, or even if it is an IBI since it is indistinguishable from hot-join at this point.

The PCI PME IRQ is not the device's IRQ. It is handled by acpi_irq() which ultimately informs the PCI subsystem to wake the PCI device. The PCI subsystem performs pm_request_resume(), refer pci_acpi_wake_dev().

When the controller is resumed, it enables the I3C bus and the IBI is finally delivered normally.

Quoted 8 lines
Quoted 5 lines

However, none of that is related to this patch.

This patch is because the PCI device has 2 I3C bus instances and only 1 PME wakeup. The PME becomes active when the PCI device is put to a low power state.

One instance 1 suspend, instance 2 running, PME is inactive, what's happen if instance 1 request IBI?

Nothing will happen. Instance 1 I3C bus is not operational and there can be no PME when the PCI device is not in a low power state (D3hot)

Quoted 2 lines

IBI will be missed?

Possibly not if instance 1 is eventually resumed and the I3C device requesting the IBI has not yet given up.

Quoted 6 lines
Quoted 5 lines

Both I3C bus instances must be runtime suspended then.
Similarly, upon resume the PME is no longer active, so both I3C bus instances must make their buses operational - we don't know which may have received an IBI.

Quoted 8 lines
Quoted 1 line

Does PME active auto by hardware or need software config?

PCI devices (hardware) advertise their PME capability in terms of which states are capable of PMEs. Currently the Intel LPSS I3C device lists only D3hot.

The PCI subsystem (software) automatically enables the PME before runtime suspend if the target power state allows it.

Does your device Hierarchy look like

           PCI device
               |
       -----------------
      HCI1            HCI2
       |               |
     I3C M1          I3C M2

You want HCI1 and HCI2 suspened only when both HCI1 and HCI2 can enter RM time suspend status?

Device Link can link two devices, but not sure if it can handle cyclic case, HCI1 and HCI2 depend each other.

Or you create common power domain for HCI1 and HCI2, in power domain to handle suspend.

It'd better ask run time pm owner to provide better suggestion.

Frank

Quoted 145 lines
Quoted 144 lines

Frank

Quoted 141 lines

And there may be further IBIs which can't be received unless the associated bus is operational. The PCI device is no longer in a low power state, so there will be no PME in that case.

Quoted 136 lines

Frank

Quoted 133 lines
Quoted 132 lines

Frank

Quoted 128 lines

every bus instance must remain able to receive IBIs up
until that point. This requires deferring the individual controllers’ runtime suspend callbacks (which disable bus activity) until the parent decides it is safe for all instances to suspend together.

To support this usage model:

* Export the controller's runtime PM suspend/resume callbacks so that the parent can invoke them directly.

* Add a new quirk, HCI_QUIRK_RPM_PARENT_MANAGED, which designates the parent device as the controller’s runtime PM device (rpm_dev). When used without HCI_QUIRK_RPM_ALLOWED, this also prevents the child instance’s system-suspend callbacks from using pm_runtime_force_suspend()/pm_runtime_force_resume(), since runtime PM is managed entirely by the parent.

* Move DEFAULT_AUTOSUSPEND_DELAY_MS into the header so it can be shared by parent-managed PM implementations.

The new quirk allows platforms with multi-bus parent-managed PM infrastructure to correctly coordinate runtime PM across all I3C HCI instances.

---
 drivers/i3c/master/mipi-i3c-hci/core.c | 25 ++++++++++++++++---------
 drivers/i3c/master/mipi-i3c-hci/hci.h  |  6 ++++++
 2 files changed, 22 insertions(+), 9 deletions(-)
drivers/i3c/master/mipi-i3c-hci/core.c +16 -9
index ec4dbe64c35e..cb974b0f9e17 100644
@@ -733,7 +733,7 @@ static int i3c_hci_reset_and_init(struct i3c_hci *hci)
 	return 0;
 }
 
-static int i3c_hci_runtime_suspend(struct device *dev)
+int i3c_hci_runtime_suspend(struct device *dev)
 {
 	struct i3c_hci *hci = dev_get_drvdata(dev);
 	int ret;
@@ -746,8 +746,9 @@ static int i3c_hci_runtime_suspend(struct device *dev)
 
 	return 0;
 }
+EXPORT_SYMBOL_GPL(i3c_hci_runtime_suspend);
 
-static int i3c_hci_runtime_resume(struct device *dev)
+int i3c_hci_runtime_resume(struct device *dev)
 {
 	struct i3c_hci *hci = dev_get_drvdata(dev);
 	int ret;
@@ -768,6 +769,7 @@ static int i3c_hci_runtime_resume(struct device *dev)
 
 	return 0;
 }
+EXPORT_SYMBOL_GPL(i3c_hci_runtime_resume);
 
 static int i3c_hci_suspend(struct device *dev)
 {
@@ -784,12 +786,14 @@ static int i3c_hci_resume_common(struct device *dev, bool rstdaa)
 	struct i3c_hci *hci = dev_get_drvdata(dev);
 	int ret;
 
-	if (!(hci->quirks & HCI_QUIRK_RPM_ALLOWED))
-		return 0;
+	if (!(hci->quirks & HCI_QUIRK_RPM_PARENT_MANAGED)) {
+		if (!(hci->quirks & HCI_QUIRK_RPM_ALLOWED))
+			return 0;
 
-	ret = pm_runtime_force_resume(dev);
-	if (ret)
-		return ret;
+		ret = pm_runtime_force_resume(dev);
+		if (ret)
+			return ret;
+	}
 
 	ret = i3c_master_do_daa_ext(&hci->master, rstdaa);
 	if (ret)
@@ -812,8 +816,6 @@ static int i3c_hci_restore(struct device *dev)
 	return i3c_hci_resume_common(dev, true);
 }
 
-#define DEFAULT_AUTOSUSPEND_DELAY_MS 1000
-
 static void i3c_hci_rpm_enable(struct device *dev)
 {
 	struct i3c_hci *hci = dev_get_drvdata(dev);
@@ -962,6 +964,11 @@ static int i3c_hci_probe(struct platform_device *pdev)
 	if (hci->quirks & HCI_QUIRK_RPM_IBI_ALLOWED)
 		hci->master.rpm_ibi_allowed = true;
 
+	if (hci->quirks & HCI_QUIRK_RPM_PARENT_MANAGED) {
+		hci->master.rpm_dev = pdev->dev.parent;
+		hci->master.rpm_allowed = true;
+	}
+
 	return i3c_master_register(&hci->master, &pdev->dev, &i3c_hci_ops, false);
 }
 
drivers/i3c/master/mipi-i3c-hci/hci.h +6 -1
index 819328a85b84..d0e7ad58ac15 100644
@@ -147,6 +147,7 @@ struct i3c_hci_dev_data {
 #define HCI_QUIRK_RESP_BUF_THLD		BIT(4)  /* Set resp buf thld to 0 for AMD platforms */
 #define HCI_QUIRK_RPM_ALLOWED		BIT(5)  /* Runtime PM allowed */
 #define HCI_QUIRK_RPM_IBI_ALLOWED	BIT(6)  /* IBI and Hot-Join allowed while runtime suspended */
+#define HCI_QUIRK_RPM_PARENT_MANAGED	BIT(7)  /* Runtime PM managed by parent device */
 
 /* global functions */
 void mipi_i3c_hci_resume(struct i3c_hci *hci);
@@ -156,4 +157,9 @@ void amd_set_od_pp_timing(struct i3c_hci *hci);
 void amd_set_resp_buf_thld(struct i3c_hci *hci);
 void i3c_hci_sync_irq_inactive(struct i3c_hci *hci);
 
+#define DEFAULT_AUTOSUSPEND_DELAY_MS 1000
+
+int i3c_hci_runtime_suspend(struct device *dev);
+int i3c_hci_runtime_resume(struct device *dev);
+
 #endif
--

2.51.0

#19 lore raw

Re: [PATCH 5/7] i3c: mipi-i3c-hci: Allow parent to manage runtime PM

+ Rafael

Rafael, this is not at all urgent, but when you have time please have a look at this patch set. There is also some more explanation below. Is it acceptable? Is there a better way?

Quoted 9 lines · Frank Li

Does your device Hierarchy look like

           PCI device
               |
       -----------------
      HCI1            HCI2
       |               |
     I3C M1          I3C M2

Yes and no. There is only 1 real device : the PCI device. It implements the MIPI I3C HCI standard which allows multiple I3C bus controllers in one device (in our case 2 controllers).

The PCI driver mipi-i3c-hci-pci creates 2 platform devices, one for each controller. The platform driver is mipi-i3c-hci:

Driver Device Bus

mipi-i3c-hci-pci 0000:00:11.1 PCI

                            /            \

mipi-i3c-hci intel-lpss-i3c.0 intel-lpss-i3c.1 Platform

LPSS I3C also supports wake-up from in-band interrupt (IBI) via PCI PME. The PME only works when the PCI device is in a low power state (D3hot in our case). Also the PME is effectively shared by the 2 controllers i.e. an IBI signal (the controller's SDA line pulled low) for either controller will cause the PME.

That means there are only 2 valid configurations:

1: Both controllers enabled to receive IBIs
PCI device D0
Controller 1 Enabled
Controller 2 Enabled

2: Both controllers disabled to enable PME wakeup
PCI device D3hot
Controller 1 Disabled
Controller 2 Disabled

However, represented as platform devices, the 2 controllers runtime suspend and resume independently from each other. Whereas they effectively need to runtime suspend (or resume) at the same time.

The proposed solution is for the PCI driver mipi-i3c-hci-pci to take over managing runtime PM for both controllers, calling into mipi-i3c-hci when it is safe to do so, to save/restore state and disable/enable the controllers one after the other.

Current situation (I3C next branch):

PCI device Runtime PM enabled, dependent on child devices
PCI subsystem controls PCI device power state

Controller 1 Runtime PM enabled plus auto-suspend
I3C subsystem runtime PM gets/puts the Platform device Runtime suspend: disable and save state Runtime resume: restore state and enable

Controller 2 Runtime PM enabled plus auto-suspend
I3C subsystem runtime PM gets/puts the Platform device Runtime suspend: disable and save state Runtime resume: restore state and enable

Proposed (this patch set):

PCI device Runtime PM enabled plus auto-suspend
I3C subsystem runtime PM gets/puts the PCI device For each controller:
Call into mipi-i3c-hci (when it is safe)
Runtime suspend: disable and save state Runtime resume: restore state and enable

Controller 1 Runtime PM disabled

Controller 2 Runtime PM disabled

Quoted 5 lines

You want HCI1 and HCI2 suspened only when both HCI1 and HCI2 can enter RM time suspend status?

Quoted 7 lines

Device Link can link two devices, but not sure if it can handle cyclic case, HCI1 and HCI2 depend each other.

Or you create common power domain for HCI1 and HCI2, in power domain to handle suspend.

It'd better ask run time pm owner to provide better suggestion.

Ok

reviewer
#20 lore raw

Re: [PATCH 5/7] i3c: mipi-i3c-hci: Allow parent to manage runtime PM

Quoted 83 lines · Adrian Hunter

+ Rafael

Rafael, this is not at all urgent, but when you have time please have a look at this patch set. There is also some more explanation below. Is it acceptable? Is there a better way?

Quoted 9 lines · Frank Li

Does your device Hierarchy look like

           PCI device
               |
       -----------------
      HCI1            HCI2
       |               |
     I3C M1          I3C M2

Yes and no. There is only 1 real device : the PCI device. It implements the MIPI I3C HCI standard which allows multiple I3C bus controllers in one device (in our case 2 controllers).

The PCI driver mipi-i3c-hci-pci creates 2 platform devices, one for each controller. The platform driver is mipi-i3c-hci:

Driver Device Bus

mipi-i3c-hci-pci 0000:00:11.1 PCI

                            /            \

mipi-i3c-hci intel-lpss-i3c.0 intel-lpss-i3c.1 Platform

LPSS I3C also supports wake-up from in-band interrupt (IBI) via PCI PME. The PME only works when the PCI device is in a low power state (D3hot in our case). Also the PME is effectively shared by the 2 controllers i.e. an IBI signal (the controller's SDA line pulled low) for either controller will cause the PME.

That means there are only 2 valid configurations:

1: Both controllers enabled to receive IBIs
PCI device D0
Controller 1 Enabled
Controller 2 Enabled

2: Both controllers disabled to enable PME wakeup
PCI device D3hot
Controller 1 Disabled
Controller 2 Disabled

However, represented as platform devices, the 2 controllers runtime suspend and resume independently from each other. Whereas they effectively need to runtime suspend (or resume) at the same time.

The proposed solution is for the PCI driver mipi-i3c-hci-pci to take over managing runtime PM for both controllers, calling into mipi-i3c-hci when it is safe to do so, to save/restore state and disable/enable the controllers one after the other.

Current situation (I3C next branch):

PCI device Runtime PM enabled, dependent on child devices
PCI subsystem controls PCI device power state

Controller 1 Runtime PM enabled plus auto-suspend
I3C subsystem runtime PM gets/puts the Platform device Runtime suspend: disable and save state Runtime resume: restore state and enable

Controller 2 Runtime PM enabled plus auto-suspend
I3C subsystem runtime PM gets/puts the Platform device Runtime suspend: disable and save state Runtime resume: restore state and enable

Proposed (this patch set):

PCI device Runtime PM enabled plus auto-suspend
I3C subsystem runtime PM gets/puts the PCI device For each controller:
Call into mipi-i3c-hci (when it is safe)
Runtime suspend: disable and save state Runtime resume: restore state and enable

Quoted 3 lines

Controller 1 Runtime PM disabled

Controller 2 Runtime PM disabled

Controller 1/2 is child device of PCI device.

So first patch "i3c: master: Allow controller drivers to select runtime PM device" is not necessary.

You can enable controller 1 and 2 Runtime PM with dummy operation.

When controller 1 run time suspend, parent PCI device will reduce ref counter when controller 2 run time suspend, parent PCI device will reduce ref counter

Only runtime pm reference counter of PCI device is 0, PCI device's run time suspend will be called, you can do actual disable and save work.

Frank

Quoted 16 lines
Quoted 6 lines

You want HCI1 and HCI2 suspened only when both HCI1 and HCI2 can enter RM time suspend status?

Quoted 7 lines

Device Link can link two devices, but not sure if it can handle cyclic case, HCI1 and HCI2 depend each other.

Or you create common power domain for HCI1 and HCI2, in power domain to handle suspend.

It'd better ask run time pm owner to provide better suggestion.

Ok

#21 lore raw

Re: [PATCH 5/7] i3c: mipi-i3c-hci: Allow parent to manage runtime PM

Quoted 90 lines · Frank Li
Quoted 81 lines · Adrian Hunter

+ Rafael

Rafael, this is not at all urgent, but when you have time please have a look at this patch set. There is also some more explanation below. Is it acceptable? Is there a better way?

Quoted 8 lines · Frank Li

Does your device Hierarchy look like

            PCI device
                |
        -----------------
       HCI1            HCI2
        |               |
      I3C M1          I3C M2

Yes and no. There is only 1 real device : the PCI device. It implements the MIPI I3C HCI standard which allows multiple I3C bus controllers in one device (in our case 2 controllers).

The PCI driver mipi-i3c-hci-pci creates 2 platform devices, one for each controller. The platform driver is mipi-i3c-hci:

Driver Device Bus

  mipi-i3c-hci-pci            0000:00:11.1                 PCI
                             /            \
  mipi-i3c-hci      intel-lpss-i3c.0  intel-lpss-i3c.1     Platform

LPSS I3C also supports wake-up from in-band interrupt (IBI) via PCI PME. The PME only works when the PCI device is in a low power state (D3hot in our case). Also the PME is effectively shared by the 2 controllers i.e. an IBI signal (the controller's SDA line pulled low) for either controller will cause the PME.

That means there are only 2 valid configurations:

1: Both controllers enabled to receive IBIs
PCI device D0
Controller 1 Enabled
Controller 2 Enabled

2: Both controllers disabled to enable PME wakeup
PCI device D3hot
Controller 1 Disabled
Controller 2 Disabled

However, represented as platform devices, the 2 controllers runtime suspend and resume independently from each other. Whereas they effectively need to runtime suspend (or resume) at the same time.

The proposed solution is for the PCI driver mipi-i3c-hci-pci to take over managing runtime PM for both controllers, calling into mipi-i3c-hci when it is safe to do so, to save/restore state and disable/enable the controllers one after the other.

Current situation (I3C next branch):

PCI device Runtime PM enabled, dependent on child devices
PCI subsystem controls PCI device power state

Controller 1 Runtime PM enabled plus auto-suspend
I3C subsystem runtime PM gets/puts the Platform device Runtime suspend: disable and save state Runtime resume: restore state and enable

Controller 2 Runtime PM enabled plus auto-suspend
I3C subsystem runtime PM gets/puts the Platform device Runtime suspend: disable and save state Runtime resume: restore state and enable

Proposed (this patch set):

PCI device Runtime PM enabled plus auto-suspend
I3C subsystem runtime PM gets/puts the PCI device For each controller:
Call into mipi-i3c-hci (when it is safe)
Runtime suspend: disable and save state Runtime resume: restore state and enable

Quoted 3 lines

Controller 1 Runtime PM disabled

Controller 2 Runtime PM disabled

Controller 1/2 is child device of PCI device.

So first patch "i3c: master: Allow controller drivers to select runtime PM device" is not necessary.

Quoted 7 lines

You can enable controller 1 and 2 Runtime PM with dummy operation.

When controller 1 run time suspend, parent PCI device will reduce ref counter when controller 2 run time suspend, parent PCI device will reduce ref counter

Only runtime pm reference counter of PCI device is 0, PCI device's run time suspend will be called, you can do actual disable and save work.

This sounds to me like it should work.

#22 lore raw

Re: [PATCH 5/7] i3c: mipi-i3c-hci: Allow parent to manage runtime PM

Quoted 94 lines · Rafael J
Quoted 92 lines · Frank Li
Quoted 81 lines · Adrian Hunter

+ Rafael

Rafael, this is not at all urgent, but when you have time please have a look at this patch set.  There is also some more explanation below.  Is it acceptable?  Is there a better way?

Quoted 8 lines · Frank Li

Does your device Hierarchy look like

            PCI device
                |
        -----------------
       HCI1            HCI2
        |               |
      I3C M1          I3C M2

Yes and no.  There is only 1 real device : the PCI device. It implements the MIPI I3C HCI standard which allows multiple I3C bus controllers in one device (in our case 2 controllers).

The PCI driver mipi-i3c-hci-pci creates 2 platform devices, one for each controller.  The platform driver is mipi-i3c-hci:

  Driver                        Device                     Bus

  mipi-i3c-hci-pci            0000:00:11.1                 PCI /            \
  mipi-i3c-hci      intel-lpss-i3c.0  intel-lpss-i3c.1     Platform

LPSS I3C also supports wake-up from in-band interrupt (IBI) via PCI PME.  The PME only works when the PCI device is in a low power state (D3hot in our case).  Also the PME is effectively shared by the 2 controllers i.e. an IBI signal (the controller's SDA line pulled low) for either controller will cause the PME.

That means there are only 2 valid configurations:

    1: Both controllers enabled to receive IBIs
        PCI device    D0
        Controller 1    Enabled
        Controller 2    Enabled

    2: Both controllers disabled to enable PME wakeup PCI device    D3hot
        Controller 1    Disabled
        Controller 2    Disabled

However, represented as platform devices, the 2 controllers runtime suspend and resume independently from each other. Whereas they effectively need to runtime suspend (or resume) at the same time.

The proposed solution is for the PCI driver mipi-i3c-hci-pci to take over managing runtime PM for both controllers, calling into mipi-i3c-hci when it is safe to do so, to save/restore state and disable/enable the controllers one after the other.

Current situation (I3C next branch):

    PCI device    Runtime PM enabled, dependent on child devices PCI subsystem controls PCI device power state

    Controller 1    Runtime PM enabled plus auto-suspend I3C subsystem runtime PM gets/puts the Platform device Runtime suspend: disable and save state Runtime resume: restore state and enable

    Controller 2    Runtime PM enabled plus auto-suspend I3C subsystem runtime PM gets/puts the Platform device Runtime suspend: disable and save state Runtime resume: restore state and enable

Proposed (this patch set):

    PCI device    Runtime PM enabled plus auto-suspend I3C subsystem runtime PM gets/puts the PCI device For each controller:
                Call into mipi-i3c-hci (when it is safe) Runtime suspend: disable and save state Runtime resume: restore state and enable

Quoted 3 lines

    Controller 1    Runtime PM disabled

    Controller 2    Runtime PM disabled

Controller 1/2 is child device of PCI device.

So first patch "i3c: master: Allow controller drivers to select runtime PM device" is not necessary.

You can enable controller 1 and 2 Runtime PM with dummy operation.

Quoted 7 lines
Quoted 5 lines

When controller 1 run time suspend, parent PCI device will reduce ref counter when controller 2 run time suspend, parent PCI device will reduce ref counter

Only runtime pm reference counter of PCI device is 0, PCI device's run time suspend will be called, you can do actual disable and save work.

This sounds to me like it should work.

So, like this, then?

PCI device Runtime PM enabled
For each controller:
Call into mipi-i3c-hci (when it is safe)
Runtime suspend: disable and save state Runtime resume: restore state and enable

Controller 1 Runtime PM enabled plus auto-suspend
I3C subsystem runtime PM gets/puts the Platform device Runtime suspend: does nothing
Runtime resume: does nothing

Controller 2 Runtime PM enabled plus auto-suspend
I3C subsystem runtime PM gets/puts the Platform device Runtime suspend: does nothing
Runtime resume: does nothing

#23 lore raw

Re: [PATCH 5/7] i3c: mipi-i3c-hci: Allow parent to manage runtime PM

Quoted 116 lines · Adrian Hunter
Quoted 94 lines · Rafael J
Quoted 92 lines · Frank Li
Quoted 81 lines · Adrian Hunter

+ Rafael

Rafael, this is not at all urgent, but when you have time please have a look at this patch set. There is also some more explanation below. Is it acceptable? Is there a better way?

Quoted 8 lines · Frank Li

Does your device Hierarchy look like

            PCI device
                |
        -----------------
       HCI1            HCI2
        |               |
      I3C M1          I3C M2

Yes and no. There is only 1 real device : the PCI device. It implements the MIPI I3C HCI standard which allows multiple I3C bus controllers in one device (in our case 2 controllers).

The PCI driver mipi-i3c-hci-pci creates 2 platform devices, one for each controller. The platform driver is mipi-i3c-hci:

Driver Device Bus

  mipi-i3c-hci-pci            0000:00:11.1                 PCI
                             /            \
  mipi-i3c-hci      intel-lpss-i3c.0  intel-lpss-i3c.1     Platform

LPSS I3C also supports wake-up from in-band interrupt (IBI) via PCI PME. The PME only works when the PCI device is in a low power state (D3hot in our case). Also the PME is effectively shared by the 2 controllers i.e. an IBI signal (the controller's SDA line pulled low) for either controller will cause the PME.

That means there are only 2 valid configurations:

1: Both controllers enabled to receive IBIs
PCI device D0
Controller 1 Enabled
Controller 2 Enabled

2: Both controllers disabled to enable PME wakeup
PCI device D3hot
Controller 1 Disabled
Controller 2 Disabled

However, represented as platform devices, the 2 controllers runtime suspend and resume independently from each other. Whereas they effectively need to runtime suspend (or resume) at the same time.

The proposed solution is for the PCI driver mipi-i3c-hci-pci to take over managing runtime PM for both controllers, calling into mipi-i3c-hci when it is safe to do so, to save/restore state and disable/enable the controllers one after the other.

Current situation (I3C next branch):

PCI device Runtime PM enabled, dependent on child devices
PCI subsystem controls PCI device power state

Controller 1 Runtime PM enabled plus auto-suspend
I3C subsystem runtime PM gets/puts the Platform device Runtime suspend: disable and save state Runtime resume: restore state and enable

Controller 2 Runtime PM enabled plus auto-suspend
I3C subsystem runtime PM gets/puts the Platform device Runtime suspend: disable and save state Runtime resume: restore state and enable

Proposed (this patch set):

PCI device Runtime PM enabled plus auto-suspend
I3C subsystem runtime PM gets/puts the PCI device For each controller:
Call into mipi-i3c-hci (when it is safe)
Runtime suspend: disable and save state Runtime resume: restore state and enable

Quoted 3 lines

Controller 1 Runtime PM disabled

Controller 2 Runtime PM disabled

Controller 1/2 is child device of PCI device.

So first patch "i3c: master: Allow controller drivers to select runtime PM device" is not necessary.

You can enable controller 1 and 2 Runtime PM with dummy operation.

Quoted 7 lines
Quoted 5 lines

When controller 1 run time suspend, parent PCI device will reduce ref counter when controller 2 run time suspend, parent PCI device will reduce ref counter

Only runtime pm reference counter of PCI device is 0, PCI device's run time suspend will be called, you can do actual disable and save work.

This sounds to me like it should work.

So, like this, then?

PCI device Runtime PM enabled
For each controller:
Call into mipi-i3c-hci (when it is safe)
Runtime suspend: disable and save state Runtime resume: restore state and enable

Controller 1 Runtime PM enabled plus auto-suspend
I3C subsystem runtime PM gets/puts the Platform device Runtime suspend: does nothing
Runtime resume: does nothing

Quoted 4 lines

Controller 2 Runtime PM enabled plus auto-suspend
I3C subsystem runtime PM gets/puts the Platform device Runtime suspend: does nothing
Runtime resume: does nothing

I would think so.

#24 lore raw

PATCH 6/7 i3c: mipi-i3c-hci-pci: Add optional ability to manage child runtime PM

In reply to Adrian Hunter

Some platforms implement the MIPI I3C HCI Multi-Bus Instance capability, where a single parent device hosts multiple I3C controller instances. In such designs, the parent - not the individual child instances - may need to coordinate runtime PM so that all controllers enter low-power states together, and all runtime suspend callbacks are invoked in a controlled and synchronized manner.

For example, if the parent enables IBI-wakeup when transitioning into a low-power state, every bus instance must remain able to receive IBIs up until that point. This requires deferring the individual controllers’ runtime suspend callbacks (which disable bus activity) until the parent decides it is safe for all instances to suspend together.

To support this usage model:

* Add runtime PM and system PM callbacks in the PCI driver to invoke the mipi-i3c-hci driver’s runtime PM callbacks for each instance.

* Introduce a driver-data flag, control_instance_pm, which opts into the new parent-managed PM behaviour.

* Ensure the callbacks are only used when the corresponding instance is operational at suspend time. This is reliable because the operational state cannot change while the parent device is undergoing a PM transition, and PCI always performs a runtime resume before system suspend on current configurations, so that suspend and resume alternate irrespective of whether it is runtime or system PM.

By that means, parent-managed runtime PM coordination for multi-instance MIPI I3C HCI PCI devices is provided without altering existing behaviour on platforms that do not require it.

---
 .../master/mipi-i3c-hci/mipi-i3c-hci-pci.c    | 154 +++++++++++++++++-
 1 file changed, 150 insertions(+), 4 deletions(-)
drivers/i3c/master/mipi-i3c-hci/mipi-i3c-hci-pci.c +150 -4
index bc83caad4197..f7f776300a0f 100644
@@ -9,6 +9,7 @@
 #include <linux/acpi.h>
 #include <linux/bitfield.h>
 #include <linux/debugfs.h>
+#include <linux/i3c/master.h>
 #include <linux/idr.h>
 #include <linux/iopoll.h>
 #include <linux/kernel.h>
@@ -20,16 +21,24 @@
 #include <linux/pm_qos.h>
 #include <linux/pm_runtime.h>
 
+#include "hci.h"
+
 /*
  * There can up to 15 instances, but implementations have at most 2 at this
  * time.
  */
 #define INST_MAX 2
 
+struct mipi_i3c_hci_pci_instance {
+	struct device *dev;
+	bool operational;
+};
+
 struct mipi_i3c_hci_pci {
 	struct pci_dev *pci;
 	void __iomem *base;
 	const struct mipi_i3c_hci_pci_info *info;
+	struct mipi_i3c_hci_pci_instance instance[INST_MAX];
 	void *private;
 };
 
@@ -40,6 +49,7 @@ struct mipi_i3c_hci_pci_info {
 	int id[INST_MAX];
 	u32 instance_offset[INST_MAX];
 	int instance_count;
+	bool control_instance_pm;
 };
 
 #define INTEL_PRIV_OFFSET		0x2b0
@@ -210,14 +220,148 @@ static const struct mipi_i3c_hci_pci_info intel_si_2_info = {
 	.instance_count = 1,
 };
 
-static void mipi_i3c_hci_pci_rpm_allow(struct device *dev)
+static int mipi_i3c_hci_pci_find_instance(struct mipi_i3c_hci_pci *hci, struct device *dev)
+{
+	for (int i = 0; i < INST_MAX; i++) {
+		if (!hci->instance[i].dev)
+			hci->instance[i].dev = dev;
+		if (hci->instance[i].dev == dev)
+			return i;
+	}
+
+	return -1;
+}
+
+#define HC_CONTROL			0x04
+#define HC_CONTROL_BUS_ENABLE		BIT(31)
+
+static bool __mipi_i3c_hci_pci_is_operational(struct device *dev)
+{
+	const struct mipi_i3c_hci_platform_data *pdata = dev->platform_data;
+	u32 hc_control = readl(pdata->base_regs + HC_CONTROL);
+
+	return hc_control & HC_CONTROL_BUS_ENABLE;
+}
+
+static bool mipi_i3c_hci_pci_is_operational(struct device *dev, bool update)
+{
+	struct mipi_i3c_hci_pci *hci = dev_get_drvdata(dev->parent);
+	int pos = mipi_i3c_hci_pci_find_instance(hci, dev);
+
+	if (pos < 0) {
+		dev_err(dev, "%s: I3C instance not found\n", __func__);
+		return false;
+	}
+
+	if (update)
+		hci->instance[pos].operational = __mipi_i3c_hci_pci_is_operational(dev);
+
+	return hci->instance[pos].operational;
+}
+
+struct mipi_i3c_hci_pci_pm_data {
+	struct device *dev[INST_MAX];
+	int dev_cnt;
+};
+
+static bool mipi_i3c_hci_pci_is_mfd(struct device *dev)
+{
+	return dev_is_platform(dev) && mfd_get_cell(to_platform_device(dev));
+}
+
+static int mipi_i3c_hci_pci_suspend_instance(struct device *dev, void *data)
+{
+	struct mipi_i3c_hci_pci_pm_data *pm_data = data;
+	int ret;
+
+	if (!mipi_i3c_hci_pci_is_mfd(dev) ||
+	    !mipi_i3c_hci_pci_is_operational(dev, true))
+		return 0;
+
+	ret = i3c_hci_runtime_suspend(dev);
+	if (ret)
+		return ret;
+
+	pm_data->dev[pm_data->dev_cnt++] = dev;
+
+	return 0;
+}
+
+static int mipi_i3c_hci_pci_resume_instance(struct device *dev, void *data)
 {
+	struct mipi_i3c_hci_pci_pm_data *pm_data = data;
+	int ret;
+
+	if (!mipi_i3c_hci_pci_is_mfd(dev) ||
+	    !mipi_i3c_hci_pci_is_operational(dev, false))
+		return 0;
+
+	ret = i3c_hci_runtime_resume(dev);
+	if (ret)
+		return ret;
+
+	pm_data->dev[pm_data->dev_cnt++] = dev;
+
+	return 0;
+}
+
+static int mipi_i3c_hci_pci_suspend(struct device *dev)
+{
+	struct mipi_i3c_hci_pci *hci = dev_get_drvdata(dev);
+	struct mipi_i3c_hci_pci_pm_data pm_data = {};
+	int ret;
+
+	if (!hci->info->control_instance_pm)
+		return 0;
+
+	ret = device_for_each_child_reverse(dev, &pm_data, mipi_i3c_hci_pci_suspend_instance);
+	if (ret) {
+		if (ret == -EAGAIN || ret == -EBUSY)
+			pm_runtime_mark_last_busy(&hci->pci->dev);
+		for (int i = 0; i < pm_data.dev_cnt; i++)
+			i3c_hci_runtime_resume(pm_data.dev[i]);
+	}
+
+	return ret;
+}
+
+static int mipi_i3c_hci_pci_resume(struct device *dev)
+{
+	struct mipi_i3c_hci_pci *hci = dev_get_drvdata(dev);
+	struct mipi_i3c_hci_pci_pm_data pm_data = {};
+	int ret;
+
+	if (!hci->info->control_instance_pm)
+		return 0;
+
+	ret = device_for_each_child(dev, &pm_data, mipi_i3c_hci_pci_resume_instance);
+	if (ret)
+		for (int i = 0; i < pm_data.dev_cnt; i++)
+			i3c_hci_runtime_suspend(pm_data.dev[i]);
+
+	return ret;
+}
+
+static void mipi_i3c_hci_pci_rpm_allow(struct mipi_i3c_hci_pci *hci)
+{
+	struct device *dev = &hci->pci->dev;
+
+	if (hci->info->control_instance_pm) {
+		pm_runtime_set_autosuspend_delay(dev, DEFAULT_AUTOSUSPEND_DELAY_MS);
+		pm_runtime_use_autosuspend(dev);
+	}
+
 	pm_runtime_put(dev);
 	pm_runtime_allow(dev);
 }
 
-static void mipi_i3c_hci_pci_rpm_forbid(struct device *dev)
+static void mipi_i3c_hci_pci_rpm_forbid(struct mipi_i3c_hci_pci *hci)
 {
+	struct device *dev = &hci->pci->dev;
+
+	if (hci->info->control_instance_pm)
+		pm_runtime_dont_use_autosuspend(dev);
+
 	pm_runtime_forbid(dev);
 	pm_runtime_get_sync(dev);
 }
@@ -299,7 +443,7 @@ static int mipi_i3c_hci_pci_probe(struct pci_dev *pci,
 
 	pci_set_drvdata(pci, hci);
 
-	mipi_i3c_hci_pci_rpm_allow(&pci->dev);
+	mipi_i3c_hci_pci_rpm_allow(hci);
 
 	return 0;
 
@@ -316,13 +460,15 @@ static void mipi_i3c_hci_pci_remove(struct pci_dev *pci)
 	if (hci->info->exit)
 		hci->info->exit(hci);
 
-	mipi_i3c_hci_pci_rpm_forbid(&pci->dev);
+	mipi_i3c_hci_pci_rpm_forbid(hci);
 
 	mfd_remove_devices(&pci->dev);
 }
 
 /* PM ops must exist for PCI to put a device to a low power state */
 static const struct dev_pm_ops mipi_i3c_hci_pci_pm_ops = {
+	RUNTIME_PM_OPS(mipi_i3c_hci_pci_suspend, mipi_i3c_hci_pci_resume, NULL)
+	SYSTEM_SLEEP_PM_OPS(mipi_i3c_hci_pci_suspend, mipi_i3c_hci_pci_resume)
 };
 
 static const struct pci_device_id mipi_i3c_hci_pci_devices[] = {
Signature · 1 line
2.51.0
#25 lore raw

PATCH 7/7 i3c: mipi-i3c-hci-pci: Enable IBI while runtime suspended for Intel controllers

In reply to Adrian Hunter

Intel LPSS I3C controllers can wake from runtime suspend to receive in-band interrupts (IBIs), and they also implement the MIPI I3C HCI Multi-Bus Instance capability. When multiple I3C bus instances share the same PCI wakeup, the PCI parent must coordinate runtime PM so that all instances suspend together and their mipi-i3c-hci runtime suspend callbacks are invoked in a consistent manner.

Enable IBI-based wakeup by setting HCI_QUIRK_RPM_IBI_ALLOWED for the intel-lpss-i3c platform device. Replace HCI_QUIRK_RPM_ALLOWED with HCI_QUIRK_RPM_PARENT_MANAGED so that the mipi-i3c-hci core driver expects runtime PM to be controlled by the PCI parent rather than by individual instances. For all Intel HCI PCI configurations, enable the corresponding control_instance_pm flag in the PCI driver.

---
 drivers/i3c/master/mipi-i3c-hci/core.c             | 2 +-
 drivers/i3c/master/mipi-i3c-hci/mipi-i3c-hci-pci.c | 3 +++
 2 files changed, 4 insertions(+), 1 deletion(-)
drivers/i3c/master/mipi-i3c-hci/core.c +1 -1
index cb974b0f9e17..67ae7441ce97 100644
@@ -992,7 +992,7 @@ static const struct acpi_device_id i3c_hci_acpi_match[] = {
 MODULE_DEVICE_TABLE(acpi, i3c_hci_acpi_match);
 
 static const struct platform_device_id i3c_hci_driver_ids[] = {
-	{ .name = "intel-lpss-i3c", HCI_QUIRK_RPM_ALLOWED },
+	{ .name = "intel-lpss-i3c", HCI_QUIRK_RPM_IBI_ALLOWED | HCI_QUIRK_RPM_PARENT_MANAGED },
 	{ /* sentinel */ }
 };
 MODULE_DEVICE_TABLE(platform, i3c_hci_driver_ids);
drivers/i3c/master/mipi-i3c-hci/mipi-i3c-hci-pci.c +3 -0
index f7f776300a0f..2f72cf48e36c 100644
@@ -200,6 +200,7 @@ static const struct mipi_i3c_hci_pci_info intel_mi_1_info = {
 	.id = {0, 1},
 	.instance_offset = {0, 0x400},
 	.instance_count = 2,
+	.control_instance_pm = true,
 };
 
 static const struct mipi_i3c_hci_pci_info intel_mi_2_info = {
@@ -209,6 +210,7 @@ static const struct mipi_i3c_hci_pci_info intel_mi_2_info = {
 	.id = {2, 3},
 	.instance_offset = {0, 0x400},
 	.instance_count = 2,
+	.control_instance_pm = true,
 };
 
 static const struct mipi_i3c_hci_pci_info intel_si_2_info = {
@@ -218,6 +220,7 @@ static const struct mipi_i3c_hci_pci_info intel_si_2_info = {
 	.id = {2},
 	.instance_offset = {0},
 	.instance_count = 1,
+	.control_instance_pm = true,
 };
 
 static int mipi_i3c_hci_pci_find_instance(struct mipi_i3c_hci_pci *hci, struct device *dev)
Signature · 1 line
2.51.0