Line data Source code
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 4 : 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 4 : ret = pm_generic_runtime_suspend(dev);
28 4 : if (ret)
29 : return ret;
30 :
31 4 : ret = ipu6_buttress_power(dev, adev->ctrl, false);
32 4 : if (!ret)
33 : return 0;
34 :
35 0 : dev_err(dev, "power down failed!\n");
36 :
37 : /* Powering down failed, attempt to resume device now */
38 0 : ret = pm_generic_runtime_resume(dev);
39 0 : if (!ret)
40 0 : return -EBUSY;
41 :
42 : return -EIO;
43 : }
44 :
45 4 : 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 4 : ret = ipu6_buttress_power(dev, adev->ctrl, true);
51 4 : if (ret)
52 : return ret;
53 :
54 4 : ret = pm_generic_runtime_resume(dev);
55 4 : if (ret)
56 0 : goto out_err;
57 :
58 : return 0;
59 :
60 : out_err:
61 0 : ipu6_buttress_power(dev, adev->ctrl, false);
62 :
63 0 : 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 0 : static void ipu6_bus_release(struct device *dev)
76 : {
77 : struct ipu6_bus_device *adev = to_ipu6_bus_device(dev);
78 :
79 0 : kfree(adev->pdata);
80 0 : kfree(adev);
81 0 : }
82 :
83 : struct ipu6_bus_device *
84 2 : ipu6_bus_initialize_device(struct pci_dev *pdev, struct device *parent,
85 : void *pdata, const 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 2 : adev = kzalloc(sizeof(*adev), GFP_KERNEL);
94 2 : if (!adev)
95 : return ERR_PTR(-ENOMEM);
96 :
97 2 : adev->isp = isp;
98 2 : adev->ctrl = ctrl;
99 2 : adev->pdata = pdata;
100 2 : auxdev = &adev->auxdev;
101 2 : auxdev->name = name;
102 2 : auxdev->id = (pci_domain_nr(pdev->bus) << 16) |
103 2 : PCI_DEVID(pdev->bus->number, pdev->devfn);
104 :
105 2 : auxdev->dev.parent = parent;
106 2 : auxdev->dev.release = ipu6_bus_release;
107 :
108 2 : ret = auxiliary_device_init(auxdev);
109 2 : if (ret < 0) {
110 0 : dev_err(&isp->pdev->dev, "auxiliary device init failed (%d)\n",
111 : ret);
112 0 : kfree(adev);
113 0 : return ERR_PTR(ret);
114 : }
115 :
116 2 : dev_pm_domain_set(&auxdev->dev, &ipu6_bus_pm_domain);
117 :
118 2 : pm_runtime_forbid(&adev->auxdev.dev);
119 2 : pm_runtime_enable(&adev->auxdev.dev);
120 :
121 2 : return adev;
122 : }
123 :
124 2 : int ipu6_bus_add_device(struct ipu6_bus_device *adev)
125 : {
126 2 : struct auxiliary_device *auxdev = &adev->auxdev;
127 : int ret;
128 :
129 2 : ret = auxiliary_device_add(auxdev);
130 2 : if (ret) {
131 : auxiliary_device_uninit(auxdev);
132 0 : return ret;
133 : }
134 :
135 2 : mutex_lock(&ipu6_bus_mutex);
136 2 : list_add(&adev->list, &adev->isp->devices);
137 2 : mutex_unlock(&ipu6_bus_mutex);
138 :
139 2 : pm_runtime_allow(&auxdev->dev);
140 :
141 2 : return 0;
142 : }
143 :
144 0 : void ipu6_bus_del_devices(struct pci_dev *pdev)
145 : {
146 : struct ipu6_device *isp = pci_get_drvdata(pdev);
147 : struct ipu6_bus_device *adev, *save;
148 :
149 0 : mutex_lock(&ipu6_bus_mutex);
150 :
151 0 : list_for_each_entry_safe(adev, save, &isp->devices, list) {
152 0 : pm_runtime_disable(&adev->auxdev.dev);
153 : list_del(&adev->list);
154 : auxiliary_device_delete(&adev->auxdev);
155 : auxiliary_device_uninit(&adev->auxdev);
156 : }
157 :
158 0 : mutex_unlock(&ipu6_bus_mutex);
159 0 : }
160 :
161 : // Implemented by Ambu for silent_reset feature
162 0 : int ipu6_bus_reset_device(struct ipu6_bus_device *adev)
163 : {
164 : int ret = 0;
165 0 : struct device *dev = &adev->auxdev.dev;
166 :
167 0 : ret = bus_pm_runtime_suspend(dev);
168 0 : if (ret < 0)
169 : return ret;
170 :
171 0 : ret = bus_pm_runtime_resume(dev);
172 0 : return ret;
173 : }
174 : EXPORT_SYMBOL(ipu6_bus_reset_device);
|