xref: /freebsd/sys/dev/smartpqi/smartpqi_sis.c (revision 13ea0450a9c8742119d36f3bf8f47accdce46e54)
1 /*-
2  * Copyright (c) 2018 Microsemi Corporation.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26 
27 /* $FreeBSD$ */
28 
29 #include "smartpqi_includes.h"
30 
31 /* */
32 void sis_disable_msix(pqisrc_softstate_t *softs)
33 {
34 	uint32_t db_reg;
35 
36 	DBG_FUNC("IN\n");
37 
38 	db_reg = PCI_MEM_GET32(softs, &softs->ioa_reg->host_to_ioa_db,
39 			LEGACY_SIS_IDBR);
40 	db_reg &= ~SIS_ENABLE_MSIX;
41 	PCI_MEM_PUT32(softs, &softs->ioa_reg->host_to_ioa_db,
42 			LEGACY_SIS_IDBR, db_reg);
43 
44 	DBG_FUNC("OUT\n");
45 }
46 
47 void sis_enable_intx(pqisrc_softstate_t *softs)
48 {
49 	uint32_t db_reg;
50 
51 	DBG_FUNC("IN\n");
52 
53 	db_reg = PCI_MEM_GET32(softs, &softs->ioa_reg->host_to_ioa_db,
54 			LEGACY_SIS_IDBR);
55 	db_reg |= SIS_ENABLE_INTX;
56 	PCI_MEM_PUT32(softs, &softs->ioa_reg->host_to_ioa_db,
57 			LEGACY_SIS_IDBR, db_reg);
58 	if (pqisrc_sis_wait_for_db_bit_to_clear(softs,SIS_ENABLE_INTX)
59 			!= PQI_STATUS_SUCCESS) {
60 		DBG_ERR("Failed to wait for enable intx db bit to clear\n");
61 	}
62 	DBG_FUNC("OUT\n");
63 }
64 
65 void sis_disable_intx(pqisrc_softstate_t *softs)
66 {
67 	uint32_t db_reg;
68 
69 	DBG_FUNC("IN\n");
70 
71 	db_reg = PCI_MEM_GET32(softs, &softs->ioa_reg->host_to_ioa_db,
72 			LEGACY_SIS_IDBR);
73 	db_reg &= ~SIS_ENABLE_INTX;
74 	PCI_MEM_PUT32(softs, &softs->ioa_reg->host_to_ioa_db,
75 			LEGACY_SIS_IDBR, db_reg);
76 
77 	DBG_FUNC("OUT\n");
78 }
79 
80 void sis_disable_interrupt(pqisrc_softstate_t *softs)
81 {
82 	DBG_FUNC("IN");
83 
84 	switch(softs->intr_type) {
85 		case INTR_TYPE_FIXED:
86 			pqisrc_configure_legacy_intx(softs,false);
87 			sis_disable_intx(softs);
88 			break;
89 		case INTR_TYPE_MSI:
90 		case INTR_TYPE_MSIX:
91 			sis_disable_msix(softs);
92 			break;
93 		default:
94 			DBG_ERR("Inerrupt mode none!\n");
95 			break;
96 	}
97 
98 	DBG_FUNC("OUT");
99 }
100 
101 /* Trigger a NMI as part of taking controller offline procedure */
102 void pqisrc_trigger_nmi_sis(pqisrc_softstate_t *softs)
103 {
104 
105 	DBG_FUNC("IN\n");
106 
107 	PCI_MEM_PUT32(softs,  &softs->ioa_reg->host_to_ioa_db,
108 			LEGACY_SIS_IDBR, LE_32(TRIGGER_NMI_SIS));
109 	DBG_FUNC("OUT\n");
110 }
111 
112 /* Switch the adapter back to SIS mode during uninitialization */
113 int pqisrc_reenable_sis(pqisrc_softstate_t *softs)
114 {
115 	int ret = PQI_STATUS_SUCCESS;
116 	uint32_t timeout = SIS_ENABLE_TIMEOUT;
117 
118 	DBG_FUNC("IN\n");
119 
120 	PCI_MEM_PUT32(softs, &softs->ioa_reg->host_to_ioa_db,
121         LEGACY_SIS_IDBR, LE_32(REENABLE_SIS));
122 
123 	COND_WAIT(((PCI_MEM_GET32(softs, &softs->ioa_reg->ioa_to_host_db, LEGACY_SIS_ODBR_R) &
124 				REENABLE_SIS) == 0), timeout)
125 	if (!timeout) {
126 		DBG_WARN(" [ %s ] failed to re enable sis\n",__func__);
127 		ret = PQI_STATUS_TIMEOUT;
128 	}
129 
130 	DBG_FUNC("OUT\n");
131 	return ret;
132 }
133 
134 /* Validate the FW status PQI_CTRL_KERNEL_UP_AND_RUNNING */
135 int pqisrc_check_fw_status(pqisrc_softstate_t *softs)
136 {
137 	int ret = PQI_STATUS_SUCCESS;
138 	uint32_t timeout = SIS_STATUS_OK_TIMEOUT;
139 
140 	DBG_FUNC("IN\n");
141 
142 	OS_SLEEP(1000000);
143 	COND_WAIT((GET_FW_STATUS(softs) &
144 		PQI_CTRL_KERNEL_UP_AND_RUNNING), timeout);
145 	if (!timeout) {
146 		DBG_ERR("FW check status timedout\n");
147 		ret = PQI_STATUS_TIMEOUT;
148 	}
149 
150 	DBG_FUNC("OUT\n");
151 	return ret;
152 }
153 
154 /* Function used to submit a SIS command to the adapter */
155 static int pqisrc_send_sis_cmd(pqisrc_softstate_t *softs,
156 					uint32_t *mb)
157 {
158 	int ret = PQI_STATUS_SUCCESS;
159 	int i = 0;
160 	uint32_t timeout = SIS_CMD_COMPLETE_TIMEOUT;
161 
162 	int val;
163 
164 	DBG_FUNC("IN\n");
165 
166 
167 	/* Copy Command to mailbox */
168 	for (i = 0; i < 6; i++)
169 		PCI_MEM_PUT32(softs, &softs->ioa_reg->mb[i],
170             LEGACY_SIS_SRCV_MAILBOX+i*4, LE_32(mb[i]));
171 
172 	PCI_MEM_PUT32(softs, &softs->ioa_reg->ioa_to_host_db_clr,
173 		LEGACY_SIS_ODBR_R, LE_32(0x1000));
174 
175 	/* Submit the command */
176 	PCI_MEM_PUT32(softs, &softs->ioa_reg->host_to_ioa_db,
177 		LEGACY_SIS_IDBR, LE_32(SIS_CMD_SUBMIT));
178 
179 #ifdef SIS_POLL_WAIT
180 	/* Wait for 20  milli sec to poll */
181 	OS_BUSYWAIT(SIS_POLL_START_WAIT_TIME);
182 #endif
183 
184 	val = PCI_MEM_GET32(softs, &softs->ioa_reg->ioa_to_host_db, LEGACY_SIS_ODBR_R);
185 
186 	DBG_FUNC("val : %x\n",val);
187 	/* Spin waiting for the command to complete */
188 	COND_WAIT((PCI_MEM_GET32(softs, &softs->ioa_reg->ioa_to_host_db, LEGACY_SIS_ODBR_R) &
189 		SIS_CMD_COMPLETE), timeout);
190 	if (!timeout) {
191 		DBG_ERR("Sync command %x, timedout\n", mb[0]);
192 		ret = PQI_STATUS_TIMEOUT;
193 		goto err_out;
194 	}
195 	/* Check command status */
196 	mb[0] = LE_32(PCI_MEM_GET32(softs, &softs->ioa_reg->mb[0], LEGACY_SIS_SRCV_MAILBOX));
197 
198 	if (mb[0] != SIS_CMD_STATUS_SUCCESS) {
199 		DBG_ERR("SIS cmd failed with status = 0x%x\n",
200 			mb[0]);
201 		ret = PQI_STATUS_FAILURE;
202 		goto err_out;
203 	}
204 
205 	/* Copy the mailbox back  */
206 	for (i = 1; i < 6; i++)
207 		mb[i] =	LE_32(PCI_MEM_GET32(softs, &softs->ioa_reg->mb[i], LEGACY_SIS_SRCV_MAILBOX+i*4));
208 
209 	DBG_FUNC("OUT\n");
210 	return ret;
211 
212 err_out:
213 	DBG_FUNC("OUT failed\n");
214 	return ret;
215 }
216 
217 /* First SIS command for the adapter to check PQI support */
218 int pqisrc_get_adapter_properties(pqisrc_softstate_t *softs,
219 				uint32_t *prop, uint32_t *ext_prop)
220 {
221 	int ret = PQI_STATUS_SUCCESS;
222 	uint32_t mb[6] = {0};
223 
224 	DBG_FUNC("IN\n");
225 
226 	mb[0] = SIS_CMD_GET_ADAPTER_PROPERTIES;
227 	ret = pqisrc_send_sis_cmd(softs, mb);
228 	if (!ret) {
229 		DBG_INIT("GET_PROPERTIES prop = %x, ext_prop = %x\n",
230 					mb[1], mb[4]);
231 		*prop = mb[1];
232 		*ext_prop = mb[4];
233 	}
234 
235 	DBG_FUNC("OUT\n");
236 	return ret;
237 }
238 
239 /* Second SIS command to the adapter GET_COMM_PREFERRED_SETTINGS */
240 int pqisrc_get_preferred_settings(pqisrc_softstate_t *softs)
241 {
242 	int ret = PQI_STATUS_SUCCESS;
243 	uint32_t mb[6] = {0};
244 
245 	DBG_FUNC("IN\n");
246 
247 	mb[0] = SIS_CMD_GET_COMM_PREFERRED_SETTINGS;
248 	ret = pqisrc_send_sis_cmd(softs, mb);
249 	if (!ret) {
250 		/* 31:16 maximum command size in KB */
251 		softs->pref_settings.max_cmd_size = mb[1] >> 16;
252 		/* 15:00: Maximum FIB size in bytes */
253 		softs->pref_settings.max_fib_size = mb[1] & 0x0000FFFF;
254 		DBG_INIT("cmd size = %x, fib size = %x\n",
255 			softs->pref_settings.max_cmd_size,
256 			softs->pref_settings.max_fib_size);
257 	}
258 
259 	DBG_FUNC("OUT\n");
260 	return ret;
261 }
262 
263 /* Get supported PQI capabilities from the adapter */
264 int pqisrc_get_sis_pqi_cap(pqisrc_softstate_t *softs)
265 {
266 	int ret = PQI_STATUS_SUCCESS;
267 	uint32_t mb[6] = {0};
268 
269 	DBG_FUNC("IN\n");
270 
271 	mb[0] = SIS_CMD_GET_PQI_CAPABILITIES;
272 	ret = pqisrc_send_sis_cmd(softs,  mb);
273 	if (!ret) {
274 		softs->pqi_cap.max_sg_elem = mb[1];
275 		softs->pqi_cap.max_transfer_size = mb[2];
276 		softs->pqi_cap.max_outstanding_io = mb[3];
277 		softs->pqi_cap.conf_tab_off = mb[4];
278 		softs->pqi_cap.conf_tab_sz =  mb[5];
279 
280 		DBG_INIT("max_sg_elem = %x\n",
281 					softs->pqi_cap.max_sg_elem);
282 		DBG_INIT("max_transfer_size = %x\n",
283 					softs->pqi_cap.max_transfer_size);
284 		DBG_INIT("max_outstanding_io = %x\n",
285 					softs->pqi_cap.max_outstanding_io);
286 	}
287 
288 	DBG_FUNC("OUT\n");
289 	return ret;
290 }
291 
292 /* Send INIT STRUCT BASE ADDR - one of the SIS command */
293 int pqisrc_init_struct_base(pqisrc_softstate_t *softs)
294 {
295 	int ret = PQI_STATUS_SUCCESS;
296 	uint32_t elem_size = 0;
297 	uint32_t num_elem = 0;
298 	struct dma_mem init_struct_mem = {0};
299 	struct init_base_struct *init_struct = NULL;
300 	uint32_t mb[6] = {0};
301 
302 	DBG_FUNC("IN\n");
303 
304 	/* Allocate init struct */
305 	memset(&init_struct_mem, 0, sizeof(struct dma_mem));
306 	init_struct_mem.size = sizeof(struct init_base_struct);
307 	init_struct_mem.align = PQISRC_INIT_STRUCT_DMA_ALIGN;
308 	init_struct_mem.tag = "init_struct";
309 	ret = os_dma_mem_alloc(softs, &init_struct_mem);
310 	if (ret) {
311 		DBG_ERR("Failed to Allocate error buffer ret : %d\n",
312 			ret);
313 		goto err_out;
314 	}
315 
316 	/* Calculate error buffer size */
317 	/* The valid tag values are from 1, 2, ..., softs->max_outstanding_io
318 	 * The rcb and error buffer will be accessed by using the tag as index
319 	 * As 0 tag  index is not used, we need to allocate one extra.
320 	 */
321 	num_elem = softs->pqi_cap.max_outstanding_io + 1;
322 	elem_size = PQISRC_ERR_BUF_ELEM_SIZE;
323 	softs->err_buf_dma_mem.size = num_elem * elem_size;
324 
325 	/* Allocate error buffer */
326 	softs->err_buf_dma_mem.align = PQISRC_ERR_BUF_DMA_ALIGN;
327 	softs->err_buf_dma_mem.tag = "error_buffer";
328 	ret = os_dma_mem_alloc(softs, &softs->err_buf_dma_mem);
329 	if (ret) {
330 		DBG_ERR("Failed to Allocate error buffer ret : %d\n",
331 			ret);
332 		goto err_error_buf_alloc;
333 	}
334 
335 	/* Fill init struct */
336 	init_struct = (struct init_base_struct *)DMA_TO_VIRT(&init_struct_mem);
337 	init_struct->revision = PQISRC_INIT_STRUCT_REVISION;
338 	init_struct->flags    = 0;
339 	init_struct->err_buf_paddr_l = DMA_PHYS_LOW(&softs->err_buf_dma_mem);
340 	init_struct->err_buf_paddr_h = DMA_PHYS_HIGH(&softs->err_buf_dma_mem);
341 	init_struct->err_buf_elem_len = elem_size;
342 	init_struct->err_buf_num_elem = num_elem;
343 
344 	mb[0] = SIS_CMD_INIT_BASE_STRUCT_ADDRESS;
345 	mb[1] = DMA_PHYS_LOW(&init_struct_mem);
346 	mb[2] = DMA_PHYS_HIGH(&init_struct_mem);
347 	mb[3] = init_struct_mem.size;
348 
349 	ret = pqisrc_send_sis_cmd(softs, mb);
350 	if (ret)
351 		goto err_sis_cmd;
352 
353 	DBG_FUNC("OUT\n");
354 	os_dma_mem_free(softs, &init_struct_mem);
355 	return ret;
356 
357 err_sis_cmd:
358 	os_dma_mem_free(softs, &softs->err_buf_dma_mem);
359 err_error_buf_alloc:
360 	os_dma_mem_free(softs, &init_struct_mem);
361 err_out:
362 	DBG_FUNC("OUT failed %d\n", ret);
363 	return PQI_STATUS_FAILURE;
364 }
365 
366 /*
367  * SIS initialization of the adapter in a sequence of
368  * - GET_ADAPTER_PROPERTIES
369  * - GET_COMM_PREFERRED_SETTINGS
370  * - GET_PQI_CAPABILITIES
371  * - INIT_STRUCT_BASE ADDR
372  */
373 int pqisrc_sis_init(pqisrc_softstate_t *softs)
374 {
375 	int ret = PQI_STATUS_SUCCESS;
376 	uint32_t prop = 0;
377 	uint32_t ext_prop = 0;
378 
379 	DBG_FUNC("IN\n");
380 
381 	ret = pqisrc_force_sis(softs);
382 	if (ret) {
383 		DBG_ERR("Failed to switch back the adapter to SIS mode!\n");
384 		goto err_out;
385 	}
386 
387 	/* Check FW status ready	*/
388 	ret = pqisrc_check_fw_status(softs);
389 	if (ret) {
390 		DBG_ERR("PQI Controller is not ready !!!\n");
391 		goto err_out;
392 	}
393 
394 	/* Check For PQI support(19h) */
395 	ret = pqisrc_get_adapter_properties(softs, &prop, &ext_prop);
396 	if (ret) {
397 		DBG_ERR("Failed to get adapter properties\n");
398 		goto err_out;
399 	}
400 	if (!((prop & SIS_SUPPORT_EXT_OPT) &&
401 		(ext_prop & SIS_SUPPORT_PQI))) {
402 		DBG_ERR("PQI Mode Not Supported\n");
403 		ret = PQI_STATUS_FAILURE;
404 		goto err_out;
405 	}
406 
407 	softs->pqi_reset_quiesce_allowed = false;
408 	if (ext_prop & SIS_SUPPORT_PQI_RESET_QUIESCE)
409 		softs->pqi_reset_quiesce_allowed = true;
410 
411 	/* Send GET_COMM_PREFERRED_SETTINGS (26h)  */
412 	ret = pqisrc_get_preferred_settings(softs);
413 	if (ret) {
414 		DBG_ERR("Failed to get adapter pref settings\n");
415 		goto err_out;
416 	}
417 
418 	/* Get PQI settings , 3000h*/
419 	ret = pqisrc_get_sis_pqi_cap(softs);
420 	if (ret) {
421 		DBG_ERR("Failed to get PQI Capabilities\n");
422 		goto err_out;
423 	}
424 
425 	/* Init struct base addr */
426 	ret = pqisrc_init_struct_base(softs);
427 	if (ret) {
428 		DBG_ERR("Failed to set init struct base addr\n");
429 		goto err_out;
430 	}
431 
432 	DBG_FUNC("OUT\n");
433 	return ret;
434 
435 err_out:
436 	DBG_FUNC("OUT failed\n");
437 	return ret;
438 }
439 
440 /* Deallocate the resources used during SIS initialization */
441 void pqisrc_sis_uninit(pqisrc_softstate_t *softs)
442 {
443 	DBG_FUNC("IN\n");
444 
445 	os_dma_mem_free(softs, &softs->err_buf_dma_mem);
446 	os_resource_free(softs);
447 	pqi_reset(softs);
448 
449 	DBG_FUNC("OUT\n");
450 }
451 
452 int pqisrc_sis_wait_for_db_bit_to_clear(pqisrc_softstate_t *softs, uint32_t bit)
453 {
454 	int rcode = PQI_STATUS_SUCCESS;
455 	uint32_t db_reg;
456 	uint32_t loop_cnt = 0;
457 
458 	DBG_FUNC("IN\n");
459 
460 	while (1) {
461 		db_reg = PCI_MEM_GET32(softs, &softs->ioa_reg->host_to_ioa_db,
462 				LEGACY_SIS_IDBR);
463 		if ((db_reg & bit) == 0)
464 			break;
465 		if (GET_FW_STATUS(softs) & PQI_CTRL_KERNEL_PANIC) {
466 			DBG_ERR("controller kernel panic\n");
467 			rcode = PQI_STATUS_FAILURE;
468 			break;
469 		}
470 		if (loop_cnt++ == SIS_DB_BIT_CLEAR_TIMEOUT_CNT) {
471 			DBG_ERR("door-bell reg bit 0x%x not cleared\n", bit);
472 			rcode = PQI_STATUS_TIMEOUT;
473 			break;
474 		}
475 		OS_SLEEP(500);
476 	}
477 
478 	DBG_FUNC("OUT\n");
479 
480 	return rcode;
481 }
482