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 <[email protected]>
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 
mipi_i3c_hci_pci_intel_init(struct pci_dev * pci)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 
mipi_i3c_hci_pci_probe(struct pci_dev * pci,const struct pci_device_id * id)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 
mipi_i3c_hci_pci_remove(struct pci_dev * pci)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 <[email protected]>");
147 MODULE_LICENSE("GPL");
148 MODULE_DESCRIPTION("MIPI I3C HCI driver on PCI bus");
149