| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148 |
- // 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");
|