1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3 * Copyright (C) 2013 - 2025 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 "ipu7.h"
18 #include "ipu7-bus.h"
19 #include "ipu7-boot.h"
20 #include "ipu7-dma.h"
21
bus_pm_runtime_suspend(struct device * dev)22 static int bus_pm_runtime_suspend(struct device *dev)
23 {
24 struct ipu7_bus_device *adev = to_ipu7_bus_device(dev);
25 int ret;
26
27 ret = pm_generic_runtime_suspend(dev);
28 if (ret)
29 return ret;
30
31 ret = ipu_buttress_powerdown(dev, adev->ctrl);
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
bus_pm_runtime_resume(struct device * dev)45 static int bus_pm_runtime_resume(struct device *dev)
46 {
47 struct ipu7_bus_device *adev = to_ipu7_bus_device(dev);
48 int ret;
49
50 ret = ipu_buttress_powerup(dev, adev->ctrl);
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 ipu_buttress_powerdown(dev, adev->ctrl);
62
63 return -EBUSY;
64 }
65
66 static struct dev_pm_domain ipu7_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(ipu7_bus_mutex);
ipu7_bus_release(struct device * dev)74 static void ipu7_bus_release(struct device *dev)
75 {
76 struct ipu7_bus_device *adev = to_ipu7_bus_device(dev);
77
78 kfree(adev->pdata);
79 kfree(adev);
80 }
81
82 struct ipu7_bus_device *
ipu7_bus_initialize_device(struct pci_dev * pdev,struct device * parent,void * pdata,const struct ipu_buttress_ctrl * ctrl,const char * name)83 ipu7_bus_initialize_device(struct pci_dev *pdev, struct device *parent,
84 void *pdata, const struct ipu_buttress_ctrl *ctrl,
85 const char *name)
86 {
87 struct auxiliary_device *auxdev;
88 struct ipu7_bus_device *adev;
89 struct ipu7_device *isp = pci_get_drvdata(pdev);
90 int ret;
91
92 adev = kzalloc(sizeof(*adev), GFP_KERNEL);
93 if (!adev)
94 return ERR_PTR(-ENOMEM);
95
96 adev->isp = isp;
97 adev->ctrl = ctrl;
98 adev->pdata = pdata;
99 auxdev = &adev->auxdev;
100 auxdev->name = name;
101 auxdev->id = (pci_domain_nr(pdev->bus) << 16) |
102 PCI_DEVID(pdev->bus->number, pdev->devfn);
103
104 auxdev->dev.parent = parent;
105 auxdev->dev.release = ipu7_bus_release;
106
107 ret = auxiliary_device_init(auxdev);
108 if (ret < 0) {
109 dev_err(&isp->pdev->dev, "auxiliary device init failed (%d)\n",
110 ret);
111 kfree(adev);
112 return ERR_PTR(ret);
113 }
114
115 dev_pm_domain_set(&auxdev->dev, &ipu7_bus_pm_domain);
116
117 pm_runtime_forbid(&adev->auxdev.dev);
118 pm_runtime_enable(&adev->auxdev.dev);
119
120 return adev;
121 }
122
ipu7_bus_add_device(struct ipu7_bus_device * adev)123 int ipu7_bus_add_device(struct ipu7_bus_device *adev)
124 {
125 struct auxiliary_device *auxdev = &adev->auxdev;
126 int ret;
127
128 ret = auxiliary_device_add(auxdev);
129 if (ret) {
130 auxiliary_device_uninit(auxdev);
131 return ret;
132 }
133
134 mutex_lock(&ipu7_bus_mutex);
135 list_add(&adev->list, &adev->isp->devices);
136 mutex_unlock(&ipu7_bus_mutex);
137
138 pm_runtime_allow(&auxdev->dev);
139
140 return 0;
141 }
142
ipu7_bus_del_devices(struct pci_dev * pdev)143 void ipu7_bus_del_devices(struct pci_dev *pdev)
144 {
145 struct ipu7_device *isp = pci_get_drvdata(pdev);
146 struct ipu7_bus_device *adev, *save;
147
148 mutex_lock(&ipu7_bus_mutex);
149
150 list_for_each_entry_safe(adev, save, &isp->devices, list) {
151 pm_runtime_disable(&adev->auxdev.dev);
152 list_del(&adev->list);
153 auxiliary_device_delete(&adev->auxdev);
154 auxiliary_device_uninit(&adev->auxdev);
155 }
156
157 mutex_unlock(&ipu7_bus_mutex);
158 }
159