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