]>
Commit | Line | Data |
---|---|---|
f2efa4ee WY |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // Copyright (C) 2013 - 2020 Intel Corporation | |
3 | ||
4 | #include <linux/debugfs.h> | |
5 | #include <linux/device.h> | |
6 | #include <linux/interrupt.h> | |
7 | #include <linux/firmware.h> | |
8 | #include <linux/module.h> | |
9 | #include <linux/mutex.h> | |
10 | #include <linux/pci.h> | |
11 | #include <linux/pm_qos.h> | |
12 | #include <linux/pm_runtime.h> | |
13 | #include <linux/timer.h> | |
14 | #include <linux/sched.h> | |
15 | ||
16 | #include "ipu.h" | |
17 | #include "ipu-buttress.h" | |
18 | #include "ipu-platform.h" | |
19 | #include "ipu-platform-buttress-regs.h" | |
20 | #include "ipu-cpd.h" | |
21 | #include "ipu-pdata.h" | |
22 | #include "ipu-bus.h" | |
23 | #include "ipu-mmu.h" | |
24 | #include "ipu-platform-regs.h" | |
25 | #include "ipu-platform-isys-csi2-reg.h" | |
26 | #include "ipu-trace.h" | |
27 | ||
28 | #define IPU_PCI_BAR 0 | |
29 | enum ipu_version ipu_ver; | |
30 | EXPORT_SYMBOL(ipu_ver); | |
31 | ||
32 | static struct ipu_bus_device *ipu_isys_init(struct pci_dev *pdev, | |
33 | struct device *parent, | |
34 | struct ipu_buttress_ctrl *ctrl, | |
35 | void __iomem *base, | |
36 | const struct ipu_isys_internal_pdata | |
37 | *ipdata, | |
38 | unsigned int nr) | |
39 | { | |
40 | struct ipu_bus_device *isys; | |
41 | struct ipu_isys_pdata *pdata; | |
42 | ||
43 | pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); | |
44 | if (!pdata) | |
45 | return ERR_PTR(-ENOMEM); | |
46 | ||
47 | pdata->base = base; | |
48 | pdata->ipdata = ipdata; | |
49 | ||
3ebd4441 WY |
50 | /* Use 250MHz for ipu6 se */ |
51 | if (ipu_ver == IPU_VER_6SE) | |
52 | ctrl->ratio = IPU6SE_IS_FREQ_CTL_DEFAULT_RATIO; | |
53 | ||
f2efa4ee WY |
54 | isys = ipu_bus_add_device(pdev, parent, pdata, ctrl, |
55 | IPU_ISYS_NAME, nr); | |
56 | if (IS_ERR(isys)) | |
57 | return ERR_PTR(-ENOMEM); | |
58 | ||
59 | isys->mmu = ipu_mmu_init(&pdev->dev, base, ISYS_MMID, | |
60 | &ipdata->hw_variant); | |
61 | if (IS_ERR(isys->mmu)) | |
62 | return ERR_PTR(-ENOMEM); | |
63 | ||
64 | isys->mmu->dev = &isys->dev; | |
65 | ||
66 | return isys; | |
67 | } | |
68 | ||
69 | static struct ipu_bus_device *ipu_psys_init(struct pci_dev *pdev, | |
70 | struct device *parent, | |
71 | struct ipu_buttress_ctrl *ctrl, | |
72 | void __iomem *base, | |
73 | const struct ipu_psys_internal_pdata | |
74 | *ipdata, unsigned int nr) | |
75 | { | |
76 | struct ipu_bus_device *psys; | |
77 | struct ipu_psys_pdata *pdata; | |
78 | ||
79 | pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); | |
80 | if (!pdata) | |
81 | return ERR_PTR(-ENOMEM); | |
82 | ||
83 | pdata->base = base; | |
84 | pdata->ipdata = ipdata; | |
85 | ||
86 | psys = ipu_bus_add_device(pdev, parent, pdata, ctrl, | |
87 | IPU_PSYS_NAME, nr); | |
88 | if (IS_ERR(psys)) | |
89 | return ERR_PTR(-ENOMEM); | |
90 | ||
91 | psys->mmu = ipu_mmu_init(&pdev->dev, base, PSYS_MMID, | |
92 | &ipdata->hw_variant); | |
93 | if (IS_ERR(psys->mmu)) | |
94 | return ERR_PTR(-ENOMEM); | |
95 | ||
96 | psys->mmu->dev = &psys->dev; | |
97 | ||
98 | return psys; | |
99 | } | |
100 | ||
101 | int ipu_fw_authenticate(void *data, u64 val) | |
102 | { | |
103 | struct ipu_device *isp = data; | |
104 | int ret; | |
105 | ||
106 | if (!isp->secure_mode) | |
107 | return -EINVAL; | |
108 | ||
109 | ret = ipu_buttress_reset_authentication(isp); | |
110 | if (ret) { | |
111 | dev_err(&isp->pdev->dev, "Failed to reset authentication!\n"); | |
112 | return ret; | |
113 | } | |
114 | ||
115 | ret = pm_runtime_get_sync(&isp->psys->dev); | |
116 | if (ret < 0) { | |
117 | dev_err(&isp->pdev->dev, "Runtime PM failed (%d)\n", ret); | |
118 | return ret; | |
119 | } | |
120 | ||
121 | ret = ipu_buttress_authenticate(isp); | |
122 | if (ret) { | |
123 | dev_err(&isp->pdev->dev, "FW authentication failed\n"); | |
124 | return ret; | |
125 | } | |
126 | ||
127 | pm_runtime_put(&isp->psys->dev); | |
128 | ||
129 | return 0; | |
130 | } | |
131 | EXPORT_SYMBOL(ipu_fw_authenticate); | |
132 | DEFINE_SIMPLE_ATTRIBUTE(authenticate_fops, NULL, ipu_fw_authenticate, "%llu\n"); | |
133 | ||
134 | #ifdef CONFIG_DEBUG_FS | |
135 | static int resume_ipu_bus_device(struct ipu_bus_device *adev) | |
136 | { | |
137 | struct device *dev = &adev->dev; | |
138 | const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; | |
139 | ||
140 | if (!pm || !pm->resume) | |
141 | return -EIO; | |
142 | ||
143 | return pm->resume(dev); | |
144 | } | |
145 | ||
146 | static int suspend_ipu_bus_device(struct ipu_bus_device *adev) | |
147 | { | |
148 | struct device *dev = &adev->dev; | |
149 | const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; | |
150 | ||
151 | if (!pm || !pm->suspend) | |
152 | return -EIO; | |
153 | ||
154 | return pm->suspend(dev); | |
155 | } | |
156 | ||
157 | static int force_suspend_get(void *data, u64 *val) | |
158 | { | |
159 | struct ipu_device *isp = data; | |
160 | struct ipu_buttress *b = &isp->buttress; | |
161 | ||
162 | *val = b->force_suspend; | |
163 | return 0; | |
164 | } | |
165 | ||
166 | static int force_suspend_set(void *data, u64 val) | |
167 | { | |
168 | struct ipu_device *isp = data; | |
169 | struct ipu_buttress *b = &isp->buttress; | |
170 | int ret = 0; | |
171 | ||
172 | if (val == b->force_suspend) | |
173 | return 0; | |
174 | ||
175 | if (val) { | |
176 | b->force_suspend = 1; | |
177 | ret = suspend_ipu_bus_device(isp->psys); | |
178 | if (ret) { | |
179 | dev_err(&isp->pdev->dev, "Failed to suspend psys\n"); | |
180 | return ret; | |
181 | } | |
182 | ret = suspend_ipu_bus_device(isp->isys); | |
183 | if (ret) { | |
184 | dev_err(&isp->pdev->dev, "Failed to suspend isys\n"); | |
185 | return ret; | |
186 | } | |
187 | ret = pci_set_power_state(isp->pdev, PCI_D3hot); | |
188 | if (ret) { | |
189 | dev_err(&isp->pdev->dev, | |
190 | "Failed to suspend IUnit PCI device\n"); | |
191 | return ret; | |
192 | } | |
193 | } else { | |
194 | ret = pci_set_power_state(isp->pdev, PCI_D0); | |
195 | if (ret) { | |
196 | dev_err(&isp->pdev->dev, | |
197 | "Failed to suspend IUnit PCI device\n"); | |
198 | return ret; | |
199 | } | |
200 | ret = resume_ipu_bus_device(isp->isys); | |
201 | if (ret) { | |
202 | dev_err(&isp->pdev->dev, "Failed to resume isys\n"); | |
203 | return ret; | |
204 | } | |
205 | ret = resume_ipu_bus_device(isp->psys); | |
206 | if (ret) { | |
207 | dev_err(&isp->pdev->dev, "Failed to resume psys\n"); | |
208 | return ret; | |
209 | } | |
210 | b->force_suspend = 0; | |
211 | } | |
212 | ||
213 | return 0; | |
214 | } | |
215 | ||
216 | DEFINE_SIMPLE_ATTRIBUTE(force_suspend_fops, force_suspend_get, | |
217 | force_suspend_set, "%llu\n"); | |
218 | /* | |
219 | * The sysfs interface for reloading cpd fw is there only for debug purpose, | |
220 | * and it must not be used when either isys or psys is in use. | |
221 | */ | |
222 | static int cpd_fw_reload(void *data, u64 val) | |
223 | { | |
224 | struct ipu_device *isp = data; | |
225 | int rval = -EINVAL; | |
226 | ||
227 | if (isp->cpd_fw_reload) | |
228 | rval = isp->cpd_fw_reload(isp); | |
229 | ||
230 | return rval; | |
231 | } | |
232 | ||
233 | DEFINE_SIMPLE_ATTRIBUTE(cpd_fw_fops, NULL, cpd_fw_reload, "%llu\n"); | |
234 | ||
235 | static int ipu_init_debugfs(struct ipu_device *isp) | |
236 | { | |
237 | struct dentry *file; | |
238 | struct dentry *dir; | |
239 | ||
240 | dir = debugfs_create_dir(pci_name(isp->pdev), NULL); | |
241 | if (!dir) | |
242 | return -ENOMEM; | |
243 | ||
244 | file = debugfs_create_file("force_suspend", 0700, dir, isp, | |
245 | &force_suspend_fops); | |
246 | if (!file) | |
247 | goto err; | |
248 | file = debugfs_create_file("authenticate", 0700, dir, isp, | |
249 | &authenticate_fops); | |
250 | if (!file) | |
251 | goto err; | |
252 | ||
253 | file = debugfs_create_file("cpd_fw_reload", 0700, dir, isp, | |
254 | &cpd_fw_fops); | |
255 | if (!file) | |
256 | goto err; | |
257 | ||
258 | if (ipu_trace_debugfs_add(isp, dir)) | |
259 | goto err; | |
260 | ||
261 | isp->ipu_dir = dir; | |
262 | ||
263 | if (ipu_buttress_debugfs_init(isp)) | |
264 | goto err; | |
265 | ||
266 | return 0; | |
267 | err: | |
268 | debugfs_remove_recursive(dir); | |
269 | return -ENOMEM; | |
270 | } | |
271 | ||
272 | static void ipu_remove_debugfs(struct ipu_device *isp) | |
273 | { | |
274 | /* | |
275 | * Since isys and psys debugfs dir will be created under ipu root dir, | |
276 | * mark its dentry to NULL to avoid duplicate removal. | |
277 | */ | |
278 | debugfs_remove_recursive(isp->ipu_dir); | |
279 | isp->ipu_dir = NULL; | |
280 | } | |
281 | #endif /* CONFIG_DEBUG_FS */ | |
282 | ||
283 | static int ipu_pci_config_setup(struct pci_dev *dev) | |
284 | { | |
285 | u16 pci_command; | |
eaffc3a7 | 286 | int rval; |
f2efa4ee WY |
287 | |
288 | pci_read_config_word(dev, PCI_COMMAND, &pci_command); | |
eaffc3a7 | 289 | pci_command |= PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER; |
f2efa4ee | 290 | pci_write_config_word(dev, PCI_COMMAND, pci_command); |
eaffc3a7 WY |
291 | if (ipu_ver == IPU_VER_6EP) { |
292 | /* likely do nothing as msi not enabled by default */ | |
293 | pci_disable_msi(dev); | |
294 | return 0; | |
295 | } | |
f2efa4ee | 296 | |
eaffc3a7 WY |
297 | rval = pci_enable_msi(dev); |
298 | if (rval) | |
299 | dev_err(&dev->dev, "Failed to enable msi (%d)\n", rval); | |
300 | ||
301 | return rval; | |
f2efa4ee WY |
302 | } |
303 | ||
304 | static void ipu_configure_vc_mechanism(struct ipu_device *isp) | |
305 | { | |
306 | u32 val = readl(isp->base + BUTTRESS_REG_BTRS_CTRL); | |
307 | ||
308 | if (IPU_BTRS_ARB_STALL_MODE_VC0 == IPU_BTRS_ARB_MODE_TYPE_STALL) | |
309 | val |= BUTTRESS_REG_BTRS_CTRL_STALL_MODE_VC0; | |
310 | else | |
311 | val &= ~BUTTRESS_REG_BTRS_CTRL_STALL_MODE_VC0; | |
312 | ||
313 | if (IPU_BTRS_ARB_STALL_MODE_VC1 == IPU_BTRS_ARB_MODE_TYPE_STALL) | |
314 | val |= BUTTRESS_REG_BTRS_CTRL_STALL_MODE_VC1; | |
315 | else | |
316 | val &= ~BUTTRESS_REG_BTRS_CTRL_STALL_MODE_VC1; | |
317 | ||
318 | writel(val, isp->base + BUTTRESS_REG_BTRS_CTRL); | |
319 | } | |
320 | ||
321 | int request_cpd_fw(const struct firmware **firmware_p, const char *name, | |
322 | struct device *device) | |
323 | { | |
324 | const struct firmware *fw; | |
325 | struct firmware *tmp; | |
326 | int ret; | |
327 | ||
328 | ret = request_firmware(&fw, name, device); | |
329 | if (ret) | |
330 | return ret; | |
331 | ||
332 | if (is_vmalloc_addr(fw->data)) { | |
333 | *firmware_p = fw; | |
334 | } else { | |
335 | tmp = kzalloc(sizeof(*tmp), GFP_KERNEL); | |
336 | if (!tmp) { | |
337 | release_firmware(fw); | |
338 | return -ENOMEM; | |
339 | } | |
340 | tmp->size = fw->size; | |
341 | tmp->data = vmalloc(fw->size); | |
342 | if (!tmp->data) { | |
343 | kfree(tmp); | |
344 | release_firmware(fw); | |
345 | return -ENOMEM; | |
346 | } | |
347 | memcpy((void *)tmp->data, fw->data, fw->size); | |
348 | *firmware_p = tmp; | |
349 | release_firmware(fw); | |
350 | } | |
351 | ||
352 | return 0; | |
353 | } | |
354 | EXPORT_SYMBOL(request_cpd_fw); | |
355 | ||
356 | static int ipu_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) | |
357 | { | |
358 | struct ipu_device *isp; | |
359 | phys_addr_t phys; | |
360 | void __iomem *const *iomap; | |
361 | void __iomem *isys_base = NULL; | |
362 | void __iomem *psys_base = NULL; | |
363 | struct ipu_buttress_ctrl *isys_ctrl = NULL, *psys_ctrl = NULL; | |
364 | unsigned int dma_mask = IPU_DMA_MASK; | |
365 | int rval; | |
366 | ||
367 | isp = devm_kzalloc(&pdev->dev, sizeof(*isp), GFP_KERNEL); | |
368 | if (!isp) | |
369 | return -ENOMEM; | |
370 | ||
371 | dev_set_name(&pdev->dev, "intel-ipu"); | |
372 | isp->pdev = pdev; | |
373 | INIT_LIST_HEAD(&isp->devices); | |
374 | ||
375 | rval = pcim_enable_device(pdev); | |
376 | if (rval) { | |
377 | dev_err(&pdev->dev, "Failed to enable CI ISP device (%d)\n", | |
378 | rval); | |
379 | return rval; | |
380 | } | |
381 | ||
382 | dev_info(&pdev->dev, "Device 0x%x (rev: 0x%x)\n", | |
383 | pdev->device, pdev->revision); | |
384 | ||
385 | phys = pci_resource_start(pdev, IPU_PCI_BAR); | |
386 | ||
387 | rval = pcim_iomap_regions(pdev, | |
388 | 1 << IPU_PCI_BAR, | |
389 | pci_name(pdev)); | |
390 | if (rval) { | |
391 | dev_err(&pdev->dev, "Failed to I/O memory remapping (%d)\n", | |
392 | rval); | |
393 | return rval; | |
394 | } | |
395 | dev_info(&pdev->dev, "physical base address 0x%llx\n", phys); | |
396 | ||
397 | iomap = pcim_iomap_table(pdev); | |
398 | if (!iomap) { | |
399 | dev_err(&pdev->dev, "Failed to iomap table (%d)\n", rval); | |
400 | return -ENODEV; | |
401 | } | |
402 | ||
403 | isp->base = iomap[IPU_PCI_BAR]; | |
404 | dev_info(&pdev->dev, "mapped as: 0x%p\n", isp->base); | |
405 | ||
406 | pci_set_drvdata(pdev, isp); | |
407 | pci_set_master(pdev); | |
408 | ||
409 | switch (id->device) { | |
410 | case IPU6_PCI_ID: | |
411 | ipu_ver = IPU_VER_6; | |
412 | isp->cpd_fw_name = IPU6_FIRMWARE_NAME; | |
413 | break; | |
414 | case IPU6SE_PCI_ID: | |
415 | ipu_ver = IPU_VER_6SE; | |
416 | isp->cpd_fw_name = IPU6SE_FIRMWARE_NAME; | |
417 | break; | |
5a771b35 HY |
418 | case IPU6EP_ADL_P_PCI_ID: |
419 | case IPU6EP_ADL_N_PCI_ID: | |
eaffc3a7 WY |
420 | ipu_ver = IPU_VER_6EP; |
421 | isp->cpd_fw_name = IPU6EP_FIRMWARE_NAME; | |
422 | break; | |
f2efa4ee WY |
423 | default: |
424 | WARN(1, "Unsupported IPU device"); | |
425 | return -ENODEV; | |
426 | } | |
427 | ||
428 | ipu_internal_pdata_init(); | |
429 | ||
430 | isys_base = isp->base + isys_ipdata.hw_variant.offset; | |
431 | psys_base = isp->base + psys_ipdata.hw_variant.offset; | |
432 | ||
433 | dev_dbg(&pdev->dev, "isys_base: 0x%lx\n", (unsigned long)isys_base); | |
434 | dev_dbg(&pdev->dev, "psys_base: 0x%lx\n", (unsigned long)psys_base); | |
435 | ||
436 | rval = pci_set_dma_mask(pdev, DMA_BIT_MASK(dma_mask)); | |
437 | if (!rval) | |
438 | rval = pci_set_consistent_dma_mask(pdev, | |
439 | DMA_BIT_MASK(dma_mask)); | |
440 | if (rval) { | |
441 | dev_err(&pdev->dev, "Failed to set DMA mask (%d)\n", rval); | |
442 | return rval; | |
443 | } | |
444 | ||
3ebd4441 WY |
445 | dma_set_max_seg_size(&pdev->dev, UINT_MAX); |
446 | ||
f2efa4ee WY |
447 | rval = ipu_pci_config_setup(pdev); |
448 | if (rval) | |
449 | return rval; | |
450 | ||
451 | rval = devm_request_threaded_irq(&pdev->dev, pdev->irq, | |
452 | ipu_buttress_isr, | |
453 | ipu_buttress_isr_threaded, | |
454 | IRQF_SHARED, IPU_NAME, isp); | |
455 | if (rval) { | |
456 | dev_err(&pdev->dev, "Requesting irq failed(%d)\n", rval); | |
457 | return rval; | |
458 | } | |
459 | ||
460 | rval = ipu_buttress_init(isp); | |
461 | if (rval) | |
462 | return rval; | |
463 | ||
464 | dev_info(&pdev->dev, "cpd file name: %s\n", isp->cpd_fw_name); | |
465 | ||
466 | rval = request_cpd_fw(&isp->cpd_fw, isp->cpd_fw_name, &pdev->dev); | |
467 | if (rval) { | |
468 | dev_err(&isp->pdev->dev, "Requesting signed firmware failed\n"); | |
469 | return rval; | |
470 | } | |
471 | ||
472 | rval = ipu_cpd_validate_cpd_file(isp, isp->cpd_fw->data, | |
473 | isp->cpd_fw->size); | |
474 | if (rval) { | |
475 | dev_err(&isp->pdev->dev, "Failed to validate cpd\n"); | |
476 | goto out_ipu_bus_del_devices; | |
477 | } | |
478 | ||
479 | rval = ipu_trace_add(isp); | |
480 | if (rval) | |
481 | dev_err(&pdev->dev, "Trace support not available\n"); | |
482 | ||
483 | pm_runtime_put_noidle(&pdev->dev); | |
484 | pm_runtime_allow(&pdev->dev); | |
485 | ||
486 | /* | |
487 | * NOTE Device hierarchy below is important to ensure proper | |
488 | * runtime suspend and resume order. | |
489 | * Also registration order is important to ensure proper | |
490 | * suspend and resume order during system | |
491 | * suspend. Registration order is as follows: | |
492 | * isys->psys | |
493 | */ | |
494 | isys_ctrl = devm_kzalloc(&pdev->dev, sizeof(*isys_ctrl), GFP_KERNEL); | |
495 | if (!isys_ctrl) { | |
496 | rval = -ENOMEM; | |
497 | goto out_ipu_bus_del_devices; | |
498 | } | |
499 | ||
500 | /* Init butress control with default values based on the HW */ | |
501 | memcpy(isys_ctrl, &isys_buttress_ctrl, sizeof(*isys_ctrl)); | |
502 | ||
503 | isp->isys = ipu_isys_init(pdev, &pdev->dev, | |
504 | isys_ctrl, isys_base, | |
505 | &isys_ipdata, | |
506 | 0); | |
507 | if (IS_ERR(isp->isys)) { | |
508 | rval = PTR_ERR(isp->isys); | |
509 | goto out_ipu_bus_del_devices; | |
510 | } | |
511 | ||
512 | psys_ctrl = devm_kzalloc(&pdev->dev, sizeof(*psys_ctrl), GFP_KERNEL); | |
513 | if (!psys_ctrl) { | |
514 | rval = -ENOMEM; | |
515 | goto out_ipu_bus_del_devices; | |
516 | } | |
517 | ||
518 | /* Init butress control with default values based on the HW */ | |
519 | memcpy(psys_ctrl, &psys_buttress_ctrl, sizeof(*psys_ctrl)); | |
520 | ||
521 | isp->psys = ipu_psys_init(pdev, &isp->isys->dev, | |
522 | psys_ctrl, psys_base, | |
523 | &psys_ipdata, 0); | |
524 | if (IS_ERR(isp->psys)) { | |
525 | rval = PTR_ERR(isp->psys); | |
526 | goto out_ipu_bus_del_devices; | |
527 | } | |
528 | ||
529 | rval = pm_runtime_get_sync(&isp->psys->dev); | |
530 | if (rval < 0) { | |
531 | dev_err(&isp->psys->dev, "Failed to get runtime PM\n"); | |
532 | goto out_ipu_bus_del_devices; | |
533 | } | |
534 | ||
535 | rval = ipu_mmu_hw_init(isp->psys->mmu); | |
536 | if (rval) { | |
537 | dev_err(&isp->pdev->dev, "Failed to set mmu hw\n"); | |
538 | goto out_ipu_bus_del_devices; | |
539 | } | |
540 | ||
541 | rval = ipu_buttress_map_fw_image(isp->psys, isp->cpd_fw, | |
542 | &isp->fw_sgt); | |
543 | if (rval) { | |
544 | dev_err(&isp->pdev->dev, "failed to map fw image\n"); | |
545 | goto out_ipu_bus_del_devices; | |
546 | } | |
547 | ||
548 | isp->pkg_dir = ipu_cpd_create_pkg_dir(isp->psys, | |
549 | isp->cpd_fw->data, | |
550 | sg_dma_address(isp->fw_sgt.sgl), | |
551 | &isp->pkg_dir_dma_addr, | |
552 | &isp->pkg_dir_size); | |
553 | if (!isp->pkg_dir) { | |
554 | rval = -ENOMEM; | |
555 | dev_err(&isp->pdev->dev, "failed to create pkg dir\n"); | |
556 | goto out_ipu_bus_del_devices; | |
557 | } | |
558 | ||
559 | rval = ipu_buttress_authenticate(isp); | |
560 | if (rval) { | |
561 | dev_err(&isp->pdev->dev, "FW authentication failed(%d)\n", | |
562 | rval); | |
563 | goto out_ipu_bus_del_devices; | |
564 | } | |
565 | ||
566 | ipu_mmu_hw_cleanup(isp->psys->mmu); | |
567 | pm_runtime_put(&isp->psys->dev); | |
568 | ||
569 | #ifdef CONFIG_DEBUG_FS | |
570 | rval = ipu_init_debugfs(isp); | |
571 | if (rval) { | |
572 | dev_err(&pdev->dev, "Failed to initialize debugfs"); | |
573 | goto out_ipu_bus_del_devices; | |
574 | } | |
575 | #endif | |
576 | ||
577 | /* Configure the arbitration mechanisms for VC requests */ | |
578 | ipu_configure_vc_mechanism(isp); | |
579 | ||
580 | dev_info(&pdev->dev, "IPU driver version %d.%d\n", IPU_MAJOR_VERSION, | |
581 | IPU_MINOR_VERSION); | |
582 | ||
583 | return 0; | |
584 | ||
585 | out_ipu_bus_del_devices: | |
586 | if (isp->pkg_dir) { | |
587 | ipu_cpd_free_pkg_dir(isp->psys, isp->pkg_dir, | |
588 | isp->pkg_dir_dma_addr, | |
589 | isp->pkg_dir_size); | |
590 | ipu_buttress_unmap_fw_image(isp->psys, &isp->fw_sgt); | |
591 | isp->pkg_dir = NULL; | |
592 | } | |
593 | if (isp->psys && isp->psys->mmu) | |
594 | ipu_mmu_cleanup(isp->psys->mmu); | |
595 | if (isp->isys && isp->isys->mmu) | |
596 | ipu_mmu_cleanup(isp->isys->mmu); | |
597 | if (isp->psys) | |
598 | pm_runtime_put(&isp->psys->dev); | |
599 | ipu_bus_del_devices(pdev); | |
600 | ipu_buttress_exit(isp); | |
601 | release_firmware(isp->cpd_fw); | |
602 | ||
603 | return rval; | |
604 | } | |
605 | ||
606 | static void ipu_pci_remove(struct pci_dev *pdev) | |
607 | { | |
608 | struct ipu_device *isp = pci_get_drvdata(pdev); | |
609 | ||
610 | #ifdef CONFIG_DEBUG_FS | |
611 | ipu_remove_debugfs(isp); | |
612 | #endif | |
613 | ipu_trace_release(isp); | |
614 | ||
af60d6fc WY |
615 | ipu_cpd_free_pkg_dir(isp->psys, isp->pkg_dir, isp->pkg_dir_dma_addr, |
616 | isp->pkg_dir_size); | |
617 | ||
618 | ipu_buttress_unmap_fw_image(isp->psys, &isp->fw_sgt); | |
619 | ||
620 | isp->pkg_dir = NULL; | |
621 | isp->pkg_dir_dma_addr = 0; | |
622 | isp->pkg_dir_size = 0; | |
623 | ||
f2efa4ee WY |
624 | ipu_bus_del_devices(pdev); |
625 | ||
626 | pm_runtime_forbid(&pdev->dev); | |
627 | pm_runtime_get_noresume(&pdev->dev); | |
628 | ||
629 | pci_release_regions(pdev); | |
630 | pci_disable_device(pdev); | |
631 | ||
632 | ipu_buttress_exit(isp); | |
633 | ||
634 | release_firmware(isp->cpd_fw); | |
635 | ||
636 | ipu_mmu_cleanup(isp->psys->mmu); | |
637 | ipu_mmu_cleanup(isp->isys->mmu); | |
638 | } | |
639 | ||
640 | static void ipu_pci_reset_prepare(struct pci_dev *pdev) | |
641 | { | |
642 | struct ipu_device *isp = pci_get_drvdata(pdev); | |
643 | ||
644 | dev_warn(&pdev->dev, "FLR prepare\n"); | |
645 | pm_runtime_forbid(&isp->pdev->dev); | |
646 | isp->flr_done = true; | |
647 | } | |
648 | ||
649 | static void ipu_pci_reset_done(struct pci_dev *pdev) | |
650 | { | |
651 | struct ipu_device *isp = pci_get_drvdata(pdev); | |
652 | ||
653 | ipu_buttress_restore(isp); | |
654 | if (isp->secure_mode) | |
655 | ipu_buttress_reset_authentication(isp); | |
656 | ||
657 | ipu_bus_flr_recovery(); | |
658 | isp->ipc_reinit = true; | |
659 | pm_runtime_allow(&isp->pdev->dev); | |
660 | ||
661 | dev_warn(&pdev->dev, "FLR completed\n"); | |
662 | } | |
663 | ||
664 | #ifdef CONFIG_PM | |
665 | ||
666 | /* | |
667 | * PCI base driver code requires driver to provide these to enable | |
668 | * PCI device level PM state transitions (D0<->D3) | |
669 | */ | |
670 | static int ipu_suspend(struct device *dev) | |
671 | { | |
672 | struct pci_dev *pdev = to_pci_dev(dev); | |
673 | struct ipu_device *isp = pci_get_drvdata(pdev); | |
674 | ||
675 | isp->flr_done = false; | |
676 | ||
677 | return 0; | |
678 | } | |
679 | ||
680 | static int ipu_resume(struct device *dev) | |
681 | { | |
682 | struct pci_dev *pdev = to_pci_dev(dev); | |
683 | struct ipu_device *isp = pci_get_drvdata(pdev); | |
684 | struct ipu_buttress *b = &isp->buttress; | |
685 | int rval; | |
686 | ||
687 | /* Configure the arbitration mechanisms for VC requests */ | |
688 | ipu_configure_vc_mechanism(isp); | |
689 | ||
690 | ipu_buttress_set_secure_mode(isp); | |
691 | isp->secure_mode = ipu_buttress_get_secure_mode(isp); | |
692 | dev_info(dev, "IPU in %s mode\n", | |
693 | isp->secure_mode ? "secure" : "non-secure"); | |
694 | ||
695 | ipu_buttress_restore(isp); | |
696 | ||
697 | rval = ipu_buttress_ipc_reset(isp, &b->cse); | |
698 | if (rval) | |
699 | dev_err(&isp->pdev->dev, "IPC reset protocol failed!\n"); | |
700 | ||
701 | rval = pm_runtime_get_sync(&isp->psys->dev); | |
702 | if (rval < 0) { | |
703 | dev_err(&isp->psys->dev, "Failed to get runtime PM\n"); | |
704 | return 0; | |
705 | } | |
706 | ||
707 | rval = ipu_buttress_authenticate(isp); | |
708 | if (rval) | |
709 | dev_err(&isp->pdev->dev, "FW authentication failed(%d)\n", | |
710 | rval); | |
711 | ||
712 | pm_runtime_put(&isp->psys->dev); | |
713 | ||
714 | return 0; | |
715 | } | |
716 | ||
717 | static int ipu_runtime_resume(struct device *dev) | |
718 | { | |
719 | struct pci_dev *pdev = to_pci_dev(dev); | |
720 | struct ipu_device *isp = pci_get_drvdata(pdev); | |
721 | int rval; | |
722 | ||
723 | ipu_configure_vc_mechanism(isp); | |
724 | ipu_buttress_restore(isp); | |
725 | ||
726 | if (isp->ipc_reinit) { | |
727 | struct ipu_buttress *b = &isp->buttress; | |
728 | ||
729 | isp->ipc_reinit = false; | |
730 | rval = ipu_buttress_ipc_reset(isp, &b->cse); | |
731 | if (rval) | |
732 | dev_err(&isp->pdev->dev, | |
733 | "IPC reset protocol failed!\n"); | |
734 | } | |
735 | ||
736 | return 0; | |
737 | } | |
738 | ||
739 | static const struct dev_pm_ops ipu_pm_ops = { | |
740 | SET_SYSTEM_SLEEP_PM_OPS(&ipu_suspend, &ipu_resume) | |
741 | SET_RUNTIME_PM_OPS(&ipu_suspend, /* Same as in suspend flow */ | |
742 | &ipu_runtime_resume, | |
743 | NULL) | |
744 | }; | |
745 | ||
746 | #define IPU_PM (&ipu_pm_ops) | |
747 | #else | |
748 | #define IPU_PM NULL | |
749 | #endif | |
750 | ||
751 | static const struct pci_device_id ipu_pci_tbl[] = { | |
752 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, IPU6_PCI_ID)}, | |
753 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, IPU6SE_PCI_ID)}, | |
5a771b35 HY |
754 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, IPU6EP_ADL_P_PCI_ID)}, |
755 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, IPU6EP_ADL_N_PCI_ID)}, | |
f2efa4ee WY |
756 | {0,} |
757 | }; | |
758 | MODULE_DEVICE_TABLE(pci, ipu_pci_tbl); | |
759 | ||
760 | static const struct pci_error_handlers pci_err_handlers = { | |
761 | .reset_prepare = ipu_pci_reset_prepare, | |
762 | .reset_done = ipu_pci_reset_done, | |
763 | }; | |
764 | ||
765 | static struct pci_driver ipu_pci_driver = { | |
766 | .name = IPU_NAME, | |
767 | .id_table = ipu_pci_tbl, | |
768 | .probe = ipu_pci_probe, | |
769 | .remove = ipu_pci_remove, | |
770 | .driver = { | |
771 | .pm = IPU_PM, | |
772 | }, | |
773 | .err_handler = &pci_err_handlers, | |
774 | }; | |
775 | ||
776 | static int __init ipu_init(void) | |
777 | { | |
778 | int rval = ipu_bus_register(); | |
779 | ||
780 | if (rval) { | |
781 | pr_warn("can't register ipu bus (%d)\n", rval); | |
782 | return rval; | |
783 | } | |
784 | ||
785 | rval = pci_register_driver(&ipu_pci_driver); | |
786 | if (rval) { | |
787 | pr_warn("can't register pci driver (%d)\n", rval); | |
788 | goto out_pci_register_driver; | |
789 | } | |
790 | ||
791 | return 0; | |
792 | ||
793 | out_pci_register_driver: | |
794 | ipu_bus_unregister(); | |
795 | ||
796 | return rval; | |
797 | } | |
798 | ||
799 | static void __exit ipu_exit(void) | |
800 | { | |
801 | pci_unregister_driver(&ipu_pci_driver); | |
802 | ipu_bus_unregister(); | |
803 | } | |
804 | ||
805 | module_init(ipu_init); | |
806 | module_exit(ipu_exit); | |
807 | ||
808 | MODULE_AUTHOR("Sakari Ailus <sakari.ailus@linux.intel.com>"); | |
809 | MODULE_AUTHOR("Jouni Högander <jouni.hogander@intel.com>"); | |
810 | MODULE_AUTHOR("Antti Laakso <antti.laakso@intel.com>"); | |
811 | MODULE_AUTHOR("Samu Onkalo <samu.onkalo@intel.com>"); | |
812 | MODULE_AUTHOR("Jianxu Zheng <jian.xu.zheng@intel.com>"); | |
813 | MODULE_AUTHOR("Tianshu Qiu <tian.shu.qiu@intel.com>"); | |
814 | MODULE_AUTHOR("Renwei Wu <renwei.wu@intel.com>"); | |
815 | MODULE_AUTHOR("Bingbu Cao <bingbu.cao@intel.com>"); | |
816 | MODULE_AUTHOR("Yunliang Ding <yunliang.ding@intel.com>"); | |
817 | MODULE_AUTHOR("Zaikuo Wang <zaikuo.wang@intel.com>"); | |
818 | MODULE_AUTHOR("Leifu Zhao <leifu.zhao@intel.com>"); | |
819 | MODULE_AUTHOR("Xia Wu <xia.wu@intel.com>"); | |
820 | MODULE_AUTHOR("Kun Jiang <kun.jiang@intel.com>"); | |
821 | MODULE_AUTHOR("Intel"); | |
822 | MODULE_LICENSE("GPL"); | |
823 | MODULE_DESCRIPTION("Intel ipu pci driver"); |