xref: /linux/drivers/media/pci/intel/ipu6/ipu6-bus.c (revision c532de5a67a70f8533d495f8f2aaa9a0491c3ad0)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (C) 2013 - 2024 Intel Corporation
4  */
5 
6 #include <linux/auxiliary_bus.h>
7 #include <linux/device.h>
8 #include <linux/dma-mapping.h>
9 #include <linux/err.h>
10 #include <linux/list.h>
11 #include <linux/mutex.h>
12 #include <linux/pci.h>
13 #include <linux/pm_domain.h>
14 #include <linux/pm_runtime.h>
15 #include <linux/slab.h>
16 
17 #include "ipu6.h"
18 #include "ipu6-bus.h"
19 #include "ipu6-buttress.h"
20 #include "ipu6-dma.h"
21 
22 static int bus_pm_runtime_suspend(struct device *dev)
23 {
24 	struct ipu6_bus_device *adev = to_ipu6_bus_device(dev);
25 	int ret;
26 
27 	ret = pm_generic_runtime_suspend(dev);
28 	if (ret)
29 		return ret;
30 
31 	ret = ipu6_buttress_power(dev, adev->ctrl, false);
32 	if (!ret)
33 		return 0;
34 
35 	dev_err(dev, "power down failed!\n");
36 
37 	/* Powering down failed, attempt to resume device now */
38 	ret = pm_generic_runtime_resume(dev);
39 	if (!ret)
40 		return -EBUSY;
41 
42 	return -EIO;
43 }
44 
45 static int bus_pm_runtime_resume(struct device *dev)
46 {
47 	struct ipu6_bus_device *adev = to_ipu6_bus_device(dev);
48 	int ret;
49 
50 	ret = ipu6_buttress_power(dev, adev->ctrl, true);
51 	if (ret)
52 		return ret;
53 
54 	ret = pm_generic_runtime_resume(dev);
55 	if (ret)
56 		goto out_err;
57 
58 	return 0;
59 
60 out_err:
61 	ipu6_buttress_power(dev, adev->ctrl, false);
62 
63 	return -EBUSY;
64 }
65 
66 static struct dev_pm_domain ipu6_bus_pm_domain = {
67 	.ops = {
68 		.runtime_suspend = bus_pm_runtime_suspend,
69 		.runtime_resume = bus_pm_runtime_resume,
70 	},
71 };
72 
73 static DEFINE_MUTEX(ipu6_bus_mutex);
74 
75 static void ipu6_bus_release(struct device *dev)
76 {
77 	struct ipu6_bus_device *adev = to_ipu6_bus_device(dev);
78 
79 	kfree(adev->pdata);
80 	kfree(adev);
81 }
82 
83 struct ipu6_bus_device *
84 ipu6_bus_initialize_device(struct pci_dev *pdev, struct device *parent,
85 			   void *pdata, struct ipu6_buttress_ctrl *ctrl,
86 			   char *name)
87 {
88 	struct auxiliary_device *auxdev;
89 	struct ipu6_bus_device *adev;
90 	struct ipu6_device *isp = pci_get_drvdata(pdev);
91 	int ret;
92 
93 	adev = kzalloc(sizeof(*adev), GFP_KERNEL);
94 	if (!adev)
95 		return ERR_PTR(-ENOMEM);
96 
97 	adev->dma_mask = DMA_BIT_MASK(isp->secure_mode ? IPU6_MMU_ADDR_BITS :
98 				      IPU6_MMU_ADDR_BITS_NON_SECURE);
99 	adev->isp = isp;
100 	adev->ctrl = ctrl;
101 	adev->pdata = pdata;
102 	auxdev = &adev->auxdev;
103 	auxdev->name = name;
104 	auxdev->id = (pci_domain_nr(pdev->bus) << 16) |
105 		      PCI_DEVID(pdev->bus->number, pdev->devfn);
106 
107 	auxdev->dev.parent = parent;
108 	auxdev->dev.release = ipu6_bus_release;
109 	auxdev->dev.dma_ops = &ipu6_dma_ops;
110 	auxdev->dev.dma_mask = &adev->dma_mask;
111 	auxdev->dev.dma_parms = pdev->dev.dma_parms;
112 	auxdev->dev.coherent_dma_mask = adev->dma_mask;
113 
114 	ret = auxiliary_device_init(auxdev);
115 	if (ret < 0) {
116 		dev_err(&isp->pdev->dev, "auxiliary device init failed (%d)\n",
117 			ret);
118 		kfree(adev);
119 		return ERR_PTR(ret);
120 	}
121 
122 	dev_pm_domain_set(&auxdev->dev, &ipu6_bus_pm_domain);
123 
124 	pm_runtime_forbid(&adev->auxdev.dev);
125 	pm_runtime_enable(&adev->auxdev.dev);
126 
127 	return adev;
128 }
129 
130 int ipu6_bus_add_device(struct ipu6_bus_device *adev)
131 {
132 	struct auxiliary_device *auxdev = &adev->auxdev;
133 	int ret;
134 
135 	ret = auxiliary_device_add(auxdev);
136 	if (ret) {
137 		auxiliary_device_uninit(auxdev);
138 		return ret;
139 	}
140 
141 	mutex_lock(&ipu6_bus_mutex);
142 	list_add(&adev->list, &adev->isp->devices);
143 	mutex_unlock(&ipu6_bus_mutex);
144 
145 	pm_runtime_allow(&auxdev->dev);
146 
147 	return 0;
148 }
149 
150 void ipu6_bus_del_devices(struct pci_dev *pdev)
151 {
152 	struct ipu6_device *isp = pci_get_drvdata(pdev);
153 	struct ipu6_bus_device *adev, *save;
154 
155 	mutex_lock(&ipu6_bus_mutex);
156 
157 	list_for_each_entry_safe(adev, save, &isp->devices, list) {
158 		pm_runtime_disable(&adev->auxdev.dev);
159 		list_del(&adev->list);
160 		auxiliary_device_delete(&adev->auxdev);
161 		auxiliary_device_uninit(&adev->auxdev);
162 	}
163 
164 	mutex_unlock(&ipu6_bus_mutex);
165 }
166