From nobody Sun Feb 8 15:28:57 2026 Received: from mgamail.intel.com (mgamail.intel.com [192.198.163.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 20F012F7478; Thu, 29 Jan 2026 18:19:07 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=192.198.163.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769710751; cv=none; b=Rzep9B3EbLr3zqLj6maulMijEaMHAavFVhzRCkHvJADj8gfHRsUodXoTbhJLNmOjyfsPnnJydKPiiw9NBaYB2yyLyoaz62C1ReWtXDyTJpM+pYOAmhJlRyIvZWgi+N2ROonR/2OXMNJ4GXXi2uHhoEWgqDUUsAxYUfw7KBJ73YQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769710751; c=relaxed/simple; bh=AWlIhdshhmjS73wOeHEVcHwr+5JYfpWGb0oWfu2v+PU=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=dkYPzBvokCjTeUx3zOdTLYOnKmocQs+xU7m8XY7t7Os4mZkoG9zDYMH3N7lLSG6WtceO5KF0dV7LR7344G414Pd3WjiYtz+IHh92gwJAhCM+r3b8OTbsua1+zWYEUpYFeH3r8raXHMmNfm0sNVIa0/Juvqc3X0C6mG0zZRVWWzA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com; spf=pass smtp.mailfrom=intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=RtXYaa/y; arc=none smtp.client-ip=192.198.163.18 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="RtXYaa/y" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1769710747; x=1801246747; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=AWlIhdshhmjS73wOeHEVcHwr+5JYfpWGb0oWfu2v+PU=; b=RtXYaa/yLJ84AGpwoTKGGW0pSoTctYRYzjIZbBfww98OE/AL9k7fLweZ UqfbfU7Opvu1ba8d3eFz+u0zHBg8BD/+nQDk0Jxr9/nkPdxVephXaOP0I CdPSuOuMZfD9IlaktJdnf6s6wINXThpH6YUz9VhR2Ds9jKIHL+qA5T111 7YtKcxVxcggFXQH9Te7dVSUx0VoJJr8msjg3NrkM/mFk41FVZPxGjrSl5 9TtrOxU3djcc3tBYfreJd/J4dD5bU/QwafCEaIjT5NU1Qn5J74gvWRTnJ F2jHuNWG2iSrm5+/Te0GPXCWqmtHxcK3HcTy6Uu6a/fT7x7UglBnI4y9I A==; X-CSE-ConnectionGUID: KzJm1VBoTWaqMehEaU7njQ== X-CSE-MsgGUID: E+RKMoT/QFiz+2W1JQntqA== X-IronPort-AV: E=McAfee;i="6800,10657,11686"; a="70158306" X-IronPort-AV: E=Sophos;i="6.21,261,1763452800"; d="scan'208";a="70158306" Received: from fmviesa007.fm.intel.com ([10.60.135.147]) by fmvoesa112.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 29 Jan 2026 10:19:07 -0800 X-CSE-ConnectionGUID: UUrurI4bRpupyvvFbJW5Pg== X-CSE-MsgGUID: mNkDKfuCTLC+4h/hhfY57Q== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.21,261,1763452800"; d="scan'208";a="208255311" Received: from egrumbac-mobl6.ger.corp.intel.com (HELO ahunter6-desk) ([10.245.245.96]) by fmviesa007-auth.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 29 Jan 2026 10:19:05 -0800 From: Adrian Hunter To: alexandre.belloni@bootlin.com Cc: Frank.Li@nxp.com, linux-i3c@lists.infradead.org, linux-kernel@vger.kernel.org, linux-pm@vger.kernel.org Subject: [PATCH 6/7] i3c: mipi-i3c-hci-pci: Add optional ability to manage child runtime PM Date: Thu, 29 Jan 2026 20:18:40 +0200 Message-ID: <20260129181841.130864-7-adrian.hunter@intel.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260129181841.130864-1-adrian.hunter@intel.com> References: <20260129181841.130864-1-adrian.hunter@intel.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Organization: Intel Finland Oy, Registered Address: c/o Alberga Business Park, 6 krs, Bertel Jungin Aukio 5, 02600 Espoo, Business Identity Code: 0357606 - 4, Domiciled in Helsinki Content-Transfer-Encoding: quoted-printable 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=E2=80= =99 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=E2=80=99s runtime PM callbacks for each instanc= e. * 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. Signed-off-by: Adrian Hunter --- .../master/mipi-i3c-hci/mipi-i3c-hci-pci.c | 154 +++++++++++++++++- 1 file changed, 150 insertions(+), 4 deletions(-) diff --git a/drivers/i3c/master/mipi-i3c-hci/mipi-i3c-hci-pci.c b/drivers/i= 3c/master/mipi-i3c-hci/mipi-i3c-hci-pci.c index bc83caad4197..f7f776300a0f 100644 --- a/drivers/i3c/master/mipi-i3c-hci/mipi-i3c-hci-pci.c +++ b/drivers/i3c/master/mipi-i3c-hci/mipi-i3c-hci-pci.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -20,16 +21,24 @@ #include #include =20 +#include "hci.h" + /* * There can up to 15 instances, but implementations have at most 2 at this * time. */ #define INST_MAX 2 =20 +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; }; =20 @@ -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; }; =20 #define INTEL_PRIV_OFFSET 0x2b0 @@ -210,14 +220,148 @@ static const struct mipi_i3c_hci_pci_info intel_si_2= _info =3D { .instance_count =3D 1, }; =20 -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, st= ruct device *dev) +{ + for (int i =3D 0; i < INST_MAX; i++) { + if (!hci->instance[i].dev) + hci->instance[i].dev =3D dev; + if (hci->instance[i].dev =3D=3D 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 =3D dev->platform_data; + u32 hc_control =3D 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 updat= e) +{ + struct mipi_i3c_hci_pci *hci =3D dev_get_drvdata(dev->parent); + int pos =3D 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 =3D __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 *dat= a) +{ + struct mipi_i3c_hci_pci_pm_data *pm_data =3D data; + int ret; + + if (!mipi_i3c_hci_pci_is_mfd(dev) || + !mipi_i3c_hci_pci_is_operational(dev, true)) + return 0; + + ret =3D i3c_hci_runtime_suspend(dev); + if (ret) + return ret; + + pm_data->dev[pm_data->dev_cnt++] =3D 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 =3D data; + int ret; + + if (!mipi_i3c_hci_pci_is_mfd(dev) || + !mipi_i3c_hci_pci_is_operational(dev, false)) + return 0; + + ret =3D i3c_hci_runtime_resume(dev); + if (ret) + return ret; + + pm_data->dev[pm_data->dev_cnt++] =3D dev; + + return 0; +} + +static int mipi_i3c_hci_pci_suspend(struct device *dev) +{ + struct mipi_i3c_hci_pci *hci =3D dev_get_drvdata(dev); + struct mipi_i3c_hci_pci_pm_data pm_data =3D {}; + int ret; + + if (!hci->info->control_instance_pm) + return 0; + + ret =3D device_for_each_child_reverse(dev, &pm_data, mipi_i3c_hci_pci_sus= pend_instance); + if (ret) { + if (ret =3D=3D -EAGAIN || ret =3D=3D -EBUSY) + pm_runtime_mark_last_busy(&hci->pci->dev); + for (int i =3D 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 =3D dev_get_drvdata(dev); + struct mipi_i3c_hci_pci_pm_data pm_data =3D {}; + int ret; + + if (!hci->info->control_instance_pm) + return 0; + + ret =3D device_for_each_child(dev, &pm_data, mipi_i3c_hci_pci_resume_inst= ance); + if (ret) + for (int i =3D 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 =3D &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); } =20 -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 =3D &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, =20 pci_set_drvdata(pci, hci); =20 - mipi_i3c_hci_pci_rpm_allow(&pci->dev); + mipi_i3c_hci_pci_rpm_allow(hci); =20 return 0; =20 @@ -316,13 +460,15 @@ static void mipi_i3c_hci_pci_remove(struct pci_dev *p= ci) if (hci->info->exit) hci->info->exit(hci); =20 - mipi_i3c_hci_pci_rpm_forbid(&pci->dev); + mipi_i3c_hci_pci_rpm_forbid(hci); =20 mfd_remove_devices(&pci->dev); } =20 /* 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 =3D { + 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) }; =20 static const struct pci_device_id mipi_i3c_hci_pci_devices[] =3D { --=20 2.51.0