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