1 /*
2  * Copyright (C) 2015 Mellanox Technologies Ltd.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of the
7  * License, or any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17  * 02110-1301, USA.
18  */
19 
20 
21 FILE_LICENCE ( GPL2_OR_LATER );
22 
23 #include "../../include/public/mlx_bail.h"
24 #include "../../include/public/mlx_icmd.h"
25 #include "../../include/public/mlx_pci_gw.h"
26 #include "../../include/public/mlx_utils.h"
27 
28 static
29 mlx_status
mlx_icmd_get_semaphore(IN mlx_utils * utils)30 mlx_icmd_get_semaphore(
31 				IN mlx_utils *utils
32 				)
33 {
34 	mlx_status status = MLX_SUCCESS;
35 	mlx_uint32 retries = 0;
36 	mlx_uint32 semaphore_id;
37 	mlx_uint32 buffer;
38 	if (utils == NULL) {
39 		status = MLX_INVALID_PARAMETER;
40 		goto invalid_param;
41 	}
42 
43 	status = mlx_utils_rand(utils, &semaphore_id);
44 	MLX_CHECK_STATUS(utils, status, rand_err, "failed to get random number");
45 #define ICMD_GET_SEMAPHORE_TRIES 2560
46 	for (retries = 0 ; retries < ICMD_GET_SEMAPHORE_TRIES ; retries++) {
47 		status = mlx_pci_gw_read( utils, PCI_GW_SPACE_SEMAPHORE,
48 					MLX_ICMD_SEMAPHORE_ADDR, &buffer);
49 		MLX_CHECK_STATUS(utils, status, read_err, "failed to read icmd semaphore");
50 		if (buffer != 0) {
51 			mlx_utils_delay_in_ms(10);
52 			continue;
53 		}
54 		mlx_pci_gw_write( utils, PCI_GW_SPACE_SEMAPHORE,
55 							MLX_ICMD_SEMAPHORE_ADDR, semaphore_id);
56 		MLX_CHECK_STATUS(utils, status, set_err, "failed to set icmd semaphore");
57 		status = mlx_pci_gw_read( utils, PCI_GW_SPACE_SEMAPHORE,
58 							MLX_ICMD_SEMAPHORE_ADDR, &buffer);
59 		MLX_CHECK_STATUS(utils, status, read_err, "failed to read icmd semaphore");
60 		if (semaphore_id == buffer) {
61 			status = MLX_SUCCESS;
62 			utils->icmd.took_semaphore = TRUE;
63 			break;
64 		}
65 		mlx_utils_delay_in_ms(10);
66 	}
67 	if (semaphore_id != buffer) {
68 		status = MLX_FAILED;
69 	}
70 read_err:
71 set_err:
72 rand_err:
73 invalid_param:
74 	return status;
75 }
76 static
77 mlx_status
mlx_icmd_clear_semaphore(IN mlx_utils * utils)78 mlx_icmd_clear_semaphore(
79 				IN mlx_utils *utils
80 				)
81 {
82 	mlx_status status = MLX_SUCCESS;
83 
84 	if (utils == NULL) {
85 		status = MLX_INVALID_PARAMETER;
86 		goto invalid_param;
87 	}
88 
89 	if (utils->icmd.took_semaphore == FALSE) {
90 		goto semaphore_not_taken;
91 	}
92 	status = mlx_pci_gw_write( utils, PCI_GW_SPACE_SEMAPHORE,
93 			MLX_ICMD_SEMAPHORE_ADDR, 0);
94 	MLX_CHECK_STATUS(utils, status, read_err, "failed to clear icmd semaphore");
95 
96 	utils->icmd.took_semaphore = FALSE;
97 read_err:
98 semaphore_not_taken:
99 invalid_param:
100 	return status;
101 }
102 
103 static
104 mlx_status
mlx_icmd_init(IN mlx_utils * utils)105 mlx_icmd_init(
106 				IN mlx_utils *utils
107 				)
108 {
109 	mlx_status status = MLX_SUCCESS;
110 
111 	if (utils == NULL) {
112 		status = MLX_INVALID_PARAMETER;
113 		goto invalid_param;
114 	}
115 	if (utils->icmd.icmd_opened == TRUE) {
116 		goto already_opened;
117 	}
118 
119 	utils->icmd.took_semaphore = FALSE;
120 
121 	status = mlx_pci_gw_read( utils, PCI_GW_SPACE_ALL_ICMD,
122 			MLX_ICMD_MB_SIZE_ADDR, &utils->icmd.max_cmd_size);
123 	MLX_CHECK_STATUS(utils, status, read_err, "failed to read icmd mail box size");
124 
125 	utils->icmd.icmd_opened = TRUE;
126 read_err:
127 already_opened:
128 invalid_param:
129 	return status;
130 }
131 
132 static
133 mlx_status
mlx_icmd_set_opcode(IN mlx_utils * utils,IN mlx_uint16 opcode)134 mlx_icmd_set_opcode(
135 				IN mlx_utils *utils,
136 				IN mlx_uint16 opcode
137 				)
138 {
139 	mlx_status status = MLX_SUCCESS;
140 	mlx_uint32 buffer;
141 
142 	if (utils == NULL) {
143 		status = MLX_INVALID_PARAMETER;
144 		goto invalid_param;
145 	}
146 
147 	status = mlx_pci_gw_read( utils, PCI_GW_SPACE_ALL_ICMD,
148 				MLX_ICMD_CTRL_ADDR, &buffer);
149 	MLX_CHECK_STATUS(utils, status, read_err, "failed to read icmd ctrl");
150 
151 #define MLX_ICMD_OPCODE_ALIGN 16
152 #define MLX_ICMD_OPCODE_MASK 0xffff
153 
154 	buffer = buffer & ~(MLX_ICMD_OPCODE_MASK << MLX_ICMD_OPCODE_ALIGN);
155 	buffer = buffer | (opcode << MLX_ICMD_OPCODE_ALIGN);
156 
157 	status = mlx_pci_gw_write( utils, PCI_GW_SPACE_ALL_ICMD,
158 					MLX_ICMD_CTRL_ADDR, buffer);
159 	MLX_CHECK_STATUS(utils, status, write_err, "failed to write icmd ctrl");
160 write_err:
161 read_err:
162 invalid_param:
163 	return status;
164 }
165 
166 static
167 mlx_status
mlx_icmd_go(IN mlx_utils * utils)168 mlx_icmd_go(
169 			IN mlx_utils *utils
170 			)
171 {
172 	mlx_status status = MLX_SUCCESS;
173 	mlx_uint32 buffer;
174 	mlx_uint32 busy;
175 	mlx_uint32 wait_iteration = 0;
176 
177 	if (utils == NULL) {
178 		status = MLX_INVALID_PARAMETER;
179 		goto invalid_param;
180 	}
181 
182 	status = mlx_pci_gw_read( utils, PCI_GW_SPACE_ALL_ICMD,
183 				MLX_ICMD_CTRL_ADDR, &buffer);
184 	MLX_CHECK_STATUS(utils, status, read_err, "failed to read icmd ctrl");
185 
186 #define MLX_ICMD_BUSY_ALIGN 0
187 #define MLX_ICMD_BUSY_MASK 0x1
188 
189 	busy = (buffer >> MLX_ICMD_BUSY_ALIGN) & MLX_ICMD_BUSY_MASK;
190 	if (busy != 0) {
191 		status = MLX_FAILED;
192 		goto already_busy;
193 	}
194 
195 	buffer = buffer | (1 << MLX_ICMD_BUSY_ALIGN);
196 
197 	status = mlx_pci_gw_write( utils, PCI_GW_SPACE_ALL_ICMD,
198 					MLX_ICMD_CTRL_ADDR, buffer);
199 	MLX_CHECK_STATUS(utils, status, write_err, "failed to write icmd ctrl");
200 
201 #define MLX_ICMD_BUSY_MAX_ITERATIONS 1024
202 	do {
203 		if (++wait_iteration > MLX_ICMD_BUSY_MAX_ITERATIONS) {
204 			status = MLX_FAILED;
205 			MLX_DEBUG_ERROR(utils, "ICMD time out");
206 			goto busy_timeout;
207 		}
208 
209 		mlx_utils_delay_in_ms(10);
210 		status = mlx_pci_gw_read( utils, PCI_GW_SPACE_ALL_ICMD,
211 					MLX_ICMD_CTRL_ADDR, &buffer);
212 		MLX_CHECK_STATUS(utils, status, read_err, "failed to read icmd ctrl");
213 		busy = (buffer >> MLX_ICMD_BUSY_ALIGN) & MLX_ICMD_BUSY_MASK;
214 	} while (busy != 0);
215 
216 busy_timeout:
217 write_err:
218 already_busy:
219 read_err:
220 invalid_param:
221 	return status;
222 }
223 
224 static
225 mlx_status
mlx_icmd_get_status(IN mlx_utils * utils,OUT mlx_uint32 * out_status)226 mlx_icmd_get_status(
227 			IN mlx_utils *utils,
228 			OUT mlx_uint32 *out_status
229 			)
230 {
231 	mlx_status status = MLX_SUCCESS;
232 	mlx_uint32 buffer;
233 
234 	if (utils == NULL || out_status == NULL) {
235 		status = MLX_INVALID_PARAMETER;
236 		goto invalid_param;
237 	}
238 
239 	status = mlx_pci_gw_read( utils, PCI_GW_SPACE_ALL_ICMD,
240 				MLX_ICMD_CTRL_ADDR, &buffer);
241 	MLX_CHECK_STATUS(utils, status, read_err, "failed to read icmd ctrl");
242 
243 #define MLX_ICMD_STATUS_ALIGN 8
244 #define MLX_ICMD_STATUS_MASK 0xff
245 
246 	*out_status = (buffer >> MLX_ICMD_STATUS_ALIGN) & MLX_ICMD_STATUS_MASK;
247 
248 read_err:
249 invalid_param:
250 	return status;
251 }
252 
253 static
254 mlx_status
mlx_icmd_write_buffer(IN mlx_utils * utils,IN mlx_void * data,IN mlx_uint32 data_size)255 mlx_icmd_write_buffer(
256 		IN mlx_utils *utils,
257 		IN mlx_void* data,
258 		IN mlx_uint32 data_size
259 		)
260 {
261 	mlx_status status = MLX_SUCCESS;
262 	mlx_uint32 data_offset = 0;
263 	mlx_size dword_size = sizeof(mlx_uint32);
264 
265 	if (utils == NULL || data == NULL) {
266 		status = MLX_INVALID_PARAMETER;
267 		goto invalid_param;
268 	}
269 
270 	for (data_offset = 0 ; data_offset*dword_size < data_size ; data_offset++) {
271 		status = mlx_pci_gw_write( utils, PCI_GW_SPACE_ALL_ICMD,
272 							MLX_ICMD_MB_ADDR + data_offset*dword_size,
273 							((mlx_uint32*)data)[data_offset]);
274 		MLX_CHECK_STATUS(utils, status, write_err, "failed to write icmd MB");
275 	}
276 write_err:
277 invalid_param:
278 	return status;
279 }
280 
281 
282 static
283 mlx_status
mlx_icmd_read_buffer(IN mlx_utils * utils,OUT mlx_void * data,IN mlx_uint32 data_size)284 mlx_icmd_read_buffer(
285 		IN mlx_utils *utils,
286 		OUT mlx_void* data,
287 		IN mlx_uint32 data_size
288 		)
289 {
290 	mlx_status status = MLX_SUCCESS;
291 	mlx_uint32 data_offset = 0;
292 	mlx_size dword_size = sizeof(mlx_uint32);
293 
294 	if (utils == NULL || data == NULL) {
295 		status = MLX_INVALID_PARAMETER;
296 		goto invalid_param;
297 	}
298 
299 	for (data_offset = 0 ; data_offset*dword_size < data_size ; data_offset++) {
300 		status = mlx_pci_gw_read( utils, PCI_GW_SPACE_ALL_ICMD,
301 							MLX_ICMD_MB_ADDR + data_offset*dword_size,
302 							(mlx_uint32*)data + data_offset);
303 		MLX_CHECK_STATUS(utils, status, read_err, "failed to read icmd MB");
304 	}
305 read_err:
306 invalid_param:
307 	return status;
308 }
309 mlx_status
mlx_icmd_send_command(IN mlx_utils * utils,IN mlx_uint16 opcode,IN OUT mlx_void * data,IN mlx_uint32 write_data_size,IN mlx_uint32 read_data_size)310 mlx_icmd_send_command(
311 				IN mlx_utils *utils,
312 				IN  mlx_uint16 opcode,
313 				IN OUT mlx_void* data,
314 				IN mlx_uint32 write_data_size,
315 				IN mlx_uint32 read_data_size
316 				)
317 {
318 	mlx_status status = MLX_SUCCESS;
319 	mlx_uint32 icmd_status = 0;
320 
321 	if (utils == NULL || data == NULL) {
322 		status = MLX_INVALID_PARAMETER;
323 		goto invalid_param;
324 	}
325 	status = mlx_icmd_init(utils);
326 	MLX_CHECK_STATUS(utils, status, open_err, "failed to open icmd");
327 
328 	if (write_data_size > utils->icmd.max_cmd_size ||
329 			read_data_size > utils->icmd.max_cmd_size) {
330 		status = MLX_INVALID_PARAMETER;
331 		goto size_err;
332 	}
333 
334 	status = mlx_icmd_get_semaphore(utils);
335 	MLX_CHECK_STATUS(utils, status, semaphore_err, "failed to get icmd semaphore");
336 
337 	status = mlx_icmd_set_opcode(utils, opcode);
338 	MLX_CHECK_STATUS(utils, status, opcode_err, "failed to set icmd opcode");
339 
340 	if (write_data_size != 0) {
341 		status = mlx_icmd_write_buffer(utils, data, write_data_size);
342 		MLX_CHECK_STATUS(utils, status, opcode_err, "failed to write icmd MB");
343 	}
344 
345 	status = mlx_icmd_go(utils);
346 	MLX_CHECK_STATUS(utils, status, go_err, "failed to activate icmd");
347 
348 	status = mlx_icmd_get_status(utils, &icmd_status);
349 	MLX_CHECK_STATUS(utils, status, get_status_err, "failed to set icmd opcode");
350 
351 	if (icmd_status != 0) {
352 		MLX_DEBUG_ERROR(utils, "icmd failed with status = %d\n", icmd_status);
353 		status = MLX_FAILED;
354 		goto icmd_failed;
355 	}
356 	if (read_data_size != 0) {
357 		status = mlx_icmd_read_buffer(utils, data, read_data_size);
358 		MLX_CHECK_STATUS(utils, status, read_err, "failed to read icmd MB");
359 	}
360 read_err:
361 icmd_failed:
362 get_status_err:
363 go_err:
364 opcode_err:
365 	mlx_icmd_clear_semaphore(utils);
366 semaphore_err:
367 size_err:
368 open_err:
369 invalid_param:
370 	return status;
371 }
372