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/* */
32void 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
47void 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
65void 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
80void 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 */
102void 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 */
113int 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 */
135int 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 */
155static 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
212err_out:
213	DBG_FUNC("OUT failed\n");
214	return ret;
215}
216
217/* First SIS command for the adapter to check PQI support */
218int 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 */
240int 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 */
264int 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 */
293int 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
357err_sis_cmd:
358	os_dma_mem_free(softs, &softs->err_buf_dma_mem);
359err_error_buf_alloc:
360	os_dma_mem_free(softs, &init_struct_mem);
361err_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 */
373int 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
435err_out:
436	DBG_FUNC("OUT failed\n");
437	return ret;
438}
439
440/* Deallocate the resources used during SIS initialization */
441void 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
452int 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