Commit | Line | Data |
---|---|---|
e29341fb IT |
1 | /* |
2 | * Copyright (c) 2017, Mellanox Technologies. All rights reserved. | |
3 | * | |
4 | * This software is available to you under a choice of one of two | |
5 | * licenses. You may choose to be licensed under the terms of the GNU | |
6 | * General Public License (GPL) Version 2, available from the file | |
7 | * COPYING in the main directory of this source tree, or the | |
8 | * OpenIB.org BSD license below: | |
9 | * | |
10 | * Redistribution and use in source and binary forms, with or | |
11 | * without modification, are permitted provided that the following | |
12 | * conditions are met: | |
13 | * | |
14 | * - Redistributions of source code must retain the above | |
15 | * copyright notice, this list of conditions and the following | |
16 | * disclaimer. | |
17 | * | |
18 | * - Redistributions in binary form must reproduce the above | |
19 | * copyright notice, this list of conditions and the following | |
20 | * disclaimer in the documentation and/or other materials | |
21 | * provided with the distribution. | |
22 | * | |
23 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
24 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
25 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |
26 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | |
27 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | |
28 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | |
29 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
30 | * SOFTWARE. | |
31 | */ | |
32 | ||
33 | #include <linux/module.h> | |
34 | #include <linux/etherdevice.h> | |
35 | #include <linux/mlx5/driver.h> | |
36 | ||
37 | #include "mlx5_core.h" | |
52ec462e | 38 | #include "lib/mlx5.h" |
e29341fb | 39 | #include "fpga/core.h" |
537a5057 | 40 | #include "fpga/conn.h" |
e29341fb IT |
41 | |
42 | static const char *const mlx5_fpga_error_strings[] = { | |
43 | "Null Syndrome", | |
44 | "Corrupted DDR", | |
45 | "Flash Timeout", | |
46 | "Internal Link Error", | |
47 | "Watchdog HW Failure", | |
48 | "I2C Failure", | |
49 | "Image Changed", | |
50 | "Temperature Critical", | |
51 | }; | |
52 | ||
53 | static struct mlx5_fpga_device *mlx5_fpga_device_alloc(void) | |
54 | { | |
55 | struct mlx5_fpga_device *fdev = NULL; | |
56 | ||
57 | fdev = kzalloc(sizeof(*fdev), GFP_KERNEL); | |
58 | if (!fdev) | |
59 | return NULL; | |
60 | ||
61 | spin_lock_init(&fdev->state_lock); | |
62 | fdev->state = MLX5_FPGA_STATUS_NONE; | |
63 | return fdev; | |
64 | } | |
65 | ||
66 | static const char *mlx5_fpga_image_name(enum mlx5_fpga_image image) | |
67 | { | |
68 | switch (image) { | |
69 | case MLX5_FPGA_IMAGE_USER: | |
70 | return "user"; | |
71 | case MLX5_FPGA_IMAGE_FACTORY: | |
72 | return "factory"; | |
73 | default: | |
74 | return "unknown"; | |
75 | } | |
76 | } | |
77 | ||
78 | static int mlx5_fpga_device_load_check(struct mlx5_fpga_device *fdev) | |
79 | { | |
80 | struct mlx5_fpga_query query; | |
81 | int err; | |
82 | ||
83 | err = mlx5_fpga_query(fdev->mdev, &query); | |
84 | if (err) { | |
85 | mlx5_fpga_err(fdev, "Failed to query status: %d\n", err); | |
86 | return err; | |
87 | } | |
88 | ||
89 | fdev->last_admin_image = query.admin_image; | |
90 | fdev->last_oper_image = query.oper_image; | |
91 | ||
92 | mlx5_fpga_dbg(fdev, "Status %u; Admin image %u; Oper image %u\n", | |
93 | query.status, query.admin_image, query.oper_image); | |
94 | ||
95 | if (query.status != MLX5_FPGA_STATUS_SUCCESS) { | |
96 | mlx5_fpga_err(fdev, "%s image failed to load; status %u\n", | |
97 | mlx5_fpga_image_name(fdev->last_oper_image), | |
98 | query.status); | |
99 | return -EIO; | |
100 | } | |
101 | ||
102 | return 0; | |
103 | } | |
104 | ||
c8af0169 | 105 | static int mlx5_fpga_device_brb(struct mlx5_fpga_device *fdev) |
c43051d7 IT |
106 | { |
107 | int err; | |
108 | struct mlx5_core_dev *mdev = fdev->mdev; | |
109 | ||
110 | err = mlx5_fpga_ctrl_op(mdev, MLX5_FPGA_CTRL_OPERATION_SANDBOX_BYPASS_ON); | |
111 | if (err) { | |
112 | mlx5_fpga_err(fdev, "Failed to set bypass on: %d\n", err); | |
113 | return err; | |
114 | } | |
115 | err = mlx5_fpga_ctrl_op(mdev, MLX5_FPGA_CTRL_OPERATION_RESET_SANDBOX); | |
116 | if (err) { | |
117 | mlx5_fpga_err(fdev, "Failed to reset SBU: %d\n", err); | |
118 | return err; | |
119 | } | |
120 | err = mlx5_fpga_ctrl_op(mdev, MLX5_FPGA_CTRL_OPERATION_SANDBOX_BYPASS_OFF); | |
121 | if (err) { | |
122 | mlx5_fpga_err(fdev, "Failed to set bypass off: %d\n", err); | |
123 | return err; | |
124 | } | |
125 | return 0; | |
126 | } | |
127 | ||
e29341fb IT |
128 | int mlx5_fpga_device_start(struct mlx5_core_dev *mdev) |
129 | { | |
130 | struct mlx5_fpga_device *fdev = mdev->fpga; | |
131 | unsigned long flags; | |
52ec462e | 132 | unsigned int max_num_qps; |
e29341fb IT |
133 | int err; |
134 | ||
135 | if (!fdev) | |
136 | return 0; | |
137 | ||
138 | err = mlx5_fpga_device_load_check(fdev); | |
139 | if (err) | |
140 | goto out; | |
141 | ||
99d3cd27 | 142 | err = mlx5_fpga_caps(fdev->mdev); |
e29341fb IT |
143 | if (err) |
144 | goto out; | |
145 | ||
146 | mlx5_fpga_info(fdev, "device %u; %s image, version %u\n", | |
147 | MLX5_CAP_FPGA(fdev->mdev, fpga_device), | |
148 | mlx5_fpga_image_name(fdev->last_oper_image), | |
149 | MLX5_CAP_FPGA(fdev->mdev, image_version)); | |
150 | ||
52ec462e IT |
151 | max_num_qps = MLX5_CAP_FPGA(mdev, shell_caps.max_num_qps); |
152 | err = mlx5_core_reserve_gids(mdev, max_num_qps); | |
537a5057 IT |
153 | if (err) |
154 | goto out; | |
155 | ||
156 | err = mlx5_fpga_conn_device_init(fdev); | |
157 | if (err) | |
158 | goto err_rsvd_gid; | |
159 | ||
c43051d7 IT |
160 | if (fdev->last_oper_image == MLX5_FPGA_IMAGE_USER) { |
161 | err = mlx5_fpga_device_brb(fdev); | |
162 | if (err) | |
163 | goto err_conn_init; | |
164 | } | |
165 | ||
537a5057 | 166 | goto out; |
52ec462e | 167 | |
c43051d7 IT |
168 | err_conn_init: |
169 | mlx5_fpga_conn_device_cleanup(fdev); | |
170 | ||
537a5057 IT |
171 | err_rsvd_gid: |
172 | mlx5_core_unreserve_gids(mdev, max_num_qps); | |
e29341fb IT |
173 | out: |
174 | spin_lock_irqsave(&fdev->state_lock, flags); | |
175 | fdev->state = err ? MLX5_FPGA_STATUS_FAILURE : MLX5_FPGA_STATUS_SUCCESS; | |
176 | spin_unlock_irqrestore(&fdev->state_lock, flags); | |
177 | return err; | |
178 | } | |
179 | ||
9410733c | 180 | int mlx5_fpga_init(struct mlx5_core_dev *mdev) |
e29341fb IT |
181 | { |
182 | struct mlx5_fpga_device *fdev = NULL; | |
183 | ||
184 | if (!MLX5_CAP_GEN(mdev, fpga)) { | |
185 | mlx5_core_dbg(mdev, "FPGA capability not present\n"); | |
186 | return 0; | |
187 | } | |
188 | ||
189 | mlx5_core_dbg(mdev, "Initializing FPGA\n"); | |
190 | ||
191 | fdev = mlx5_fpga_device_alloc(); | |
192 | if (!fdev) | |
193 | return -ENOMEM; | |
194 | ||
195 | fdev->mdev = mdev; | |
196 | mdev->fpga = fdev; | |
197 | ||
198 | return 0; | |
199 | } | |
200 | ||
52ec462e IT |
201 | void mlx5_fpga_device_stop(struct mlx5_core_dev *mdev) |
202 | { | |
203 | struct mlx5_fpga_device *fdev = mdev->fpga; | |
204 | unsigned int max_num_qps; | |
205 | unsigned long flags; | |
c43051d7 | 206 | int err; |
52ec462e IT |
207 | |
208 | if (!fdev) | |
209 | return; | |
210 | ||
211 | spin_lock_irqsave(&fdev->state_lock, flags); | |
212 | if (fdev->state != MLX5_FPGA_STATUS_SUCCESS) { | |
213 | spin_unlock_irqrestore(&fdev->state_lock, flags); | |
214 | return; | |
215 | } | |
216 | fdev->state = MLX5_FPGA_STATUS_NONE; | |
217 | spin_unlock_irqrestore(&fdev->state_lock, flags); | |
218 | ||
c43051d7 IT |
219 | if (fdev->last_oper_image == MLX5_FPGA_IMAGE_USER) { |
220 | err = mlx5_fpga_ctrl_op(mdev, MLX5_FPGA_CTRL_OPERATION_SANDBOX_BYPASS_ON); | |
221 | if (err) | |
222 | mlx5_fpga_err(fdev, "Failed to re-set SBU bypass on: %d\n", | |
223 | err); | |
224 | } | |
225 | ||
537a5057 | 226 | mlx5_fpga_conn_device_cleanup(fdev); |
52ec462e IT |
227 | max_num_qps = MLX5_CAP_FPGA(mdev, shell_caps.max_num_qps); |
228 | mlx5_core_unreserve_gids(mdev, max_num_qps); | |
229 | } | |
230 | ||
9410733c | 231 | void mlx5_fpga_cleanup(struct mlx5_core_dev *mdev) |
e29341fb | 232 | { |
52ec462e IT |
233 | struct mlx5_fpga_device *fdev = mdev->fpga; |
234 | ||
235 | mlx5_fpga_device_stop(mdev); | |
236 | kfree(fdev); | |
e29341fb IT |
237 | mdev->fpga = NULL; |
238 | } | |
239 | ||
240 | static const char *mlx5_fpga_syndrome_to_string(u8 syndrome) | |
241 | { | |
242 | if (syndrome < ARRAY_SIZE(mlx5_fpga_error_strings)) | |
243 | return mlx5_fpga_error_strings[syndrome]; | |
244 | return "Unknown"; | |
245 | } | |
246 | ||
247 | void mlx5_fpga_event(struct mlx5_core_dev *mdev, u8 event, void *data) | |
248 | { | |
249 | struct mlx5_fpga_device *fdev = mdev->fpga; | |
250 | const char *event_name; | |
251 | bool teardown = false; | |
252 | unsigned long flags; | |
253 | u8 syndrome; | |
254 | ||
255 | if (event != MLX5_EVENT_TYPE_FPGA_ERROR) { | |
256 | mlx5_fpga_warn_ratelimited(fdev, "Unexpected event %u\n", | |
257 | event); | |
258 | return; | |
259 | } | |
260 | ||
261 | syndrome = MLX5_GET(fpga_error_event, data, syndrome); | |
262 | event_name = mlx5_fpga_syndrome_to_string(syndrome); | |
263 | ||
264 | spin_lock_irqsave(&fdev->state_lock, flags); | |
265 | switch (fdev->state) { | |
266 | case MLX5_FPGA_STATUS_SUCCESS: | |
267 | mlx5_fpga_warn(fdev, "Error %u: %s\n", syndrome, event_name); | |
268 | teardown = true; | |
269 | break; | |
270 | default: | |
271 | mlx5_fpga_warn_ratelimited(fdev, "Unexpected error event %u: %s\n", | |
272 | syndrome, event_name); | |
273 | } | |
274 | spin_unlock_irqrestore(&fdev->state_lock, flags); | |
275 | /* We tear-down the card's interfaces and functionality because | |
276 | * the FPGA bump-on-the-wire is misbehaving and we lose ability | |
277 | * to communicate with the network. User may still be able to | |
278 | * recover by re-programming or debugging the FPGA | |
279 | */ | |
280 | if (teardown) | |
281 | mlx5_trigger_health_work(fdev->mdev); | |
282 | } |