xref: /linux/drivers/i3c/master/mipi-i3c-hci/mipi-i3c-hci-pci.c (revision c2f2b01b74be8b40a2173372bcd770723f87e7b2)
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/bitfield.h>
11 #include <linux/debugfs.h>
12 #include <linux/idr.h>
13 #include <linux/iopoll.h>
14 #include <linux/kernel.h>
15 #include <linux/module.h>
16 #include <linux/pci.h>
17 #include <linux/platform_device.h>
18 #include <linux/pm_qos.h>
19 
20 struct mipi_i3c_hci_pci {
21 	struct pci_dev *pci;
22 	struct platform_device *pdev;
23 	const struct mipi_i3c_hci_pci_info *info;
24 	void *private;
25 };
26 
27 struct mipi_i3c_hci_pci_info {
28 	int (*init)(struct mipi_i3c_hci_pci *hci);
29 	void (*exit)(struct mipi_i3c_hci_pci *hci);
30 };
31 
32 static DEFINE_IDA(mipi_i3c_hci_pci_ida);
33 
34 #define INTEL_PRIV_OFFSET		0x2b0
35 #define INTEL_PRIV_SIZE			0x28
36 #define INTEL_RESETS			0x04
37 #define INTEL_RESETS_RESET		BIT(0)
38 #define INTEL_RESETS_RESET_DONE		BIT(1)
39 #define INTEL_RESETS_TIMEOUT_US		(10 * USEC_PER_MSEC)
40 
41 #define INTEL_ACTIVELTR			0x0c
42 #define INTEL_IDLELTR			0x10
43 
44 #define INTEL_LTR_REQ			BIT(15)
45 #define INTEL_LTR_SCALE_MASK		GENMASK(11, 10)
46 #define INTEL_LTR_SCALE_1US		FIELD_PREP(INTEL_LTR_SCALE_MASK, 2)
47 #define INTEL_LTR_SCALE_32US		FIELD_PREP(INTEL_LTR_SCALE_MASK, 3)
48 #define INTEL_LTR_VALUE_MASK		GENMASK(9, 0)
49 
50 struct intel_host {
51 	void __iomem	*priv;
52 	u32		active_ltr;
53 	u32		idle_ltr;
54 	struct dentry	*debugfs_root;
55 };
56 
intel_cache_ltr(struct intel_host * host)57 static void intel_cache_ltr(struct intel_host *host)
58 {
59 	host->active_ltr = readl(host->priv + INTEL_ACTIVELTR);
60 	host->idle_ltr = readl(host->priv + INTEL_IDLELTR);
61 }
62 
intel_ltr_set(struct device * dev,s32 val)63 static void intel_ltr_set(struct device *dev, s32 val)
64 {
65 	struct mipi_i3c_hci_pci *hci = dev_get_drvdata(dev);
66 	struct intel_host *host = hci->private;
67 	u32 ltr;
68 
69 	/*
70 	 * Program latency tolerance (LTR) accordingly what has been asked
71 	 * by the PM QoS layer or disable it in case we were passed
72 	 * negative value or PM_QOS_LATENCY_ANY.
73 	 */
74 	ltr = readl(host->priv + INTEL_ACTIVELTR);
75 
76 	if (val == PM_QOS_LATENCY_ANY || val < 0) {
77 		ltr &= ~INTEL_LTR_REQ;
78 	} else {
79 		ltr |= INTEL_LTR_REQ;
80 		ltr &= ~INTEL_LTR_SCALE_MASK;
81 		ltr &= ~INTEL_LTR_VALUE_MASK;
82 
83 		if (val > INTEL_LTR_VALUE_MASK) {
84 			val >>= 5;
85 			if (val > INTEL_LTR_VALUE_MASK)
86 				val = INTEL_LTR_VALUE_MASK;
87 			ltr |= INTEL_LTR_SCALE_32US | val;
88 		} else {
89 			ltr |= INTEL_LTR_SCALE_1US | val;
90 		}
91 	}
92 
93 	if (ltr == host->active_ltr)
94 		return;
95 
96 	writel(ltr, host->priv + INTEL_ACTIVELTR);
97 	writel(ltr, host->priv + INTEL_IDLELTR);
98 
99 	/* Cache the values into intel_host structure */
100 	intel_cache_ltr(host);
101 }
102 
intel_ltr_expose(struct device * dev)103 static void intel_ltr_expose(struct device *dev)
104 {
105 	dev->power.set_latency_tolerance = intel_ltr_set;
106 	dev_pm_qos_expose_latency_tolerance(dev);
107 }
108 
intel_ltr_hide(struct device * dev)109 static void intel_ltr_hide(struct device *dev)
110 {
111 	dev_pm_qos_hide_latency_tolerance(dev);
112 	dev->power.set_latency_tolerance = NULL;
113 }
114 
intel_add_debugfs(struct mipi_i3c_hci_pci * hci)115 static void intel_add_debugfs(struct mipi_i3c_hci_pci *hci)
116 {
117 	struct dentry *dir = debugfs_create_dir(dev_name(&hci->pci->dev), NULL);
118 	struct intel_host *host = hci->private;
119 
120 	intel_cache_ltr(host);
121 
122 	host->debugfs_root = dir;
123 	debugfs_create_x32("active_ltr", 0444, dir, &host->active_ltr);
124 	debugfs_create_x32("idle_ltr", 0444, dir, &host->idle_ltr);
125 }
126 
intel_remove_debugfs(struct mipi_i3c_hci_pci * hci)127 static void intel_remove_debugfs(struct mipi_i3c_hci_pci *hci)
128 {
129 	struct intel_host *host = hci->private;
130 
131 	debugfs_remove_recursive(host->debugfs_root);
132 }
133 
intel_reset(void __iomem * priv)134 static void intel_reset(void __iomem *priv)
135 {
136 	u32 reg;
137 
138 	/* Assert reset, wait for completion and release reset */
139 	writel(0, priv + INTEL_RESETS);
140 	readl_poll_timeout(priv + INTEL_RESETS, reg,
141 			   reg & INTEL_RESETS_RESET_DONE, 0,
142 			   INTEL_RESETS_TIMEOUT_US);
143 	writel(INTEL_RESETS_RESET, priv + INTEL_RESETS);
144 }
145 
intel_priv(struct pci_dev * pci)146 static void __iomem *intel_priv(struct pci_dev *pci)
147 {
148 	resource_size_t base = pci_resource_start(pci, 0);
149 
150 	return devm_ioremap(&pci->dev, base + INTEL_PRIV_OFFSET, INTEL_PRIV_SIZE);
151 }
152 
intel_i3c_init(struct mipi_i3c_hci_pci * hci)153 static int intel_i3c_init(struct mipi_i3c_hci_pci *hci)
154 {
155 	struct intel_host *host = devm_kzalloc(&hci->pci->dev, sizeof(*host), GFP_KERNEL);
156 	void __iomem *priv = intel_priv(hci->pci);
157 
158 	if (!host || !priv)
159 		return -ENOMEM;
160 
161 	dma_set_mask_and_coherent(&hci->pci->dev, DMA_BIT_MASK(64));
162 
163 	hci->pci->d3cold_delay = 0;
164 
165 	hci->private = host;
166 	host->priv = priv;
167 
168 	intel_reset(priv);
169 
170 	intel_ltr_expose(&hci->pci->dev);
171 	intel_add_debugfs(hci);
172 
173 	return 0;
174 }
175 
intel_i3c_exit(struct mipi_i3c_hci_pci * hci)176 static void intel_i3c_exit(struct mipi_i3c_hci_pci *hci)
177 {
178 	intel_remove_debugfs(hci);
179 	intel_ltr_hide(&hci->pci->dev);
180 }
181 
182 static const struct mipi_i3c_hci_pci_info intel_info = {
183 	.init = intel_i3c_init,
184 	.exit = intel_i3c_exit,
185 };
186 
mipi_i3c_hci_pci_probe(struct pci_dev * pci,const struct pci_device_id * id)187 static int mipi_i3c_hci_pci_probe(struct pci_dev *pci,
188 				  const struct pci_device_id *id)
189 {
190 	struct mipi_i3c_hci_pci *hci;
191 	struct resource res[2];
192 	int dev_id, ret;
193 
194 	hci = devm_kzalloc(&pci->dev, sizeof(*hci), GFP_KERNEL);
195 	if (!hci)
196 		return -ENOMEM;
197 
198 	hci->pci = pci;
199 
200 	ret = pcim_enable_device(pci);
201 	if (ret)
202 		return ret;
203 
204 	pci_set_master(pci);
205 
206 	memset(&res, 0, sizeof(res));
207 
208 	res[0].flags = IORESOURCE_MEM;
209 	res[0].start = pci_resource_start(pci, 0);
210 	res[0].end = pci_resource_end(pci, 0);
211 
212 	res[1].flags = IORESOURCE_IRQ;
213 	res[1].start = pci->irq;
214 	res[1].end = pci->irq;
215 
216 	dev_id = ida_alloc(&mipi_i3c_hci_pci_ida, GFP_KERNEL);
217 	if (dev_id < 0)
218 		return dev_id;
219 
220 	hci->pdev = platform_device_alloc("mipi-i3c-hci", dev_id);
221 	if (!hci->pdev)
222 		return -ENOMEM;
223 
224 	hci->pdev->dev.parent = &pci->dev;
225 	device_set_node(&hci->pdev->dev, dev_fwnode(&pci->dev));
226 
227 	ret = platform_device_add_resources(hci->pdev, res, ARRAY_SIZE(res));
228 	if (ret)
229 		goto err;
230 
231 	hci->info = (const struct mipi_i3c_hci_pci_info *)id->driver_data;
232 	if (hci->info && hci->info->init) {
233 		ret = hci->info->init(hci);
234 		if (ret)
235 			goto err;
236 	}
237 
238 	ret = platform_device_add(hci->pdev);
239 	if (ret)
240 		goto err_exit;
241 
242 	pci_set_drvdata(pci, hci);
243 
244 	return 0;
245 
246 err_exit:
247 	if (hci->info && hci->info->exit)
248 		hci->info->exit(hci);
249 err:
250 	platform_device_put(hci->pdev);
251 	ida_free(&mipi_i3c_hci_pci_ida, dev_id);
252 	return ret;
253 }
254 
mipi_i3c_hci_pci_remove(struct pci_dev * pci)255 static void mipi_i3c_hci_pci_remove(struct pci_dev *pci)
256 {
257 	struct mipi_i3c_hci_pci *hci = pci_get_drvdata(pci);
258 	struct platform_device *pdev = hci->pdev;
259 	int dev_id = pdev->id;
260 
261 	if (hci->info && hci->info->exit)
262 		hci->info->exit(hci);
263 
264 	platform_device_unregister(pdev);
265 	ida_free(&mipi_i3c_hci_pci_ida, dev_id);
266 }
267 
268 static const struct pci_device_id mipi_i3c_hci_pci_devices[] = {
269 	/* Wildcat Lake-U */
270 	{ PCI_VDEVICE(INTEL, 0x4d7c), (kernel_ulong_t)&intel_info},
271 	{ PCI_VDEVICE(INTEL, 0x4d6f), (kernel_ulong_t)&intel_info},
272 	/* Panther Lake-H */
273 	{ PCI_VDEVICE(INTEL, 0xe37c), (kernel_ulong_t)&intel_info},
274 	{ PCI_VDEVICE(INTEL, 0xe36f), (kernel_ulong_t)&intel_info},
275 	/* Panther Lake-P */
276 	{ PCI_VDEVICE(INTEL, 0xe47c), (kernel_ulong_t)&intel_info},
277 	{ PCI_VDEVICE(INTEL, 0xe46f), (kernel_ulong_t)&intel_info},
278 	/* Nova Lake-S */
279 	{ PCI_VDEVICE(INTEL, 0x6e2c), (kernel_ulong_t)&intel_info},
280 	{ PCI_VDEVICE(INTEL, 0x6e2d), (kernel_ulong_t)&intel_info},
281 	{ },
282 };
283 MODULE_DEVICE_TABLE(pci, mipi_i3c_hci_pci_devices);
284 
285 static struct pci_driver mipi_i3c_hci_pci_driver = {
286 	.name = "mipi_i3c_hci_pci",
287 	.id_table = mipi_i3c_hci_pci_devices,
288 	.probe = mipi_i3c_hci_pci_probe,
289 	.remove = mipi_i3c_hci_pci_remove,
290 };
291 
292 module_pci_driver(mipi_i3c_hci_pci_driver);
293 
294 MODULE_AUTHOR("Jarkko Nikula <jarkko.nikula@intel.com>");
295 MODULE_LICENSE("GPL");
296 MODULE_DESCRIPTION("MIPI I3C HCI driver on PCI bus");
297