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