Commit | Line | Data |
---|---|---|
31c88965 GR |
1 | /* Copyright 2013-2014 Freescale Semiconductor Inc. |
2 | * | |
3 | * I/O services to send MC commands to the MC hardware | |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions are met: | |
7 | * * Redistributions of source code must retain the above copyright | |
8 | * notice, this list of conditions and the following disclaimer. | |
9 | * * Redistributions in binary form must reproduce the above copyright | |
10 | * notice, this list of conditions and the following disclaimer in the | |
11 | * documentation and/or other materials provided with the distribution. | |
12 | * * Neither the name of the above-listed copyright holders nor the | |
13 | * names of any contributors may be used to endorse or promote products | |
14 | * derived from this software without specific prior written permission. | |
15 | * | |
16 | * | |
17 | * ALTERNATIVELY, this software may be distributed under the terms of the | |
18 | * GNU General Public License ("GPL") as published by the Free Software | |
19 | * Foundation, either version 2 of that License or (at your option) any | |
20 | * later version. | |
21 | * | |
22 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
23 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
24 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
25 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE | |
26 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
27 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
28 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
29 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |
30 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
31 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | |
32 | * POSSIBILITY OF SUCH DAMAGE. | |
33 | */ | |
34 | ||
31c88965 GR |
35 | #include <linux/delay.h> |
36 | #include <linux/slab.h> | |
37 | #include <linux/ioport.h> | |
38 | #include <linux/device.h> | |
5143ecf6 | 39 | #include <linux/io.h> |
d4e75132 SY |
40 | #include "../include/mc-sys.h" |
41 | #include "../include/mc-cmd.h" | |
42 | #include "../include/mc.h" | |
5143ecf6 | 43 | |
ffcd52ef | 44 | #include "dpmcp.h" |
31c88965 GR |
45 | |
46 | /** | |
c6a3363c | 47 | * Timeout in milliseconds to wait for the completion of an MC command |
31c88965 | 48 | */ |
c6a3363c | 49 | #define MC_CMD_COMPLETION_TIMEOUT_MS 500 |
31c88965 GR |
50 | |
51 | /* | |
52 | * usleep_range() min and max values used to throttle down polling | |
53 | * iterations while waiting for MC command completion | |
54 | */ | |
55 | #define MC_CMD_COMPLETION_POLLING_MIN_SLEEP_USECS 10 | |
56 | #define MC_CMD_COMPLETION_POLLING_MAX_SLEEP_USECS 500 | |
57 | ||
9989b599 IR |
58 | static enum mc_cmd_status mc_cmd_hdr_read_status(struct mc_command *cmd) |
59 | { | |
60 | struct mc_cmd_header *hdr = (struct mc_cmd_header *)&cmd->header; | |
61 | ||
62 | return (enum mc_cmd_status)hdr->status; | |
63 | } | |
64 | ||
65 | static u16 mc_cmd_hdr_read_cmdid(struct mc_command *cmd) | |
66 | { | |
67 | struct mc_cmd_header *hdr = (struct mc_cmd_header *)&cmd->header; | |
68 | u16 cmd_id = le16_to_cpu(hdr->cmd_id); | |
69 | ||
70 | return (cmd_id & MC_CMD_HDR_CMDID_MASK) >> MC_CMD_HDR_CMDID_SHIFT; | |
71 | } | |
31c88965 | 72 | |
31c88965 GR |
73 | static int mc_status_to_error(enum mc_cmd_status status) |
74 | { | |
75 | static const int mc_status_to_error_map[] = { | |
76 | [MC_CMD_STATUS_OK] = 0, | |
77 | [MC_CMD_STATUS_AUTH_ERR] = -EACCES, | |
78 | [MC_CMD_STATUS_NO_PRIVILEGE] = -EPERM, | |
79 | [MC_CMD_STATUS_DMA_ERR] = -EIO, | |
80 | [MC_CMD_STATUS_CONFIG_ERR] = -ENXIO, | |
81 | [MC_CMD_STATUS_TIMEOUT] = -ETIMEDOUT, | |
82 | [MC_CMD_STATUS_NO_RESOURCE] = -ENAVAIL, | |
83 | [MC_CMD_STATUS_NO_MEMORY] = -ENOMEM, | |
84 | [MC_CMD_STATUS_BUSY] = -EBUSY, | |
85 | [MC_CMD_STATUS_UNSUPPORTED_OP] = -ENOTSUPP, | |
86 | [MC_CMD_STATUS_INVALID_STATE] = -ENODEV, | |
87 | }; | |
88 | ||
89 | if (WARN_ON((u32)status >= ARRAY_SIZE(mc_status_to_error_map))) | |
90 | return -EINVAL; | |
91 | ||
92 | return mc_status_to_error_map[status]; | |
93 | } | |
94 | ||
95 | static const char *mc_status_to_string(enum mc_cmd_status status) | |
96 | { | |
97 | static const char *const status_strings[] = { | |
98 | [MC_CMD_STATUS_OK] = "Command completed successfully", | |
99 | [MC_CMD_STATUS_READY] = "Command ready to be processed", | |
100 | [MC_CMD_STATUS_AUTH_ERR] = "Authentication error", | |
101 | [MC_CMD_STATUS_NO_PRIVILEGE] = "No privilege", | |
102 | [MC_CMD_STATUS_DMA_ERR] = "DMA or I/O error", | |
103 | [MC_CMD_STATUS_CONFIG_ERR] = "Configuration error", | |
104 | [MC_CMD_STATUS_TIMEOUT] = "Operation timed out", | |
105 | [MC_CMD_STATUS_NO_RESOURCE] = "No resources", | |
106 | [MC_CMD_STATUS_NO_MEMORY] = "No memory available", | |
107 | [MC_CMD_STATUS_BUSY] = "Device is busy", | |
108 | [MC_CMD_STATUS_UNSUPPORTED_OP] = "Unsupported operation", | |
109 | [MC_CMD_STATUS_INVALID_STATE] = "Invalid state" | |
110 | }; | |
111 | ||
112 | if ((unsigned int)status >= ARRAY_SIZE(status_strings)) | |
113 | return "Unknown MC error"; | |
114 | ||
115 | return status_strings[status]; | |
116 | } | |
117 | ||
118 | /** | |
119 | * mc_write_command - writes a command to a Management Complex (MC) portal | |
120 | * | |
121 | * @portal: pointer to an MC portal | |
122 | * @cmd: pointer to a filled command | |
123 | */ | |
124 | static inline void mc_write_command(struct mc_command __iomem *portal, | |
125 | struct mc_command *cmd) | |
126 | { | |
127 | int i; | |
128 | ||
129 | /* copy command parameters into the portal */ | |
130 | for (i = 0; i < MC_CMD_NUM_OF_PARAMS; i++) | |
9989b599 IR |
131 | __raw_writeq(cmd->params[i], &portal->params[i]); |
132 | __iowmb(); | |
31c88965 GR |
133 | |
134 | /* submit the command by writing the header */ | |
9989b599 | 135 | __raw_writeq(cmd->header, &portal->header); |
31c88965 GR |
136 | } |
137 | ||
138 | /** | |
139 | * mc_read_response - reads the response for the last MC command from a | |
140 | * Management Complex (MC) portal | |
141 | * | |
142 | * @portal: pointer to an MC portal | |
143 | * @resp: pointer to command response buffer | |
144 | * | |
145 | * Returns MC_CMD_STATUS_OK on Success; Error code otherwise. | |
146 | */ | |
147 | static inline enum mc_cmd_status mc_read_response(struct mc_command __iomem * | |
148 | portal, | |
149 | struct mc_command *resp) | |
150 | { | |
151 | int i; | |
152 | enum mc_cmd_status status; | |
153 | ||
154 | /* Copy command response header from MC portal: */ | |
9989b599 IR |
155 | __iormb(); |
156 | resp->header = __raw_readq(&portal->header); | |
157 | __iormb(); | |
158 | status = mc_cmd_hdr_read_status(resp); | |
31c88965 GR |
159 | if (status != MC_CMD_STATUS_OK) |
160 | return status; | |
161 | ||
162 | /* Copy command response data from MC portal: */ | |
163 | for (i = 0; i < MC_CMD_NUM_OF_PARAMS; i++) | |
9989b599 IR |
164 | resp->params[i] = __raw_readq(&portal->params[i]); |
165 | __iormb(); | |
31c88965 GR |
166 | |
167 | return status; | |
168 | } | |
169 | ||
170 | /** | |
140305e7 GR |
171 | * Waits for the completion of an MC command doing preemptible polling. |
172 | * uslepp_range() is called between polling iterations. | |
31c88965 GR |
173 | * |
174 | * @mc_io: MC I/O object to be used | |
140305e7 GR |
175 | * @cmd: command buffer to receive MC response |
176 | * @mc_status: MC command completion status | |
31c88965 | 177 | */ |
140305e7 GR |
178 | static int mc_polling_wait_preemptible(struct fsl_mc_io *mc_io, |
179 | struct mc_command *cmd, | |
180 | enum mc_cmd_status *mc_status) | |
31c88965 GR |
181 | { |
182 | enum mc_cmd_status status; | |
183 | unsigned long jiffies_until_timeout = | |
c6a3363c | 184 | jiffies + msecs_to_jiffies(MC_CMD_COMPLETION_TIMEOUT_MS); |
31c88965 | 185 | |
31c88965 GR |
186 | /* |
187 | * Wait for response from the MC hardware: | |
188 | */ | |
189 | for (;;) { | |
190 | status = mc_read_response(mc_io->portal_virt_addr, cmd); | |
191 | if (status != MC_CMD_STATUS_READY) | |
192 | break; | |
193 | ||
194 | /* | |
195 | * TODO: When MC command completion interrupts are supported | |
196 | * call wait function here instead of usleep_range() | |
197 | */ | |
198 | usleep_range(MC_CMD_COMPLETION_POLLING_MIN_SLEEP_USECS, | |
199 | MC_CMD_COMPLETION_POLLING_MAX_SLEEP_USECS); | |
200 | ||
201 | if (time_after_eq(jiffies, jiffies_until_timeout)) { | |
e79e344a | 202 | dev_dbg(mc_io->dev, |
de71daf5 | 203 | "MC command timed out (portal: %#llx, obj handle: %#x, command: %#x)\n", |
31c88965 | 204 | mc_io->portal_phys_addr, |
9989b599 IR |
205 | (unsigned int)mc_cmd_hdr_read_token(cmd), |
206 | (unsigned int)mc_cmd_hdr_read_cmdid(cmd)); | |
31c88965 GR |
207 | |
208 | return -ETIMEDOUT; | |
209 | } | |
210 | } | |
211 | ||
140305e7 GR |
212 | *mc_status = status; |
213 | return 0; | |
214 | } | |
215 | ||
3f95ad21 GR |
216 | /** |
217 | * Waits for the completion of an MC command doing atomic polling. | |
218 | * udelay() is called between polling iterations. | |
219 | * | |
220 | * @mc_io: MC I/O object to be used | |
221 | * @cmd: command buffer to receive MC response | |
222 | * @mc_status: MC command completion status | |
223 | */ | |
224 | static int mc_polling_wait_atomic(struct fsl_mc_io *mc_io, | |
225 | struct mc_command *cmd, | |
226 | enum mc_cmd_status *mc_status) | |
227 | { | |
228 | enum mc_cmd_status status; | |
229 | unsigned long timeout_usecs = MC_CMD_COMPLETION_TIMEOUT_MS * 1000; | |
230 | ||
231 | BUILD_BUG_ON((MC_CMD_COMPLETION_TIMEOUT_MS * 1000) % | |
232 | MC_CMD_COMPLETION_POLLING_MAX_SLEEP_USECS != 0); | |
233 | ||
234 | for (;;) { | |
235 | status = mc_read_response(mc_io->portal_virt_addr, cmd); | |
236 | if (status != MC_CMD_STATUS_READY) | |
237 | break; | |
238 | ||
239 | udelay(MC_CMD_COMPLETION_POLLING_MAX_SLEEP_USECS); | |
240 | timeout_usecs -= MC_CMD_COMPLETION_POLLING_MAX_SLEEP_USECS; | |
241 | if (timeout_usecs == 0) { | |
e79e344a | 242 | dev_dbg(mc_io->dev, |
de71daf5 | 243 | "MC command timed out (portal: %#llx, obj handle: %#x, command: %#x)\n", |
3f95ad21 | 244 | mc_io->portal_phys_addr, |
9989b599 IR |
245 | (unsigned int)mc_cmd_hdr_read_token(cmd), |
246 | (unsigned int)mc_cmd_hdr_read_cmdid(cmd)); | |
3f95ad21 GR |
247 | |
248 | return -ETIMEDOUT; | |
249 | } | |
250 | } | |
251 | ||
252 | *mc_status = status; | |
253 | return 0; | |
254 | } | |
255 | ||
140305e7 GR |
256 | /** |
257 | * Sends a command to the MC device using the given MC I/O object | |
258 | * | |
259 | * @mc_io: MC I/O object to be used | |
260 | * @cmd: command to be sent | |
261 | * | |
262 | * Returns '0' on Success; Error code otherwise. | |
140305e7 GR |
263 | */ |
264 | int mc_send_command(struct fsl_mc_io *mc_io, struct mc_command *cmd) | |
265 | { | |
266 | int error; | |
267 | enum mc_cmd_status status; | |
63f2be5c | 268 | unsigned long irq_flags = 0; |
140305e7 | 269 | |
3f95ad21 GR |
270 | if (WARN_ON(in_irq() && |
271 | !(mc_io->flags & FSL_MC_IO_ATOMIC_CONTEXT_PORTAL))) | |
272 | return -EINVAL; | |
273 | ||
63f2be5c GR |
274 | if (mc_io->flags & FSL_MC_IO_ATOMIC_CONTEXT_PORTAL) |
275 | spin_lock_irqsave(&mc_io->spinlock, irq_flags); | |
276 | else | |
277 | mutex_lock(&mc_io->mutex); | |
278 | ||
140305e7 GR |
279 | /* |
280 | * Send command to the MC hardware: | |
281 | */ | |
282 | mc_write_command(mc_io->portal_virt_addr, cmd); | |
283 | ||
284 | /* | |
285 | * Wait for response from the MC hardware: | |
286 | */ | |
3f95ad21 GR |
287 | if (!(mc_io->flags & FSL_MC_IO_ATOMIC_CONTEXT_PORTAL)) |
288 | error = mc_polling_wait_preemptible(mc_io, cmd, &status); | |
289 | else | |
290 | error = mc_polling_wait_atomic(mc_io, cmd, &status); | |
291 | ||
140305e7 | 292 | if (error < 0) |
63f2be5c | 293 | goto common_exit; |
140305e7 | 294 | |
31c88965 | 295 | if (status != MC_CMD_STATUS_OK) { |
e79e344a | 296 | dev_dbg(mc_io->dev, |
de71daf5 | 297 | "MC command failed: portal: %#llx, obj handle: %#x, command: %#x, status: %s (%#x)\n", |
31c88965 | 298 | mc_io->portal_phys_addr, |
9989b599 IR |
299 | (unsigned int)mc_cmd_hdr_read_token(cmd), |
300 | (unsigned int)mc_cmd_hdr_read_cmdid(cmd), | |
31c88965 GR |
301 | mc_status_to_string(status), |
302 | (unsigned int)status); | |
303 | ||
63f2be5c GR |
304 | error = mc_status_to_error(status); |
305 | goto common_exit; | |
31c88965 GR |
306 | } |
307 | ||
63f2be5c GR |
308 | error = 0; |
309 | common_exit: | |
310 | if (mc_io->flags & FSL_MC_IO_ATOMIC_CONTEXT_PORTAL) | |
311 | spin_unlock_irqrestore(&mc_io->spinlock, irq_flags); | |
312 | else | |
313 | mutex_unlock(&mc_io->mutex); | |
314 | ||
315 | return error; | |
31c88965 GR |
316 | } |
317 | EXPORT_SYMBOL(mc_send_command); |