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 | cmd_flags: 0, |
25 | dpmcp_id: dpmcp_dev->obj_desc.id, |
26 | token: &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 | cmd_flags: 0, |
42 | token: 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, size: 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 | offset: mc_portal_phys_addr, |
102 | size: 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(dev: mc_io->dev, addr: 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(dev: mc_io->dev, p: 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, pool_type: FSL_MC_POOL_DPMCP, new_resource: &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(res: dpmcp_dev->regions); |
209 | |
210 | error = fsl_create_mc_io(dev: &mc_bus_dev->dev, |
211 | mc_portal_phys_addr, |
212 | mc_portal_size, dpmcp_dev, |
213 | flags: mc_io_flags, new_mc_io: &mc_io); |
214 | if (error < 0) |
215 | goto error_cleanup_resource; |
216 | |
217 | dpmcp_dev->consumer_link = device_link_add(consumer: &mc_dev->dev, |
218 | supplier: &dpmcp_dev->dev, |
219 | DL_FLAG_AUTOREMOVE_CONSUMER); |
220 | if (!dpmcp_dev->consumer_link) { |
221 | error = -EINVAL; |
222 | goto error_cleanup_mc_io; |
223 | } |
224 | |
225 | *new_mc_io = mc_io; |
226 | return 0; |
227 | |
228 | error_cleanup_mc_io: |
229 | fsl_destroy_mc_io(mc_io); |
230 | error_cleanup_resource: |
231 | fsl_mc_resource_free(resource); |
232 | return error; |
233 | } |
234 | EXPORT_SYMBOL_GPL(fsl_mc_portal_allocate); |
235 | |
236 | /** |
237 | * fsl_mc_portal_free - Returns an MC portal to the pool of free MC portals |
238 | * of a given MC bus |
239 | * |
240 | * @mc_io: Pointer to the fsl_mc_io object that wraps the MC portal to free |
241 | */ |
242 | void fsl_mc_portal_free(struct fsl_mc_io *mc_io) |
243 | { |
244 | struct fsl_mc_device *dpmcp_dev; |
245 | struct fsl_mc_resource *resource; |
246 | |
247 | /* |
248 | * Every mc_io obtained by calling fsl_mc_portal_allocate() is supposed |
249 | * to have a DPMCP object associated with. |
250 | */ |
251 | dpmcp_dev = mc_io->dpmcp_dev; |
252 | |
253 | resource = dpmcp_dev->resource; |
254 | if (!resource || resource->type != FSL_MC_POOL_DPMCP) |
255 | return; |
256 | |
257 | if (resource->data != dpmcp_dev) |
258 | return; |
259 | |
260 | fsl_destroy_mc_io(mc_io); |
261 | fsl_mc_resource_free(resource); |
262 | |
263 | dpmcp_dev->consumer_link = NULL; |
264 | } |
265 | EXPORT_SYMBOL_GPL(fsl_mc_portal_free); |
266 | |
267 | /** |
268 | * fsl_mc_portal_reset - Resets the dpmcp object for a given fsl_mc_io object |
269 | * |
270 | * @mc_io: Pointer to the fsl_mc_io object that wraps the MC portal to free |
271 | */ |
272 | int fsl_mc_portal_reset(struct fsl_mc_io *mc_io) |
273 | { |
274 | int error; |
275 | struct fsl_mc_device *dpmcp_dev = mc_io->dpmcp_dev; |
276 | |
277 | error = dpmcp_reset(mc_io, cmd_flags: 0, token: dpmcp_dev->mc_handle); |
278 | if (error < 0) { |
279 | dev_err(&dpmcp_dev->dev, "dpmcp_reset() failed: %d\n" , error); |
280 | return error; |
281 | } |
282 | |
283 | return 0; |
284 | } |
285 | EXPORT_SYMBOL_GPL(fsl_mc_portal_reset); |
286 | |