diff options
Diffstat (limited to 'drivers/net/mlx4/cmd.c')
-rw-r--r-- | drivers/net/mlx4/cmd.c | 429 |
1 files changed, 429 insertions, 0 deletions
diff --git a/drivers/net/mlx4/cmd.c b/drivers/net/mlx4/cmd.c new file mode 100644 index 000000000000..c1f81a993f5d --- /dev/null +++ b/drivers/net/mlx4/cmd.c | |||
@@ -0,0 +1,429 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2004, 2005 Topspin Communications. All rights reserved. | ||
3 | * Copyright (c) 2005 Mellanox Technologies. All rights reserved. | ||
4 | * Copyright (c) 2005, 2006, 2007 Cisco Systems, Inc. All rights reserved. | ||
5 | * | ||
6 | * This software is available to you under a choice of one of two | ||
7 | * licenses. You may choose to be licensed under the terms of the GNU | ||
8 | * General Public License (GPL) Version 2, available from the file | ||
9 | * COPYING in the main directory of this source tree, or the | ||
10 | * OpenIB.org BSD license below: | ||
11 | * | ||
12 | * Redistribution and use in source and binary forms, with or | ||
13 | * without modification, are permitted provided that the following | ||
14 | * conditions are met: | ||
15 | * | ||
16 | * - Redistributions of source code must retain the above | ||
17 | * copyright notice, this list of conditions and the following | ||
18 | * disclaimer. | ||
19 | * | ||
20 | * - Redistributions in binary form must reproduce the above | ||
21 | * copyright notice, this list of conditions and the following | ||
22 | * disclaimer in the documentation and/or other materials | ||
23 | * provided with the distribution. | ||
24 | * | ||
25 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
26 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
27 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | ||
28 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | ||
29 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | ||
30 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | ||
31 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
32 | * SOFTWARE. | ||
33 | */ | ||
34 | |||
35 | #include <linux/sched.h> | ||
36 | #include <linux/pci.h> | ||
37 | #include <linux/errno.h> | ||
38 | |||
39 | #include <linux/mlx4/cmd.h> | ||
40 | |||
41 | #include <asm/io.h> | ||
42 | |||
43 | #include "mlx4.h" | ||
44 | |||
45 | #define CMD_POLL_TOKEN 0xffff | ||
46 | |||
47 | enum { | ||
48 | /* command completed successfully: */ | ||
49 | CMD_STAT_OK = 0x00, | ||
50 | /* Internal error (such as a bus error) occurred while processing command: */ | ||
51 | CMD_STAT_INTERNAL_ERR = 0x01, | ||
52 | /* Operation/command not supported or opcode modifier not supported: */ | ||
53 | CMD_STAT_BAD_OP = 0x02, | ||
54 | /* Parameter not supported or parameter out of range: */ | ||
55 | CMD_STAT_BAD_PARAM = 0x03, | ||
56 | /* System not enabled or bad system state: */ | ||
57 | CMD_STAT_BAD_SYS_STATE = 0x04, | ||
58 | /* Attempt to access reserved or unallocaterd resource: */ | ||
59 | CMD_STAT_BAD_RESOURCE = 0x05, | ||
60 | /* Requested resource is currently executing a command, or is otherwise busy: */ | ||
61 | CMD_STAT_RESOURCE_BUSY = 0x06, | ||
62 | /* Required capability exceeds device limits: */ | ||
63 | CMD_STAT_EXCEED_LIM = 0x08, | ||
64 | /* Resource is not in the appropriate state or ownership: */ | ||
65 | CMD_STAT_BAD_RES_STATE = 0x09, | ||
66 | /* Index out of range: */ | ||
67 | CMD_STAT_BAD_INDEX = 0x0a, | ||
68 | /* FW image corrupted: */ | ||
69 | CMD_STAT_BAD_NVMEM = 0x0b, | ||
70 | /* Attempt to modify a QP/EE which is not in the presumed state: */ | ||
71 | CMD_STAT_BAD_QP_STATE = 0x10, | ||
72 | /* Bad segment parameters (Address/Size): */ | ||
73 | CMD_STAT_BAD_SEG_PARAM = 0x20, | ||
74 | /* Memory Region has Memory Windows bound to: */ | ||
75 | CMD_STAT_REG_BOUND = 0x21, | ||
76 | /* HCA local attached memory not present: */ | ||
77 | CMD_STAT_LAM_NOT_PRE = 0x22, | ||
78 | /* Bad management packet (silently discarded): */ | ||
79 | CMD_STAT_BAD_PKT = 0x30, | ||
80 | /* More outstanding CQEs in CQ than new CQ size: */ | ||
81 | CMD_STAT_BAD_SIZE = 0x40 | ||
82 | }; | ||
83 | |||
84 | enum { | ||
85 | HCR_IN_PARAM_OFFSET = 0x00, | ||
86 | HCR_IN_MODIFIER_OFFSET = 0x08, | ||
87 | HCR_OUT_PARAM_OFFSET = 0x0c, | ||
88 | HCR_TOKEN_OFFSET = 0x14, | ||
89 | HCR_STATUS_OFFSET = 0x18, | ||
90 | |||
91 | HCR_OPMOD_SHIFT = 12, | ||
92 | HCR_T_BIT = 21, | ||
93 | HCR_E_BIT = 22, | ||
94 | HCR_GO_BIT = 23 | ||
95 | }; | ||
96 | |||
97 | enum { | ||
98 | GO_BIT_TIMEOUT = 10000 | ||
99 | }; | ||
100 | |||
101 | struct mlx4_cmd_context { | ||
102 | struct completion done; | ||
103 | int result; | ||
104 | int next; | ||
105 | u64 out_param; | ||
106 | u16 token; | ||
107 | }; | ||
108 | |||
109 | static int mlx4_status_to_errno(u8 status) { | ||
110 | static const int trans_table[] = { | ||
111 | [CMD_STAT_INTERNAL_ERR] = -EIO, | ||
112 | [CMD_STAT_BAD_OP] = -EPERM, | ||
113 | [CMD_STAT_BAD_PARAM] = -EINVAL, | ||
114 | [CMD_STAT_BAD_SYS_STATE] = -ENXIO, | ||
115 | [CMD_STAT_BAD_RESOURCE] = -EBADF, | ||
116 | [CMD_STAT_RESOURCE_BUSY] = -EBUSY, | ||
117 | [CMD_STAT_EXCEED_LIM] = -ENOMEM, | ||
118 | [CMD_STAT_BAD_RES_STATE] = -EBADF, | ||
119 | [CMD_STAT_BAD_INDEX] = -EBADF, | ||
120 | [CMD_STAT_BAD_NVMEM] = -EFAULT, | ||
121 | [CMD_STAT_BAD_QP_STATE] = -EINVAL, | ||
122 | [CMD_STAT_BAD_SEG_PARAM] = -EFAULT, | ||
123 | [CMD_STAT_REG_BOUND] = -EBUSY, | ||
124 | [CMD_STAT_LAM_NOT_PRE] = -EAGAIN, | ||
125 | [CMD_STAT_BAD_PKT] = -EINVAL, | ||
126 | [CMD_STAT_BAD_SIZE] = -ENOMEM, | ||
127 | }; | ||
128 | |||
129 | if (status >= ARRAY_SIZE(trans_table) || | ||
130 | (status != CMD_STAT_OK && trans_table[status] == 0)) | ||
131 | return -EIO; | ||
132 | |||
133 | return trans_table[status]; | ||
134 | } | ||
135 | |||
136 | static int cmd_pending(struct mlx4_dev *dev) | ||
137 | { | ||
138 | u32 status = readl(mlx4_priv(dev)->cmd.hcr + HCR_STATUS_OFFSET); | ||
139 | |||
140 | return (status & swab32(1 << HCR_GO_BIT)) || | ||
141 | (mlx4_priv(dev)->cmd.toggle == | ||
142 | !!(status & swab32(1 << HCR_T_BIT))); | ||
143 | } | ||
144 | |||
145 | static int mlx4_cmd_post(struct mlx4_dev *dev, u64 in_param, u64 out_param, | ||
146 | u32 in_modifier, u8 op_modifier, u16 op, u16 token, | ||
147 | int event) | ||
148 | { | ||
149 | struct mlx4_cmd *cmd = &mlx4_priv(dev)->cmd; | ||
150 | u32 __iomem *hcr = cmd->hcr; | ||
151 | int ret = -EAGAIN; | ||
152 | unsigned long end; | ||
153 | |||
154 | mutex_lock(&cmd->hcr_mutex); | ||
155 | |||
156 | end = jiffies; | ||
157 | if (event) | ||
158 | end += HZ * 10; | ||
159 | |||
160 | while (cmd_pending(dev)) { | ||
161 | if (time_after_eq(jiffies, end)) | ||
162 | goto out; | ||
163 | cond_resched(); | ||
164 | } | ||
165 | |||
166 | /* | ||
167 | * We use writel (instead of something like memcpy_toio) | ||
168 | * because writes of less than 32 bits to the HCR don't work | ||
169 | * (and some architectures such as ia64 implement memcpy_toio | ||
170 | * in terms of writeb). | ||
171 | */ | ||
172 | __raw_writel((__force u32) cpu_to_be32(in_param >> 32), hcr + 0); | ||
173 | __raw_writel((__force u32) cpu_to_be32(in_param & 0xfffffffful), hcr + 1); | ||
174 | __raw_writel((__force u32) cpu_to_be32(in_modifier), hcr + 2); | ||
175 | __raw_writel((__force u32) cpu_to_be32(out_param >> 32), hcr + 3); | ||
176 | __raw_writel((__force u32) cpu_to_be32(out_param & 0xfffffffful), hcr + 4); | ||
177 | __raw_writel((__force u32) cpu_to_be32(token << 16), hcr + 5); | ||
178 | |||
179 | /* __raw_writel may not order writes. */ | ||
180 | wmb(); | ||
181 | |||
182 | __raw_writel((__force u32) cpu_to_be32((1 << HCR_GO_BIT) | | ||
183 | (cmd->toggle << HCR_T_BIT) | | ||
184 | (event ? (1 << HCR_E_BIT) : 0) | | ||
185 | (op_modifier << HCR_OPMOD_SHIFT) | | ||
186 | op), hcr + 6); | ||
187 | cmd->toggle = cmd->toggle ^ 1; | ||
188 | |||
189 | ret = 0; | ||
190 | |||
191 | out: | ||
192 | mutex_unlock(&cmd->hcr_mutex); | ||
193 | return ret; | ||
194 | } | ||
195 | |||
196 | static int mlx4_cmd_poll(struct mlx4_dev *dev, u64 in_param, u64 *out_param, | ||
197 | int out_is_imm, u32 in_modifier, u8 op_modifier, | ||
198 | u16 op, unsigned long timeout) | ||
199 | { | ||
200 | struct mlx4_priv *priv = mlx4_priv(dev); | ||
201 | void __iomem *hcr = priv->cmd.hcr; | ||
202 | int err = 0; | ||
203 | unsigned long end; | ||
204 | |||
205 | down(&priv->cmd.poll_sem); | ||
206 | |||
207 | err = mlx4_cmd_post(dev, in_param, out_param ? *out_param : 0, | ||
208 | in_modifier, op_modifier, op, CMD_POLL_TOKEN, 0); | ||
209 | if (err) | ||
210 | goto out; | ||
211 | |||
212 | end = msecs_to_jiffies(timeout) + jiffies; | ||
213 | while (cmd_pending(dev) && time_before(jiffies, end)) | ||
214 | cond_resched(); | ||
215 | |||
216 | if (cmd_pending(dev)) { | ||
217 | err = -ETIMEDOUT; | ||
218 | goto out; | ||
219 | } | ||
220 | |||
221 | if (out_is_imm) | ||
222 | *out_param = | ||
223 | (u64) be32_to_cpu((__force __be32) | ||
224 | __raw_readl(hcr + HCR_OUT_PARAM_OFFSET)) << 32 | | ||
225 | (u64) be32_to_cpu((__force __be32) | ||
226 | __raw_readl(hcr + HCR_OUT_PARAM_OFFSET + 4)); | ||
227 | |||
228 | err = mlx4_status_to_errno(be32_to_cpu((__force __be32) | ||
229 | __raw_readl(hcr + HCR_STATUS_OFFSET)) >> 24); | ||
230 | |||
231 | out: | ||
232 | up(&priv->cmd.poll_sem); | ||
233 | return err; | ||
234 | } | ||
235 | |||
236 | void mlx4_cmd_event(struct mlx4_dev *dev, u16 token, u8 status, u64 out_param) | ||
237 | { | ||
238 | struct mlx4_priv *priv = mlx4_priv(dev); | ||
239 | struct mlx4_cmd_context *context = | ||
240 | &priv->cmd.context[token & priv->cmd.token_mask]; | ||
241 | |||
242 | /* previously timed out command completing at long last */ | ||
243 | if (token != context->token) | ||
244 | return; | ||
245 | |||
246 | context->result = mlx4_status_to_errno(status); | ||
247 | context->out_param = out_param; | ||
248 | |||
249 | context->token += priv->cmd.token_mask + 1; | ||
250 | |||
251 | complete(&context->done); | ||
252 | } | ||
253 | |||
254 | static int mlx4_cmd_wait(struct mlx4_dev *dev, u64 in_param, u64 *out_param, | ||
255 | int out_is_imm, u32 in_modifier, u8 op_modifier, | ||
256 | u16 op, unsigned long timeout) | ||
257 | { | ||
258 | struct mlx4_cmd *cmd = &mlx4_priv(dev)->cmd; | ||
259 | struct mlx4_cmd_context *context; | ||
260 | int err = 0; | ||
261 | |||
262 | down(&cmd->event_sem); | ||
263 | |||
264 | spin_lock(&cmd->context_lock); | ||
265 | BUG_ON(cmd->free_head < 0); | ||
266 | context = &cmd->context[cmd->free_head]; | ||
267 | cmd->free_head = context->next; | ||
268 | spin_unlock(&cmd->context_lock); | ||
269 | |||
270 | init_completion(&context->done); | ||
271 | |||
272 | mlx4_cmd_post(dev, in_param, out_param ? *out_param : 0, | ||
273 | in_modifier, op_modifier, op, context->token, 1); | ||
274 | |||
275 | if (!wait_for_completion_timeout(&context->done, msecs_to_jiffies(timeout))) { | ||
276 | err = -EBUSY; | ||
277 | goto out; | ||
278 | } | ||
279 | |||
280 | err = context->result; | ||
281 | if (err) | ||
282 | goto out; | ||
283 | |||
284 | if (out_is_imm) | ||
285 | *out_param = context->out_param; | ||
286 | |||
287 | out: | ||
288 | spin_lock(&cmd->context_lock); | ||
289 | context->next = cmd->free_head; | ||
290 | cmd->free_head = context - cmd->context; | ||
291 | spin_unlock(&cmd->context_lock); | ||
292 | |||
293 | up(&cmd->event_sem); | ||
294 | return err; | ||
295 | } | ||
296 | |||
297 | int __mlx4_cmd(struct mlx4_dev *dev, u64 in_param, u64 *out_param, | ||
298 | int out_is_imm, u32 in_modifier, u8 op_modifier, | ||
299 | u16 op, unsigned long timeout) | ||
300 | { | ||
301 | if (mlx4_priv(dev)->cmd.use_events) | ||
302 | return mlx4_cmd_wait(dev, in_param, out_param, out_is_imm, | ||
303 | in_modifier, op_modifier, op, timeout); | ||
304 | else | ||
305 | return mlx4_cmd_poll(dev, in_param, out_param, out_is_imm, | ||
306 | in_modifier, op_modifier, op, timeout); | ||
307 | } | ||
308 | EXPORT_SYMBOL_GPL(__mlx4_cmd); | ||
309 | |||
310 | int mlx4_cmd_init(struct mlx4_dev *dev) | ||
311 | { | ||
312 | struct mlx4_priv *priv = mlx4_priv(dev); | ||
313 | |||
314 | mutex_init(&priv->cmd.hcr_mutex); | ||
315 | sema_init(&priv->cmd.poll_sem, 1); | ||
316 | priv->cmd.use_events = 0; | ||
317 | priv->cmd.toggle = 1; | ||
318 | |||
319 | priv->cmd.hcr = ioremap(pci_resource_start(dev->pdev, 0) + MLX4_HCR_BASE, | ||
320 | MLX4_HCR_SIZE); | ||
321 | if (!priv->cmd.hcr) { | ||
322 | mlx4_err(dev, "Couldn't map command register."); | ||
323 | return -ENOMEM; | ||
324 | } | ||
325 | |||
326 | priv->cmd.pool = pci_pool_create("mlx4_cmd", dev->pdev, | ||
327 | MLX4_MAILBOX_SIZE, | ||
328 | MLX4_MAILBOX_SIZE, 0); | ||
329 | if (!priv->cmd.pool) { | ||
330 | iounmap(priv->cmd.hcr); | ||
331 | return -ENOMEM; | ||
332 | } | ||
333 | |||
334 | return 0; | ||
335 | } | ||
336 | |||
337 | void mlx4_cmd_cleanup(struct mlx4_dev *dev) | ||
338 | { | ||
339 | struct mlx4_priv *priv = mlx4_priv(dev); | ||
340 | |||
341 | pci_pool_destroy(priv->cmd.pool); | ||
342 | iounmap(priv->cmd.hcr); | ||
343 | } | ||
344 | |||
345 | /* | ||
346 | * Switch to using events to issue FW commands (can only be called | ||
347 | * after event queue for command events has been initialized). | ||
348 | */ | ||
349 | int mlx4_cmd_use_events(struct mlx4_dev *dev) | ||
350 | { | ||
351 | struct mlx4_priv *priv = mlx4_priv(dev); | ||
352 | int i; | ||
353 | |||
354 | priv->cmd.context = kmalloc(priv->cmd.max_cmds * | ||
355 | sizeof (struct mlx4_cmd_context), | ||
356 | GFP_KERNEL); | ||
357 | if (!priv->cmd.context) | ||
358 | return -ENOMEM; | ||
359 | |||
360 | for (i = 0; i < priv->cmd.max_cmds; ++i) { | ||
361 | priv->cmd.context[i].token = i; | ||
362 | priv->cmd.context[i].next = i + 1; | ||
363 | } | ||
364 | |||
365 | priv->cmd.context[priv->cmd.max_cmds - 1].next = -1; | ||
366 | priv->cmd.free_head = 0; | ||
367 | |||
368 | sema_init(&priv->cmd.event_sem, priv->cmd.max_cmds); | ||
369 | spin_lock_init(&priv->cmd.context_lock); | ||
370 | |||
371 | for (priv->cmd.token_mask = 1; | ||
372 | priv->cmd.token_mask < priv->cmd.max_cmds; | ||
373 | priv->cmd.token_mask <<= 1) | ||
374 | ; /* nothing */ | ||
375 | --priv->cmd.token_mask; | ||
376 | |||
377 | priv->cmd.use_events = 1; | ||
378 | |||
379 | down(&priv->cmd.poll_sem); | ||
380 | |||
381 | return 0; | ||
382 | } | ||
383 | |||
384 | /* | ||
385 | * Switch back to polling (used when shutting down the device) | ||
386 | */ | ||
387 | void mlx4_cmd_use_polling(struct mlx4_dev *dev) | ||
388 | { | ||
389 | struct mlx4_priv *priv = mlx4_priv(dev); | ||
390 | int i; | ||
391 | |||
392 | priv->cmd.use_events = 0; | ||
393 | |||
394 | for (i = 0; i < priv->cmd.max_cmds; ++i) | ||
395 | down(&priv->cmd.event_sem); | ||
396 | |||
397 | kfree(priv->cmd.context); | ||
398 | |||
399 | up(&priv->cmd.poll_sem); | ||
400 | } | ||
401 | |||
402 | struct mlx4_cmd_mailbox *mlx4_alloc_cmd_mailbox(struct mlx4_dev *dev) | ||
403 | { | ||
404 | struct mlx4_cmd_mailbox *mailbox; | ||
405 | |||
406 | mailbox = kmalloc(sizeof *mailbox, GFP_KERNEL); | ||
407 | if (!mailbox) | ||
408 | return ERR_PTR(-ENOMEM); | ||
409 | |||
410 | mailbox->buf = pci_pool_alloc(mlx4_priv(dev)->cmd.pool, GFP_KERNEL, | ||
411 | &mailbox->dma); | ||
412 | if (!mailbox->buf) { | ||
413 | kfree(mailbox); | ||
414 | return ERR_PTR(-ENOMEM); | ||
415 | } | ||
416 | |||
417 | return mailbox; | ||
418 | } | ||
419 | EXPORT_SYMBOL_GPL(mlx4_alloc_cmd_mailbox); | ||
420 | |||
421 | void mlx4_free_cmd_mailbox(struct mlx4_dev *dev, struct mlx4_cmd_mailbox *mailbox) | ||
422 | { | ||
423 | if (!mailbox) | ||
424 | return; | ||
425 | |||
426 | pci_pool_free(mlx4_priv(dev)->cmd.pool, mailbox->buf, mailbox->dma); | ||
427 | kfree(mailbox); | ||
428 | } | ||
429 | EXPORT_SYMBOL_GPL(mlx4_free_cmd_mailbox); | ||