1
0
Fork 0
linux/drivers/i3c/master/mipi-i3c-hci/mipi-i3c-hci-pci.c
Daniel Baumann 79d69e5050
Adding upstream version 6.12.33.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
2025-06-22 12:14:28 +02:00

148 lines
3.5 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* PCI glue code for MIPI I3C HCI driver
*
* Copyright (C) 2024 Intel Corporation
*
* Author: Jarkko Nikula <jarkko.nikula@linux.intel.com>
*/
#include <linux/acpi.h>
#include <linux/idr.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/platform_device.h>
struct mipi_i3c_hci_pci_info {
int (*init)(struct pci_dev *pci);
};
#define INTEL_PRIV_OFFSET 0x2b0
#define INTEL_PRIV_SIZE 0x28
#define INTEL_PRIV_RESETS 0x04
#define INTEL_PRIV_RESETS_RESET BIT(0)
#define INTEL_PRIV_RESETS_RESET_DONE BIT(1)
static DEFINE_IDA(mipi_i3c_hci_pci_ida);
static int mipi_i3c_hci_pci_intel_init(struct pci_dev *pci)
{
unsigned long timeout;
void __iomem *priv;
priv = devm_ioremap(&pci->dev,
pci_resource_start(pci, 0) + INTEL_PRIV_OFFSET,
INTEL_PRIV_SIZE);
if (!priv)
return -ENOMEM;
/* Assert reset, wait for completion and release reset */
writel(0, priv + INTEL_PRIV_RESETS);
timeout = jiffies + msecs_to_jiffies(10);
while (!(readl(priv + INTEL_PRIV_RESETS) &
INTEL_PRIV_RESETS_RESET_DONE)) {
if (time_after(jiffies, timeout))
break;
cpu_relax();
}
writel(INTEL_PRIV_RESETS_RESET, priv + INTEL_PRIV_RESETS);
return 0;
}
static struct mipi_i3c_hci_pci_info intel_info = {
.init = mipi_i3c_hci_pci_intel_init,
};
static int mipi_i3c_hci_pci_probe(struct pci_dev *pci,
const struct pci_device_id *id)
{
struct mipi_i3c_hci_pci_info *info;
struct platform_device *pdev;
struct resource res[2];
int dev_id, ret;
ret = pcim_enable_device(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;
dev_id = ida_alloc(&mipi_i3c_hci_pci_ida, GFP_KERNEL);
if (dev_id < 0)
return dev_id;
pdev = platform_device_alloc("mipi-i3c-hci", dev_id);
if (!pdev)
return -ENOMEM;
pdev->dev.parent = &pci->dev;
device_set_node(&pdev->dev, dev_fwnode(&pci->dev));
ret = platform_device_add_resources(pdev, res, ARRAY_SIZE(res));
if (ret)
goto err;
info = (struct mipi_i3c_hci_pci_info *)id->driver_data;
if (info && info->init) {
ret = info->init(pci);
if (ret)
goto err;
}
ret = platform_device_add(pdev);
if (ret)
goto err;
pci_set_drvdata(pci, pdev);
return 0;
err:
platform_device_put(pdev);
ida_free(&mipi_i3c_hci_pci_ida, dev_id);
return ret;
}
static void mipi_i3c_hci_pci_remove(struct pci_dev *pci)
{
struct platform_device *pdev = pci_get_drvdata(pci);
int dev_id = pdev->id;
platform_device_unregister(pdev);
ida_free(&mipi_i3c_hci_pci_ida, dev_id);
}
static const struct pci_device_id mipi_i3c_hci_pci_devices[] = {
/* Panther Lake-H */
{ PCI_VDEVICE(INTEL, 0xe37c), (kernel_ulong_t)&intel_info},
{ PCI_VDEVICE(INTEL, 0xe36f), (kernel_ulong_t)&intel_info},
/* Panther Lake-P */
{ PCI_VDEVICE(INTEL, 0xe47c), (kernel_ulong_t)&intel_info},
{ PCI_VDEVICE(INTEL, 0xe46f), (kernel_ulong_t)&intel_info},
{ },
};
MODULE_DEVICE_TABLE(pci, mipi_i3c_hci_pci_devices);
static struct pci_driver mipi_i3c_hci_pci_driver = {
.name = "mipi_i3c_hci_pci",
.id_table = mipi_i3c_hci_pci_devices,
.probe = mipi_i3c_hci_pci_probe,
.remove = mipi_i3c_hci_pci_remove,
};
module_pci_driver(mipi_i3c_hci_pci_driver);
MODULE_AUTHOR("Jarkko Nikula <jarkko.nikula@intel.com>");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("MIPI I3C HCI driver on PCI bus");