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