From: Adrian Hunter <adrian.hunter@intel.com>
To: alexandre.belloni@bootlin.com
Cc: Frank.Li@nxp.com, linux-i3c@lists.infradead.org
Subject: [PATCH 6/7] i3c: mipi-i3c-hci-pci: Add support for Multi-Bus Instances
Date: Tue, 9 Dec 2025 13:51:03 +0200 [thread overview]
Message-ID: <20251209115104.124156-7-adrian.hunter@intel.com> (raw)
In-Reply-To: <20251209115104.124156-1-adrian.hunter@intel.com>
A MIPI I3C Host Controller with the Multi-Bus Instance capability supports
multiple I3C Buses (up to 15), with one instance of the HCI Register Set
and one instance of I3C Bus Controller Logic for each I3C Bus, in a single
hardware function (e.g. PCIe B/D/F).
Convert to a Multifunction driver and create an MFD cell for each instance.
Create a separate platform device for each instance. Use platform_data to
pass the instance's register set start address.
MIPI I3C specification defines an Extended Capability to hold the offset
of each instance register set. However parsing to find that information is
relatively complicated compared with just including it in the driver data.
Do that for now.
Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
---
drivers/i3c/master/Kconfig | 1 +
.../master/mipi-i3c-hci/mipi-i3c-hci-pci.c | 180 +++++++++++++-----
2 files changed, 131 insertions(+), 50 deletions(-)
diff --git a/drivers/i3c/master/Kconfig b/drivers/i3c/master/Kconfig
index 82cf330778d5..2609f2b18e0a 100644
--- a/drivers/i3c/master/Kconfig
+++ b/drivers/i3c/master/Kconfig
@@ -69,6 +69,7 @@ config MIPI_I3C_HCI_PCI
tristate "MIPI I3C Host Controller Interface PCI support"
depends on MIPI_I3C_HCI
depends on PCI
+ select MFD_CORE
help
Support for MIPI I3C Host Controller Interface compatible hardware
on the PCI bus.
diff --git a/drivers/i3c/master/mipi-i3c-hci/mipi-i3c-hci-pci.c b/drivers/i3c/master/mipi-i3c-hci/mipi-i3c-hci-pci.c
index ccaec5d3d248..145f9adadf75 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
@@ -12,27 +12,43 @@
#include <linux/idr.h>
#include <linux/iopoll.h>
#include <linux/kernel.h>
+#include <linux/mfd/core.h>
#include <linux/module.h>
#include <linux/pci.h>
+#include <linux/platform_data/mipi-i3c-hci.h>
#include <linux/platform_device.h>
#include <linux/pm_qos.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 {
+ int dev_id;
+ u32 offset;
+};
+
struct mipi_i3c_hci_pci {
struct pci_dev *pci;
- struct platform_device *pdev;
+ void __iomem *base;
const struct mipi_i3c_hci_pci_info *info;
+ struct mipi_i3c_hci_pci_instance instances[INST_MAX];
void *private;
};
struct mipi_i3c_hci_pci_info {
+ struct mipi_i3c_hci_platform_data pdata;
int (*init)(struct mipi_i3c_hci_pci *hci);
void (*exit)(struct mipi_i3c_hci_pci *hci);
+ u32 instance_offset[INST_MAX - 1]; /* Excludes instance at offset 0 */
+ int instance_count;
};
static DEFINE_IDA(mipi_i3c_hci_pci_ida);
#define INTEL_PRIV_OFFSET 0x2b0
-#define INTEL_PRIV_SIZE 0x28
#define INTEL_RESETS 0x04
#define INTEL_RESETS_RESET BIT(0)
#define INTEL_RESETS_RESET_DONE BIT(1)
@@ -143,19 +159,12 @@ static void intel_reset(void __iomem *priv)
writel(INTEL_RESETS_RESET, priv + INTEL_RESETS);
}
-static void __iomem *intel_priv(struct pci_dev *pci)
-{
- resource_size_t base = pci_resource_start(pci, 0);
-
- return devm_ioremap(&pci->dev, base + INTEL_PRIV_OFFSET, INTEL_PRIV_SIZE);
-}
-
static int intel_i3c_init(struct mipi_i3c_hci_pci *hci)
{
struct intel_host *host = devm_kzalloc(&hci->pci->dev, sizeof(*host), GFP_KERNEL);
- void __iomem *priv = intel_priv(hci->pci);
+ void __iomem *priv = hci->base + INTEL_PRIV_OFFSET;
- if (!host || !priv)
+ if (!host)
return -ENOMEM;
dma_set_mask_and_coherent(&hci->pci->dev, DMA_BIT_MASK(64));
@@ -187,12 +196,106 @@ static const struct mipi_i3c_hci_pci_info intel_info = {
static const struct mipi_i3c_hci_pci_info dflt_info = {
};
+static void mipi_i3c_hci_pci_init_ids(struct mipi_i3c_hci_pci *hci)
+{
+ /* 0 is a valid id, so set unallocated ids to -1 */
+ for (int i = 0; i < INST_MAX; i++)
+ hci->instances[i].dev_id = -1;
+}
+
+static void mipi_i3c_hci_pci_free_ids(struct mipi_i3c_hci_pci *hci)
+{
+ /* ida_free() ignores negative ids */
+ for (int i = 0; i < INST_MAX; i++)
+ ida_free(&mipi_i3c_hci_pci_ida, hci->instances[i].dev_id);
+
+ mipi_i3c_hci_pci_init_ids(hci);
+}
+
+static int mipi_i3c_hci_pci_alloc_ids(struct mipi_i3c_hci_pci *hci, int nr)
+{
+ int dev_id;
+
+ mipi_i3c_hci_pci_init_ids(hci);
+
+ for (int i = 0; i < nr; i++) {
+ dev_id = ida_alloc(&mipi_i3c_hci_pci_ida, GFP_KERNEL);
+ if (dev_id < 0)
+ goto err_free_ids;
+ hci->instances[i].dev_id = dev_id;
+ }
+
+ return 0;
+
+err_free_ids:
+ mipi_i3c_hci_pci_free_ids(hci);
+ return -ENOMEM;
+}
+
+struct mipi_i3c_hci_pci_cell_data {
+ struct mipi_i3c_hci_platform_data pdata;
+ struct resource res;
+};
+
+static void mipi_i3c_hci_pci_setup_cell(struct mipi_i3c_hci_pci *hci, int idx,
+ struct mipi_i3c_hci_pci_cell_data *data,
+ struct mfd_cell *cell)
+{
+ u32 offset = idx ? hci->info->instance_offset[idx - 1] : 0;
+
+ hci->instances[idx].offset = offset;
+
+ data->pdata = hci->info->pdata;
+ data->pdata.base_regs = hci->base + offset;
+
+ data->res = DEFINE_RES_IRQ(0);
+
+ cell->name = "mipi-i3c-hci";
+ cell->id = hci->instances[idx].dev_id;
+ cell->platform_data = &data->pdata;
+ cell->pdata_size = sizeof(data->pdata);
+ cell->num_resources = 1;
+ cell->resources = &data->res;
+}
+
+static int mipi_i3c_hci_pci_add_instances(struct mipi_i3c_hci_pci *hci)
+{
+ int instance_count = hci->info->instance_count + 1; /* Include instance at offset 0 */
+ struct mipi_i3c_hci_pci_cell_data *data __free(kfree);
+ struct mfd_cell *cells __free(kfree);
+ int irq;
+ int ret;
+
+ cells = kcalloc(instance_count, sizeof(*cells), GFP_KERNEL);
+ data = kcalloc(instance_count, sizeof(*data), GFP_KERNEL);
+ if (!cells || !data)
+ return -ENOMEM;
+
+ ret = mipi_i3c_hci_pci_alloc_ids(hci, instance_count);
+ if (ret)
+ return ret;
+
+ for (int i = 0; i < instance_count; i++)
+ mipi_i3c_hci_pci_setup_cell(hci, i, data + i, cells + i);
+
+ irq = pci_irq_vector(hci->pci, 0);
+
+ ret = mfd_add_devices(&hci->pci->dev, 0, cells, instance_count, NULL, irq, NULL);
+ if (ret)
+ goto err_free_ids;
+
+ return 0;
+
+err_free_ids:
+ mipi_i3c_hci_pci_free_ids(hci);
+ return ret;
+}
+
static int mipi_i3c_hci_pci_probe(struct pci_dev *pci,
const struct pci_device_id *id)
{
struct mipi_i3c_hci_pci *hci;
- struct resource res[2];
- int dev_id, ret;
+ int ret;
hci = devm_kzalloc(&pci->dev, sizeof(*hci), GFP_KERNEL);
if (!hci)
@@ -204,68 +307,45 @@ static int mipi_i3c_hci_pci_probe(struct pci_dev *pci,
if (ret)
return ret;
- pci_set_master(pci);
-
- memset(&res, 0, sizeof(res));
-
- res[0].flags = IORESOURCE_MEM;
- res[0].start = pci_resource_start(pci, 0);
- res[0].end = pci_resource_end(pci, 0);
-
- res[1].flags = IORESOURCE_IRQ;
- res[1].start = pci->irq;
- res[1].end = pci->irq;
+ ret = pci_alloc_irq_vectors(pci, 1, 1, PCI_IRQ_ALL_TYPES);
+ if (ret < 0)
+ return ret;
- dev_id = ida_alloc(&mipi_i3c_hci_pci_ida, GFP_KERNEL);
- if (dev_id < 0)
- return dev_id;
+ pci_set_master(pci);
- hci->pdev = platform_device_alloc("mipi-i3c-hci", dev_id);
- if (!hci->pdev)
- return -ENOMEM;
+ hci->base = pcim_iomap_region(pci, 0, pci_name(pci));
+ if (IS_ERR(hci->base))
+ return PTR_ERR(hci->base);
- hci->pdev->dev.parent = &pci->dev;
- device_set_node(&hci->pdev->dev, dev_fwnode(&pci->dev));
+ hci->info = (const struct mipi_i3c_hci_pci_info *)id->driver_data ?: &dflt_info;
- ret = platform_device_add_resources(hci->pdev, res, ARRAY_SIZE(res));
+ ret = hci->info->init ? hci->info->init(hci) : 0;
if (ret)
- goto err;
+ return ret;
- hci->info = (const struct mipi_i3c_hci_pci_info *)id->driver_data ?: &dflt_info;
- if (hci->info->init) {
- ret = hci->info->init(hci);
- if (ret)
- goto err;
- }
+ pci_set_drvdata(pci, hci);
- ret = platform_device_add(hci->pdev);
+ ret = mipi_i3c_hci_pci_add_instances(hci);
if (ret)
goto err_exit;
- pci_set_drvdata(pci, hci);
-
return 0;
err_exit:
if (hci->info->exit)
hci->info->exit(hci);
-err:
- platform_device_put(hci->pdev);
- ida_free(&mipi_i3c_hci_pci_ida, dev_id);
return ret;
}
static void mipi_i3c_hci_pci_remove(struct pci_dev *pci)
{
struct mipi_i3c_hci_pci *hci = pci_get_drvdata(pci);
- struct platform_device *pdev = hci->pdev;
- int dev_id = pdev->id;
if (hci->info->exit)
hci->info->exit(hci);
- platform_device_unregister(pdev);
- ida_free(&mipi_i3c_hci_pci_ida, dev_id);
+ mfd_remove_devices(&pci->dev);
+ mipi_i3c_hci_pci_free_ids(hci);
}
static const struct pci_device_id mipi_i3c_hci_pci_devices[] = {
--
2.51.0
--
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c
next prev parent reply other threads:[~2025-12-09 11:51 UTC|newest]
Thread overview: 18+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-12-09 11:50 [PATCH 0/7] i3c: mipi-i3c-hci-pci: Define Multi-Bus Instances for Intel controllers Adrian Hunter
2025-12-09 11:50 ` [PATCH 1/7] i3c: mipi-i3c-hci: Remove duplicate blank lines Adrian Hunter
2025-12-09 16:21 ` Frank Li
2025-12-09 11:50 ` [PATCH 2/7] i3c: mipi-i3c-hci: Stop reading Extended Capabilities if capability ID is 0 Adrian Hunter
2025-12-09 16:22 ` Frank Li
2025-12-09 11:51 ` [PATCH 3/7] i3c: mipi-i3c-hci: Quieten initialization messages Adrian Hunter
2025-12-09 16:25 ` Frank Li
2025-12-09 11:51 ` [PATCH 4/7] i3c: mipi-i3c-hci: Allow for Multi-Bus Instances Adrian Hunter
2025-12-09 11:51 ` [PATCH 5/7] i3c: mipi-i3c-hci-pci: Define default driver data Adrian Hunter
2025-12-09 16:36 ` Frank Li
2025-12-10 8:27 ` Adrian Hunter
2025-12-10 15:14 ` Frank Li
2025-12-09 11:51 ` Adrian Hunter [this message]
2025-12-09 17:10 ` [PATCH 6/7] i3c: mipi-i3c-hci-pci: Add support for Multi-Bus Instances Frank Li
2025-12-10 9:17 ` Adrian Hunter
2025-12-10 15:13 ` Frank Li
2025-12-10 6:01 ` Krzysztof Kozlowski
2025-12-09 11:51 ` [PATCH 7/7] i3c: mipi-i3c-hci-pci: Define Multi-Bus Instances for Intel controllers Adrian Hunter
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20251209115104.124156-7-adrian.hunter@intel.com \
--to=adrian.hunter@intel.com \
--cc=Frank.Li@nxp.com \
--cc=alexandre.belloni@bootlin.com \
--cc=linux-i3c@lists.infradead.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox