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
22static 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
45static 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
60out_err:
61	ipu6_buttress_power(dev, adev->ctrl, false);
62
63	return -EBUSY;
64}
65
66static 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
73static DEFINE_MUTEX(ipu6_bus_mutex);
74
75static 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
83struct ipu6_bus_device *
84ipu6_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
130int 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
150void 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