Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/bus/fsl-mc/mc-io.c
26282 views
1
// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
2
/*
3
* Copyright 2013-2016 Freescale Semiconductor Inc.
4
*
5
*/
6
7
#include <linux/io.h>
8
#include <linux/fsl/mc.h>
9
10
#include "fsl-mc-private.h"
11
12
static int fsl_mc_io_set_dpmcp(struct fsl_mc_io *mc_io,
13
struct fsl_mc_device *dpmcp_dev)
14
{
15
int error;
16
17
if (mc_io->dpmcp_dev)
18
return -EINVAL;
19
20
if (dpmcp_dev->mc_io)
21
return -EINVAL;
22
23
error = dpmcp_open(mc_io,
24
0,
25
dpmcp_dev->obj_desc.id,
26
&dpmcp_dev->mc_handle);
27
if (error < 0)
28
return error;
29
30
mc_io->dpmcp_dev = dpmcp_dev;
31
dpmcp_dev->mc_io = mc_io;
32
return 0;
33
}
34
35
static void fsl_mc_io_unset_dpmcp(struct fsl_mc_io *mc_io)
36
{
37
int error;
38
struct fsl_mc_device *dpmcp_dev = mc_io->dpmcp_dev;
39
40
error = dpmcp_close(mc_io,
41
0,
42
dpmcp_dev->mc_handle);
43
if (error < 0) {
44
dev_err(&dpmcp_dev->dev, "dpmcp_close() failed: %d\n",
45
error);
46
}
47
48
mc_io->dpmcp_dev = NULL;
49
dpmcp_dev->mc_io = NULL;
50
}
51
52
/**
53
* fsl_create_mc_io() - Creates an MC I/O object
54
*
55
* @dev: device to be associated with the MC I/O object
56
* @mc_portal_phys_addr: physical address of the MC portal to use
57
* @mc_portal_size: size in bytes of the MC portal
58
* @dpmcp_dev: Pointer to the DPMCP object associated with this MC I/O
59
* object or NULL if none.
60
* @flags: flags for the new MC I/O object
61
* @new_mc_io: Area to return pointer to newly created MC I/O object
62
*
63
* Returns '0' on Success; Error code otherwise.
64
*/
65
int __must_check fsl_create_mc_io(struct device *dev,
66
phys_addr_t mc_portal_phys_addr,
67
u32 mc_portal_size,
68
struct fsl_mc_device *dpmcp_dev,
69
u32 flags, struct fsl_mc_io **new_mc_io)
70
{
71
int error;
72
struct fsl_mc_io *mc_io;
73
void __iomem *mc_portal_virt_addr;
74
struct resource *res;
75
76
mc_io = devm_kzalloc(dev, sizeof(*mc_io), GFP_KERNEL);
77
if (!mc_io)
78
return -ENOMEM;
79
80
mc_io->dev = dev;
81
mc_io->flags = flags;
82
mc_io->portal_phys_addr = mc_portal_phys_addr;
83
mc_io->portal_size = mc_portal_size;
84
if (flags & FSL_MC_IO_ATOMIC_CONTEXT_PORTAL)
85
raw_spin_lock_init(&mc_io->spinlock);
86
else
87
mutex_init(&mc_io->mutex);
88
89
res = devm_request_mem_region(dev,
90
mc_portal_phys_addr,
91
mc_portal_size,
92
"mc_portal");
93
if (!res) {
94
dev_err(dev,
95
"devm_request_mem_region failed for MC portal %pa\n",
96
&mc_portal_phys_addr);
97
return -EBUSY;
98
}
99
100
mc_portal_virt_addr = devm_ioremap(dev,
101
mc_portal_phys_addr,
102
mc_portal_size);
103
if (!mc_portal_virt_addr) {
104
dev_err(dev,
105
"devm_ioremap failed for MC portal %pa\n",
106
&mc_portal_phys_addr);
107
return -ENXIO;
108
}
109
110
mc_io->portal_virt_addr = mc_portal_virt_addr;
111
if (dpmcp_dev) {
112
error = fsl_mc_io_set_dpmcp(mc_io, dpmcp_dev);
113
if (error < 0)
114
goto error_destroy_mc_io;
115
}
116
117
*new_mc_io = mc_io;
118
return 0;
119
120
error_destroy_mc_io:
121
fsl_destroy_mc_io(mc_io);
122
return error;
123
}
124
125
/**
126
* fsl_destroy_mc_io() - Destroys an MC I/O object
127
*
128
* @mc_io: MC I/O object to destroy
129
*/
130
void fsl_destroy_mc_io(struct fsl_mc_io *mc_io)
131
{
132
struct fsl_mc_device *dpmcp_dev;
133
134
if (!mc_io)
135
return;
136
137
dpmcp_dev = mc_io->dpmcp_dev;
138
139
if (dpmcp_dev)
140
fsl_mc_io_unset_dpmcp(mc_io);
141
142
devm_iounmap(mc_io->dev, mc_io->portal_virt_addr);
143
devm_release_mem_region(mc_io->dev,
144
mc_io->portal_phys_addr,
145
mc_io->portal_size);
146
147
mc_io->portal_virt_addr = NULL;
148
devm_kfree(mc_io->dev, mc_io);
149
}
150
151
/**
152
* fsl_mc_portal_allocate - Allocates an MC portal
153
*
154
* @mc_dev: MC device for which the MC portal is to be allocated
155
* @mc_io_flags: Flags for the fsl_mc_io object that wraps the allocated
156
* MC portal.
157
* @new_mc_io: Pointer to area where the pointer to the fsl_mc_io object
158
* that wraps the allocated MC portal is to be returned
159
*
160
* This function allocates an MC portal from the device's parent DPRC,
161
* from the corresponding MC bus' pool of MC portals and wraps
162
* it in a new fsl_mc_io object. If 'mc_dev' is a DPRC itself, the
163
* portal is allocated from its own MC bus.
164
*/
165
int __must_check fsl_mc_portal_allocate(struct fsl_mc_device *mc_dev,
166
u16 mc_io_flags,
167
struct fsl_mc_io **new_mc_io)
168
{
169
struct fsl_mc_device *mc_bus_dev;
170
struct fsl_mc_bus *mc_bus;
171
phys_addr_t mc_portal_phys_addr;
172
size_t mc_portal_size;
173
struct fsl_mc_device *dpmcp_dev;
174
int error = -EINVAL;
175
struct fsl_mc_resource *resource = NULL;
176
struct fsl_mc_io *mc_io = NULL;
177
178
if (mc_dev->flags & FSL_MC_IS_DPRC) {
179
mc_bus_dev = mc_dev;
180
} else {
181
if (!dev_is_fsl_mc(mc_dev->dev.parent))
182
return error;
183
184
mc_bus_dev = to_fsl_mc_device(mc_dev->dev.parent);
185
}
186
187
mc_bus = to_fsl_mc_bus(mc_bus_dev);
188
*new_mc_io = NULL;
189
error = fsl_mc_resource_allocate(mc_bus, FSL_MC_POOL_DPMCP, &resource);
190
if (error < 0)
191
return error;
192
193
error = -EINVAL;
194
dpmcp_dev = resource->data;
195
196
if (dpmcp_dev->obj_desc.ver_major < DPMCP_MIN_VER_MAJOR ||
197
(dpmcp_dev->obj_desc.ver_major == DPMCP_MIN_VER_MAJOR &&
198
dpmcp_dev->obj_desc.ver_minor < DPMCP_MIN_VER_MINOR)) {
199
dev_err(&dpmcp_dev->dev,
200
"ERROR: Version %d.%d of DPMCP not supported.\n",
201
dpmcp_dev->obj_desc.ver_major,
202
dpmcp_dev->obj_desc.ver_minor);
203
error = -ENOTSUPP;
204
goto error_cleanup_resource;
205
}
206
207
mc_portal_phys_addr = dpmcp_dev->regions[0].start;
208
mc_portal_size = resource_size(dpmcp_dev->regions);
209
210
error = fsl_create_mc_io(&mc_bus_dev->dev,
211
mc_portal_phys_addr,
212
mc_portal_size, dpmcp_dev,
213
mc_io_flags, &mc_io);
214
if (error < 0)
215
goto error_cleanup_resource;
216
217
/* If the DPRC device itself tries to allocate a portal (usually for
218
* UAPI interaction), don't add a device link between them since the
219
* DPMCP device is an actual child device of the DPRC and a reverse
220
* dependency is not allowed.
221
*/
222
if (mc_dev != mc_bus_dev) {
223
dpmcp_dev->consumer_link = device_link_add(&mc_dev->dev,
224
&dpmcp_dev->dev,
225
DL_FLAG_AUTOREMOVE_CONSUMER);
226
if (!dpmcp_dev->consumer_link) {
227
error = -EINVAL;
228
goto error_cleanup_mc_io;
229
}
230
}
231
232
*new_mc_io = mc_io;
233
return 0;
234
235
error_cleanup_mc_io:
236
fsl_destroy_mc_io(mc_io);
237
error_cleanup_resource:
238
fsl_mc_resource_free(resource);
239
return error;
240
}
241
EXPORT_SYMBOL_GPL(fsl_mc_portal_allocate);
242
243
/**
244
* fsl_mc_portal_free - Returns an MC portal to the pool of free MC portals
245
* of a given MC bus
246
*
247
* @mc_io: Pointer to the fsl_mc_io object that wraps the MC portal to free
248
*/
249
void fsl_mc_portal_free(struct fsl_mc_io *mc_io)
250
{
251
struct fsl_mc_device *dpmcp_dev;
252
struct fsl_mc_resource *resource;
253
254
/*
255
* Every mc_io obtained by calling fsl_mc_portal_allocate() is supposed
256
* to have a DPMCP object associated with.
257
*/
258
dpmcp_dev = mc_io->dpmcp_dev;
259
260
resource = dpmcp_dev->resource;
261
if (!resource || resource->type != FSL_MC_POOL_DPMCP)
262
return;
263
264
if (resource->data != dpmcp_dev)
265
return;
266
267
fsl_destroy_mc_io(mc_io);
268
fsl_mc_resource_free(resource);
269
270
dpmcp_dev->consumer_link = NULL;
271
}
272
EXPORT_SYMBOL_GPL(fsl_mc_portal_free);
273
274