]>
Commit | Line | Data |
---|---|---|
203e2de3 | 1 | // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) |
363b0cbf | 2 | /* |
6466dac7 | 3 | * Copyright 2013-2016 Freescale Semiconductor Inc. |
31c88965 GR |
4 | * |
5 | * I/O services to send MC commands to the MC hardware | |
6 | * | |
31c88965 GR |
7 | */ |
8 | ||
31c88965 GR |
9 | #include <linux/delay.h> |
10 | #include <linux/slab.h> | |
11 | #include <linux/ioport.h> | |
12 | #include <linux/device.h> | |
5143ecf6 | 13 | #include <linux/io.h> |
a7fb818e | 14 | #include <linux/io-64-nonatomic-hi-lo.h> |
6bd067c4 | 15 | #include <linux/fsl/mc.h> |
5143ecf6 | 16 | |
39d14e4e | 17 | #include "fsl-mc-private.h" |
31c88965 GR |
18 | |
19 | /** | |
c6a3363c | 20 | * Timeout in milliseconds to wait for the completion of an MC command |
31c88965 | 21 | */ |
c6a3363c | 22 | #define MC_CMD_COMPLETION_TIMEOUT_MS 500 |
31c88965 GR |
23 | |
24 | /* | |
25 | * usleep_range() min and max values used to throttle down polling | |
26 | * iterations while waiting for MC command completion | |
27 | */ | |
28 | #define MC_CMD_COMPLETION_POLLING_MIN_SLEEP_USECS 10 | |
29 | #define MC_CMD_COMPLETION_POLLING_MAX_SLEEP_USECS 500 | |
30 | ||
5b04cede | 31 | static enum mc_cmd_status mc_cmd_hdr_read_status(struct fsl_mc_command *cmd) |
9989b599 IR |
32 | { |
33 | struct mc_cmd_header *hdr = (struct mc_cmd_header *)&cmd->header; | |
34 | ||
35 | return (enum mc_cmd_status)hdr->status; | |
36 | } | |
37 | ||
5b04cede | 38 | static u16 mc_cmd_hdr_read_cmdid(struct fsl_mc_command *cmd) |
9989b599 IR |
39 | { |
40 | struct mc_cmd_header *hdr = (struct mc_cmd_header *)&cmd->header; | |
41 | u16 cmd_id = le16_to_cpu(hdr->cmd_id); | |
42 | ||
decd3d0c | 43 | return cmd_id; |
9989b599 | 44 | } |
31c88965 | 45 | |
31c88965 GR |
46 | static int mc_status_to_error(enum mc_cmd_status status) |
47 | { | |
48 | static const int mc_status_to_error_map[] = { | |
49 | [MC_CMD_STATUS_OK] = 0, | |
50 | [MC_CMD_STATUS_AUTH_ERR] = -EACCES, | |
51 | [MC_CMD_STATUS_NO_PRIVILEGE] = -EPERM, | |
52 | [MC_CMD_STATUS_DMA_ERR] = -EIO, | |
53 | [MC_CMD_STATUS_CONFIG_ERR] = -ENXIO, | |
54 | [MC_CMD_STATUS_TIMEOUT] = -ETIMEDOUT, | |
55 | [MC_CMD_STATUS_NO_RESOURCE] = -ENAVAIL, | |
56 | [MC_CMD_STATUS_NO_MEMORY] = -ENOMEM, | |
57 | [MC_CMD_STATUS_BUSY] = -EBUSY, | |
58 | [MC_CMD_STATUS_UNSUPPORTED_OP] = -ENOTSUPP, | |
59 | [MC_CMD_STATUS_INVALID_STATE] = -ENODEV, | |
60 | }; | |
61 | ||
a385dd7b | 62 | if ((u32)status >= ARRAY_SIZE(mc_status_to_error_map)) |
31c88965 GR |
63 | return -EINVAL; |
64 | ||
65 | return mc_status_to_error_map[status]; | |
66 | } | |
67 | ||
68 | static const char *mc_status_to_string(enum mc_cmd_status status) | |
69 | { | |
70 | static const char *const status_strings[] = { | |
71 | [MC_CMD_STATUS_OK] = "Command completed successfully", | |
72 | [MC_CMD_STATUS_READY] = "Command ready to be processed", | |
73 | [MC_CMD_STATUS_AUTH_ERR] = "Authentication error", | |
74 | [MC_CMD_STATUS_NO_PRIVILEGE] = "No privilege", | |
75 | [MC_CMD_STATUS_DMA_ERR] = "DMA or I/O error", | |
76 | [MC_CMD_STATUS_CONFIG_ERR] = "Configuration error", | |
77 | [MC_CMD_STATUS_TIMEOUT] = "Operation timed out", | |
78 | [MC_CMD_STATUS_NO_RESOURCE] = "No resources", | |
79 | [MC_CMD_STATUS_NO_MEMORY] = "No memory available", | |
80 | [MC_CMD_STATUS_BUSY] = "Device is busy", | |
81 | [MC_CMD_STATUS_UNSUPPORTED_OP] = "Unsupported operation", | |
82 | [MC_CMD_STATUS_INVALID_STATE] = "Invalid state" | |
83 | }; | |
84 | ||
85 | if ((unsigned int)status >= ARRAY_SIZE(status_strings)) | |
86 | return "Unknown MC error"; | |
87 | ||
88 | return status_strings[status]; | |
89 | } | |
90 | ||
91 | /** | |
92 | * mc_write_command - writes a command to a Management Complex (MC) portal | |
93 | * | |
94 | * @portal: pointer to an MC portal | |
95 | * @cmd: pointer to a filled command | |
96 | */ | |
5b04cede IC |
97 | static inline void mc_write_command(struct fsl_mc_command __iomem *portal, |
98 | struct fsl_mc_command *cmd) | |
31c88965 GR |
99 | { |
100 | int i; | |
101 | ||
102 | /* copy command parameters into the portal */ | |
103 | for (i = 0; i < MC_CMD_NUM_OF_PARAMS; i++) | |
b064d0e6 LT |
104 | /* |
105 | * Data is already in the expected LE byte-order. Do an | |
106 | * extra LE -> CPU conversion so that the CPU -> LE done in | |
107 | * the device io write api puts it back in the right order. | |
108 | */ | |
109 | writeq_relaxed(le64_to_cpu(cmd->params[i]), &portal->params[i]); | |
31c88965 GR |
110 | |
111 | /* submit the command by writing the header */ | |
b064d0e6 | 112 | writeq(le64_to_cpu(cmd->header), &portal->header); |
31c88965 GR |
113 | } |
114 | ||
115 | /** | |
116 | * mc_read_response - reads the response for the last MC command from a | |
117 | * Management Complex (MC) portal | |
118 | * | |
119 | * @portal: pointer to an MC portal | |
120 | * @resp: pointer to command response buffer | |
121 | * | |
122 | * Returns MC_CMD_STATUS_OK on Success; Error code otherwise. | |
123 | */ | |
5b04cede IC |
124 | static inline enum mc_cmd_status mc_read_response(struct fsl_mc_command __iomem |
125 | *portal, | |
126 | struct fsl_mc_command *resp) | |
31c88965 GR |
127 | { |
128 | int i; | |
129 | enum mc_cmd_status status; | |
130 | ||
131 | /* Copy command response header from MC portal: */ | |
b064d0e6 | 132 | resp->header = cpu_to_le64(readq_relaxed(&portal->header)); |
9989b599 | 133 | status = mc_cmd_hdr_read_status(resp); |
31c88965 GR |
134 | if (status != MC_CMD_STATUS_OK) |
135 | return status; | |
136 | ||
137 | /* Copy command response data from MC portal: */ | |
138 | for (i = 0; i < MC_CMD_NUM_OF_PARAMS; i++) | |
b064d0e6 LT |
139 | /* |
140 | * Data is expected to be in LE byte-order. Do an | |
141 | * extra CPU -> LE to revert the LE -> CPU done in | |
142 | * the device io read api. | |
143 | */ | |
144 | resp->params[i] = | |
145 | cpu_to_le64(readq_relaxed(&portal->params[i])); | |
31c88965 GR |
146 | |
147 | return status; | |
148 | } | |
149 | ||
150 | /** | |
140305e7 GR |
151 | * Waits for the completion of an MC command doing preemptible polling. |
152 | * uslepp_range() is called between polling iterations. | |
31c88965 GR |
153 | * |
154 | * @mc_io: MC I/O object to be used | |
140305e7 GR |
155 | * @cmd: command buffer to receive MC response |
156 | * @mc_status: MC command completion status | |
31c88965 | 157 | */ |
140305e7 | 158 | static int mc_polling_wait_preemptible(struct fsl_mc_io *mc_io, |
5b04cede | 159 | struct fsl_mc_command *cmd, |
140305e7 | 160 | enum mc_cmd_status *mc_status) |
31c88965 GR |
161 | { |
162 | enum mc_cmd_status status; | |
163 | unsigned long jiffies_until_timeout = | |
c6a3363c | 164 | jiffies + msecs_to_jiffies(MC_CMD_COMPLETION_TIMEOUT_MS); |
31c88965 | 165 | |
31c88965 GR |
166 | /* |
167 | * Wait for response from the MC hardware: | |
168 | */ | |
169 | for (;;) { | |
170 | status = mc_read_response(mc_io->portal_virt_addr, cmd); | |
171 | if (status != MC_CMD_STATUS_READY) | |
172 | break; | |
173 | ||
174 | /* | |
175 | * TODO: When MC command completion interrupts are supported | |
176 | * call wait function here instead of usleep_range() | |
177 | */ | |
178 | usleep_range(MC_CMD_COMPLETION_POLLING_MIN_SLEEP_USECS, | |
179 | MC_CMD_COMPLETION_POLLING_MAX_SLEEP_USECS); | |
180 | ||
181 | if (time_after_eq(jiffies, jiffies_until_timeout)) { | |
e79e344a | 182 | dev_dbg(mc_io->dev, |
f1027a8c LT |
183 | "MC command timed out (portal: %pa, dprc handle: %#x, command: %#x)\n", |
184 | &mc_io->portal_phys_addr, | |
9989b599 IR |
185 | (unsigned int)mc_cmd_hdr_read_token(cmd), |
186 | (unsigned int)mc_cmd_hdr_read_cmdid(cmd)); | |
31c88965 GR |
187 | |
188 | return -ETIMEDOUT; | |
189 | } | |
190 | } | |
191 | ||
140305e7 GR |
192 | *mc_status = status; |
193 | return 0; | |
194 | } | |
195 | ||
3f95ad21 GR |
196 | /** |
197 | * Waits for the completion of an MC command doing atomic polling. | |
198 | * udelay() is called between polling iterations. | |
199 | * | |
200 | * @mc_io: MC I/O object to be used | |
201 | * @cmd: command buffer to receive MC response | |
202 | * @mc_status: MC command completion status | |
203 | */ | |
204 | static int mc_polling_wait_atomic(struct fsl_mc_io *mc_io, | |
5b04cede | 205 | struct fsl_mc_command *cmd, |
3f95ad21 GR |
206 | enum mc_cmd_status *mc_status) |
207 | { | |
208 | enum mc_cmd_status status; | |
209 | unsigned long timeout_usecs = MC_CMD_COMPLETION_TIMEOUT_MS * 1000; | |
210 | ||
211 | BUILD_BUG_ON((MC_CMD_COMPLETION_TIMEOUT_MS * 1000) % | |
212 | MC_CMD_COMPLETION_POLLING_MAX_SLEEP_USECS != 0); | |
213 | ||
214 | for (;;) { | |
215 | status = mc_read_response(mc_io->portal_virt_addr, cmd); | |
216 | if (status != MC_CMD_STATUS_READY) | |
217 | break; | |
218 | ||
219 | udelay(MC_CMD_COMPLETION_POLLING_MAX_SLEEP_USECS); | |
220 | timeout_usecs -= MC_CMD_COMPLETION_POLLING_MAX_SLEEP_USECS; | |
221 | if (timeout_usecs == 0) { | |
e79e344a | 222 | dev_dbg(mc_io->dev, |
f1027a8c LT |
223 | "MC command timed out (portal: %pa, dprc handle: %#x, command: %#x)\n", |
224 | &mc_io->portal_phys_addr, | |
9989b599 IR |
225 | (unsigned int)mc_cmd_hdr_read_token(cmd), |
226 | (unsigned int)mc_cmd_hdr_read_cmdid(cmd)); | |
3f95ad21 GR |
227 | |
228 | return -ETIMEDOUT; | |
229 | } | |
230 | } | |
231 | ||
232 | *mc_status = status; | |
233 | return 0; | |
234 | } | |
235 | ||
140305e7 GR |
236 | /** |
237 | * Sends a command to the MC device using the given MC I/O object | |
238 | * | |
239 | * @mc_io: MC I/O object to be used | |
240 | * @cmd: command to be sent | |
241 | * | |
242 | * Returns '0' on Success; Error code otherwise. | |
140305e7 | 243 | */ |
5b04cede | 244 | int mc_send_command(struct fsl_mc_io *mc_io, struct fsl_mc_command *cmd) |
140305e7 GR |
245 | { |
246 | int error; | |
247 | enum mc_cmd_status status; | |
63f2be5c | 248 | unsigned long irq_flags = 0; |
140305e7 | 249 | |
a385dd7b | 250 | if (in_irq() && !(mc_io->flags & FSL_MC_IO_ATOMIC_CONTEXT_PORTAL)) |
3f95ad21 GR |
251 | return -EINVAL; |
252 | ||
63f2be5c GR |
253 | if (mc_io->flags & FSL_MC_IO_ATOMIC_CONTEXT_PORTAL) |
254 | spin_lock_irqsave(&mc_io->spinlock, irq_flags); | |
255 | else | |
256 | mutex_lock(&mc_io->mutex); | |
257 | ||
140305e7 GR |
258 | /* |
259 | * Send command to the MC hardware: | |
260 | */ | |
261 | mc_write_command(mc_io->portal_virt_addr, cmd); | |
262 | ||
263 | /* | |
264 | * Wait for response from the MC hardware: | |
265 | */ | |
3f95ad21 GR |
266 | if (!(mc_io->flags & FSL_MC_IO_ATOMIC_CONTEXT_PORTAL)) |
267 | error = mc_polling_wait_preemptible(mc_io, cmd, &status); | |
268 | else | |
269 | error = mc_polling_wait_atomic(mc_io, cmd, &status); | |
270 | ||
140305e7 | 271 | if (error < 0) |
63f2be5c | 272 | goto common_exit; |
140305e7 | 273 | |
31c88965 | 274 | if (status != MC_CMD_STATUS_OK) { |
e79e344a | 275 | dev_dbg(mc_io->dev, |
f1027a8c LT |
276 | "MC command failed: portal: %pa, dprc handle: %#x, command: %#x, status: %s (%#x)\n", |
277 | &mc_io->portal_phys_addr, | |
9989b599 IR |
278 | (unsigned int)mc_cmd_hdr_read_token(cmd), |
279 | (unsigned int)mc_cmd_hdr_read_cmdid(cmd), | |
31c88965 GR |
280 | mc_status_to_string(status), |
281 | (unsigned int)status); | |
282 | ||
63f2be5c GR |
283 | error = mc_status_to_error(status); |
284 | goto common_exit; | |
31c88965 GR |
285 | } |
286 | ||
63f2be5c GR |
287 | error = 0; |
288 | common_exit: | |
289 | if (mc_io->flags & FSL_MC_IO_ATOMIC_CONTEXT_PORTAL) | |
290 | spin_unlock_irqrestore(&mc_io->spinlock, irq_flags); | |
291 | else | |
292 | mutex_unlock(&mc_io->mutex); | |
293 | ||
294 | return error; | |
31c88965 | 295 | } |
c9d57ea0 | 296 | EXPORT_SYMBOL_GPL(mc_send_command); |