mipi-i3c-hci-pci.c 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * PCI glue code for MIPI I3C HCI driver
  4. *
  5. * Copyright (C) 2024 Intel Corporation
  6. *
  7. * Author: Jarkko Nikula <jarkko.nikula@linux.intel.com>
  8. */
  9. #include <linux/acpi.h>
  10. #include <linux/idr.h>
  11. #include <linux/kernel.h>
  12. #include <linux/module.h>
  13. #include <linux/pci.h>
  14. #include <linux/platform_device.h>
  15. struct mipi_i3c_hci_pci_info {
  16. int (*init)(struct pci_dev *pci);
  17. };
  18. #define INTEL_PRIV_OFFSET 0x2b0
  19. #define INTEL_PRIV_SIZE 0x28
  20. #define INTEL_PRIV_RESETS 0x04
  21. #define INTEL_PRIV_RESETS_RESET BIT(0)
  22. #define INTEL_PRIV_RESETS_RESET_DONE BIT(1)
  23. static DEFINE_IDA(mipi_i3c_hci_pci_ida);
  24. static int mipi_i3c_hci_pci_intel_init(struct pci_dev *pci)
  25. {
  26. unsigned long timeout;
  27. void __iomem *priv;
  28. priv = devm_ioremap(&pci->dev,
  29. pci_resource_start(pci, 0) + INTEL_PRIV_OFFSET,
  30. INTEL_PRIV_SIZE);
  31. if (!priv)
  32. return -ENOMEM;
  33. /* Assert reset, wait for completion and release reset */
  34. writel(0, priv + INTEL_PRIV_RESETS);
  35. timeout = jiffies + msecs_to_jiffies(10);
  36. while (!(readl(priv + INTEL_PRIV_RESETS) &
  37. INTEL_PRIV_RESETS_RESET_DONE)) {
  38. if (time_after(jiffies, timeout))
  39. break;
  40. cpu_relax();
  41. }
  42. writel(INTEL_PRIV_RESETS_RESET, priv + INTEL_PRIV_RESETS);
  43. return 0;
  44. }
  45. static struct mipi_i3c_hci_pci_info intel_info = {
  46. .init = mipi_i3c_hci_pci_intel_init,
  47. };
  48. static int mipi_i3c_hci_pci_probe(struct pci_dev *pci,
  49. const struct pci_device_id *id)
  50. {
  51. struct mipi_i3c_hci_pci_info *info;
  52. struct platform_device *pdev;
  53. struct resource res[2];
  54. int dev_id, ret;
  55. ret = pcim_enable_device(pci);
  56. if (ret)
  57. return ret;
  58. pci_set_master(pci);
  59. memset(&res, 0, sizeof(res));
  60. res[0].flags = IORESOURCE_MEM;
  61. res[0].start = pci_resource_start(pci, 0);
  62. res[0].end = pci_resource_end(pci, 0);
  63. res[1].flags = IORESOURCE_IRQ;
  64. res[1].start = pci->irq;
  65. res[1].end = pci->irq;
  66. dev_id = ida_alloc(&mipi_i3c_hci_pci_ida, GFP_KERNEL);
  67. if (dev_id < 0)
  68. return dev_id;
  69. pdev = platform_device_alloc("mipi-i3c-hci", dev_id);
  70. if (!pdev)
  71. return -ENOMEM;
  72. pdev->dev.parent = &pci->dev;
  73. device_set_node(&pdev->dev, dev_fwnode(&pci->dev));
  74. ret = platform_device_add_resources(pdev, res, ARRAY_SIZE(res));
  75. if (ret)
  76. goto err;
  77. info = (struct mipi_i3c_hci_pci_info *)id->driver_data;
  78. if (info && info->init) {
  79. ret = info->init(pci);
  80. if (ret)
  81. goto err;
  82. }
  83. ret = platform_device_add(pdev);
  84. if (ret)
  85. goto err;
  86. pci_set_drvdata(pci, pdev);
  87. return 0;
  88. err:
  89. platform_device_put(pdev);
  90. ida_free(&mipi_i3c_hci_pci_ida, dev_id);
  91. return ret;
  92. }
  93. static void mipi_i3c_hci_pci_remove(struct pci_dev *pci)
  94. {
  95. struct platform_device *pdev = pci_get_drvdata(pci);
  96. int dev_id = pdev->id;
  97. platform_device_unregister(pdev);
  98. ida_free(&mipi_i3c_hci_pci_ida, dev_id);
  99. }
  100. static const struct pci_device_id mipi_i3c_hci_pci_devices[] = {
  101. /* Panther Lake-H */
  102. { PCI_VDEVICE(INTEL, 0xe37c), (kernel_ulong_t)&intel_info},
  103. { PCI_VDEVICE(INTEL, 0xe36f), (kernel_ulong_t)&intel_info},
  104. /* Panther Lake-P */
  105. { PCI_VDEVICE(INTEL, 0xe47c), (kernel_ulong_t)&intel_info},
  106. { PCI_VDEVICE(INTEL, 0xe46f), (kernel_ulong_t)&intel_info},
  107. { },
  108. };
  109. MODULE_DEVICE_TABLE(pci, mipi_i3c_hci_pci_devices);
  110. static struct pci_driver mipi_i3c_hci_pci_driver = {
  111. .name = "mipi_i3c_hci_pci",
  112. .id_table = mipi_i3c_hci_pci_devices,
  113. .probe = mipi_i3c_hci_pci_probe,
  114. .remove = mipi_i3c_hci_pci_remove,
  115. };
  116. module_pci_driver(mipi_i3c_hci_pci_driver);
  117. MODULE_AUTHOR("Jarkko Nikula <jarkko.nikula@intel.com>");
  118. MODULE_LICENSE("GPL");
  119. MODULE_DESCRIPTION("MIPI I3C HCI driver on PCI bus");