162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * This file is part of the Chelsio FCoE driver for Linux.
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright (c) 2008-2012 Chelsio Communications, Inc. All rights reserved.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * This software is available to you under a choice of one of two
762306a36Sopenharmony_ci * licenses.  You may choose to be licensed under the terms of the GNU
862306a36Sopenharmony_ci * General Public License (GPL) Version 2, available from the file
962306a36Sopenharmony_ci * COPYING in the main directory of this source tree, or the
1062306a36Sopenharmony_ci * OpenIB.org BSD license below:
1162306a36Sopenharmony_ci *
1262306a36Sopenharmony_ci *     Redistribution and use in source and binary forms, with or
1362306a36Sopenharmony_ci *     without modification, are permitted provided that the following
1462306a36Sopenharmony_ci *     conditions are met:
1562306a36Sopenharmony_ci *
1662306a36Sopenharmony_ci *      - Redistributions of source code must retain the above
1762306a36Sopenharmony_ci *        copyright notice, this list of conditions and the following
1862306a36Sopenharmony_ci *        disclaimer.
1962306a36Sopenharmony_ci *
2062306a36Sopenharmony_ci *      - Redistributions in binary form must reproduce the above
2162306a36Sopenharmony_ci *        copyright notice, this list of conditions and the following
2262306a36Sopenharmony_ci *        disclaimer in the documentation and/or other materials
2362306a36Sopenharmony_ci *        provided with the distribution.
2462306a36Sopenharmony_ci *
2562306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
2662306a36Sopenharmony_ci * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
2762306a36Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
2862306a36Sopenharmony_ci * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
2962306a36Sopenharmony_ci * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
3062306a36Sopenharmony_ci * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
3162306a36Sopenharmony_ci * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
3262306a36Sopenharmony_ci * SOFTWARE.
3362306a36Sopenharmony_ci */
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci#include <linux/pci.h>
3662306a36Sopenharmony_ci#include <linux/pci_regs.h>
3762306a36Sopenharmony_ci#include <linux/firmware.h>
3862306a36Sopenharmony_ci#include <linux/stddef.h>
3962306a36Sopenharmony_ci#include <linux/delay.h>
4062306a36Sopenharmony_ci#include <linux/string.h>
4162306a36Sopenharmony_ci#include <linux/compiler.h>
4262306a36Sopenharmony_ci#include <linux/jiffies.h>
4362306a36Sopenharmony_ci#include <linux/kernel.h>
4462306a36Sopenharmony_ci#include <linux/log2.h>
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci#include "csio_hw.h"
4762306a36Sopenharmony_ci#include "csio_lnode.h"
4862306a36Sopenharmony_ci#include "csio_rnode.h"
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ciint csio_dbg_level = 0xFEFF;
5162306a36Sopenharmony_ciunsigned int csio_port_mask = 0xf;
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci/* Default FW event queue entries. */
5462306a36Sopenharmony_cistatic uint32_t csio_evtq_sz = CSIO_EVTQ_SIZE;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci/* Default MSI param level */
5762306a36Sopenharmony_ciint csio_msi = 2;
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci/* FCoE function instances */
6062306a36Sopenharmony_cistatic int dev_num;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci/* FCoE Adapter types & its description */
6362306a36Sopenharmony_cistatic const struct csio_adap_desc csio_t5_fcoe_adapters[] = {
6462306a36Sopenharmony_ci	{"T580-Dbg 10G", "Chelsio T580-Dbg 10G [FCoE]"},
6562306a36Sopenharmony_ci	{"T520-CR 10G", "Chelsio T520-CR 10G [FCoE]"},
6662306a36Sopenharmony_ci	{"T522-CR 10G/1G", "Chelsio T522-CR 10G/1G [FCoE]"},
6762306a36Sopenharmony_ci	{"T540-CR 10G", "Chelsio T540-CR 10G [FCoE]"},
6862306a36Sopenharmony_ci	{"T520-BCH 10G", "Chelsio T520-BCH 10G [FCoE]"},
6962306a36Sopenharmony_ci	{"T540-BCH 10G", "Chelsio T540-BCH 10G [FCoE]"},
7062306a36Sopenharmony_ci	{"T540-CH 10G", "Chelsio T540-CH 10G [FCoE]"},
7162306a36Sopenharmony_ci	{"T520-SO 10G", "Chelsio T520-SO 10G [FCoE]"},
7262306a36Sopenharmony_ci	{"T520-CX4 10G", "Chelsio T520-CX4 10G [FCoE]"},
7362306a36Sopenharmony_ci	{"T520-BT 10G", "Chelsio T520-BT 10G [FCoE]"},
7462306a36Sopenharmony_ci	{"T504-BT 1G", "Chelsio T504-BT 1G [FCoE]"},
7562306a36Sopenharmony_ci	{"B520-SR 10G", "Chelsio B520-SR 10G [FCoE]"},
7662306a36Sopenharmony_ci	{"B504-BT 1G", "Chelsio B504-BT 1G [FCoE]"},
7762306a36Sopenharmony_ci	{"T580-CR 10G", "Chelsio T580-CR 10G [FCoE]"},
7862306a36Sopenharmony_ci	{"T540-LP-CR 10G", "Chelsio T540-LP-CR 10G [FCoE]"},
7962306a36Sopenharmony_ci	{"AMSTERDAM 10G", "Chelsio AMSTERDAM 10G [FCoE]"},
8062306a36Sopenharmony_ci	{"T580-LP-CR 40G", "Chelsio T580-LP-CR 40G [FCoE]"},
8162306a36Sopenharmony_ci	{"T520-LL-CR 10G", "Chelsio T520-LL-CR 10G [FCoE]"},
8262306a36Sopenharmony_ci	{"T560-CR 40G", "Chelsio T560-CR 40G [FCoE]"},
8362306a36Sopenharmony_ci	{"T580-CR 40G", "Chelsio T580-CR 40G [FCoE]"},
8462306a36Sopenharmony_ci	{"T580-SO 40G", "Chelsio T580-SO 40G [FCoE]"},
8562306a36Sopenharmony_ci	{"T502-BT 1G", "Chelsio T502-BT 1G [FCoE]"}
8662306a36Sopenharmony_ci};
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_cistatic void csio_mgmtm_cleanup(struct csio_mgmtm *);
8962306a36Sopenharmony_cistatic void csio_hw_mbm_cleanup(struct csio_hw *);
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci/* State machine forward declarations */
9262306a36Sopenharmony_cistatic void csio_hws_uninit(struct csio_hw *, enum csio_hw_ev);
9362306a36Sopenharmony_cistatic void csio_hws_configuring(struct csio_hw *, enum csio_hw_ev);
9462306a36Sopenharmony_cistatic void csio_hws_initializing(struct csio_hw *, enum csio_hw_ev);
9562306a36Sopenharmony_cistatic void csio_hws_ready(struct csio_hw *, enum csio_hw_ev);
9662306a36Sopenharmony_cistatic void csio_hws_quiescing(struct csio_hw *, enum csio_hw_ev);
9762306a36Sopenharmony_cistatic void csio_hws_quiesced(struct csio_hw *, enum csio_hw_ev);
9862306a36Sopenharmony_cistatic void csio_hws_resetting(struct csio_hw *, enum csio_hw_ev);
9962306a36Sopenharmony_cistatic void csio_hws_removing(struct csio_hw *, enum csio_hw_ev);
10062306a36Sopenharmony_cistatic void csio_hws_pcierr(struct csio_hw *, enum csio_hw_ev);
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_cistatic void csio_hw_initialize(struct csio_hw *hw);
10362306a36Sopenharmony_cistatic void csio_evtq_stop(struct csio_hw *hw);
10462306a36Sopenharmony_cistatic void csio_evtq_start(struct csio_hw *hw);
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ciint csio_is_hw_ready(struct csio_hw *hw)
10762306a36Sopenharmony_ci{
10862306a36Sopenharmony_ci	return csio_match_state(hw, csio_hws_ready);
10962306a36Sopenharmony_ci}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ciint csio_is_hw_removing(struct csio_hw *hw)
11262306a36Sopenharmony_ci{
11362306a36Sopenharmony_ci	return csio_match_state(hw, csio_hws_removing);
11462306a36Sopenharmony_ci}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci/*
11862306a36Sopenharmony_ci *	csio_hw_wait_op_done_val - wait until an operation is completed
11962306a36Sopenharmony_ci *	@hw: the HW module
12062306a36Sopenharmony_ci *	@reg: the register to check for completion
12162306a36Sopenharmony_ci *	@mask: a single-bit field within @reg that indicates completion
12262306a36Sopenharmony_ci *	@polarity: the value of the field when the operation is completed
12362306a36Sopenharmony_ci *	@attempts: number of check iterations
12462306a36Sopenharmony_ci *	@delay: delay in usecs between iterations
12562306a36Sopenharmony_ci *	@valp: where to store the value of the register at completion time
12662306a36Sopenharmony_ci *
12762306a36Sopenharmony_ci *	Wait until an operation is completed by checking a bit in a register
12862306a36Sopenharmony_ci *	up to @attempts times.  If @valp is not NULL the value of the register
12962306a36Sopenharmony_ci *	at the time it indicated completion is stored there.  Returns 0 if the
13062306a36Sopenharmony_ci *	operation completes and	-EAGAIN	otherwise.
13162306a36Sopenharmony_ci */
13262306a36Sopenharmony_ciint
13362306a36Sopenharmony_cicsio_hw_wait_op_done_val(struct csio_hw *hw, int reg, uint32_t mask,
13462306a36Sopenharmony_ci			 int polarity, int attempts, int delay, uint32_t *valp)
13562306a36Sopenharmony_ci{
13662306a36Sopenharmony_ci	uint32_t val;
13762306a36Sopenharmony_ci	while (1) {
13862306a36Sopenharmony_ci		val = csio_rd_reg32(hw, reg);
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci		if (!!(val & mask) == polarity) {
14162306a36Sopenharmony_ci			if (valp)
14262306a36Sopenharmony_ci				*valp = val;
14362306a36Sopenharmony_ci			return 0;
14462306a36Sopenharmony_ci		}
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci		if (--attempts == 0)
14762306a36Sopenharmony_ci			return -EAGAIN;
14862306a36Sopenharmony_ci		if (delay)
14962306a36Sopenharmony_ci			udelay(delay);
15062306a36Sopenharmony_ci	}
15162306a36Sopenharmony_ci}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci/*
15462306a36Sopenharmony_ci *	csio_hw_tp_wr_bits_indirect - set/clear bits in an indirect TP register
15562306a36Sopenharmony_ci *	@hw: the adapter
15662306a36Sopenharmony_ci *	@addr: the indirect TP register address
15762306a36Sopenharmony_ci *	@mask: specifies the field within the register to modify
15862306a36Sopenharmony_ci *	@val: new value for the field
15962306a36Sopenharmony_ci *
16062306a36Sopenharmony_ci *	Sets a field of an indirect TP register to the given value.
16162306a36Sopenharmony_ci */
16262306a36Sopenharmony_civoid
16362306a36Sopenharmony_cicsio_hw_tp_wr_bits_indirect(struct csio_hw *hw, unsigned int addr,
16462306a36Sopenharmony_ci			unsigned int mask, unsigned int val)
16562306a36Sopenharmony_ci{
16662306a36Sopenharmony_ci	csio_wr_reg32(hw, addr, TP_PIO_ADDR_A);
16762306a36Sopenharmony_ci	val |= csio_rd_reg32(hw, TP_PIO_DATA_A) & ~mask;
16862306a36Sopenharmony_ci	csio_wr_reg32(hw, val, TP_PIO_DATA_A);
16962306a36Sopenharmony_ci}
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_civoid
17262306a36Sopenharmony_cicsio_set_reg_field(struct csio_hw *hw, uint32_t reg, uint32_t mask,
17362306a36Sopenharmony_ci		   uint32_t value)
17462306a36Sopenharmony_ci{
17562306a36Sopenharmony_ci	uint32_t val = csio_rd_reg32(hw, reg) & ~mask;
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	csio_wr_reg32(hw, val | value, reg);
17862306a36Sopenharmony_ci	/* Flush */
17962306a36Sopenharmony_ci	csio_rd_reg32(hw, reg);
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci}
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_cistatic int
18462306a36Sopenharmony_cicsio_memory_write(struct csio_hw *hw, int mtype, u32 addr, u32 len, u32 *buf)
18562306a36Sopenharmony_ci{
18662306a36Sopenharmony_ci	return hw->chip_ops->chip_memory_rw(hw, MEMWIN_CSIOSTOR, mtype,
18762306a36Sopenharmony_ci					    addr, len, buf, 0);
18862306a36Sopenharmony_ci}
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci/*
19162306a36Sopenharmony_ci * EEPROM reads take a few tens of us while writes can take a bit over 5 ms.
19262306a36Sopenharmony_ci */
19362306a36Sopenharmony_ci#define EEPROM_MAX_RD_POLL	40
19462306a36Sopenharmony_ci#define EEPROM_MAX_WR_POLL	6
19562306a36Sopenharmony_ci#define EEPROM_STAT_ADDR	0x7bfc
19662306a36Sopenharmony_ci#define VPD_BASE		0x400
19762306a36Sopenharmony_ci#define VPD_BASE_OLD		0
19862306a36Sopenharmony_ci#define VPD_LEN			1024
19962306a36Sopenharmony_ci#define VPD_INFO_FLD_HDR_SIZE	3
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci/*
20262306a36Sopenharmony_ci *	csio_hw_seeprom_read - read a serial EEPROM location
20362306a36Sopenharmony_ci *	@hw: hw to read
20462306a36Sopenharmony_ci *	@addr: EEPROM virtual address
20562306a36Sopenharmony_ci *	@data: where to store the read data
20662306a36Sopenharmony_ci *
20762306a36Sopenharmony_ci *	Read a 32-bit word from a location in serial EEPROM using the card's PCI
20862306a36Sopenharmony_ci *	VPD capability.  Note that this function must be called with a virtual
20962306a36Sopenharmony_ci *	address.
21062306a36Sopenharmony_ci */
21162306a36Sopenharmony_cistatic int
21262306a36Sopenharmony_cicsio_hw_seeprom_read(struct csio_hw *hw, uint32_t addr, uint32_t *data)
21362306a36Sopenharmony_ci{
21462306a36Sopenharmony_ci	uint16_t val = 0;
21562306a36Sopenharmony_ci	int attempts = EEPROM_MAX_RD_POLL;
21662306a36Sopenharmony_ci	uint32_t base = hw->params.pci.vpd_cap_addr;
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	if (addr >= EEPROMVSIZE || (addr & 3))
21962306a36Sopenharmony_ci		return -EINVAL;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	pci_write_config_word(hw->pdev, base + PCI_VPD_ADDR, (uint16_t)addr);
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	do {
22462306a36Sopenharmony_ci		udelay(10);
22562306a36Sopenharmony_ci		pci_read_config_word(hw->pdev, base + PCI_VPD_ADDR, &val);
22662306a36Sopenharmony_ci	} while (!(val & PCI_VPD_ADDR_F) && --attempts);
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	if (!(val & PCI_VPD_ADDR_F)) {
22962306a36Sopenharmony_ci		csio_err(hw, "reading EEPROM address 0x%x failed\n", addr);
23062306a36Sopenharmony_ci		return -EINVAL;
23162306a36Sopenharmony_ci	}
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	pci_read_config_dword(hw->pdev, base + PCI_VPD_DATA, data);
23462306a36Sopenharmony_ci	*data = le32_to_cpu(*(__le32 *)data);
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	return 0;
23762306a36Sopenharmony_ci}
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci/*
24062306a36Sopenharmony_ci * Partial EEPROM Vital Product Data structure.  Includes only the ID and
24162306a36Sopenharmony_ci * VPD-R sections.
24262306a36Sopenharmony_ci */
24362306a36Sopenharmony_cistruct t4_vpd_hdr {
24462306a36Sopenharmony_ci	u8  id_tag;
24562306a36Sopenharmony_ci	u8  id_len[2];
24662306a36Sopenharmony_ci	u8  id_data[ID_LEN];
24762306a36Sopenharmony_ci	u8  vpdr_tag;
24862306a36Sopenharmony_ci	u8  vpdr_len[2];
24962306a36Sopenharmony_ci};
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci/*
25262306a36Sopenharmony_ci *	csio_hw_get_vpd_keyword_val - Locates an information field keyword in
25362306a36Sopenharmony_ci *				      the VPD
25462306a36Sopenharmony_ci *	@v: Pointer to buffered vpd data structure
25562306a36Sopenharmony_ci *	@kw: The keyword to search for
25662306a36Sopenharmony_ci *
25762306a36Sopenharmony_ci *	Returns the value of the information field keyword or
25862306a36Sopenharmony_ci *	-EINVAL otherwise.
25962306a36Sopenharmony_ci */
26062306a36Sopenharmony_cistatic int
26162306a36Sopenharmony_cicsio_hw_get_vpd_keyword_val(const struct t4_vpd_hdr *v, const char *kw)
26262306a36Sopenharmony_ci{
26362306a36Sopenharmony_ci	int32_t i;
26462306a36Sopenharmony_ci	int32_t offset , len;
26562306a36Sopenharmony_ci	const uint8_t *buf = &v->id_tag;
26662306a36Sopenharmony_ci	const uint8_t *vpdr_len = &v->vpdr_tag;
26762306a36Sopenharmony_ci	offset = sizeof(struct t4_vpd_hdr);
26862306a36Sopenharmony_ci	len =  (uint16_t)vpdr_len[1] + ((uint16_t)vpdr_len[2] << 8);
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	if (len + sizeof(struct t4_vpd_hdr) > VPD_LEN)
27162306a36Sopenharmony_ci		return -EINVAL;
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	for (i = offset; (i + VPD_INFO_FLD_HDR_SIZE) <= (offset + len);) {
27462306a36Sopenharmony_ci		if (memcmp(buf + i , kw, 2) == 0) {
27562306a36Sopenharmony_ci			i += VPD_INFO_FLD_HDR_SIZE;
27662306a36Sopenharmony_ci			return i;
27762306a36Sopenharmony_ci		}
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci		i += VPD_INFO_FLD_HDR_SIZE + buf[i+2];
28062306a36Sopenharmony_ci	}
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	return -EINVAL;
28362306a36Sopenharmony_ci}
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_cistatic int
28662306a36Sopenharmony_cicsio_pci_capability(struct pci_dev *pdev, int cap, int *pos)
28762306a36Sopenharmony_ci{
28862306a36Sopenharmony_ci	*pos = pci_find_capability(pdev, cap);
28962306a36Sopenharmony_ci	if (*pos)
29062306a36Sopenharmony_ci		return 0;
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	return -1;
29362306a36Sopenharmony_ci}
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci/*
29662306a36Sopenharmony_ci *	csio_hw_get_vpd_params - read VPD parameters from VPD EEPROM
29762306a36Sopenharmony_ci *	@hw: HW module
29862306a36Sopenharmony_ci *	@p: where to store the parameters
29962306a36Sopenharmony_ci *
30062306a36Sopenharmony_ci *	Reads card parameters stored in VPD EEPROM.
30162306a36Sopenharmony_ci */
30262306a36Sopenharmony_cistatic int
30362306a36Sopenharmony_cicsio_hw_get_vpd_params(struct csio_hw *hw, struct csio_vpd *p)
30462306a36Sopenharmony_ci{
30562306a36Sopenharmony_ci	int i, ret, ec, sn, addr;
30662306a36Sopenharmony_ci	uint8_t *vpd, csum;
30762306a36Sopenharmony_ci	const struct t4_vpd_hdr *v;
30862306a36Sopenharmony_ci	/* To get around compilation warning from strstrip */
30962306a36Sopenharmony_ci	char __always_unused *s;
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	if (csio_is_valid_vpd(hw))
31262306a36Sopenharmony_ci		return 0;
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	ret = csio_pci_capability(hw->pdev, PCI_CAP_ID_VPD,
31562306a36Sopenharmony_ci				  &hw->params.pci.vpd_cap_addr);
31662306a36Sopenharmony_ci	if (ret)
31762306a36Sopenharmony_ci		return -EINVAL;
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	vpd = kzalloc(VPD_LEN, GFP_ATOMIC);
32062306a36Sopenharmony_ci	if (vpd == NULL)
32162306a36Sopenharmony_ci		return -ENOMEM;
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	/*
32462306a36Sopenharmony_ci	 * Card information normally starts at VPD_BASE but early cards had
32562306a36Sopenharmony_ci	 * it at 0.
32662306a36Sopenharmony_ci	 */
32762306a36Sopenharmony_ci	ret = csio_hw_seeprom_read(hw, VPD_BASE, (uint32_t *)(vpd));
32862306a36Sopenharmony_ci	addr = *vpd == 0x82 ? VPD_BASE : VPD_BASE_OLD;
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	for (i = 0; i < VPD_LEN; i += 4) {
33162306a36Sopenharmony_ci		ret = csio_hw_seeprom_read(hw, addr + i, (uint32_t *)(vpd + i));
33262306a36Sopenharmony_ci		if (ret) {
33362306a36Sopenharmony_ci			kfree(vpd);
33462306a36Sopenharmony_ci			return ret;
33562306a36Sopenharmony_ci		}
33662306a36Sopenharmony_ci	}
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	/* Reset the VPD flag! */
33962306a36Sopenharmony_ci	hw->flags &= (~CSIO_HWF_VPD_VALID);
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	v = (const struct t4_vpd_hdr *)vpd;
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci#define FIND_VPD_KW(var, name) do { \
34462306a36Sopenharmony_ci	var = csio_hw_get_vpd_keyword_val(v, name); \
34562306a36Sopenharmony_ci	if (var < 0) { \
34662306a36Sopenharmony_ci		csio_err(hw, "missing VPD keyword " name "\n"); \
34762306a36Sopenharmony_ci		kfree(vpd); \
34862306a36Sopenharmony_ci		return -EINVAL; \
34962306a36Sopenharmony_ci	} \
35062306a36Sopenharmony_ci} while (0)
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	FIND_VPD_KW(i, "RV");
35362306a36Sopenharmony_ci	for (csum = 0; i >= 0; i--)
35462306a36Sopenharmony_ci		csum += vpd[i];
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	if (csum) {
35762306a36Sopenharmony_ci		csio_err(hw, "corrupted VPD EEPROM, actual csum %u\n", csum);
35862306a36Sopenharmony_ci		kfree(vpd);
35962306a36Sopenharmony_ci		return -EINVAL;
36062306a36Sopenharmony_ci	}
36162306a36Sopenharmony_ci	FIND_VPD_KW(ec, "EC");
36262306a36Sopenharmony_ci	FIND_VPD_KW(sn, "SN");
36362306a36Sopenharmony_ci#undef FIND_VPD_KW
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	memcpy(p->id, v->id_data, ID_LEN);
36662306a36Sopenharmony_ci	s = strstrip(p->id);
36762306a36Sopenharmony_ci	memcpy(p->ec, vpd + ec, EC_LEN);
36862306a36Sopenharmony_ci	s = strstrip(p->ec);
36962306a36Sopenharmony_ci	i = vpd[sn - VPD_INFO_FLD_HDR_SIZE + 2];
37062306a36Sopenharmony_ci	memcpy(p->sn, vpd + sn, min(i, SERNUM_LEN));
37162306a36Sopenharmony_ci	s = strstrip(p->sn);
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	csio_valid_vpd_copied(hw);
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	kfree(vpd);
37662306a36Sopenharmony_ci	return 0;
37762306a36Sopenharmony_ci}
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci/*
38062306a36Sopenharmony_ci *	csio_hw_sf1_read - read data from the serial flash
38162306a36Sopenharmony_ci *	@hw: the HW module
38262306a36Sopenharmony_ci *	@byte_cnt: number of bytes to read
38362306a36Sopenharmony_ci *	@cont: whether another operation will be chained
38462306a36Sopenharmony_ci *      @lock: whether to lock SF for PL access only
38562306a36Sopenharmony_ci *	@valp: where to store the read data
38662306a36Sopenharmony_ci *
38762306a36Sopenharmony_ci *	Reads up to 4 bytes of data from the serial flash.  The location of
38862306a36Sopenharmony_ci *	the read needs to be specified prior to calling this by issuing the
38962306a36Sopenharmony_ci *	appropriate commands to the serial flash.
39062306a36Sopenharmony_ci */
39162306a36Sopenharmony_cistatic int
39262306a36Sopenharmony_cicsio_hw_sf1_read(struct csio_hw *hw, uint32_t byte_cnt, int32_t cont,
39362306a36Sopenharmony_ci		 int32_t lock, uint32_t *valp)
39462306a36Sopenharmony_ci{
39562306a36Sopenharmony_ci	int ret;
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	if (!byte_cnt || byte_cnt > 4)
39862306a36Sopenharmony_ci		return -EINVAL;
39962306a36Sopenharmony_ci	if (csio_rd_reg32(hw, SF_OP_A) & SF_BUSY_F)
40062306a36Sopenharmony_ci		return -EBUSY;
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	csio_wr_reg32(hw,  SF_LOCK_V(lock) | SF_CONT_V(cont) |
40362306a36Sopenharmony_ci		      BYTECNT_V(byte_cnt - 1), SF_OP_A);
40462306a36Sopenharmony_ci	ret = csio_hw_wait_op_done_val(hw, SF_OP_A, SF_BUSY_F, 0, SF_ATTEMPTS,
40562306a36Sopenharmony_ci				       10, NULL);
40662306a36Sopenharmony_ci	if (!ret)
40762306a36Sopenharmony_ci		*valp = csio_rd_reg32(hw, SF_DATA_A);
40862306a36Sopenharmony_ci	return ret;
40962306a36Sopenharmony_ci}
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci/*
41262306a36Sopenharmony_ci *	csio_hw_sf1_write - write data to the serial flash
41362306a36Sopenharmony_ci *	@hw: the HW module
41462306a36Sopenharmony_ci *	@byte_cnt: number of bytes to write
41562306a36Sopenharmony_ci *	@cont: whether another operation will be chained
41662306a36Sopenharmony_ci *      @lock: whether to lock SF for PL access only
41762306a36Sopenharmony_ci *	@val: value to write
41862306a36Sopenharmony_ci *
41962306a36Sopenharmony_ci *	Writes up to 4 bytes of data to the serial flash.  The location of
42062306a36Sopenharmony_ci *	the write needs to be specified prior to calling this by issuing the
42162306a36Sopenharmony_ci *	appropriate commands to the serial flash.
42262306a36Sopenharmony_ci */
42362306a36Sopenharmony_cistatic int
42462306a36Sopenharmony_cicsio_hw_sf1_write(struct csio_hw *hw, uint32_t byte_cnt, uint32_t cont,
42562306a36Sopenharmony_ci		  int32_t lock, uint32_t val)
42662306a36Sopenharmony_ci{
42762306a36Sopenharmony_ci	if (!byte_cnt || byte_cnt > 4)
42862306a36Sopenharmony_ci		return -EINVAL;
42962306a36Sopenharmony_ci	if (csio_rd_reg32(hw, SF_OP_A) & SF_BUSY_F)
43062306a36Sopenharmony_ci		return -EBUSY;
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	csio_wr_reg32(hw, val, SF_DATA_A);
43362306a36Sopenharmony_ci	csio_wr_reg32(hw, SF_CONT_V(cont) | BYTECNT_V(byte_cnt - 1) |
43462306a36Sopenharmony_ci		      OP_V(1) | SF_LOCK_V(lock), SF_OP_A);
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	return csio_hw_wait_op_done_val(hw, SF_OP_A, SF_BUSY_F, 0, SF_ATTEMPTS,
43762306a36Sopenharmony_ci					10, NULL);
43862306a36Sopenharmony_ci}
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci/*
44162306a36Sopenharmony_ci *	csio_hw_flash_wait_op - wait for a flash operation to complete
44262306a36Sopenharmony_ci *	@hw: the HW module
44362306a36Sopenharmony_ci *	@attempts: max number of polls of the status register
44462306a36Sopenharmony_ci *	@delay: delay between polls in ms
44562306a36Sopenharmony_ci *
44662306a36Sopenharmony_ci *	Wait for a flash operation to complete by polling the status register.
44762306a36Sopenharmony_ci */
44862306a36Sopenharmony_cistatic int
44962306a36Sopenharmony_cicsio_hw_flash_wait_op(struct csio_hw *hw, int32_t attempts, int32_t delay)
45062306a36Sopenharmony_ci{
45162306a36Sopenharmony_ci	int ret;
45262306a36Sopenharmony_ci	uint32_t status;
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	while (1) {
45562306a36Sopenharmony_ci		ret = csio_hw_sf1_write(hw, 1, 1, 1, SF_RD_STATUS);
45662306a36Sopenharmony_ci		if (ret != 0)
45762306a36Sopenharmony_ci			return ret;
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci		ret = csio_hw_sf1_read(hw, 1, 0, 1, &status);
46062306a36Sopenharmony_ci		if (ret != 0)
46162306a36Sopenharmony_ci			return ret;
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci		if (!(status & 1))
46462306a36Sopenharmony_ci			return 0;
46562306a36Sopenharmony_ci		if (--attempts == 0)
46662306a36Sopenharmony_ci			return -EAGAIN;
46762306a36Sopenharmony_ci		if (delay)
46862306a36Sopenharmony_ci			msleep(delay);
46962306a36Sopenharmony_ci	}
47062306a36Sopenharmony_ci}
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci/*
47362306a36Sopenharmony_ci *	csio_hw_read_flash - read words from serial flash
47462306a36Sopenharmony_ci *	@hw: the HW module
47562306a36Sopenharmony_ci *	@addr: the start address for the read
47662306a36Sopenharmony_ci *	@nwords: how many 32-bit words to read
47762306a36Sopenharmony_ci *	@data: where to store the read data
47862306a36Sopenharmony_ci *	@byte_oriented: whether to store data as bytes or as words
47962306a36Sopenharmony_ci *
48062306a36Sopenharmony_ci *	Read the specified number of 32-bit words from the serial flash.
48162306a36Sopenharmony_ci *	If @byte_oriented is set the read data is stored as a byte array
48262306a36Sopenharmony_ci *	(i.e., big-endian), otherwise as 32-bit words in the platform's
48362306a36Sopenharmony_ci *	natural endianess.
48462306a36Sopenharmony_ci */
48562306a36Sopenharmony_cistatic int
48662306a36Sopenharmony_cicsio_hw_read_flash(struct csio_hw *hw, uint32_t addr, uint32_t nwords,
48762306a36Sopenharmony_ci		  uint32_t *data, int32_t byte_oriented)
48862306a36Sopenharmony_ci{
48962306a36Sopenharmony_ci	int ret;
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci	if (addr + nwords * sizeof(uint32_t) > hw->params.sf_size || (addr & 3))
49262306a36Sopenharmony_ci		return -EINVAL;
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	addr = swab32(addr) | SF_RD_DATA_FAST;
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	ret = csio_hw_sf1_write(hw, 4, 1, 0, addr);
49762306a36Sopenharmony_ci	if (ret != 0)
49862306a36Sopenharmony_ci		return ret;
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	ret = csio_hw_sf1_read(hw, 1, 1, 0, data);
50162306a36Sopenharmony_ci	if (ret != 0)
50262306a36Sopenharmony_ci		return ret;
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci	for ( ; nwords; nwords--, data++) {
50562306a36Sopenharmony_ci		ret = csio_hw_sf1_read(hw, 4, nwords > 1, nwords == 1, data);
50662306a36Sopenharmony_ci		if (nwords == 1)
50762306a36Sopenharmony_ci			csio_wr_reg32(hw, 0, SF_OP_A);    /* unlock SF */
50862306a36Sopenharmony_ci		if (ret)
50962306a36Sopenharmony_ci			return ret;
51062306a36Sopenharmony_ci		if (byte_oriented)
51162306a36Sopenharmony_ci			*data = (__force __u32) htonl(*data);
51262306a36Sopenharmony_ci	}
51362306a36Sopenharmony_ci	return 0;
51462306a36Sopenharmony_ci}
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci/*
51762306a36Sopenharmony_ci *	csio_hw_write_flash - write up to a page of data to the serial flash
51862306a36Sopenharmony_ci *	@hw: the hw
51962306a36Sopenharmony_ci *	@addr: the start address to write
52062306a36Sopenharmony_ci *	@n: length of data to write in bytes
52162306a36Sopenharmony_ci *	@data: the data to write
52262306a36Sopenharmony_ci *
52362306a36Sopenharmony_ci *	Writes up to a page of data (256 bytes) to the serial flash starting
52462306a36Sopenharmony_ci *	at the given address.  All the data must be written to the same page.
52562306a36Sopenharmony_ci */
52662306a36Sopenharmony_cistatic int
52762306a36Sopenharmony_cicsio_hw_write_flash(struct csio_hw *hw, uint32_t addr,
52862306a36Sopenharmony_ci		    uint32_t n, const uint8_t *data)
52962306a36Sopenharmony_ci{
53062306a36Sopenharmony_ci	int ret = -EINVAL;
53162306a36Sopenharmony_ci	uint32_t buf[64];
53262306a36Sopenharmony_ci	uint32_t i, c, left, val, offset = addr & 0xff;
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	if (addr >= hw->params.sf_size || offset + n > SF_PAGE_SIZE)
53562306a36Sopenharmony_ci		return -EINVAL;
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci	val = swab32(addr) | SF_PROG_PAGE;
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci	ret = csio_hw_sf1_write(hw, 1, 0, 1, SF_WR_ENABLE);
54062306a36Sopenharmony_ci	if (ret != 0)
54162306a36Sopenharmony_ci		goto unlock;
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	ret = csio_hw_sf1_write(hw, 4, 1, 1, val);
54462306a36Sopenharmony_ci	if (ret != 0)
54562306a36Sopenharmony_ci		goto unlock;
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	for (left = n; left; left -= c) {
54862306a36Sopenharmony_ci		c = min(left, 4U);
54962306a36Sopenharmony_ci		for (val = 0, i = 0; i < c; ++i)
55062306a36Sopenharmony_ci			val = (val << 8) + *data++;
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci		ret = csio_hw_sf1_write(hw, c, c != left, 1, val);
55362306a36Sopenharmony_ci		if (ret)
55462306a36Sopenharmony_ci			goto unlock;
55562306a36Sopenharmony_ci	}
55662306a36Sopenharmony_ci	ret = csio_hw_flash_wait_op(hw, 8, 1);
55762306a36Sopenharmony_ci	if (ret)
55862306a36Sopenharmony_ci		goto unlock;
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	csio_wr_reg32(hw, 0, SF_OP_A);    /* unlock SF */
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	/* Read the page to verify the write succeeded */
56362306a36Sopenharmony_ci	ret = csio_hw_read_flash(hw, addr & ~0xff, ARRAY_SIZE(buf), buf, 1);
56462306a36Sopenharmony_ci	if (ret)
56562306a36Sopenharmony_ci		return ret;
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	if (memcmp(data - n, (uint8_t *)buf + offset, n)) {
56862306a36Sopenharmony_ci		csio_err(hw,
56962306a36Sopenharmony_ci			 "failed to correctly write the flash page at %#x\n",
57062306a36Sopenharmony_ci			 addr);
57162306a36Sopenharmony_ci		return -EINVAL;
57262306a36Sopenharmony_ci	}
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci	return 0;
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ciunlock:
57762306a36Sopenharmony_ci	csio_wr_reg32(hw, 0, SF_OP_A);    /* unlock SF */
57862306a36Sopenharmony_ci	return ret;
57962306a36Sopenharmony_ci}
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci/*
58262306a36Sopenharmony_ci *	csio_hw_flash_erase_sectors - erase a range of flash sectors
58362306a36Sopenharmony_ci *	@hw: the HW module
58462306a36Sopenharmony_ci *	@start: the first sector to erase
58562306a36Sopenharmony_ci *	@end: the last sector to erase
58662306a36Sopenharmony_ci *
58762306a36Sopenharmony_ci *	Erases the sectors in the given inclusive range.
58862306a36Sopenharmony_ci */
58962306a36Sopenharmony_cistatic int
59062306a36Sopenharmony_cicsio_hw_flash_erase_sectors(struct csio_hw *hw, int32_t start, int32_t end)
59162306a36Sopenharmony_ci{
59262306a36Sopenharmony_ci	int ret = 0;
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	while (start <= end) {
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci		ret = csio_hw_sf1_write(hw, 1, 0, 1, SF_WR_ENABLE);
59762306a36Sopenharmony_ci		if (ret != 0)
59862306a36Sopenharmony_ci			goto out;
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci		ret = csio_hw_sf1_write(hw, 4, 0, 1,
60162306a36Sopenharmony_ci					SF_ERASE_SECTOR | (start << 8));
60262306a36Sopenharmony_ci		if (ret != 0)
60362306a36Sopenharmony_ci			goto out;
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci		ret = csio_hw_flash_wait_op(hw, 14, 500);
60662306a36Sopenharmony_ci		if (ret != 0)
60762306a36Sopenharmony_ci			goto out;
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci		start++;
61062306a36Sopenharmony_ci	}
61162306a36Sopenharmony_ciout:
61262306a36Sopenharmony_ci	if (ret)
61362306a36Sopenharmony_ci		csio_err(hw, "erase of flash sector %d failed, error %d\n",
61462306a36Sopenharmony_ci			 start, ret);
61562306a36Sopenharmony_ci	csio_wr_reg32(hw, 0, SF_OP_A);    /* unlock SF */
61662306a36Sopenharmony_ci	return 0;
61762306a36Sopenharmony_ci}
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_cistatic void
62062306a36Sopenharmony_cicsio_hw_print_fw_version(struct csio_hw *hw, char *str)
62162306a36Sopenharmony_ci{
62262306a36Sopenharmony_ci	csio_info(hw, "%s: %u.%u.%u.%u\n", str,
62362306a36Sopenharmony_ci		    FW_HDR_FW_VER_MAJOR_G(hw->fwrev),
62462306a36Sopenharmony_ci		    FW_HDR_FW_VER_MINOR_G(hw->fwrev),
62562306a36Sopenharmony_ci		    FW_HDR_FW_VER_MICRO_G(hw->fwrev),
62662306a36Sopenharmony_ci		    FW_HDR_FW_VER_BUILD_G(hw->fwrev));
62762306a36Sopenharmony_ci}
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci/*
63062306a36Sopenharmony_ci * csio_hw_get_fw_version - read the firmware version
63162306a36Sopenharmony_ci * @hw: HW module
63262306a36Sopenharmony_ci * @vers: where to place the version
63362306a36Sopenharmony_ci *
63462306a36Sopenharmony_ci * Reads the FW version from flash.
63562306a36Sopenharmony_ci */
63662306a36Sopenharmony_cistatic int
63762306a36Sopenharmony_cicsio_hw_get_fw_version(struct csio_hw *hw, uint32_t *vers)
63862306a36Sopenharmony_ci{
63962306a36Sopenharmony_ci	return csio_hw_read_flash(hw, FLASH_FW_START +
64062306a36Sopenharmony_ci				  offsetof(struct fw_hdr, fw_ver), 1,
64162306a36Sopenharmony_ci				  vers, 0);
64262306a36Sopenharmony_ci}
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci/*
64562306a36Sopenharmony_ci *	csio_hw_get_tp_version - read the TP microcode version
64662306a36Sopenharmony_ci *	@hw: HW module
64762306a36Sopenharmony_ci *	@vers: where to place the version
64862306a36Sopenharmony_ci *
64962306a36Sopenharmony_ci *	Reads the TP microcode version from flash.
65062306a36Sopenharmony_ci */
65162306a36Sopenharmony_cistatic int
65262306a36Sopenharmony_cicsio_hw_get_tp_version(struct csio_hw *hw, u32 *vers)
65362306a36Sopenharmony_ci{
65462306a36Sopenharmony_ci	return csio_hw_read_flash(hw, FLASH_FW_START +
65562306a36Sopenharmony_ci			offsetof(struct fw_hdr, tp_microcode_ver), 1,
65662306a36Sopenharmony_ci			vers, 0);
65762306a36Sopenharmony_ci}
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_ci/*
66062306a36Sopenharmony_ci * csio_hw_fw_dload - download firmware.
66162306a36Sopenharmony_ci * @hw: HW module
66262306a36Sopenharmony_ci * @fw_data: firmware image to write.
66362306a36Sopenharmony_ci * @size: image size
66462306a36Sopenharmony_ci *
66562306a36Sopenharmony_ci * Write the supplied firmware image to the card's serial flash.
66662306a36Sopenharmony_ci */
66762306a36Sopenharmony_cistatic int
66862306a36Sopenharmony_cicsio_hw_fw_dload(struct csio_hw *hw, uint8_t *fw_data, uint32_t size)
66962306a36Sopenharmony_ci{
67062306a36Sopenharmony_ci	uint32_t csum;
67162306a36Sopenharmony_ci	int32_t addr;
67262306a36Sopenharmony_ci	int ret;
67362306a36Sopenharmony_ci	uint32_t i;
67462306a36Sopenharmony_ci	uint8_t first_page[SF_PAGE_SIZE];
67562306a36Sopenharmony_ci	const __be32 *p = (const __be32 *)fw_data;
67662306a36Sopenharmony_ci	struct fw_hdr *hdr = (struct fw_hdr *)fw_data;
67762306a36Sopenharmony_ci	uint32_t sf_sec_size;
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci	if ((!hw->params.sf_size) || (!hw->params.sf_nsec)) {
68062306a36Sopenharmony_ci		csio_err(hw, "Serial Flash data invalid\n");
68162306a36Sopenharmony_ci		return -EINVAL;
68262306a36Sopenharmony_ci	}
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ci	if (!size) {
68562306a36Sopenharmony_ci		csio_err(hw, "FW image has no data\n");
68662306a36Sopenharmony_ci		return -EINVAL;
68762306a36Sopenharmony_ci	}
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	if (size & 511) {
69062306a36Sopenharmony_ci		csio_err(hw, "FW image size not multiple of 512 bytes\n");
69162306a36Sopenharmony_ci		return -EINVAL;
69262306a36Sopenharmony_ci	}
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci	if (ntohs(hdr->len512) * 512 != size) {
69562306a36Sopenharmony_ci		csio_err(hw, "FW image size differs from size in FW header\n");
69662306a36Sopenharmony_ci		return -EINVAL;
69762306a36Sopenharmony_ci	}
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci	if (size > FLASH_FW_MAX_SIZE) {
70062306a36Sopenharmony_ci		csio_err(hw, "FW image too large, max is %u bytes\n",
70162306a36Sopenharmony_ci			    FLASH_FW_MAX_SIZE);
70262306a36Sopenharmony_ci		return -EINVAL;
70362306a36Sopenharmony_ci	}
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_ci	for (csum = 0, i = 0; i < size / sizeof(csum); i++)
70662306a36Sopenharmony_ci		csum += ntohl(p[i]);
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci	if (csum != 0xffffffff) {
70962306a36Sopenharmony_ci		csio_err(hw, "corrupted firmware image, checksum %#x\n", csum);
71062306a36Sopenharmony_ci		return -EINVAL;
71162306a36Sopenharmony_ci	}
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_ci	sf_sec_size = hw->params.sf_size / hw->params.sf_nsec;
71462306a36Sopenharmony_ci	i = DIV_ROUND_UP(size, sf_sec_size);        /* # of sectors spanned */
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_ci	csio_dbg(hw, "Erasing sectors... start:%d end:%d\n",
71762306a36Sopenharmony_ci			  FLASH_FW_START_SEC, FLASH_FW_START_SEC + i - 1);
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci	ret = csio_hw_flash_erase_sectors(hw, FLASH_FW_START_SEC,
72062306a36Sopenharmony_ci					  FLASH_FW_START_SEC + i - 1);
72162306a36Sopenharmony_ci	if (ret) {
72262306a36Sopenharmony_ci		csio_err(hw, "Flash Erase failed\n");
72362306a36Sopenharmony_ci		goto out;
72462306a36Sopenharmony_ci	}
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci	/*
72762306a36Sopenharmony_ci	 * We write the correct version at the end so the driver can see a bad
72862306a36Sopenharmony_ci	 * version if the FW write fails.  Start by writing a copy of the
72962306a36Sopenharmony_ci	 * first page with a bad version.
73062306a36Sopenharmony_ci	 */
73162306a36Sopenharmony_ci	memcpy(first_page, fw_data, SF_PAGE_SIZE);
73262306a36Sopenharmony_ci	((struct fw_hdr *)first_page)->fw_ver = htonl(0xffffffff);
73362306a36Sopenharmony_ci	ret = csio_hw_write_flash(hw, FLASH_FW_START, SF_PAGE_SIZE, first_page);
73462306a36Sopenharmony_ci	if (ret)
73562306a36Sopenharmony_ci		goto out;
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci	csio_dbg(hw, "Writing Flash .. start:%d end:%d\n",
73862306a36Sopenharmony_ci		    FW_IMG_START, FW_IMG_START + size);
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci	addr = FLASH_FW_START;
74162306a36Sopenharmony_ci	for (size -= SF_PAGE_SIZE; size; size -= SF_PAGE_SIZE) {
74262306a36Sopenharmony_ci		addr += SF_PAGE_SIZE;
74362306a36Sopenharmony_ci		fw_data += SF_PAGE_SIZE;
74462306a36Sopenharmony_ci		ret = csio_hw_write_flash(hw, addr, SF_PAGE_SIZE, fw_data);
74562306a36Sopenharmony_ci		if (ret)
74662306a36Sopenharmony_ci			goto out;
74762306a36Sopenharmony_ci	}
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_ci	ret = csio_hw_write_flash(hw,
75062306a36Sopenharmony_ci				  FLASH_FW_START +
75162306a36Sopenharmony_ci					offsetof(struct fw_hdr, fw_ver),
75262306a36Sopenharmony_ci				  sizeof(hdr->fw_ver),
75362306a36Sopenharmony_ci				  (const uint8_t *)&hdr->fw_ver);
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ciout:
75662306a36Sopenharmony_ci	if (ret)
75762306a36Sopenharmony_ci		csio_err(hw, "firmware download failed, error %d\n", ret);
75862306a36Sopenharmony_ci	return ret;
75962306a36Sopenharmony_ci}
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_cistatic int
76262306a36Sopenharmony_cicsio_hw_get_flash_params(struct csio_hw *hw)
76362306a36Sopenharmony_ci{
76462306a36Sopenharmony_ci	/* Table for non-Numonix supported flash parts.  Numonix parts are left
76562306a36Sopenharmony_ci	 * to the preexisting code.  All flash parts have 64KB sectors.
76662306a36Sopenharmony_ci	 */
76762306a36Sopenharmony_ci	static struct flash_desc {
76862306a36Sopenharmony_ci		u32 vendor_and_model_id;
76962306a36Sopenharmony_ci		u32 size_mb;
77062306a36Sopenharmony_ci	} supported_flash[] = {
77162306a36Sopenharmony_ci		{ 0x150201, 4 << 20 },       /* Spansion 4MB S25FL032P */
77262306a36Sopenharmony_ci	};
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ci	u32 part, manufacturer;
77562306a36Sopenharmony_ci	u32 density, size = 0;
77662306a36Sopenharmony_ci	u32 flashid = 0;
77762306a36Sopenharmony_ci	int ret;
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci	ret = csio_hw_sf1_write(hw, 1, 1, 0, SF_RD_ID);
78062306a36Sopenharmony_ci	if (!ret)
78162306a36Sopenharmony_ci		ret = csio_hw_sf1_read(hw, 3, 0, 1, &flashid);
78262306a36Sopenharmony_ci	csio_wr_reg32(hw, 0, SF_OP_A);    /* unlock SF */
78362306a36Sopenharmony_ci	if (ret)
78462306a36Sopenharmony_ci		return ret;
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_ci	/* Check to see if it's one of our non-standard supported Flash parts.
78762306a36Sopenharmony_ci	 */
78862306a36Sopenharmony_ci	for (part = 0; part < ARRAY_SIZE(supported_flash); part++)
78962306a36Sopenharmony_ci		if (supported_flash[part].vendor_and_model_id == flashid) {
79062306a36Sopenharmony_ci			hw->params.sf_size = supported_flash[part].size_mb;
79162306a36Sopenharmony_ci			hw->params.sf_nsec =
79262306a36Sopenharmony_ci				hw->params.sf_size / SF_SEC_SIZE;
79362306a36Sopenharmony_ci			goto found;
79462306a36Sopenharmony_ci		}
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci	/* Decode Flash part size.  The code below looks repetitive with
79762306a36Sopenharmony_ci	 * common encodings, but that's not guaranteed in the JEDEC
79862306a36Sopenharmony_ci	 * specification for the Read JEDEC ID command.  The only thing that
79962306a36Sopenharmony_ci	 * we're guaranteed by the JEDEC specification is where the
80062306a36Sopenharmony_ci	 * Manufacturer ID is in the returned result.  After that each
80162306a36Sopenharmony_ci	 * Manufacturer ~could~ encode things completely differently.
80262306a36Sopenharmony_ci	 * Note, all Flash parts must have 64KB sectors.
80362306a36Sopenharmony_ci	 */
80462306a36Sopenharmony_ci	manufacturer = flashid & 0xff;
80562306a36Sopenharmony_ci	switch (manufacturer) {
80662306a36Sopenharmony_ci	case 0x20: { /* Micron/Numonix */
80762306a36Sopenharmony_ci		/* This Density -> Size decoding table is taken from Micron
80862306a36Sopenharmony_ci		 * Data Sheets.
80962306a36Sopenharmony_ci		 */
81062306a36Sopenharmony_ci		density = (flashid >> 16) & 0xff;
81162306a36Sopenharmony_ci		switch (density) {
81262306a36Sopenharmony_ci		case 0x14 ... 0x19: /* 1MB - 32MB */
81362306a36Sopenharmony_ci			size = 1 << density;
81462306a36Sopenharmony_ci			break;
81562306a36Sopenharmony_ci		case 0x20: /* 64MB */
81662306a36Sopenharmony_ci			size = 1 << 26;
81762306a36Sopenharmony_ci			break;
81862306a36Sopenharmony_ci		case 0x21: /* 128MB */
81962306a36Sopenharmony_ci			size = 1 << 27;
82062306a36Sopenharmony_ci			break;
82162306a36Sopenharmony_ci		case 0x22: /* 256MB */
82262306a36Sopenharmony_ci			size = 1 << 28;
82362306a36Sopenharmony_ci		}
82462306a36Sopenharmony_ci		break;
82562306a36Sopenharmony_ci	}
82662306a36Sopenharmony_ci	case 0x9d: { /* ISSI -- Integrated Silicon Solution, Inc. */
82762306a36Sopenharmony_ci		/* This Density -> Size decoding table is taken from ISSI
82862306a36Sopenharmony_ci		 * Data Sheets.
82962306a36Sopenharmony_ci		 */
83062306a36Sopenharmony_ci		density = (flashid >> 16) & 0xff;
83162306a36Sopenharmony_ci		switch (density) {
83262306a36Sopenharmony_ci		case 0x16: /* 32 MB */
83362306a36Sopenharmony_ci			size = 1 << 25;
83462306a36Sopenharmony_ci			break;
83562306a36Sopenharmony_ci		case 0x17: /* 64MB */
83662306a36Sopenharmony_ci			size = 1 << 26;
83762306a36Sopenharmony_ci		}
83862306a36Sopenharmony_ci		break;
83962306a36Sopenharmony_ci	}
84062306a36Sopenharmony_ci	case 0xc2: /* Macronix */
84162306a36Sopenharmony_ci	case 0xef: /* Winbond */ {
84262306a36Sopenharmony_ci		/* This Density -> Size decoding table is taken from
84362306a36Sopenharmony_ci		 * Macronix and Winbond Data Sheets.
84462306a36Sopenharmony_ci		 */
84562306a36Sopenharmony_ci		density = (flashid >> 16) & 0xff;
84662306a36Sopenharmony_ci		switch (density) {
84762306a36Sopenharmony_ci		case 0x17: /* 8MB */
84862306a36Sopenharmony_ci		case 0x18: /* 16MB */
84962306a36Sopenharmony_ci			size = 1 << density;
85062306a36Sopenharmony_ci		}
85162306a36Sopenharmony_ci	}
85262306a36Sopenharmony_ci	}
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ci	/* If we didn't recognize the FLASH part, that's no real issue: the
85562306a36Sopenharmony_ci	 * Hardware/Software contract says that Hardware will _*ALWAYS*_
85662306a36Sopenharmony_ci	 * use a FLASH part which is at least 4MB in size and has 64KB
85762306a36Sopenharmony_ci	 * sectors.  The unrecognized FLASH part is likely to be much larger
85862306a36Sopenharmony_ci	 * than 4MB, but that's all we really need.
85962306a36Sopenharmony_ci	 */
86062306a36Sopenharmony_ci	if (size == 0) {
86162306a36Sopenharmony_ci		csio_warn(hw, "Unknown Flash Part, ID = %#x, assuming 4MB\n",
86262306a36Sopenharmony_ci			  flashid);
86362306a36Sopenharmony_ci		size = 1 << 22;
86462306a36Sopenharmony_ci	}
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_ci	/* Store decoded Flash size */
86762306a36Sopenharmony_ci	hw->params.sf_size = size;
86862306a36Sopenharmony_ci	hw->params.sf_nsec = size / SF_SEC_SIZE;
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_cifound:
87162306a36Sopenharmony_ci	if (hw->params.sf_size < FLASH_MIN_SIZE)
87262306a36Sopenharmony_ci		csio_warn(hw, "WARNING: Flash Part ID %#x, size %#x < %#x\n",
87362306a36Sopenharmony_ci			  flashid, hw->params.sf_size, FLASH_MIN_SIZE);
87462306a36Sopenharmony_ci	return 0;
87562306a36Sopenharmony_ci}
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_ci/*****************************************************************************/
87862306a36Sopenharmony_ci/* HW State machine assists                                                  */
87962306a36Sopenharmony_ci/*****************************************************************************/
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_cistatic int
88262306a36Sopenharmony_cicsio_hw_dev_ready(struct csio_hw *hw)
88362306a36Sopenharmony_ci{
88462306a36Sopenharmony_ci	uint32_t reg;
88562306a36Sopenharmony_ci	int cnt = 6;
88662306a36Sopenharmony_ci	int src_pf;
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_ci	while (((reg = csio_rd_reg32(hw, PL_WHOAMI_A)) == 0xFFFFFFFF) &&
88962306a36Sopenharmony_ci	       (--cnt != 0))
89062306a36Sopenharmony_ci		mdelay(100);
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_ci	if (csio_is_t5(hw->pdev->device & CSIO_HW_CHIP_MASK))
89362306a36Sopenharmony_ci		src_pf = SOURCEPF_G(reg);
89462306a36Sopenharmony_ci	else
89562306a36Sopenharmony_ci		src_pf = T6_SOURCEPF_G(reg);
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci	if ((cnt == 0) && (((int32_t)(src_pf) < 0) ||
89862306a36Sopenharmony_ci			   (src_pf >= CSIO_MAX_PFN))) {
89962306a36Sopenharmony_ci		csio_err(hw, "PL_WHOAMI returned 0x%x, cnt:%d\n", reg, cnt);
90062306a36Sopenharmony_ci		return -EIO;
90162306a36Sopenharmony_ci	}
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_ci	hw->pfn = src_pf;
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ci	return 0;
90662306a36Sopenharmony_ci}
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_ci/*
90962306a36Sopenharmony_ci * csio_do_hello - Perform the HELLO FW Mailbox command and process response.
91062306a36Sopenharmony_ci * @hw: HW module
91162306a36Sopenharmony_ci * @state: Device state
91262306a36Sopenharmony_ci *
91362306a36Sopenharmony_ci * FW_HELLO_CMD has to be polled for completion.
91462306a36Sopenharmony_ci */
91562306a36Sopenharmony_cistatic int
91662306a36Sopenharmony_cicsio_do_hello(struct csio_hw *hw, enum csio_dev_state *state)
91762306a36Sopenharmony_ci{
91862306a36Sopenharmony_ci	struct csio_mb	*mbp;
91962306a36Sopenharmony_ci	int	rv = 0;
92062306a36Sopenharmony_ci	enum fw_retval retval;
92162306a36Sopenharmony_ci	uint8_t mpfn;
92262306a36Sopenharmony_ci	char state_str[16];
92362306a36Sopenharmony_ci	int retries = FW_CMD_HELLO_RETRIES;
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_ci	memset(state_str, 0, sizeof(state_str));
92662306a36Sopenharmony_ci
92762306a36Sopenharmony_ci	mbp = mempool_alloc(hw->mb_mempool, GFP_ATOMIC);
92862306a36Sopenharmony_ci	if (!mbp) {
92962306a36Sopenharmony_ci		rv = -ENOMEM;
93062306a36Sopenharmony_ci		CSIO_INC_STATS(hw, n_err_nomem);
93162306a36Sopenharmony_ci		goto out;
93262306a36Sopenharmony_ci	}
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_ciretry:
93562306a36Sopenharmony_ci	csio_mb_hello(hw, mbp, CSIO_MB_DEFAULT_TMO, hw->pfn,
93662306a36Sopenharmony_ci		      hw->pfn, CSIO_MASTER_MAY, NULL);
93762306a36Sopenharmony_ci
93862306a36Sopenharmony_ci	rv = csio_mb_issue(hw, mbp);
93962306a36Sopenharmony_ci	if (rv) {
94062306a36Sopenharmony_ci		csio_err(hw, "failed to issue HELLO cmd. ret:%d.\n", rv);
94162306a36Sopenharmony_ci		goto out_free_mb;
94262306a36Sopenharmony_ci	}
94362306a36Sopenharmony_ci
94462306a36Sopenharmony_ci	csio_mb_process_hello_rsp(hw, mbp, &retval, state, &mpfn);
94562306a36Sopenharmony_ci	if (retval != FW_SUCCESS) {
94662306a36Sopenharmony_ci		csio_err(hw, "HELLO cmd failed with ret: %d\n", retval);
94762306a36Sopenharmony_ci		rv = -EINVAL;
94862306a36Sopenharmony_ci		goto out_free_mb;
94962306a36Sopenharmony_ci	}
95062306a36Sopenharmony_ci
95162306a36Sopenharmony_ci	/* Firmware has designated us to be master */
95262306a36Sopenharmony_ci	if (hw->pfn == mpfn) {
95362306a36Sopenharmony_ci		hw->flags |= CSIO_HWF_MASTER;
95462306a36Sopenharmony_ci	} else if (*state == CSIO_DEV_STATE_UNINIT) {
95562306a36Sopenharmony_ci		/*
95662306a36Sopenharmony_ci		 * If we're not the Master PF then we need to wait around for
95762306a36Sopenharmony_ci		 * the Master PF Driver to finish setting up the adapter.
95862306a36Sopenharmony_ci		 *
95962306a36Sopenharmony_ci		 * Note that we also do this wait if we're a non-Master-capable
96062306a36Sopenharmony_ci		 * PF and there is no current Master PF; a Master PF may show up
96162306a36Sopenharmony_ci		 * momentarily and we wouldn't want to fail pointlessly.  (This
96262306a36Sopenharmony_ci		 * can happen when an OS loads lots of different drivers rapidly
96362306a36Sopenharmony_ci		 * at the same time). In this case, the Master PF returned by
96462306a36Sopenharmony_ci		 * the firmware will be PCIE_FW_MASTER_MASK so the test below
96562306a36Sopenharmony_ci		 * will work ...
96662306a36Sopenharmony_ci		 */
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_ci		int waiting = FW_CMD_HELLO_TIMEOUT;
96962306a36Sopenharmony_ci
97062306a36Sopenharmony_ci		/*
97162306a36Sopenharmony_ci		 * Wait for the firmware to either indicate an error or
97262306a36Sopenharmony_ci		 * initialized state.  If we see either of these we bail out
97362306a36Sopenharmony_ci		 * and report the issue to the caller.  If we exhaust the
97462306a36Sopenharmony_ci		 * "hello timeout" and we haven't exhausted our retries, try
97562306a36Sopenharmony_ci		 * again.  Otherwise bail with a timeout error.
97662306a36Sopenharmony_ci		 */
97762306a36Sopenharmony_ci		for (;;) {
97862306a36Sopenharmony_ci			uint32_t pcie_fw;
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_ci			spin_unlock_irq(&hw->lock);
98162306a36Sopenharmony_ci			msleep(50);
98262306a36Sopenharmony_ci			spin_lock_irq(&hw->lock);
98362306a36Sopenharmony_ci			waiting -= 50;
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_ci			/*
98662306a36Sopenharmony_ci			 * If neither Error nor Initialized are indicated
98762306a36Sopenharmony_ci			 * by the firmware keep waiting till we exhaust our
98862306a36Sopenharmony_ci			 * timeout ... and then retry if we haven't exhausted
98962306a36Sopenharmony_ci			 * our retries ...
99062306a36Sopenharmony_ci			 */
99162306a36Sopenharmony_ci			pcie_fw = csio_rd_reg32(hw, PCIE_FW_A);
99262306a36Sopenharmony_ci			if (!(pcie_fw & (PCIE_FW_ERR_F|PCIE_FW_INIT_F))) {
99362306a36Sopenharmony_ci				if (waiting <= 0) {
99462306a36Sopenharmony_ci					if (retries-- > 0)
99562306a36Sopenharmony_ci						goto retry;
99662306a36Sopenharmony_ci
99762306a36Sopenharmony_ci					rv = -ETIMEDOUT;
99862306a36Sopenharmony_ci					break;
99962306a36Sopenharmony_ci				}
100062306a36Sopenharmony_ci				continue;
100162306a36Sopenharmony_ci			}
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_ci			/*
100462306a36Sopenharmony_ci			 * We either have an Error or Initialized condition
100562306a36Sopenharmony_ci			 * report errors preferentially.
100662306a36Sopenharmony_ci			 */
100762306a36Sopenharmony_ci			if (state) {
100862306a36Sopenharmony_ci				if (pcie_fw & PCIE_FW_ERR_F) {
100962306a36Sopenharmony_ci					*state = CSIO_DEV_STATE_ERR;
101062306a36Sopenharmony_ci					rv = -ETIMEDOUT;
101162306a36Sopenharmony_ci				} else if (pcie_fw & PCIE_FW_INIT_F)
101262306a36Sopenharmony_ci					*state = CSIO_DEV_STATE_INIT;
101362306a36Sopenharmony_ci			}
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_ci			/*
101662306a36Sopenharmony_ci			 * If we arrived before a Master PF was selected and
101762306a36Sopenharmony_ci			 * there's not a valid Master PF, grab its identity
101862306a36Sopenharmony_ci			 * for our caller.
101962306a36Sopenharmony_ci			 */
102062306a36Sopenharmony_ci			if (mpfn == PCIE_FW_MASTER_M &&
102162306a36Sopenharmony_ci			    (pcie_fw & PCIE_FW_MASTER_VLD_F))
102262306a36Sopenharmony_ci				mpfn = PCIE_FW_MASTER_G(pcie_fw);
102362306a36Sopenharmony_ci			break;
102462306a36Sopenharmony_ci		}
102562306a36Sopenharmony_ci		hw->flags &= ~CSIO_HWF_MASTER;
102662306a36Sopenharmony_ci	}
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_ci	switch (*state) {
102962306a36Sopenharmony_ci	case CSIO_DEV_STATE_UNINIT:
103062306a36Sopenharmony_ci		strcpy(state_str, "Initializing");
103162306a36Sopenharmony_ci		break;
103262306a36Sopenharmony_ci	case CSIO_DEV_STATE_INIT:
103362306a36Sopenharmony_ci		strcpy(state_str, "Initialized");
103462306a36Sopenharmony_ci		break;
103562306a36Sopenharmony_ci	case CSIO_DEV_STATE_ERR:
103662306a36Sopenharmony_ci		strcpy(state_str, "Error");
103762306a36Sopenharmony_ci		break;
103862306a36Sopenharmony_ci	default:
103962306a36Sopenharmony_ci		strcpy(state_str, "Unknown");
104062306a36Sopenharmony_ci		break;
104162306a36Sopenharmony_ci	}
104262306a36Sopenharmony_ci
104362306a36Sopenharmony_ci	if (hw->pfn == mpfn)
104462306a36Sopenharmony_ci		csio_info(hw, "PF: %d, Coming up as MASTER, HW state: %s\n",
104562306a36Sopenharmony_ci			hw->pfn, state_str);
104662306a36Sopenharmony_ci	else
104762306a36Sopenharmony_ci		csio_info(hw,
104862306a36Sopenharmony_ci		    "PF: %d, Coming up as SLAVE, Master PF: %d, HW state: %s\n",
104962306a36Sopenharmony_ci		    hw->pfn, mpfn, state_str);
105062306a36Sopenharmony_ci
105162306a36Sopenharmony_ciout_free_mb:
105262306a36Sopenharmony_ci	mempool_free(mbp, hw->mb_mempool);
105362306a36Sopenharmony_ciout:
105462306a36Sopenharmony_ci	return rv;
105562306a36Sopenharmony_ci}
105662306a36Sopenharmony_ci
105762306a36Sopenharmony_ci/*
105862306a36Sopenharmony_ci * csio_do_bye - Perform the BYE FW Mailbox command and process response.
105962306a36Sopenharmony_ci * @hw: HW module
106062306a36Sopenharmony_ci *
106162306a36Sopenharmony_ci */
106262306a36Sopenharmony_cistatic int
106362306a36Sopenharmony_cicsio_do_bye(struct csio_hw *hw)
106462306a36Sopenharmony_ci{
106562306a36Sopenharmony_ci	struct csio_mb	*mbp;
106662306a36Sopenharmony_ci	enum fw_retval retval;
106762306a36Sopenharmony_ci
106862306a36Sopenharmony_ci	mbp = mempool_alloc(hw->mb_mempool, GFP_ATOMIC);
106962306a36Sopenharmony_ci	if (!mbp) {
107062306a36Sopenharmony_ci		CSIO_INC_STATS(hw, n_err_nomem);
107162306a36Sopenharmony_ci		return -ENOMEM;
107262306a36Sopenharmony_ci	}
107362306a36Sopenharmony_ci
107462306a36Sopenharmony_ci	csio_mb_bye(hw, mbp, CSIO_MB_DEFAULT_TMO, NULL);
107562306a36Sopenharmony_ci
107662306a36Sopenharmony_ci	if (csio_mb_issue(hw, mbp)) {
107762306a36Sopenharmony_ci		csio_err(hw, "Issue of BYE command failed\n");
107862306a36Sopenharmony_ci		mempool_free(mbp, hw->mb_mempool);
107962306a36Sopenharmony_ci		return -EINVAL;
108062306a36Sopenharmony_ci	}
108162306a36Sopenharmony_ci
108262306a36Sopenharmony_ci	retval = csio_mb_fw_retval(mbp);
108362306a36Sopenharmony_ci	if (retval != FW_SUCCESS) {
108462306a36Sopenharmony_ci		mempool_free(mbp, hw->mb_mempool);
108562306a36Sopenharmony_ci		return -EINVAL;
108662306a36Sopenharmony_ci	}
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_ci	mempool_free(mbp, hw->mb_mempool);
108962306a36Sopenharmony_ci
109062306a36Sopenharmony_ci	return 0;
109162306a36Sopenharmony_ci}
109262306a36Sopenharmony_ci
109362306a36Sopenharmony_ci/*
109462306a36Sopenharmony_ci * csio_do_reset- Perform the device reset.
109562306a36Sopenharmony_ci * @hw: HW module
109662306a36Sopenharmony_ci * @fw_rst: FW reset
109762306a36Sopenharmony_ci *
109862306a36Sopenharmony_ci * If fw_rst is set, issues FW reset mbox cmd otherwise
109962306a36Sopenharmony_ci * does PIO reset.
110062306a36Sopenharmony_ci * Performs reset of the function.
110162306a36Sopenharmony_ci */
110262306a36Sopenharmony_cistatic int
110362306a36Sopenharmony_cicsio_do_reset(struct csio_hw *hw, bool fw_rst)
110462306a36Sopenharmony_ci{
110562306a36Sopenharmony_ci	struct csio_mb	*mbp;
110662306a36Sopenharmony_ci	enum fw_retval retval;
110762306a36Sopenharmony_ci
110862306a36Sopenharmony_ci	if (!fw_rst) {
110962306a36Sopenharmony_ci		/* PIO reset */
111062306a36Sopenharmony_ci		csio_wr_reg32(hw, PIORSTMODE_F | PIORST_F, PL_RST_A);
111162306a36Sopenharmony_ci		mdelay(2000);
111262306a36Sopenharmony_ci		return 0;
111362306a36Sopenharmony_ci	}
111462306a36Sopenharmony_ci
111562306a36Sopenharmony_ci	mbp = mempool_alloc(hw->mb_mempool, GFP_ATOMIC);
111662306a36Sopenharmony_ci	if (!mbp) {
111762306a36Sopenharmony_ci		CSIO_INC_STATS(hw, n_err_nomem);
111862306a36Sopenharmony_ci		return -ENOMEM;
111962306a36Sopenharmony_ci	}
112062306a36Sopenharmony_ci
112162306a36Sopenharmony_ci	csio_mb_reset(hw, mbp, CSIO_MB_DEFAULT_TMO,
112262306a36Sopenharmony_ci		      PIORSTMODE_F | PIORST_F, 0, NULL);
112362306a36Sopenharmony_ci
112462306a36Sopenharmony_ci	if (csio_mb_issue(hw, mbp)) {
112562306a36Sopenharmony_ci		csio_err(hw, "Issue of RESET command failed.n");
112662306a36Sopenharmony_ci		mempool_free(mbp, hw->mb_mempool);
112762306a36Sopenharmony_ci		return -EINVAL;
112862306a36Sopenharmony_ci	}
112962306a36Sopenharmony_ci
113062306a36Sopenharmony_ci	retval = csio_mb_fw_retval(mbp);
113162306a36Sopenharmony_ci	if (retval != FW_SUCCESS) {
113262306a36Sopenharmony_ci		csio_err(hw, "RESET cmd failed with ret:0x%x.\n", retval);
113362306a36Sopenharmony_ci		mempool_free(mbp, hw->mb_mempool);
113462306a36Sopenharmony_ci		return -EINVAL;
113562306a36Sopenharmony_ci	}
113662306a36Sopenharmony_ci
113762306a36Sopenharmony_ci	mempool_free(mbp, hw->mb_mempool);
113862306a36Sopenharmony_ci
113962306a36Sopenharmony_ci	return 0;
114062306a36Sopenharmony_ci}
114162306a36Sopenharmony_ci
114262306a36Sopenharmony_cistatic int
114362306a36Sopenharmony_cicsio_hw_validate_caps(struct csio_hw *hw, struct csio_mb *mbp)
114462306a36Sopenharmony_ci{
114562306a36Sopenharmony_ci	struct fw_caps_config_cmd *rsp = (struct fw_caps_config_cmd *)mbp->mb;
114662306a36Sopenharmony_ci	uint16_t caps;
114762306a36Sopenharmony_ci
114862306a36Sopenharmony_ci	caps = ntohs(rsp->fcoecaps);
114962306a36Sopenharmony_ci
115062306a36Sopenharmony_ci	if (!(caps & FW_CAPS_CONFIG_FCOE_INITIATOR)) {
115162306a36Sopenharmony_ci		csio_err(hw, "No FCoE Initiator capability in the firmware.\n");
115262306a36Sopenharmony_ci		return -EINVAL;
115362306a36Sopenharmony_ci	}
115462306a36Sopenharmony_ci
115562306a36Sopenharmony_ci	if (!(caps & FW_CAPS_CONFIG_FCOE_CTRL_OFLD)) {
115662306a36Sopenharmony_ci		csio_err(hw, "No FCoE Control Offload capability\n");
115762306a36Sopenharmony_ci		return -EINVAL;
115862306a36Sopenharmony_ci	}
115962306a36Sopenharmony_ci
116062306a36Sopenharmony_ci	return 0;
116162306a36Sopenharmony_ci}
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_ci/*
116462306a36Sopenharmony_ci *	csio_hw_fw_halt - issue a reset/halt to FW and put uP into RESET
116562306a36Sopenharmony_ci *	@hw: the HW module
116662306a36Sopenharmony_ci *	@mbox: mailbox to use for the FW RESET command (if desired)
116762306a36Sopenharmony_ci *	@force: force uP into RESET even if FW RESET command fails
116862306a36Sopenharmony_ci *
116962306a36Sopenharmony_ci *	Issues a RESET command to firmware (if desired) with a HALT indication
117062306a36Sopenharmony_ci *	and then puts the microprocessor into RESET state.  The RESET command
117162306a36Sopenharmony_ci *	will only be issued if a legitimate mailbox is provided (mbox <=
117262306a36Sopenharmony_ci *	PCIE_FW_MASTER_MASK).
117362306a36Sopenharmony_ci *
117462306a36Sopenharmony_ci *	This is generally used in order for the host to safely manipulate the
117562306a36Sopenharmony_ci *	adapter without fear of conflicting with whatever the firmware might
117662306a36Sopenharmony_ci *	be doing.  The only way out of this state is to RESTART the firmware
117762306a36Sopenharmony_ci *	...
117862306a36Sopenharmony_ci */
117962306a36Sopenharmony_cistatic int
118062306a36Sopenharmony_cicsio_hw_fw_halt(struct csio_hw *hw, uint32_t mbox, int32_t force)
118162306a36Sopenharmony_ci{
118262306a36Sopenharmony_ci	enum fw_retval retval = 0;
118362306a36Sopenharmony_ci
118462306a36Sopenharmony_ci	/*
118562306a36Sopenharmony_ci	 * If a legitimate mailbox is provided, issue a RESET command
118662306a36Sopenharmony_ci	 * with a HALT indication.
118762306a36Sopenharmony_ci	 */
118862306a36Sopenharmony_ci	if (mbox <= PCIE_FW_MASTER_M) {
118962306a36Sopenharmony_ci		struct csio_mb	*mbp;
119062306a36Sopenharmony_ci
119162306a36Sopenharmony_ci		mbp = mempool_alloc(hw->mb_mempool, GFP_ATOMIC);
119262306a36Sopenharmony_ci		if (!mbp) {
119362306a36Sopenharmony_ci			CSIO_INC_STATS(hw, n_err_nomem);
119462306a36Sopenharmony_ci			return -ENOMEM;
119562306a36Sopenharmony_ci		}
119662306a36Sopenharmony_ci
119762306a36Sopenharmony_ci		csio_mb_reset(hw, mbp, CSIO_MB_DEFAULT_TMO,
119862306a36Sopenharmony_ci			      PIORSTMODE_F | PIORST_F, FW_RESET_CMD_HALT_F,
119962306a36Sopenharmony_ci			      NULL);
120062306a36Sopenharmony_ci
120162306a36Sopenharmony_ci		if (csio_mb_issue(hw, mbp)) {
120262306a36Sopenharmony_ci			csio_err(hw, "Issue of RESET command failed!\n");
120362306a36Sopenharmony_ci			mempool_free(mbp, hw->mb_mempool);
120462306a36Sopenharmony_ci			return -EINVAL;
120562306a36Sopenharmony_ci		}
120662306a36Sopenharmony_ci
120762306a36Sopenharmony_ci		retval = csio_mb_fw_retval(mbp);
120862306a36Sopenharmony_ci		mempool_free(mbp, hw->mb_mempool);
120962306a36Sopenharmony_ci	}
121062306a36Sopenharmony_ci
121162306a36Sopenharmony_ci	/*
121262306a36Sopenharmony_ci	 * Normally we won't complete the operation if the firmware RESET
121362306a36Sopenharmony_ci	 * command fails but if our caller insists we'll go ahead and put the
121462306a36Sopenharmony_ci	 * uP into RESET.  This can be useful if the firmware is hung or even
121562306a36Sopenharmony_ci	 * missing ...  We'll have to take the risk of putting the uP into
121662306a36Sopenharmony_ci	 * RESET without the cooperation of firmware in that case.
121762306a36Sopenharmony_ci	 *
121862306a36Sopenharmony_ci	 * We also force the firmware's HALT flag to be on in case we bypassed
121962306a36Sopenharmony_ci	 * the firmware RESET command above or we're dealing with old firmware
122062306a36Sopenharmony_ci	 * which doesn't have the HALT capability.  This will serve as a flag
122162306a36Sopenharmony_ci	 * for the incoming firmware to know that it's coming out of a HALT
122262306a36Sopenharmony_ci	 * rather than a RESET ... if it's new enough to understand that ...
122362306a36Sopenharmony_ci	 */
122462306a36Sopenharmony_ci	if (retval == 0 || force) {
122562306a36Sopenharmony_ci		csio_set_reg_field(hw, CIM_BOOT_CFG_A, UPCRST_F, UPCRST_F);
122662306a36Sopenharmony_ci		csio_set_reg_field(hw, PCIE_FW_A, PCIE_FW_HALT_F,
122762306a36Sopenharmony_ci				   PCIE_FW_HALT_F);
122862306a36Sopenharmony_ci	}
122962306a36Sopenharmony_ci
123062306a36Sopenharmony_ci	/*
123162306a36Sopenharmony_ci	 * And we always return the result of the firmware RESET command
123262306a36Sopenharmony_ci	 * even when we force the uP into RESET ...
123362306a36Sopenharmony_ci	 */
123462306a36Sopenharmony_ci	return retval ? -EINVAL : 0;
123562306a36Sopenharmony_ci}
123662306a36Sopenharmony_ci
123762306a36Sopenharmony_ci/*
123862306a36Sopenharmony_ci *	csio_hw_fw_restart - restart the firmware by taking the uP out of RESET
123962306a36Sopenharmony_ci *	@hw: the HW module
124062306a36Sopenharmony_ci *	@reset: if we want to do a RESET to restart things
124162306a36Sopenharmony_ci *
124262306a36Sopenharmony_ci *	Restart firmware previously halted by csio_hw_fw_halt().  On successful
124362306a36Sopenharmony_ci *	return the previous PF Master remains as the new PF Master and there
124462306a36Sopenharmony_ci *	is no need to issue a new HELLO command, etc.
124562306a36Sopenharmony_ci *
124662306a36Sopenharmony_ci *	We do this in two ways:
124762306a36Sopenharmony_ci *
124862306a36Sopenharmony_ci *	 1. If we're dealing with newer firmware we'll simply want to take
124962306a36Sopenharmony_ci *	    the chip's microprocessor out of RESET.  This will cause the
125062306a36Sopenharmony_ci *	    firmware to start up from its start vector.  And then we'll loop
125162306a36Sopenharmony_ci *	    until the firmware indicates it's started again (PCIE_FW.HALT
125262306a36Sopenharmony_ci *	    reset to 0) or we timeout.
125362306a36Sopenharmony_ci *
125462306a36Sopenharmony_ci *	 2. If we're dealing with older firmware then we'll need to RESET
125562306a36Sopenharmony_ci *	    the chip since older firmware won't recognize the PCIE_FW.HALT
125662306a36Sopenharmony_ci *	    flag and automatically RESET itself on startup.
125762306a36Sopenharmony_ci */
125862306a36Sopenharmony_cistatic int
125962306a36Sopenharmony_cicsio_hw_fw_restart(struct csio_hw *hw, uint32_t mbox, int32_t reset)
126062306a36Sopenharmony_ci{
126162306a36Sopenharmony_ci	if (reset) {
126262306a36Sopenharmony_ci		/*
126362306a36Sopenharmony_ci		 * Since we're directing the RESET instead of the firmware
126462306a36Sopenharmony_ci		 * doing it automatically, we need to clear the PCIE_FW.HALT
126562306a36Sopenharmony_ci		 * bit.
126662306a36Sopenharmony_ci		 */
126762306a36Sopenharmony_ci		csio_set_reg_field(hw, PCIE_FW_A, PCIE_FW_HALT_F, 0);
126862306a36Sopenharmony_ci
126962306a36Sopenharmony_ci		/*
127062306a36Sopenharmony_ci		 * If we've been given a valid mailbox, first try to get the
127162306a36Sopenharmony_ci		 * firmware to do the RESET.  If that works, great and we can
127262306a36Sopenharmony_ci		 * return success.  Otherwise, if we haven't been given a
127362306a36Sopenharmony_ci		 * valid mailbox or the RESET command failed, fall back to
127462306a36Sopenharmony_ci		 * hitting the chip with a hammer.
127562306a36Sopenharmony_ci		 */
127662306a36Sopenharmony_ci		if (mbox <= PCIE_FW_MASTER_M) {
127762306a36Sopenharmony_ci			csio_set_reg_field(hw, CIM_BOOT_CFG_A, UPCRST_F, 0);
127862306a36Sopenharmony_ci			msleep(100);
127962306a36Sopenharmony_ci			if (csio_do_reset(hw, true) == 0)
128062306a36Sopenharmony_ci				return 0;
128162306a36Sopenharmony_ci		}
128262306a36Sopenharmony_ci
128362306a36Sopenharmony_ci		csio_wr_reg32(hw, PIORSTMODE_F | PIORST_F, PL_RST_A);
128462306a36Sopenharmony_ci		msleep(2000);
128562306a36Sopenharmony_ci	} else {
128662306a36Sopenharmony_ci		int ms;
128762306a36Sopenharmony_ci
128862306a36Sopenharmony_ci		csio_set_reg_field(hw, CIM_BOOT_CFG_A, UPCRST_F, 0);
128962306a36Sopenharmony_ci		for (ms = 0; ms < FW_CMD_MAX_TIMEOUT; ) {
129062306a36Sopenharmony_ci			if (!(csio_rd_reg32(hw, PCIE_FW_A) & PCIE_FW_HALT_F))
129162306a36Sopenharmony_ci				return 0;
129262306a36Sopenharmony_ci			msleep(100);
129362306a36Sopenharmony_ci			ms += 100;
129462306a36Sopenharmony_ci		}
129562306a36Sopenharmony_ci		return -ETIMEDOUT;
129662306a36Sopenharmony_ci	}
129762306a36Sopenharmony_ci	return 0;
129862306a36Sopenharmony_ci}
129962306a36Sopenharmony_ci
130062306a36Sopenharmony_ci/*
130162306a36Sopenharmony_ci *	csio_hw_fw_upgrade - perform all of the steps necessary to upgrade FW
130262306a36Sopenharmony_ci *	@hw: the HW module
130362306a36Sopenharmony_ci *	@mbox: mailbox to use for the FW RESET command (if desired)
130462306a36Sopenharmony_ci *	@fw_data: the firmware image to write
130562306a36Sopenharmony_ci *	@size: image size
130662306a36Sopenharmony_ci *	@force: force upgrade even if firmware doesn't cooperate
130762306a36Sopenharmony_ci *
130862306a36Sopenharmony_ci *	Perform all of the steps necessary for upgrading an adapter's
130962306a36Sopenharmony_ci *	firmware image.  Normally this requires the cooperation of the
131062306a36Sopenharmony_ci *	existing firmware in order to halt all existing activities
131162306a36Sopenharmony_ci *	but if an invalid mailbox token is passed in we skip that step
131262306a36Sopenharmony_ci *	(though we'll still put the adapter microprocessor into RESET in
131362306a36Sopenharmony_ci *	that case).
131462306a36Sopenharmony_ci *
131562306a36Sopenharmony_ci *	On successful return the new firmware will have been loaded and
131662306a36Sopenharmony_ci *	the adapter will have been fully RESET losing all previous setup
131762306a36Sopenharmony_ci *	state.  On unsuccessful return the adapter may be completely hosed ...
131862306a36Sopenharmony_ci *	positive errno indicates that the adapter is ~probably~ intact, a
131962306a36Sopenharmony_ci *	negative errno indicates that things are looking bad ...
132062306a36Sopenharmony_ci */
132162306a36Sopenharmony_cistatic int
132262306a36Sopenharmony_cicsio_hw_fw_upgrade(struct csio_hw *hw, uint32_t mbox,
132362306a36Sopenharmony_ci		  const u8 *fw_data, uint32_t size, int32_t force)
132462306a36Sopenharmony_ci{
132562306a36Sopenharmony_ci	const struct fw_hdr *fw_hdr = (const struct fw_hdr *)fw_data;
132662306a36Sopenharmony_ci	int reset, ret;
132762306a36Sopenharmony_ci
132862306a36Sopenharmony_ci	ret = csio_hw_fw_halt(hw, mbox, force);
132962306a36Sopenharmony_ci	if (ret != 0 && !force)
133062306a36Sopenharmony_ci		return ret;
133162306a36Sopenharmony_ci
133262306a36Sopenharmony_ci	ret = csio_hw_fw_dload(hw, (uint8_t *) fw_data, size);
133362306a36Sopenharmony_ci	if (ret != 0)
133462306a36Sopenharmony_ci		return ret;
133562306a36Sopenharmony_ci
133662306a36Sopenharmony_ci	/*
133762306a36Sopenharmony_ci	 * Older versions of the firmware don't understand the new
133862306a36Sopenharmony_ci	 * PCIE_FW.HALT flag and so won't know to perform a RESET when they
133962306a36Sopenharmony_ci	 * restart.  So for newly loaded older firmware we'll have to do the
134062306a36Sopenharmony_ci	 * RESET for it so it starts up on a clean slate.  We can tell if
134162306a36Sopenharmony_ci	 * the newly loaded firmware will handle this right by checking
134262306a36Sopenharmony_ci	 * its header flags to see if it advertises the capability.
134362306a36Sopenharmony_ci	 */
134462306a36Sopenharmony_ci	reset = ((ntohl(fw_hdr->flags) & FW_HDR_FLAGS_RESET_HALT) == 0);
134562306a36Sopenharmony_ci	return csio_hw_fw_restart(hw, mbox, reset);
134662306a36Sopenharmony_ci}
134762306a36Sopenharmony_ci
134862306a36Sopenharmony_ci/*
134962306a36Sopenharmony_ci * csio_get_device_params - Get device parameters.
135062306a36Sopenharmony_ci * @hw: HW module
135162306a36Sopenharmony_ci *
135262306a36Sopenharmony_ci */
135362306a36Sopenharmony_cistatic int
135462306a36Sopenharmony_cicsio_get_device_params(struct csio_hw *hw)
135562306a36Sopenharmony_ci{
135662306a36Sopenharmony_ci	struct csio_wrm *wrm	= csio_hw_to_wrm(hw);
135762306a36Sopenharmony_ci	struct csio_mb	*mbp;
135862306a36Sopenharmony_ci	enum fw_retval retval;
135962306a36Sopenharmony_ci	u32 param[6];
136062306a36Sopenharmony_ci	int i, j = 0;
136162306a36Sopenharmony_ci
136262306a36Sopenharmony_ci	/* Initialize portids to -1 */
136362306a36Sopenharmony_ci	for (i = 0; i < CSIO_MAX_PPORTS; i++)
136462306a36Sopenharmony_ci		hw->pport[i].portid = -1;
136562306a36Sopenharmony_ci
136662306a36Sopenharmony_ci	mbp = mempool_alloc(hw->mb_mempool, GFP_ATOMIC);
136762306a36Sopenharmony_ci	if (!mbp) {
136862306a36Sopenharmony_ci		CSIO_INC_STATS(hw, n_err_nomem);
136962306a36Sopenharmony_ci		return -ENOMEM;
137062306a36Sopenharmony_ci	}
137162306a36Sopenharmony_ci
137262306a36Sopenharmony_ci	/* Get port vec information. */
137362306a36Sopenharmony_ci	param[0] = FW_PARAM_DEV(PORTVEC);
137462306a36Sopenharmony_ci
137562306a36Sopenharmony_ci	/* Get Core clock. */
137662306a36Sopenharmony_ci	param[1] = FW_PARAM_DEV(CCLK);
137762306a36Sopenharmony_ci
137862306a36Sopenharmony_ci	/* Get EQ id start and end. */
137962306a36Sopenharmony_ci	param[2] = FW_PARAM_PFVF(EQ_START);
138062306a36Sopenharmony_ci	param[3] = FW_PARAM_PFVF(EQ_END);
138162306a36Sopenharmony_ci
138262306a36Sopenharmony_ci	/* Get IQ id start and end. */
138362306a36Sopenharmony_ci	param[4] = FW_PARAM_PFVF(IQFLINT_START);
138462306a36Sopenharmony_ci	param[5] = FW_PARAM_PFVF(IQFLINT_END);
138562306a36Sopenharmony_ci
138662306a36Sopenharmony_ci	csio_mb_params(hw, mbp, CSIO_MB_DEFAULT_TMO, hw->pfn, 0,
138762306a36Sopenharmony_ci		       ARRAY_SIZE(param), param, NULL, false, NULL);
138862306a36Sopenharmony_ci	if (csio_mb_issue(hw, mbp)) {
138962306a36Sopenharmony_ci		csio_err(hw, "Issue of FW_PARAMS_CMD(read) failed!\n");
139062306a36Sopenharmony_ci		mempool_free(mbp, hw->mb_mempool);
139162306a36Sopenharmony_ci		return -EINVAL;
139262306a36Sopenharmony_ci	}
139362306a36Sopenharmony_ci
139462306a36Sopenharmony_ci	csio_mb_process_read_params_rsp(hw, mbp, &retval,
139562306a36Sopenharmony_ci			ARRAY_SIZE(param), param);
139662306a36Sopenharmony_ci	if (retval != FW_SUCCESS) {
139762306a36Sopenharmony_ci		csio_err(hw, "FW_PARAMS_CMD(read) failed with ret:0x%x!\n",
139862306a36Sopenharmony_ci				retval);
139962306a36Sopenharmony_ci		mempool_free(mbp, hw->mb_mempool);
140062306a36Sopenharmony_ci		return -EINVAL;
140162306a36Sopenharmony_ci	}
140262306a36Sopenharmony_ci
140362306a36Sopenharmony_ci	/* cache the information. */
140462306a36Sopenharmony_ci	hw->port_vec = param[0];
140562306a36Sopenharmony_ci	hw->vpd.cclk = param[1];
140662306a36Sopenharmony_ci	wrm->fw_eq_start = param[2];
140762306a36Sopenharmony_ci	wrm->fw_iq_start = param[4];
140862306a36Sopenharmony_ci
140962306a36Sopenharmony_ci	/* Using FW configured max iqs & eqs */
141062306a36Sopenharmony_ci	if ((hw->flags & CSIO_HWF_USING_SOFT_PARAMS) ||
141162306a36Sopenharmony_ci		!csio_is_hw_master(hw)) {
141262306a36Sopenharmony_ci		hw->cfg_niq = param[5] - param[4] + 1;
141362306a36Sopenharmony_ci		hw->cfg_neq = param[3] - param[2] + 1;
141462306a36Sopenharmony_ci		csio_dbg(hw, "Using fwconfig max niqs %d neqs %d\n",
141562306a36Sopenharmony_ci			hw->cfg_niq, hw->cfg_neq);
141662306a36Sopenharmony_ci	}
141762306a36Sopenharmony_ci
141862306a36Sopenharmony_ci	hw->port_vec &= csio_port_mask;
141962306a36Sopenharmony_ci
142062306a36Sopenharmony_ci	hw->num_pports	= hweight32(hw->port_vec);
142162306a36Sopenharmony_ci
142262306a36Sopenharmony_ci	csio_dbg(hw, "Port vector: 0x%x, #ports: %d\n",
142362306a36Sopenharmony_ci		    hw->port_vec, hw->num_pports);
142462306a36Sopenharmony_ci
142562306a36Sopenharmony_ci	for (i = 0; i < hw->num_pports; i++) {
142662306a36Sopenharmony_ci		while ((hw->port_vec & (1 << j)) == 0)
142762306a36Sopenharmony_ci			j++;
142862306a36Sopenharmony_ci		hw->pport[i].portid = j++;
142962306a36Sopenharmony_ci		csio_dbg(hw, "Found Port:%d\n", hw->pport[i].portid);
143062306a36Sopenharmony_ci	}
143162306a36Sopenharmony_ci	mempool_free(mbp, hw->mb_mempool);
143262306a36Sopenharmony_ci
143362306a36Sopenharmony_ci	return 0;
143462306a36Sopenharmony_ci}
143562306a36Sopenharmony_ci
143662306a36Sopenharmony_ci
143762306a36Sopenharmony_ci/*
143862306a36Sopenharmony_ci * csio_config_device_caps - Get and set device capabilities.
143962306a36Sopenharmony_ci * @hw: HW module
144062306a36Sopenharmony_ci *
144162306a36Sopenharmony_ci */
144262306a36Sopenharmony_cistatic int
144362306a36Sopenharmony_cicsio_config_device_caps(struct csio_hw *hw)
144462306a36Sopenharmony_ci{
144562306a36Sopenharmony_ci	struct csio_mb	*mbp;
144662306a36Sopenharmony_ci	enum fw_retval retval;
144762306a36Sopenharmony_ci	int rv = -EINVAL;
144862306a36Sopenharmony_ci
144962306a36Sopenharmony_ci	mbp = mempool_alloc(hw->mb_mempool, GFP_ATOMIC);
145062306a36Sopenharmony_ci	if (!mbp) {
145162306a36Sopenharmony_ci		CSIO_INC_STATS(hw, n_err_nomem);
145262306a36Sopenharmony_ci		return -ENOMEM;
145362306a36Sopenharmony_ci	}
145462306a36Sopenharmony_ci
145562306a36Sopenharmony_ci	/* Get device capabilities */
145662306a36Sopenharmony_ci	csio_mb_caps_config(hw, mbp, CSIO_MB_DEFAULT_TMO, 0, 0, 0, 0, NULL);
145762306a36Sopenharmony_ci
145862306a36Sopenharmony_ci	if (csio_mb_issue(hw, mbp)) {
145962306a36Sopenharmony_ci		csio_err(hw, "Issue of FW_CAPS_CONFIG_CMD(r) failed!\n");
146062306a36Sopenharmony_ci		goto out;
146162306a36Sopenharmony_ci	}
146262306a36Sopenharmony_ci
146362306a36Sopenharmony_ci	retval = csio_mb_fw_retval(mbp);
146462306a36Sopenharmony_ci	if (retval != FW_SUCCESS) {
146562306a36Sopenharmony_ci		csio_err(hw, "FW_CAPS_CONFIG_CMD(r) returned %d!\n", retval);
146662306a36Sopenharmony_ci		goto out;
146762306a36Sopenharmony_ci	}
146862306a36Sopenharmony_ci
146962306a36Sopenharmony_ci	/* Validate device capabilities */
147062306a36Sopenharmony_ci	rv = csio_hw_validate_caps(hw, mbp);
147162306a36Sopenharmony_ci	if (rv != 0)
147262306a36Sopenharmony_ci		goto out;
147362306a36Sopenharmony_ci
147462306a36Sopenharmony_ci	/* Don't config device capabilities if already configured */
147562306a36Sopenharmony_ci	if (hw->fw_state == CSIO_DEV_STATE_INIT) {
147662306a36Sopenharmony_ci		rv = 0;
147762306a36Sopenharmony_ci		goto out;
147862306a36Sopenharmony_ci	}
147962306a36Sopenharmony_ci
148062306a36Sopenharmony_ci	/* Write back desired device capabilities */
148162306a36Sopenharmony_ci	csio_mb_caps_config(hw, mbp, CSIO_MB_DEFAULT_TMO, true, true,
148262306a36Sopenharmony_ci			    false, true, NULL);
148362306a36Sopenharmony_ci
148462306a36Sopenharmony_ci	if (csio_mb_issue(hw, mbp)) {
148562306a36Sopenharmony_ci		csio_err(hw, "Issue of FW_CAPS_CONFIG_CMD(w) failed!\n");
148662306a36Sopenharmony_ci		goto out;
148762306a36Sopenharmony_ci	}
148862306a36Sopenharmony_ci
148962306a36Sopenharmony_ci	retval = csio_mb_fw_retval(mbp);
149062306a36Sopenharmony_ci	if (retval != FW_SUCCESS) {
149162306a36Sopenharmony_ci		csio_err(hw, "FW_CAPS_CONFIG_CMD(w) returned %d!\n", retval);
149262306a36Sopenharmony_ci		goto out;
149362306a36Sopenharmony_ci	}
149462306a36Sopenharmony_ci
149562306a36Sopenharmony_ci	rv = 0;
149662306a36Sopenharmony_ciout:
149762306a36Sopenharmony_ci	mempool_free(mbp, hw->mb_mempool);
149862306a36Sopenharmony_ci	return rv;
149962306a36Sopenharmony_ci}
150062306a36Sopenharmony_ci
150162306a36Sopenharmony_cistatic inline enum cc_fec fwcap_to_cc_fec(fw_port_cap32_t fw_fec)
150262306a36Sopenharmony_ci{
150362306a36Sopenharmony_ci	enum cc_fec cc_fec = 0;
150462306a36Sopenharmony_ci
150562306a36Sopenharmony_ci	if (fw_fec & FW_PORT_CAP32_FEC_RS)
150662306a36Sopenharmony_ci		cc_fec |= FEC_RS;
150762306a36Sopenharmony_ci	if (fw_fec & FW_PORT_CAP32_FEC_BASER_RS)
150862306a36Sopenharmony_ci		cc_fec |= FEC_BASER_RS;
150962306a36Sopenharmony_ci
151062306a36Sopenharmony_ci	return cc_fec;
151162306a36Sopenharmony_ci}
151262306a36Sopenharmony_ci
151362306a36Sopenharmony_cistatic inline fw_port_cap32_t cc_to_fwcap_pause(enum cc_pause cc_pause)
151462306a36Sopenharmony_ci{
151562306a36Sopenharmony_ci	fw_port_cap32_t fw_pause = 0;
151662306a36Sopenharmony_ci
151762306a36Sopenharmony_ci	if (cc_pause & PAUSE_RX)
151862306a36Sopenharmony_ci		fw_pause |= FW_PORT_CAP32_FC_RX;
151962306a36Sopenharmony_ci	if (cc_pause & PAUSE_TX)
152062306a36Sopenharmony_ci		fw_pause |= FW_PORT_CAP32_FC_TX;
152162306a36Sopenharmony_ci
152262306a36Sopenharmony_ci	return fw_pause;
152362306a36Sopenharmony_ci}
152462306a36Sopenharmony_ci
152562306a36Sopenharmony_cistatic inline fw_port_cap32_t cc_to_fwcap_fec(enum cc_fec cc_fec)
152662306a36Sopenharmony_ci{
152762306a36Sopenharmony_ci	fw_port_cap32_t fw_fec = 0;
152862306a36Sopenharmony_ci
152962306a36Sopenharmony_ci	if (cc_fec & FEC_RS)
153062306a36Sopenharmony_ci		fw_fec |= FW_PORT_CAP32_FEC_RS;
153162306a36Sopenharmony_ci	if (cc_fec & FEC_BASER_RS)
153262306a36Sopenharmony_ci		fw_fec |= FW_PORT_CAP32_FEC_BASER_RS;
153362306a36Sopenharmony_ci
153462306a36Sopenharmony_ci	return fw_fec;
153562306a36Sopenharmony_ci}
153662306a36Sopenharmony_ci
153762306a36Sopenharmony_ci/**
153862306a36Sopenharmony_ci * fwcap_to_fwspeed - return highest speed in Port Capabilities
153962306a36Sopenharmony_ci * @acaps: advertised Port Capabilities
154062306a36Sopenharmony_ci *
154162306a36Sopenharmony_ci * Get the highest speed for the port from the advertised Port
154262306a36Sopenharmony_ci * Capabilities.
154362306a36Sopenharmony_ci */
154462306a36Sopenharmony_cifw_port_cap32_t fwcap_to_fwspeed(fw_port_cap32_t acaps)
154562306a36Sopenharmony_ci{
154662306a36Sopenharmony_ci	#define TEST_SPEED_RETURN(__caps_speed) \
154762306a36Sopenharmony_ci		do { \
154862306a36Sopenharmony_ci			if (acaps & FW_PORT_CAP32_SPEED_##__caps_speed) \
154962306a36Sopenharmony_ci				return FW_PORT_CAP32_SPEED_##__caps_speed; \
155062306a36Sopenharmony_ci		} while (0)
155162306a36Sopenharmony_ci
155262306a36Sopenharmony_ci	TEST_SPEED_RETURN(400G);
155362306a36Sopenharmony_ci	TEST_SPEED_RETURN(200G);
155462306a36Sopenharmony_ci	TEST_SPEED_RETURN(100G);
155562306a36Sopenharmony_ci	TEST_SPEED_RETURN(50G);
155662306a36Sopenharmony_ci	TEST_SPEED_RETURN(40G);
155762306a36Sopenharmony_ci	TEST_SPEED_RETURN(25G);
155862306a36Sopenharmony_ci	TEST_SPEED_RETURN(10G);
155962306a36Sopenharmony_ci	TEST_SPEED_RETURN(1G);
156062306a36Sopenharmony_ci	TEST_SPEED_RETURN(100M);
156162306a36Sopenharmony_ci
156262306a36Sopenharmony_ci	#undef TEST_SPEED_RETURN
156362306a36Sopenharmony_ci
156462306a36Sopenharmony_ci	return 0;
156562306a36Sopenharmony_ci}
156662306a36Sopenharmony_ci
156762306a36Sopenharmony_ci/**
156862306a36Sopenharmony_ci *      fwcaps16_to_caps32 - convert 16-bit Port Capabilities to 32-bits
156962306a36Sopenharmony_ci *      @caps16: a 16-bit Port Capabilities value
157062306a36Sopenharmony_ci *
157162306a36Sopenharmony_ci *      Returns the equivalent 32-bit Port Capabilities value.
157262306a36Sopenharmony_ci */
157362306a36Sopenharmony_cifw_port_cap32_t fwcaps16_to_caps32(fw_port_cap16_t caps16)
157462306a36Sopenharmony_ci{
157562306a36Sopenharmony_ci	fw_port_cap32_t caps32 = 0;
157662306a36Sopenharmony_ci
157762306a36Sopenharmony_ci	#define CAP16_TO_CAP32(__cap) \
157862306a36Sopenharmony_ci		do { \
157962306a36Sopenharmony_ci			if (caps16 & FW_PORT_CAP_##__cap) \
158062306a36Sopenharmony_ci				caps32 |= FW_PORT_CAP32_##__cap; \
158162306a36Sopenharmony_ci		} while (0)
158262306a36Sopenharmony_ci
158362306a36Sopenharmony_ci	CAP16_TO_CAP32(SPEED_100M);
158462306a36Sopenharmony_ci	CAP16_TO_CAP32(SPEED_1G);
158562306a36Sopenharmony_ci	CAP16_TO_CAP32(SPEED_25G);
158662306a36Sopenharmony_ci	CAP16_TO_CAP32(SPEED_10G);
158762306a36Sopenharmony_ci	CAP16_TO_CAP32(SPEED_40G);
158862306a36Sopenharmony_ci	CAP16_TO_CAP32(SPEED_100G);
158962306a36Sopenharmony_ci	CAP16_TO_CAP32(FC_RX);
159062306a36Sopenharmony_ci	CAP16_TO_CAP32(FC_TX);
159162306a36Sopenharmony_ci	CAP16_TO_CAP32(ANEG);
159262306a36Sopenharmony_ci	CAP16_TO_CAP32(MDIAUTO);
159362306a36Sopenharmony_ci	CAP16_TO_CAP32(MDISTRAIGHT);
159462306a36Sopenharmony_ci	CAP16_TO_CAP32(FEC_RS);
159562306a36Sopenharmony_ci	CAP16_TO_CAP32(FEC_BASER_RS);
159662306a36Sopenharmony_ci	CAP16_TO_CAP32(802_3_PAUSE);
159762306a36Sopenharmony_ci	CAP16_TO_CAP32(802_3_ASM_DIR);
159862306a36Sopenharmony_ci
159962306a36Sopenharmony_ci	#undef CAP16_TO_CAP32
160062306a36Sopenharmony_ci
160162306a36Sopenharmony_ci	return caps32;
160262306a36Sopenharmony_ci}
160362306a36Sopenharmony_ci
160462306a36Sopenharmony_ci/**
160562306a36Sopenharmony_ci *	fwcaps32_to_caps16 - convert 32-bit Port Capabilities to 16-bits
160662306a36Sopenharmony_ci *	@caps32: a 32-bit Port Capabilities value
160762306a36Sopenharmony_ci *
160862306a36Sopenharmony_ci *	Returns the equivalent 16-bit Port Capabilities value.  Note that
160962306a36Sopenharmony_ci *	not all 32-bit Port Capabilities can be represented in the 16-bit
161062306a36Sopenharmony_ci *	Port Capabilities and some fields/values may not make it.
161162306a36Sopenharmony_ci */
161262306a36Sopenharmony_cifw_port_cap16_t fwcaps32_to_caps16(fw_port_cap32_t caps32)
161362306a36Sopenharmony_ci{
161462306a36Sopenharmony_ci	fw_port_cap16_t caps16 = 0;
161562306a36Sopenharmony_ci
161662306a36Sopenharmony_ci	#define CAP32_TO_CAP16(__cap) \
161762306a36Sopenharmony_ci		do { \
161862306a36Sopenharmony_ci			if (caps32 & FW_PORT_CAP32_##__cap) \
161962306a36Sopenharmony_ci				caps16 |= FW_PORT_CAP_##__cap; \
162062306a36Sopenharmony_ci		} while (0)
162162306a36Sopenharmony_ci
162262306a36Sopenharmony_ci	CAP32_TO_CAP16(SPEED_100M);
162362306a36Sopenharmony_ci	CAP32_TO_CAP16(SPEED_1G);
162462306a36Sopenharmony_ci	CAP32_TO_CAP16(SPEED_10G);
162562306a36Sopenharmony_ci	CAP32_TO_CAP16(SPEED_25G);
162662306a36Sopenharmony_ci	CAP32_TO_CAP16(SPEED_40G);
162762306a36Sopenharmony_ci	CAP32_TO_CAP16(SPEED_100G);
162862306a36Sopenharmony_ci	CAP32_TO_CAP16(FC_RX);
162962306a36Sopenharmony_ci	CAP32_TO_CAP16(FC_TX);
163062306a36Sopenharmony_ci	CAP32_TO_CAP16(802_3_PAUSE);
163162306a36Sopenharmony_ci	CAP32_TO_CAP16(802_3_ASM_DIR);
163262306a36Sopenharmony_ci	CAP32_TO_CAP16(ANEG);
163362306a36Sopenharmony_ci	CAP32_TO_CAP16(FORCE_PAUSE);
163462306a36Sopenharmony_ci	CAP32_TO_CAP16(MDIAUTO);
163562306a36Sopenharmony_ci	CAP32_TO_CAP16(MDISTRAIGHT);
163662306a36Sopenharmony_ci	CAP32_TO_CAP16(FEC_RS);
163762306a36Sopenharmony_ci	CAP32_TO_CAP16(FEC_BASER_RS);
163862306a36Sopenharmony_ci
163962306a36Sopenharmony_ci	#undef CAP32_TO_CAP16
164062306a36Sopenharmony_ci
164162306a36Sopenharmony_ci	return caps16;
164262306a36Sopenharmony_ci}
164362306a36Sopenharmony_ci
164462306a36Sopenharmony_ci/**
164562306a36Sopenharmony_ci *      lstatus_to_fwcap - translate old lstatus to 32-bit Port Capabilities
164662306a36Sopenharmony_ci *      @lstatus: old FW_PORT_ACTION_GET_PORT_INFO lstatus value
164762306a36Sopenharmony_ci *
164862306a36Sopenharmony_ci *      Translates old FW_PORT_ACTION_GET_PORT_INFO lstatus field into new
164962306a36Sopenharmony_ci *      32-bit Port Capabilities value.
165062306a36Sopenharmony_ci */
165162306a36Sopenharmony_cifw_port_cap32_t lstatus_to_fwcap(u32 lstatus)
165262306a36Sopenharmony_ci{
165362306a36Sopenharmony_ci	fw_port_cap32_t linkattr = 0;
165462306a36Sopenharmony_ci
165562306a36Sopenharmony_ci	/* The format of the Link Status in the old
165662306a36Sopenharmony_ci	 * 16-bit Port Information message isn't the same as the
165762306a36Sopenharmony_ci	 * 16-bit Port Capabilities bitfield used everywhere else.
165862306a36Sopenharmony_ci	 */
165962306a36Sopenharmony_ci	if (lstatus & FW_PORT_CMD_RXPAUSE_F)
166062306a36Sopenharmony_ci		linkattr |= FW_PORT_CAP32_FC_RX;
166162306a36Sopenharmony_ci	if (lstatus & FW_PORT_CMD_TXPAUSE_F)
166262306a36Sopenharmony_ci		linkattr |= FW_PORT_CAP32_FC_TX;
166362306a36Sopenharmony_ci	if (lstatus & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_100M))
166462306a36Sopenharmony_ci		linkattr |= FW_PORT_CAP32_SPEED_100M;
166562306a36Sopenharmony_ci	if (lstatus & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_1G))
166662306a36Sopenharmony_ci		linkattr |= FW_PORT_CAP32_SPEED_1G;
166762306a36Sopenharmony_ci	if (lstatus & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_10G))
166862306a36Sopenharmony_ci		linkattr |= FW_PORT_CAP32_SPEED_10G;
166962306a36Sopenharmony_ci	if (lstatus & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_25G))
167062306a36Sopenharmony_ci		linkattr |= FW_PORT_CAP32_SPEED_25G;
167162306a36Sopenharmony_ci	if (lstatus & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_40G))
167262306a36Sopenharmony_ci		linkattr |= FW_PORT_CAP32_SPEED_40G;
167362306a36Sopenharmony_ci	if (lstatus & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_100G))
167462306a36Sopenharmony_ci		linkattr |= FW_PORT_CAP32_SPEED_100G;
167562306a36Sopenharmony_ci
167662306a36Sopenharmony_ci	return linkattr;
167762306a36Sopenharmony_ci}
167862306a36Sopenharmony_ci
167962306a36Sopenharmony_ci/**
168062306a36Sopenharmony_ci *      csio_init_link_config - initialize a link's SW state
168162306a36Sopenharmony_ci *      @lc: pointer to structure holding the link state
168262306a36Sopenharmony_ci *      @pcaps: link Port Capabilities
168362306a36Sopenharmony_ci *      @acaps: link current Advertised Port Capabilities
168462306a36Sopenharmony_ci *
168562306a36Sopenharmony_ci *      Initializes the SW state maintained for each link, including the link's
168662306a36Sopenharmony_ci *      capabilities and default speed/flow-control/autonegotiation settings.
168762306a36Sopenharmony_ci */
168862306a36Sopenharmony_cistatic void csio_init_link_config(struct link_config *lc, fw_port_cap32_t pcaps,
168962306a36Sopenharmony_ci				  fw_port_cap32_t acaps)
169062306a36Sopenharmony_ci{
169162306a36Sopenharmony_ci	lc->pcaps = pcaps;
169262306a36Sopenharmony_ci	lc->def_acaps = acaps;
169362306a36Sopenharmony_ci	lc->lpacaps = 0;
169462306a36Sopenharmony_ci	lc->speed_caps = 0;
169562306a36Sopenharmony_ci	lc->speed = 0;
169662306a36Sopenharmony_ci	lc->requested_fc = PAUSE_RX | PAUSE_TX;
169762306a36Sopenharmony_ci	lc->fc = lc->requested_fc;
169862306a36Sopenharmony_ci
169962306a36Sopenharmony_ci	/*
170062306a36Sopenharmony_ci	 * For Forward Error Control, we default to whatever the Firmware
170162306a36Sopenharmony_ci	 * tells us the Link is currently advertising.
170262306a36Sopenharmony_ci	 */
170362306a36Sopenharmony_ci	lc->requested_fec = FEC_AUTO;
170462306a36Sopenharmony_ci	lc->fec = fwcap_to_cc_fec(lc->def_acaps);
170562306a36Sopenharmony_ci
170662306a36Sopenharmony_ci	/* If the Port is capable of Auto-Negtotiation, initialize it as
170762306a36Sopenharmony_ci	 * "enabled" and copy over all of the Physical Port Capabilities
170862306a36Sopenharmony_ci	 * to the Advertised Port Capabilities.  Otherwise mark it as
170962306a36Sopenharmony_ci	 * Auto-Negotiate disabled and select the highest supported speed
171062306a36Sopenharmony_ci	 * for the link.  Note parallel structure in t4_link_l1cfg_core()
171162306a36Sopenharmony_ci	 * and t4_handle_get_port_info().
171262306a36Sopenharmony_ci	 */
171362306a36Sopenharmony_ci	if (lc->pcaps & FW_PORT_CAP32_ANEG) {
171462306a36Sopenharmony_ci		lc->acaps = lc->pcaps & ADVERT_MASK;
171562306a36Sopenharmony_ci		lc->autoneg = AUTONEG_ENABLE;
171662306a36Sopenharmony_ci		lc->requested_fc |= PAUSE_AUTONEG;
171762306a36Sopenharmony_ci	} else {
171862306a36Sopenharmony_ci		lc->acaps = 0;
171962306a36Sopenharmony_ci		lc->autoneg = AUTONEG_DISABLE;
172062306a36Sopenharmony_ci	}
172162306a36Sopenharmony_ci}
172262306a36Sopenharmony_ci
172362306a36Sopenharmony_cistatic void csio_link_l1cfg(struct link_config *lc, uint16_t fw_caps,
172462306a36Sopenharmony_ci			    uint32_t *rcaps)
172562306a36Sopenharmony_ci{
172662306a36Sopenharmony_ci	unsigned int fw_mdi = FW_PORT_CAP32_MDI_V(FW_PORT_CAP32_MDI_AUTO);
172762306a36Sopenharmony_ci	fw_port_cap32_t fw_fc, cc_fec, fw_fec, lrcap;
172862306a36Sopenharmony_ci
172962306a36Sopenharmony_ci	lc->link_ok = 0;
173062306a36Sopenharmony_ci
173162306a36Sopenharmony_ci	/*
173262306a36Sopenharmony_ci	 * Convert driver coding of Pause Frame Flow Control settings into the
173362306a36Sopenharmony_ci	 * Firmware's API.
173462306a36Sopenharmony_ci	 */
173562306a36Sopenharmony_ci	fw_fc = cc_to_fwcap_pause(lc->requested_fc);
173662306a36Sopenharmony_ci
173762306a36Sopenharmony_ci	/*
173862306a36Sopenharmony_ci	 * Convert Common Code Forward Error Control settings into the
173962306a36Sopenharmony_ci	 * Firmware's API.  If the current Requested FEC has "Automatic"
174062306a36Sopenharmony_ci	 * (IEEE 802.3) specified, then we use whatever the Firmware
174162306a36Sopenharmony_ci	 * sent us as part of it's IEEE 802.3-based interpretation of
174262306a36Sopenharmony_ci	 * the Transceiver Module EPROM FEC parameters.  Otherwise we
174362306a36Sopenharmony_ci	 * use whatever is in the current Requested FEC settings.
174462306a36Sopenharmony_ci	 */
174562306a36Sopenharmony_ci	if (lc->requested_fec & FEC_AUTO)
174662306a36Sopenharmony_ci		cc_fec = fwcap_to_cc_fec(lc->def_acaps);
174762306a36Sopenharmony_ci	else
174862306a36Sopenharmony_ci		cc_fec = lc->requested_fec;
174962306a36Sopenharmony_ci	fw_fec = cc_to_fwcap_fec(cc_fec);
175062306a36Sopenharmony_ci
175162306a36Sopenharmony_ci	/* Figure out what our Requested Port Capabilities are going to be.
175262306a36Sopenharmony_ci	 * Note parallel structure in t4_handle_get_port_info() and
175362306a36Sopenharmony_ci	 * init_link_config().
175462306a36Sopenharmony_ci	 */
175562306a36Sopenharmony_ci	if (!(lc->pcaps & FW_PORT_CAP32_ANEG)) {
175662306a36Sopenharmony_ci		lrcap = (lc->pcaps & ADVERT_MASK) | fw_fc | fw_fec;
175762306a36Sopenharmony_ci		lc->fc = lc->requested_fc & ~PAUSE_AUTONEG;
175862306a36Sopenharmony_ci		lc->fec = cc_fec;
175962306a36Sopenharmony_ci	} else if (lc->autoneg == AUTONEG_DISABLE) {
176062306a36Sopenharmony_ci		lrcap = lc->speed_caps | fw_fc | fw_fec | fw_mdi;
176162306a36Sopenharmony_ci		lc->fc = lc->requested_fc & ~PAUSE_AUTONEG;
176262306a36Sopenharmony_ci		lc->fec = cc_fec;
176362306a36Sopenharmony_ci	} else {
176462306a36Sopenharmony_ci		lrcap = lc->acaps | fw_fc | fw_fec | fw_mdi;
176562306a36Sopenharmony_ci	}
176662306a36Sopenharmony_ci
176762306a36Sopenharmony_ci	*rcaps = lrcap;
176862306a36Sopenharmony_ci}
176962306a36Sopenharmony_ci
177062306a36Sopenharmony_ci/*
177162306a36Sopenharmony_ci * csio_enable_ports - Bring up all available ports.
177262306a36Sopenharmony_ci * @hw: HW module.
177362306a36Sopenharmony_ci *
177462306a36Sopenharmony_ci */
177562306a36Sopenharmony_cistatic int
177662306a36Sopenharmony_cicsio_enable_ports(struct csio_hw *hw)
177762306a36Sopenharmony_ci{
177862306a36Sopenharmony_ci	struct csio_mb  *mbp;
177962306a36Sopenharmony_ci	u16 fw_caps = FW_CAPS_UNKNOWN;
178062306a36Sopenharmony_ci	enum fw_retval retval;
178162306a36Sopenharmony_ci	uint8_t portid;
178262306a36Sopenharmony_ci	fw_port_cap32_t pcaps, acaps, rcaps;
178362306a36Sopenharmony_ci	int i;
178462306a36Sopenharmony_ci
178562306a36Sopenharmony_ci	mbp = mempool_alloc(hw->mb_mempool, GFP_ATOMIC);
178662306a36Sopenharmony_ci	if (!mbp) {
178762306a36Sopenharmony_ci		CSIO_INC_STATS(hw, n_err_nomem);
178862306a36Sopenharmony_ci		return -ENOMEM;
178962306a36Sopenharmony_ci	}
179062306a36Sopenharmony_ci
179162306a36Sopenharmony_ci	for (i = 0; i < hw->num_pports; i++) {
179262306a36Sopenharmony_ci		portid = hw->pport[i].portid;
179362306a36Sopenharmony_ci
179462306a36Sopenharmony_ci		if (fw_caps == FW_CAPS_UNKNOWN) {
179562306a36Sopenharmony_ci			u32 param, val;
179662306a36Sopenharmony_ci
179762306a36Sopenharmony_ci			param = (FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_PFVF) |
179862306a36Sopenharmony_ci			 FW_PARAMS_PARAM_X_V(FW_PARAMS_PARAM_PFVF_PORT_CAPS32));
179962306a36Sopenharmony_ci			val = 1;
180062306a36Sopenharmony_ci
180162306a36Sopenharmony_ci			csio_mb_params(hw, mbp, CSIO_MB_DEFAULT_TMO,
180262306a36Sopenharmony_ci				       hw->pfn, 0, 1, &param, &val, true,
180362306a36Sopenharmony_ci				       NULL);
180462306a36Sopenharmony_ci
180562306a36Sopenharmony_ci			if (csio_mb_issue(hw, mbp)) {
180662306a36Sopenharmony_ci				csio_err(hw, "failed to issue FW_PARAMS_CMD(r) port:%d\n",
180762306a36Sopenharmony_ci					 portid);
180862306a36Sopenharmony_ci				mempool_free(mbp, hw->mb_mempool);
180962306a36Sopenharmony_ci				return -EINVAL;
181062306a36Sopenharmony_ci			}
181162306a36Sopenharmony_ci
181262306a36Sopenharmony_ci			csio_mb_process_read_params_rsp(hw, mbp, &retval,
181362306a36Sopenharmony_ci							0, NULL);
181462306a36Sopenharmony_ci			fw_caps = retval ? FW_CAPS16 : FW_CAPS32;
181562306a36Sopenharmony_ci		}
181662306a36Sopenharmony_ci
181762306a36Sopenharmony_ci		/* Read PORT information */
181862306a36Sopenharmony_ci		csio_mb_port(hw, mbp, CSIO_MB_DEFAULT_TMO, portid,
181962306a36Sopenharmony_ci			     false, 0, fw_caps, NULL);
182062306a36Sopenharmony_ci
182162306a36Sopenharmony_ci		if (csio_mb_issue(hw, mbp)) {
182262306a36Sopenharmony_ci			csio_err(hw, "failed to issue FW_PORT_CMD(r) port:%d\n",
182362306a36Sopenharmony_ci				 portid);
182462306a36Sopenharmony_ci			mempool_free(mbp, hw->mb_mempool);
182562306a36Sopenharmony_ci			return -EINVAL;
182662306a36Sopenharmony_ci		}
182762306a36Sopenharmony_ci
182862306a36Sopenharmony_ci		csio_mb_process_read_port_rsp(hw, mbp, &retval, fw_caps,
182962306a36Sopenharmony_ci					      &pcaps, &acaps);
183062306a36Sopenharmony_ci		if (retval != FW_SUCCESS) {
183162306a36Sopenharmony_ci			csio_err(hw, "FW_PORT_CMD(r) port:%d failed: 0x%x\n",
183262306a36Sopenharmony_ci				 portid, retval);
183362306a36Sopenharmony_ci			mempool_free(mbp, hw->mb_mempool);
183462306a36Sopenharmony_ci			return -EINVAL;
183562306a36Sopenharmony_ci		}
183662306a36Sopenharmony_ci
183762306a36Sopenharmony_ci		csio_init_link_config(&hw->pport[i].link_cfg, pcaps, acaps);
183862306a36Sopenharmony_ci
183962306a36Sopenharmony_ci		csio_link_l1cfg(&hw->pport[i].link_cfg, fw_caps, &rcaps);
184062306a36Sopenharmony_ci
184162306a36Sopenharmony_ci		/* Write back PORT information */
184262306a36Sopenharmony_ci		csio_mb_port(hw, mbp, CSIO_MB_DEFAULT_TMO, portid,
184362306a36Sopenharmony_ci			     true, rcaps, fw_caps, NULL);
184462306a36Sopenharmony_ci
184562306a36Sopenharmony_ci		if (csio_mb_issue(hw, mbp)) {
184662306a36Sopenharmony_ci			csio_err(hw, "failed to issue FW_PORT_CMD(w) port:%d\n",
184762306a36Sopenharmony_ci				 portid);
184862306a36Sopenharmony_ci			mempool_free(mbp, hw->mb_mempool);
184962306a36Sopenharmony_ci			return -EINVAL;
185062306a36Sopenharmony_ci		}
185162306a36Sopenharmony_ci
185262306a36Sopenharmony_ci		retval = csio_mb_fw_retval(mbp);
185362306a36Sopenharmony_ci		if (retval != FW_SUCCESS) {
185462306a36Sopenharmony_ci			csio_err(hw, "FW_PORT_CMD(w) port:%d failed :0x%x\n",
185562306a36Sopenharmony_ci				 portid, retval);
185662306a36Sopenharmony_ci			mempool_free(mbp, hw->mb_mempool);
185762306a36Sopenharmony_ci			return -EINVAL;
185862306a36Sopenharmony_ci		}
185962306a36Sopenharmony_ci
186062306a36Sopenharmony_ci	} /* For all ports */
186162306a36Sopenharmony_ci
186262306a36Sopenharmony_ci	mempool_free(mbp, hw->mb_mempool);
186362306a36Sopenharmony_ci
186462306a36Sopenharmony_ci	return 0;
186562306a36Sopenharmony_ci}
186662306a36Sopenharmony_ci
186762306a36Sopenharmony_ci/*
186862306a36Sopenharmony_ci * csio_get_fcoe_resinfo - Read fcoe fw resource info.
186962306a36Sopenharmony_ci * @hw: HW module
187062306a36Sopenharmony_ci * Issued with lock held.
187162306a36Sopenharmony_ci */
187262306a36Sopenharmony_cistatic int
187362306a36Sopenharmony_cicsio_get_fcoe_resinfo(struct csio_hw *hw)
187462306a36Sopenharmony_ci{
187562306a36Sopenharmony_ci	struct csio_fcoe_res_info *res_info = &hw->fres_info;
187662306a36Sopenharmony_ci	struct fw_fcoe_res_info_cmd *rsp;
187762306a36Sopenharmony_ci	struct csio_mb  *mbp;
187862306a36Sopenharmony_ci	enum fw_retval retval;
187962306a36Sopenharmony_ci
188062306a36Sopenharmony_ci	mbp = mempool_alloc(hw->mb_mempool, GFP_ATOMIC);
188162306a36Sopenharmony_ci	if (!mbp) {
188262306a36Sopenharmony_ci		CSIO_INC_STATS(hw, n_err_nomem);
188362306a36Sopenharmony_ci		return -ENOMEM;
188462306a36Sopenharmony_ci	}
188562306a36Sopenharmony_ci
188662306a36Sopenharmony_ci	/* Get FCoE FW resource information */
188762306a36Sopenharmony_ci	csio_fcoe_read_res_info_init_mb(hw, mbp, CSIO_MB_DEFAULT_TMO, NULL);
188862306a36Sopenharmony_ci
188962306a36Sopenharmony_ci	if (csio_mb_issue(hw, mbp)) {
189062306a36Sopenharmony_ci		csio_err(hw, "failed to issue FW_FCOE_RES_INFO_CMD\n");
189162306a36Sopenharmony_ci		mempool_free(mbp, hw->mb_mempool);
189262306a36Sopenharmony_ci		return -EINVAL;
189362306a36Sopenharmony_ci	}
189462306a36Sopenharmony_ci
189562306a36Sopenharmony_ci	rsp = (struct fw_fcoe_res_info_cmd *)(mbp->mb);
189662306a36Sopenharmony_ci	retval = FW_CMD_RETVAL_G(ntohl(rsp->retval_len16));
189762306a36Sopenharmony_ci	if (retval != FW_SUCCESS) {
189862306a36Sopenharmony_ci		csio_err(hw, "FW_FCOE_RES_INFO_CMD failed with ret x%x\n",
189962306a36Sopenharmony_ci			 retval);
190062306a36Sopenharmony_ci		mempool_free(mbp, hw->mb_mempool);
190162306a36Sopenharmony_ci		return -EINVAL;
190262306a36Sopenharmony_ci	}
190362306a36Sopenharmony_ci
190462306a36Sopenharmony_ci	res_info->e_d_tov = ntohs(rsp->e_d_tov);
190562306a36Sopenharmony_ci	res_info->r_a_tov_seq = ntohs(rsp->r_a_tov_seq);
190662306a36Sopenharmony_ci	res_info->r_a_tov_els = ntohs(rsp->r_a_tov_els);
190762306a36Sopenharmony_ci	res_info->r_r_tov = ntohs(rsp->r_r_tov);
190862306a36Sopenharmony_ci	res_info->max_xchgs = ntohl(rsp->max_xchgs);
190962306a36Sopenharmony_ci	res_info->max_ssns = ntohl(rsp->max_ssns);
191062306a36Sopenharmony_ci	res_info->used_xchgs = ntohl(rsp->used_xchgs);
191162306a36Sopenharmony_ci	res_info->used_ssns = ntohl(rsp->used_ssns);
191262306a36Sopenharmony_ci	res_info->max_fcfs = ntohl(rsp->max_fcfs);
191362306a36Sopenharmony_ci	res_info->max_vnps = ntohl(rsp->max_vnps);
191462306a36Sopenharmony_ci	res_info->used_fcfs = ntohl(rsp->used_fcfs);
191562306a36Sopenharmony_ci	res_info->used_vnps = ntohl(rsp->used_vnps);
191662306a36Sopenharmony_ci
191762306a36Sopenharmony_ci	csio_dbg(hw, "max ssns:%d max xchgs:%d\n", res_info->max_ssns,
191862306a36Sopenharmony_ci						  res_info->max_xchgs);
191962306a36Sopenharmony_ci	mempool_free(mbp, hw->mb_mempool);
192062306a36Sopenharmony_ci
192162306a36Sopenharmony_ci	return 0;
192262306a36Sopenharmony_ci}
192362306a36Sopenharmony_ci
192462306a36Sopenharmony_cistatic int
192562306a36Sopenharmony_cicsio_hw_check_fwconfig(struct csio_hw *hw, u32 *param)
192662306a36Sopenharmony_ci{
192762306a36Sopenharmony_ci	struct csio_mb	*mbp;
192862306a36Sopenharmony_ci	enum fw_retval retval;
192962306a36Sopenharmony_ci	u32 _param[1];
193062306a36Sopenharmony_ci
193162306a36Sopenharmony_ci	mbp = mempool_alloc(hw->mb_mempool, GFP_ATOMIC);
193262306a36Sopenharmony_ci	if (!mbp) {
193362306a36Sopenharmony_ci		CSIO_INC_STATS(hw, n_err_nomem);
193462306a36Sopenharmony_ci		return -ENOMEM;
193562306a36Sopenharmony_ci	}
193662306a36Sopenharmony_ci
193762306a36Sopenharmony_ci	/*
193862306a36Sopenharmony_ci	 * Find out whether we're dealing with a version of
193962306a36Sopenharmony_ci	 * the firmware which has configuration file support.
194062306a36Sopenharmony_ci	 */
194162306a36Sopenharmony_ci	_param[0] = (FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_DEV) |
194262306a36Sopenharmony_ci		     FW_PARAMS_PARAM_X_V(FW_PARAMS_PARAM_DEV_CF));
194362306a36Sopenharmony_ci
194462306a36Sopenharmony_ci	csio_mb_params(hw, mbp, CSIO_MB_DEFAULT_TMO, hw->pfn, 0,
194562306a36Sopenharmony_ci		       ARRAY_SIZE(_param), _param, NULL, false, NULL);
194662306a36Sopenharmony_ci	if (csio_mb_issue(hw, mbp)) {
194762306a36Sopenharmony_ci		csio_err(hw, "Issue of FW_PARAMS_CMD(read) failed!\n");
194862306a36Sopenharmony_ci		mempool_free(mbp, hw->mb_mempool);
194962306a36Sopenharmony_ci		return -EINVAL;
195062306a36Sopenharmony_ci	}
195162306a36Sopenharmony_ci
195262306a36Sopenharmony_ci	csio_mb_process_read_params_rsp(hw, mbp, &retval,
195362306a36Sopenharmony_ci			ARRAY_SIZE(_param), _param);
195462306a36Sopenharmony_ci	if (retval != FW_SUCCESS) {
195562306a36Sopenharmony_ci		csio_err(hw, "FW_PARAMS_CMD(read) failed with ret:0x%x!\n",
195662306a36Sopenharmony_ci				retval);
195762306a36Sopenharmony_ci		mempool_free(mbp, hw->mb_mempool);
195862306a36Sopenharmony_ci		return -EINVAL;
195962306a36Sopenharmony_ci	}
196062306a36Sopenharmony_ci
196162306a36Sopenharmony_ci	mempool_free(mbp, hw->mb_mempool);
196262306a36Sopenharmony_ci	*param = _param[0];
196362306a36Sopenharmony_ci
196462306a36Sopenharmony_ci	return 0;
196562306a36Sopenharmony_ci}
196662306a36Sopenharmony_ci
196762306a36Sopenharmony_cistatic int
196862306a36Sopenharmony_cicsio_hw_flash_config(struct csio_hw *hw, u32 *fw_cfg_param, char *path)
196962306a36Sopenharmony_ci{
197062306a36Sopenharmony_ci	int ret = 0;
197162306a36Sopenharmony_ci	const struct firmware *cf;
197262306a36Sopenharmony_ci	struct pci_dev *pci_dev = hw->pdev;
197362306a36Sopenharmony_ci	struct device *dev = &pci_dev->dev;
197462306a36Sopenharmony_ci	unsigned int mtype = 0, maddr = 0;
197562306a36Sopenharmony_ci	uint32_t *cfg_data;
197662306a36Sopenharmony_ci	int value_to_add = 0;
197762306a36Sopenharmony_ci	const char *fw_cfg_file;
197862306a36Sopenharmony_ci
197962306a36Sopenharmony_ci	if (csio_is_t5(pci_dev->device & CSIO_HW_CHIP_MASK))
198062306a36Sopenharmony_ci		fw_cfg_file = FW_CFG_NAME_T5;
198162306a36Sopenharmony_ci	else
198262306a36Sopenharmony_ci		fw_cfg_file = FW_CFG_NAME_T6;
198362306a36Sopenharmony_ci
198462306a36Sopenharmony_ci	if (request_firmware(&cf, fw_cfg_file, dev) < 0) {
198562306a36Sopenharmony_ci		csio_err(hw, "could not find config file %s, err: %d\n",
198662306a36Sopenharmony_ci			 fw_cfg_file, ret);
198762306a36Sopenharmony_ci		return -ENOENT;
198862306a36Sopenharmony_ci	}
198962306a36Sopenharmony_ci
199062306a36Sopenharmony_ci	if (cf->size%4 != 0)
199162306a36Sopenharmony_ci		value_to_add = 4 - (cf->size % 4);
199262306a36Sopenharmony_ci
199362306a36Sopenharmony_ci	cfg_data = kzalloc(cf->size+value_to_add, GFP_KERNEL);
199462306a36Sopenharmony_ci	if (cfg_data == NULL) {
199562306a36Sopenharmony_ci		ret = -ENOMEM;
199662306a36Sopenharmony_ci		goto leave;
199762306a36Sopenharmony_ci	}
199862306a36Sopenharmony_ci
199962306a36Sopenharmony_ci	memcpy((void *)cfg_data, (const void *)cf->data, cf->size);
200062306a36Sopenharmony_ci	if (csio_hw_check_fwconfig(hw, fw_cfg_param) != 0) {
200162306a36Sopenharmony_ci		ret = -EINVAL;
200262306a36Sopenharmony_ci		goto leave;
200362306a36Sopenharmony_ci	}
200462306a36Sopenharmony_ci
200562306a36Sopenharmony_ci	mtype = FW_PARAMS_PARAM_Y_G(*fw_cfg_param);
200662306a36Sopenharmony_ci	maddr = FW_PARAMS_PARAM_Z_G(*fw_cfg_param) << 16;
200762306a36Sopenharmony_ci
200862306a36Sopenharmony_ci	ret = csio_memory_write(hw, mtype, maddr,
200962306a36Sopenharmony_ci				cf->size + value_to_add, cfg_data);
201062306a36Sopenharmony_ci
201162306a36Sopenharmony_ci	if ((ret == 0) && (value_to_add != 0)) {
201262306a36Sopenharmony_ci		union {
201362306a36Sopenharmony_ci			u32 word;
201462306a36Sopenharmony_ci			char buf[4];
201562306a36Sopenharmony_ci		} last;
201662306a36Sopenharmony_ci		size_t size = cf->size & ~0x3;
201762306a36Sopenharmony_ci		int i;
201862306a36Sopenharmony_ci
201962306a36Sopenharmony_ci		last.word = cfg_data[size >> 2];
202062306a36Sopenharmony_ci		for (i = value_to_add; i < 4; i++)
202162306a36Sopenharmony_ci			last.buf[i] = 0;
202262306a36Sopenharmony_ci		ret = csio_memory_write(hw, mtype, maddr + size, 4, &last.word);
202362306a36Sopenharmony_ci	}
202462306a36Sopenharmony_ci	if (ret == 0) {
202562306a36Sopenharmony_ci		csio_info(hw, "config file upgraded to %s\n", fw_cfg_file);
202662306a36Sopenharmony_ci		snprintf(path, 64, "%s%s", "/lib/firmware/", fw_cfg_file);
202762306a36Sopenharmony_ci	}
202862306a36Sopenharmony_ci
202962306a36Sopenharmony_cileave:
203062306a36Sopenharmony_ci	kfree(cfg_data);
203162306a36Sopenharmony_ci	release_firmware(cf);
203262306a36Sopenharmony_ci	return ret;
203362306a36Sopenharmony_ci}
203462306a36Sopenharmony_ci
203562306a36Sopenharmony_ci/*
203662306a36Sopenharmony_ci * HW initialization: contact FW, obtain config, perform basic init.
203762306a36Sopenharmony_ci *
203862306a36Sopenharmony_ci * If the firmware we're dealing with has Configuration File support, then
203962306a36Sopenharmony_ci * we use that to perform all configuration -- either using the configuration
204062306a36Sopenharmony_ci * file stored in flash on the adapter or using a filesystem-local file
204162306a36Sopenharmony_ci * if available.
204262306a36Sopenharmony_ci *
204362306a36Sopenharmony_ci * If we don't have configuration file support in the firmware, then we'll
204462306a36Sopenharmony_ci * have to set things up the old fashioned way with hard-coded register
204562306a36Sopenharmony_ci * writes and firmware commands ...
204662306a36Sopenharmony_ci */
204762306a36Sopenharmony_ci
204862306a36Sopenharmony_ci/*
204962306a36Sopenharmony_ci * Attempt to initialize the HW via a Firmware Configuration File.
205062306a36Sopenharmony_ci */
205162306a36Sopenharmony_cistatic int
205262306a36Sopenharmony_cicsio_hw_use_fwconfig(struct csio_hw *hw, int reset, u32 *fw_cfg_param)
205362306a36Sopenharmony_ci{
205462306a36Sopenharmony_ci	struct csio_mb	*mbp = NULL;
205562306a36Sopenharmony_ci	struct fw_caps_config_cmd *caps_cmd;
205662306a36Sopenharmony_ci	unsigned int mtype, maddr;
205762306a36Sopenharmony_ci	int rv = -EINVAL;
205862306a36Sopenharmony_ci	uint32_t finiver = 0, finicsum = 0, cfcsum = 0;
205962306a36Sopenharmony_ci	char path[64];
206062306a36Sopenharmony_ci	char *config_name = NULL;
206162306a36Sopenharmony_ci
206262306a36Sopenharmony_ci	/*
206362306a36Sopenharmony_ci	 * Reset device if necessary
206462306a36Sopenharmony_ci	 */
206562306a36Sopenharmony_ci	if (reset) {
206662306a36Sopenharmony_ci		rv = csio_do_reset(hw, true);
206762306a36Sopenharmony_ci		if (rv != 0)
206862306a36Sopenharmony_ci			goto bye;
206962306a36Sopenharmony_ci	}
207062306a36Sopenharmony_ci
207162306a36Sopenharmony_ci	/*
207262306a36Sopenharmony_ci	 * If we have a configuration file in host ,
207362306a36Sopenharmony_ci	 * then use that.  Otherwise, use the configuration file stored
207462306a36Sopenharmony_ci	 * in the HW flash ...
207562306a36Sopenharmony_ci	 */
207662306a36Sopenharmony_ci	spin_unlock_irq(&hw->lock);
207762306a36Sopenharmony_ci	rv = csio_hw_flash_config(hw, fw_cfg_param, path);
207862306a36Sopenharmony_ci	spin_lock_irq(&hw->lock);
207962306a36Sopenharmony_ci	if (rv != 0) {
208062306a36Sopenharmony_ci		/*
208162306a36Sopenharmony_ci		 * config file was not found. Use default
208262306a36Sopenharmony_ci		 * config file from flash.
208362306a36Sopenharmony_ci		 */
208462306a36Sopenharmony_ci		config_name = "On FLASH";
208562306a36Sopenharmony_ci		mtype = FW_MEMTYPE_CF_FLASH;
208662306a36Sopenharmony_ci		maddr = hw->chip_ops->chip_flash_cfg_addr(hw);
208762306a36Sopenharmony_ci	} else {
208862306a36Sopenharmony_ci		config_name = path;
208962306a36Sopenharmony_ci		mtype = FW_PARAMS_PARAM_Y_G(*fw_cfg_param);
209062306a36Sopenharmony_ci		maddr = FW_PARAMS_PARAM_Z_G(*fw_cfg_param) << 16;
209162306a36Sopenharmony_ci	}
209262306a36Sopenharmony_ci
209362306a36Sopenharmony_ci	mbp = mempool_alloc(hw->mb_mempool, GFP_ATOMIC);
209462306a36Sopenharmony_ci	if (!mbp) {
209562306a36Sopenharmony_ci		CSIO_INC_STATS(hw, n_err_nomem);
209662306a36Sopenharmony_ci		return -ENOMEM;
209762306a36Sopenharmony_ci	}
209862306a36Sopenharmony_ci	/*
209962306a36Sopenharmony_ci	 * Tell the firmware to process the indicated Configuration File.
210062306a36Sopenharmony_ci	 * If there are no errors and the caller has provided return value
210162306a36Sopenharmony_ci	 * pointers for the [fini] section version, checksum and computed
210262306a36Sopenharmony_ci	 * checksum, pass those back to the caller.
210362306a36Sopenharmony_ci	 */
210462306a36Sopenharmony_ci	caps_cmd = (struct fw_caps_config_cmd *)(mbp->mb);
210562306a36Sopenharmony_ci	CSIO_INIT_MBP(mbp, caps_cmd, CSIO_MB_DEFAULT_TMO, hw, NULL, 1);
210662306a36Sopenharmony_ci	caps_cmd->op_to_write =
210762306a36Sopenharmony_ci		htonl(FW_CMD_OP_V(FW_CAPS_CONFIG_CMD) |
210862306a36Sopenharmony_ci		      FW_CMD_REQUEST_F |
210962306a36Sopenharmony_ci		      FW_CMD_READ_F);
211062306a36Sopenharmony_ci	caps_cmd->cfvalid_to_len16 =
211162306a36Sopenharmony_ci		htonl(FW_CAPS_CONFIG_CMD_CFVALID_F |
211262306a36Sopenharmony_ci		      FW_CAPS_CONFIG_CMD_MEMTYPE_CF_V(mtype) |
211362306a36Sopenharmony_ci		      FW_CAPS_CONFIG_CMD_MEMADDR64K_CF_V(maddr >> 16) |
211462306a36Sopenharmony_ci		      FW_LEN16(*caps_cmd));
211562306a36Sopenharmony_ci
211662306a36Sopenharmony_ci	if (csio_mb_issue(hw, mbp)) {
211762306a36Sopenharmony_ci		rv = -EINVAL;
211862306a36Sopenharmony_ci		goto bye;
211962306a36Sopenharmony_ci	}
212062306a36Sopenharmony_ci
212162306a36Sopenharmony_ci	rv = csio_mb_fw_retval(mbp);
212262306a36Sopenharmony_ci	 /* If the CAPS_CONFIG failed with an ENOENT (for a Firmware
212362306a36Sopenharmony_ci	  * Configuration File in FLASH), our last gasp effort is to use the
212462306a36Sopenharmony_ci	  * Firmware Configuration File which is embedded in the
212562306a36Sopenharmony_ci	  * firmware.  A very few early versions of the firmware didn't
212662306a36Sopenharmony_ci	  * have one embedded but we can ignore those.
212762306a36Sopenharmony_ci	  */
212862306a36Sopenharmony_ci	if (rv == ENOENT) {
212962306a36Sopenharmony_ci		CSIO_INIT_MBP(mbp, caps_cmd, CSIO_MB_DEFAULT_TMO, hw, NULL, 1);
213062306a36Sopenharmony_ci		caps_cmd->op_to_write = htonl(FW_CMD_OP_V(FW_CAPS_CONFIG_CMD) |
213162306a36Sopenharmony_ci					      FW_CMD_REQUEST_F |
213262306a36Sopenharmony_ci					      FW_CMD_READ_F);
213362306a36Sopenharmony_ci		caps_cmd->cfvalid_to_len16 = htonl(FW_LEN16(*caps_cmd));
213462306a36Sopenharmony_ci
213562306a36Sopenharmony_ci		if (csio_mb_issue(hw, mbp)) {
213662306a36Sopenharmony_ci			rv = -EINVAL;
213762306a36Sopenharmony_ci			goto bye;
213862306a36Sopenharmony_ci		}
213962306a36Sopenharmony_ci
214062306a36Sopenharmony_ci		rv = csio_mb_fw_retval(mbp);
214162306a36Sopenharmony_ci		config_name = "Firmware Default";
214262306a36Sopenharmony_ci	}
214362306a36Sopenharmony_ci	if (rv != FW_SUCCESS)
214462306a36Sopenharmony_ci		goto bye;
214562306a36Sopenharmony_ci
214662306a36Sopenharmony_ci	finiver = ntohl(caps_cmd->finiver);
214762306a36Sopenharmony_ci	finicsum = ntohl(caps_cmd->finicsum);
214862306a36Sopenharmony_ci	cfcsum = ntohl(caps_cmd->cfcsum);
214962306a36Sopenharmony_ci
215062306a36Sopenharmony_ci	/*
215162306a36Sopenharmony_ci	 * And now tell the firmware to use the configuration we just loaded.
215262306a36Sopenharmony_ci	 */
215362306a36Sopenharmony_ci	caps_cmd->op_to_write =
215462306a36Sopenharmony_ci		htonl(FW_CMD_OP_V(FW_CAPS_CONFIG_CMD) |
215562306a36Sopenharmony_ci		      FW_CMD_REQUEST_F |
215662306a36Sopenharmony_ci		      FW_CMD_WRITE_F);
215762306a36Sopenharmony_ci	caps_cmd->cfvalid_to_len16 = htonl(FW_LEN16(*caps_cmd));
215862306a36Sopenharmony_ci
215962306a36Sopenharmony_ci	if (csio_mb_issue(hw, mbp)) {
216062306a36Sopenharmony_ci		rv = -EINVAL;
216162306a36Sopenharmony_ci		goto bye;
216262306a36Sopenharmony_ci	}
216362306a36Sopenharmony_ci
216462306a36Sopenharmony_ci	rv = csio_mb_fw_retval(mbp);
216562306a36Sopenharmony_ci	if (rv != FW_SUCCESS) {
216662306a36Sopenharmony_ci		csio_dbg(hw, "FW_CAPS_CONFIG_CMD returned %d!\n", rv);
216762306a36Sopenharmony_ci		goto bye;
216862306a36Sopenharmony_ci	}
216962306a36Sopenharmony_ci
217062306a36Sopenharmony_ci	if (finicsum != cfcsum) {
217162306a36Sopenharmony_ci		csio_warn(hw,
217262306a36Sopenharmony_ci		      "Config File checksum mismatch: csum=%#x, computed=%#x\n",
217362306a36Sopenharmony_ci		      finicsum, cfcsum);
217462306a36Sopenharmony_ci	}
217562306a36Sopenharmony_ci
217662306a36Sopenharmony_ci	/* Validate device capabilities */
217762306a36Sopenharmony_ci	rv = csio_hw_validate_caps(hw, mbp);
217862306a36Sopenharmony_ci	if (rv != 0)
217962306a36Sopenharmony_ci		goto bye;
218062306a36Sopenharmony_ci
218162306a36Sopenharmony_ci	mempool_free(mbp, hw->mb_mempool);
218262306a36Sopenharmony_ci	mbp = NULL;
218362306a36Sopenharmony_ci
218462306a36Sopenharmony_ci	/*
218562306a36Sopenharmony_ci	 * Note that we're operating with parameters
218662306a36Sopenharmony_ci	 * not supplied by the driver, rather than from hard-wired
218762306a36Sopenharmony_ci	 * initialization constants buried in the driver.
218862306a36Sopenharmony_ci	 */
218962306a36Sopenharmony_ci	hw->flags |= CSIO_HWF_USING_SOFT_PARAMS;
219062306a36Sopenharmony_ci
219162306a36Sopenharmony_ci	/* device parameters */
219262306a36Sopenharmony_ci	rv = csio_get_device_params(hw);
219362306a36Sopenharmony_ci	if (rv != 0)
219462306a36Sopenharmony_ci		goto bye;
219562306a36Sopenharmony_ci
219662306a36Sopenharmony_ci	/* Configure SGE */
219762306a36Sopenharmony_ci	csio_wr_sge_init(hw);
219862306a36Sopenharmony_ci
219962306a36Sopenharmony_ci	/*
220062306a36Sopenharmony_ci	 * And finally tell the firmware to initialize itself using the
220162306a36Sopenharmony_ci	 * parameters from the Configuration File.
220262306a36Sopenharmony_ci	 */
220362306a36Sopenharmony_ci	/* Post event to notify completion of configuration */
220462306a36Sopenharmony_ci	csio_post_event(&hw->sm, CSIO_HWE_INIT);
220562306a36Sopenharmony_ci
220662306a36Sopenharmony_ci	csio_info(hw, "Successfully configure using Firmware "
220762306a36Sopenharmony_ci		  "Configuration File %s, version %#x, computed checksum %#x\n",
220862306a36Sopenharmony_ci		  config_name, finiver, cfcsum);
220962306a36Sopenharmony_ci	return 0;
221062306a36Sopenharmony_ci
221162306a36Sopenharmony_ci	/*
221262306a36Sopenharmony_ci	 * Something bad happened.  Return the error ...
221362306a36Sopenharmony_ci	 */
221462306a36Sopenharmony_cibye:
221562306a36Sopenharmony_ci	if (mbp)
221662306a36Sopenharmony_ci		mempool_free(mbp, hw->mb_mempool);
221762306a36Sopenharmony_ci	hw->flags &= ~CSIO_HWF_USING_SOFT_PARAMS;
221862306a36Sopenharmony_ci	csio_warn(hw, "Configuration file error %d\n", rv);
221962306a36Sopenharmony_ci	return rv;
222062306a36Sopenharmony_ci}
222162306a36Sopenharmony_ci
222262306a36Sopenharmony_ci/* Is the given firmware API compatible with the one the driver was compiled
222362306a36Sopenharmony_ci * with?
222462306a36Sopenharmony_ci */
222562306a36Sopenharmony_cistatic int fw_compatible(const struct fw_hdr *hdr1, const struct fw_hdr *hdr2)
222662306a36Sopenharmony_ci{
222762306a36Sopenharmony_ci
222862306a36Sopenharmony_ci	/* short circuit if it's the exact same firmware version */
222962306a36Sopenharmony_ci	if (hdr1->chip == hdr2->chip && hdr1->fw_ver == hdr2->fw_ver)
223062306a36Sopenharmony_ci		return 1;
223162306a36Sopenharmony_ci
223262306a36Sopenharmony_ci#define SAME_INTF(x) (hdr1->intfver_##x == hdr2->intfver_##x)
223362306a36Sopenharmony_ci	if (hdr1->chip == hdr2->chip && SAME_INTF(nic) && SAME_INTF(vnic) &&
223462306a36Sopenharmony_ci	    SAME_INTF(ri) && SAME_INTF(iscsi) && SAME_INTF(fcoe))
223562306a36Sopenharmony_ci		return 1;
223662306a36Sopenharmony_ci#undef SAME_INTF
223762306a36Sopenharmony_ci
223862306a36Sopenharmony_ci	return 0;
223962306a36Sopenharmony_ci}
224062306a36Sopenharmony_ci
224162306a36Sopenharmony_ci/* The firmware in the filesystem is usable, but should it be installed?
224262306a36Sopenharmony_ci * This routine explains itself in detail if it indicates the filesystem
224362306a36Sopenharmony_ci * firmware should be installed.
224462306a36Sopenharmony_ci */
224562306a36Sopenharmony_cistatic int csio_should_install_fs_fw(struct csio_hw *hw, int card_fw_usable,
224662306a36Sopenharmony_ci				int k, int c)
224762306a36Sopenharmony_ci{
224862306a36Sopenharmony_ci	const char *reason;
224962306a36Sopenharmony_ci
225062306a36Sopenharmony_ci	if (!card_fw_usable) {
225162306a36Sopenharmony_ci		reason = "incompatible or unusable";
225262306a36Sopenharmony_ci		goto install;
225362306a36Sopenharmony_ci	}
225462306a36Sopenharmony_ci
225562306a36Sopenharmony_ci	if (k > c) {
225662306a36Sopenharmony_ci		reason = "older than the version supported with this driver";
225762306a36Sopenharmony_ci		goto install;
225862306a36Sopenharmony_ci	}
225962306a36Sopenharmony_ci
226062306a36Sopenharmony_ci	return 0;
226162306a36Sopenharmony_ci
226262306a36Sopenharmony_ciinstall:
226362306a36Sopenharmony_ci	csio_err(hw, "firmware on card (%u.%u.%u.%u) is %s, "
226462306a36Sopenharmony_ci		"installing firmware %u.%u.%u.%u on card.\n",
226562306a36Sopenharmony_ci		FW_HDR_FW_VER_MAJOR_G(c), FW_HDR_FW_VER_MINOR_G(c),
226662306a36Sopenharmony_ci		FW_HDR_FW_VER_MICRO_G(c), FW_HDR_FW_VER_BUILD_G(c), reason,
226762306a36Sopenharmony_ci		FW_HDR_FW_VER_MAJOR_G(k), FW_HDR_FW_VER_MINOR_G(k),
226862306a36Sopenharmony_ci		FW_HDR_FW_VER_MICRO_G(k), FW_HDR_FW_VER_BUILD_G(k));
226962306a36Sopenharmony_ci
227062306a36Sopenharmony_ci	return 1;
227162306a36Sopenharmony_ci}
227262306a36Sopenharmony_ci
227362306a36Sopenharmony_cistatic struct fw_info fw_info_array[] = {
227462306a36Sopenharmony_ci	{
227562306a36Sopenharmony_ci		.chip = CHELSIO_T5,
227662306a36Sopenharmony_ci		.fs_name = FW_CFG_NAME_T5,
227762306a36Sopenharmony_ci		.fw_mod_name = FW_FNAME_T5,
227862306a36Sopenharmony_ci		.fw_hdr = {
227962306a36Sopenharmony_ci			.chip = FW_HDR_CHIP_T5,
228062306a36Sopenharmony_ci			.fw_ver = __cpu_to_be32(FW_VERSION(T5)),
228162306a36Sopenharmony_ci			.intfver_nic = FW_INTFVER(T5, NIC),
228262306a36Sopenharmony_ci			.intfver_vnic = FW_INTFVER(T5, VNIC),
228362306a36Sopenharmony_ci			.intfver_ri = FW_INTFVER(T5, RI),
228462306a36Sopenharmony_ci			.intfver_iscsi = FW_INTFVER(T5, ISCSI),
228562306a36Sopenharmony_ci			.intfver_fcoe = FW_INTFVER(T5, FCOE),
228662306a36Sopenharmony_ci		},
228762306a36Sopenharmony_ci	}, {
228862306a36Sopenharmony_ci		.chip = CHELSIO_T6,
228962306a36Sopenharmony_ci		.fs_name = FW_CFG_NAME_T6,
229062306a36Sopenharmony_ci		.fw_mod_name = FW_FNAME_T6,
229162306a36Sopenharmony_ci		.fw_hdr = {
229262306a36Sopenharmony_ci			.chip = FW_HDR_CHIP_T6,
229362306a36Sopenharmony_ci			.fw_ver = __cpu_to_be32(FW_VERSION(T6)),
229462306a36Sopenharmony_ci			.intfver_nic = FW_INTFVER(T6, NIC),
229562306a36Sopenharmony_ci			.intfver_vnic = FW_INTFVER(T6, VNIC),
229662306a36Sopenharmony_ci			.intfver_ri = FW_INTFVER(T6, RI),
229762306a36Sopenharmony_ci			.intfver_iscsi = FW_INTFVER(T6, ISCSI),
229862306a36Sopenharmony_ci			.intfver_fcoe = FW_INTFVER(T6, FCOE),
229962306a36Sopenharmony_ci		},
230062306a36Sopenharmony_ci	}
230162306a36Sopenharmony_ci};
230262306a36Sopenharmony_ci
230362306a36Sopenharmony_cistatic struct fw_info *find_fw_info(int chip)
230462306a36Sopenharmony_ci{
230562306a36Sopenharmony_ci	int i;
230662306a36Sopenharmony_ci
230762306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(fw_info_array); i++) {
230862306a36Sopenharmony_ci		if (fw_info_array[i].chip == chip)
230962306a36Sopenharmony_ci			return &fw_info_array[i];
231062306a36Sopenharmony_ci	}
231162306a36Sopenharmony_ci	return NULL;
231262306a36Sopenharmony_ci}
231362306a36Sopenharmony_ci
231462306a36Sopenharmony_cistatic int csio_hw_prep_fw(struct csio_hw *hw, struct fw_info *fw_info,
231562306a36Sopenharmony_ci	       const u8 *fw_data, unsigned int fw_size,
231662306a36Sopenharmony_ci	       struct fw_hdr *card_fw, enum csio_dev_state state,
231762306a36Sopenharmony_ci	       int *reset)
231862306a36Sopenharmony_ci{
231962306a36Sopenharmony_ci	int ret, card_fw_usable, fs_fw_usable;
232062306a36Sopenharmony_ci	const struct fw_hdr *fs_fw;
232162306a36Sopenharmony_ci	const struct fw_hdr *drv_fw;
232262306a36Sopenharmony_ci
232362306a36Sopenharmony_ci	drv_fw = &fw_info->fw_hdr;
232462306a36Sopenharmony_ci
232562306a36Sopenharmony_ci	/* Read the header of the firmware on the card */
232662306a36Sopenharmony_ci	ret = csio_hw_read_flash(hw, FLASH_FW_START,
232762306a36Sopenharmony_ci			    sizeof(*card_fw) / sizeof(uint32_t),
232862306a36Sopenharmony_ci			    (uint32_t *)card_fw, 1);
232962306a36Sopenharmony_ci	if (ret == 0) {
233062306a36Sopenharmony_ci		card_fw_usable = fw_compatible(drv_fw, (const void *)card_fw);
233162306a36Sopenharmony_ci	} else {
233262306a36Sopenharmony_ci		csio_err(hw,
233362306a36Sopenharmony_ci			"Unable to read card's firmware header: %d\n", ret);
233462306a36Sopenharmony_ci		card_fw_usable = 0;
233562306a36Sopenharmony_ci	}
233662306a36Sopenharmony_ci
233762306a36Sopenharmony_ci	if (fw_data != NULL) {
233862306a36Sopenharmony_ci		fs_fw = (const void *)fw_data;
233962306a36Sopenharmony_ci		fs_fw_usable = fw_compatible(drv_fw, fs_fw);
234062306a36Sopenharmony_ci	} else {
234162306a36Sopenharmony_ci		fs_fw = NULL;
234262306a36Sopenharmony_ci		fs_fw_usable = 0;
234362306a36Sopenharmony_ci	}
234462306a36Sopenharmony_ci
234562306a36Sopenharmony_ci	if (card_fw_usable && card_fw->fw_ver == drv_fw->fw_ver &&
234662306a36Sopenharmony_ci	    (!fs_fw_usable || fs_fw->fw_ver == drv_fw->fw_ver)) {
234762306a36Sopenharmony_ci		/* Common case: the firmware on the card is an exact match and
234862306a36Sopenharmony_ci		 * the filesystem one is an exact match too, or the filesystem
234962306a36Sopenharmony_ci		 * one is absent/incompatible.
235062306a36Sopenharmony_ci		 */
235162306a36Sopenharmony_ci	} else if (fs_fw_usable && state == CSIO_DEV_STATE_UNINIT &&
235262306a36Sopenharmony_ci		   csio_should_install_fs_fw(hw, card_fw_usable,
235362306a36Sopenharmony_ci					be32_to_cpu(fs_fw->fw_ver),
235462306a36Sopenharmony_ci					be32_to_cpu(card_fw->fw_ver))) {
235562306a36Sopenharmony_ci		ret = csio_hw_fw_upgrade(hw, hw->pfn, fw_data,
235662306a36Sopenharmony_ci				     fw_size, 0);
235762306a36Sopenharmony_ci		if (ret != 0) {
235862306a36Sopenharmony_ci			csio_err(hw,
235962306a36Sopenharmony_ci				"failed to install firmware: %d\n", ret);
236062306a36Sopenharmony_ci			goto bye;
236162306a36Sopenharmony_ci		}
236262306a36Sopenharmony_ci
236362306a36Sopenharmony_ci		/* Installed successfully, update the cached header too. */
236462306a36Sopenharmony_ci		memcpy(card_fw, fs_fw, sizeof(*card_fw));
236562306a36Sopenharmony_ci		card_fw_usable = 1;
236662306a36Sopenharmony_ci		*reset = 0;	/* already reset as part of load_fw */
236762306a36Sopenharmony_ci	}
236862306a36Sopenharmony_ci
236962306a36Sopenharmony_ci	if (!card_fw_usable) {
237062306a36Sopenharmony_ci		uint32_t d, c, k;
237162306a36Sopenharmony_ci
237262306a36Sopenharmony_ci		d = be32_to_cpu(drv_fw->fw_ver);
237362306a36Sopenharmony_ci		c = be32_to_cpu(card_fw->fw_ver);
237462306a36Sopenharmony_ci		k = fs_fw ? be32_to_cpu(fs_fw->fw_ver) : 0;
237562306a36Sopenharmony_ci
237662306a36Sopenharmony_ci		csio_err(hw, "Cannot find a usable firmware: "
237762306a36Sopenharmony_ci			"chip state %d, "
237862306a36Sopenharmony_ci			"driver compiled with %d.%d.%d.%d, "
237962306a36Sopenharmony_ci			"card has %d.%d.%d.%d, filesystem has %d.%d.%d.%d\n",
238062306a36Sopenharmony_ci			state,
238162306a36Sopenharmony_ci			FW_HDR_FW_VER_MAJOR_G(d), FW_HDR_FW_VER_MINOR_G(d),
238262306a36Sopenharmony_ci			FW_HDR_FW_VER_MICRO_G(d), FW_HDR_FW_VER_BUILD_G(d),
238362306a36Sopenharmony_ci			FW_HDR_FW_VER_MAJOR_G(c), FW_HDR_FW_VER_MINOR_G(c),
238462306a36Sopenharmony_ci			FW_HDR_FW_VER_MICRO_G(c), FW_HDR_FW_VER_BUILD_G(c),
238562306a36Sopenharmony_ci			FW_HDR_FW_VER_MAJOR_G(k), FW_HDR_FW_VER_MINOR_G(k),
238662306a36Sopenharmony_ci			FW_HDR_FW_VER_MICRO_G(k), FW_HDR_FW_VER_BUILD_G(k));
238762306a36Sopenharmony_ci		ret = -EINVAL;
238862306a36Sopenharmony_ci		goto bye;
238962306a36Sopenharmony_ci	}
239062306a36Sopenharmony_ci
239162306a36Sopenharmony_ci	/* We're using whatever's on the card and it's known to be good. */
239262306a36Sopenharmony_ci	hw->fwrev = be32_to_cpu(card_fw->fw_ver);
239362306a36Sopenharmony_ci	hw->tp_vers = be32_to_cpu(card_fw->tp_microcode_ver);
239462306a36Sopenharmony_ci
239562306a36Sopenharmony_cibye:
239662306a36Sopenharmony_ci	return ret;
239762306a36Sopenharmony_ci}
239862306a36Sopenharmony_ci
239962306a36Sopenharmony_ci/*
240062306a36Sopenharmony_ci * Returns -EINVAL if attempts to flash the firmware failed,
240162306a36Sopenharmony_ci * -ENOMEM if memory allocation failed else returns 0,
240262306a36Sopenharmony_ci * if flashing was not attempted because the card had the
240362306a36Sopenharmony_ci * latest firmware ECANCELED is returned
240462306a36Sopenharmony_ci */
240562306a36Sopenharmony_cistatic int
240662306a36Sopenharmony_cicsio_hw_flash_fw(struct csio_hw *hw, int *reset)
240762306a36Sopenharmony_ci{
240862306a36Sopenharmony_ci	int ret = -ECANCELED;
240962306a36Sopenharmony_ci	const struct firmware *fw;
241062306a36Sopenharmony_ci	struct fw_info *fw_info;
241162306a36Sopenharmony_ci	struct fw_hdr *card_fw;
241262306a36Sopenharmony_ci	struct pci_dev *pci_dev = hw->pdev;
241362306a36Sopenharmony_ci	struct device *dev = &pci_dev->dev ;
241462306a36Sopenharmony_ci	const u8 *fw_data = NULL;
241562306a36Sopenharmony_ci	unsigned int fw_size = 0;
241662306a36Sopenharmony_ci	const char *fw_bin_file;
241762306a36Sopenharmony_ci
241862306a36Sopenharmony_ci	/* This is the firmware whose headers the driver was compiled
241962306a36Sopenharmony_ci	 * against
242062306a36Sopenharmony_ci	 */
242162306a36Sopenharmony_ci	fw_info = find_fw_info(CHELSIO_CHIP_VERSION(hw->chip_id));
242262306a36Sopenharmony_ci	if (fw_info == NULL) {
242362306a36Sopenharmony_ci		csio_err(hw,
242462306a36Sopenharmony_ci			"unable to get firmware info for chip %d.\n",
242562306a36Sopenharmony_ci			CHELSIO_CHIP_VERSION(hw->chip_id));
242662306a36Sopenharmony_ci		return -EINVAL;
242762306a36Sopenharmony_ci	}
242862306a36Sopenharmony_ci
242962306a36Sopenharmony_ci	/* allocate memory to read the header of the firmware on the
243062306a36Sopenharmony_ci	 * card
243162306a36Sopenharmony_ci	 */
243262306a36Sopenharmony_ci	card_fw = kmalloc(sizeof(*card_fw), GFP_KERNEL);
243362306a36Sopenharmony_ci	if (!card_fw)
243462306a36Sopenharmony_ci		return -ENOMEM;
243562306a36Sopenharmony_ci
243662306a36Sopenharmony_ci	if (csio_is_t5(pci_dev->device & CSIO_HW_CHIP_MASK))
243762306a36Sopenharmony_ci		fw_bin_file = FW_FNAME_T5;
243862306a36Sopenharmony_ci	else
243962306a36Sopenharmony_ci		fw_bin_file = FW_FNAME_T6;
244062306a36Sopenharmony_ci
244162306a36Sopenharmony_ci	if (request_firmware(&fw, fw_bin_file, dev) < 0) {
244262306a36Sopenharmony_ci		csio_err(hw, "could not find firmware image %s, err: %d\n",
244362306a36Sopenharmony_ci			 fw_bin_file, ret);
244462306a36Sopenharmony_ci	} else {
244562306a36Sopenharmony_ci		fw_data = fw->data;
244662306a36Sopenharmony_ci		fw_size = fw->size;
244762306a36Sopenharmony_ci	}
244862306a36Sopenharmony_ci
244962306a36Sopenharmony_ci	/* upgrade FW logic */
245062306a36Sopenharmony_ci	ret = csio_hw_prep_fw(hw, fw_info, fw_data, fw_size, card_fw,
245162306a36Sopenharmony_ci			 hw->fw_state, reset);
245262306a36Sopenharmony_ci
245362306a36Sopenharmony_ci	/* Cleaning up */
245462306a36Sopenharmony_ci	if (fw != NULL)
245562306a36Sopenharmony_ci		release_firmware(fw);
245662306a36Sopenharmony_ci	kfree(card_fw);
245762306a36Sopenharmony_ci	return ret;
245862306a36Sopenharmony_ci}
245962306a36Sopenharmony_ci
246062306a36Sopenharmony_cistatic int csio_hw_check_fwver(struct csio_hw *hw)
246162306a36Sopenharmony_ci{
246262306a36Sopenharmony_ci	if (csio_is_t6(hw->pdev->device & CSIO_HW_CHIP_MASK) &&
246362306a36Sopenharmony_ci	    (hw->fwrev < CSIO_MIN_T6_FW)) {
246462306a36Sopenharmony_ci		csio_hw_print_fw_version(hw, "T6 unsupported fw");
246562306a36Sopenharmony_ci		return -1;
246662306a36Sopenharmony_ci	}
246762306a36Sopenharmony_ci
246862306a36Sopenharmony_ci	return 0;
246962306a36Sopenharmony_ci}
247062306a36Sopenharmony_ci
247162306a36Sopenharmony_ci/*
247262306a36Sopenharmony_ci * csio_hw_configure - Configure HW
247362306a36Sopenharmony_ci * @hw - HW module
247462306a36Sopenharmony_ci *
247562306a36Sopenharmony_ci */
247662306a36Sopenharmony_cistatic void
247762306a36Sopenharmony_cicsio_hw_configure(struct csio_hw *hw)
247862306a36Sopenharmony_ci{
247962306a36Sopenharmony_ci	int reset = 1;
248062306a36Sopenharmony_ci	int rv;
248162306a36Sopenharmony_ci	u32 param[1];
248262306a36Sopenharmony_ci
248362306a36Sopenharmony_ci	rv = csio_hw_dev_ready(hw);
248462306a36Sopenharmony_ci	if (rv != 0) {
248562306a36Sopenharmony_ci		CSIO_INC_STATS(hw, n_err_fatal);
248662306a36Sopenharmony_ci		csio_post_event(&hw->sm, CSIO_HWE_FATAL);
248762306a36Sopenharmony_ci		goto out;
248862306a36Sopenharmony_ci	}
248962306a36Sopenharmony_ci
249062306a36Sopenharmony_ci	/* HW version */
249162306a36Sopenharmony_ci	hw->chip_ver = (char)csio_rd_reg32(hw, PL_REV_A);
249262306a36Sopenharmony_ci
249362306a36Sopenharmony_ci	/* Needed for FW download */
249462306a36Sopenharmony_ci	rv = csio_hw_get_flash_params(hw);
249562306a36Sopenharmony_ci	if (rv != 0) {
249662306a36Sopenharmony_ci		csio_err(hw, "Failed to get serial flash params rv:%d\n", rv);
249762306a36Sopenharmony_ci		csio_post_event(&hw->sm, CSIO_HWE_FATAL);
249862306a36Sopenharmony_ci		goto out;
249962306a36Sopenharmony_ci	}
250062306a36Sopenharmony_ci
250162306a36Sopenharmony_ci	/* Set PCIe completion timeout to 4 seconds */
250262306a36Sopenharmony_ci	if (pci_is_pcie(hw->pdev))
250362306a36Sopenharmony_ci		pcie_capability_clear_and_set_word(hw->pdev, PCI_EXP_DEVCTL2,
250462306a36Sopenharmony_ci				PCI_EXP_DEVCTL2_COMP_TIMEOUT, 0xd);
250562306a36Sopenharmony_ci
250662306a36Sopenharmony_ci	hw->chip_ops->chip_set_mem_win(hw, MEMWIN_CSIOSTOR);
250762306a36Sopenharmony_ci
250862306a36Sopenharmony_ci	rv = csio_hw_get_fw_version(hw, &hw->fwrev);
250962306a36Sopenharmony_ci	if (rv != 0)
251062306a36Sopenharmony_ci		goto out;
251162306a36Sopenharmony_ci
251262306a36Sopenharmony_ci	csio_hw_print_fw_version(hw, "Firmware revision");
251362306a36Sopenharmony_ci
251462306a36Sopenharmony_ci	rv = csio_do_hello(hw, &hw->fw_state);
251562306a36Sopenharmony_ci	if (rv != 0) {
251662306a36Sopenharmony_ci		CSIO_INC_STATS(hw, n_err_fatal);
251762306a36Sopenharmony_ci		csio_post_event(&hw->sm, CSIO_HWE_FATAL);
251862306a36Sopenharmony_ci		goto out;
251962306a36Sopenharmony_ci	}
252062306a36Sopenharmony_ci
252162306a36Sopenharmony_ci	/* Read vpd */
252262306a36Sopenharmony_ci	rv = csio_hw_get_vpd_params(hw, &hw->vpd);
252362306a36Sopenharmony_ci	if (rv != 0)
252462306a36Sopenharmony_ci		goto out;
252562306a36Sopenharmony_ci
252662306a36Sopenharmony_ci	csio_hw_get_fw_version(hw, &hw->fwrev);
252762306a36Sopenharmony_ci	csio_hw_get_tp_version(hw, &hw->tp_vers);
252862306a36Sopenharmony_ci	if (csio_is_hw_master(hw) && hw->fw_state != CSIO_DEV_STATE_INIT) {
252962306a36Sopenharmony_ci
253062306a36Sopenharmony_ci			/* Do firmware update */
253162306a36Sopenharmony_ci		spin_unlock_irq(&hw->lock);
253262306a36Sopenharmony_ci		rv = csio_hw_flash_fw(hw, &reset);
253362306a36Sopenharmony_ci		spin_lock_irq(&hw->lock);
253462306a36Sopenharmony_ci
253562306a36Sopenharmony_ci		if (rv != 0)
253662306a36Sopenharmony_ci			goto out;
253762306a36Sopenharmony_ci
253862306a36Sopenharmony_ci		rv = csio_hw_check_fwver(hw);
253962306a36Sopenharmony_ci		if (rv < 0)
254062306a36Sopenharmony_ci			goto out;
254162306a36Sopenharmony_ci
254262306a36Sopenharmony_ci		/* If the firmware doesn't support Configuration Files,
254362306a36Sopenharmony_ci		 * return an error.
254462306a36Sopenharmony_ci		 */
254562306a36Sopenharmony_ci		rv = csio_hw_check_fwconfig(hw, param);
254662306a36Sopenharmony_ci		if (rv != 0) {
254762306a36Sopenharmony_ci			csio_info(hw, "Firmware doesn't support "
254862306a36Sopenharmony_ci				  "Firmware Configuration files\n");
254962306a36Sopenharmony_ci			goto out;
255062306a36Sopenharmony_ci		}
255162306a36Sopenharmony_ci
255262306a36Sopenharmony_ci		/* The firmware provides us with a memory buffer where we can
255362306a36Sopenharmony_ci		 * load a Configuration File from the host if we want to
255462306a36Sopenharmony_ci		 * override the Configuration File in flash.
255562306a36Sopenharmony_ci		 */
255662306a36Sopenharmony_ci		rv = csio_hw_use_fwconfig(hw, reset, param);
255762306a36Sopenharmony_ci		if (rv == -ENOENT) {
255862306a36Sopenharmony_ci			csio_info(hw, "Could not initialize "
255962306a36Sopenharmony_ci				  "adapter, error%d\n", rv);
256062306a36Sopenharmony_ci			goto out;
256162306a36Sopenharmony_ci		}
256262306a36Sopenharmony_ci		if (rv != 0) {
256362306a36Sopenharmony_ci			csio_info(hw, "Could not initialize "
256462306a36Sopenharmony_ci				  "adapter, error%d\n", rv);
256562306a36Sopenharmony_ci			goto out;
256662306a36Sopenharmony_ci		}
256762306a36Sopenharmony_ci
256862306a36Sopenharmony_ci	} else {
256962306a36Sopenharmony_ci		rv = csio_hw_check_fwver(hw);
257062306a36Sopenharmony_ci		if (rv < 0)
257162306a36Sopenharmony_ci			goto out;
257262306a36Sopenharmony_ci
257362306a36Sopenharmony_ci		if (hw->fw_state == CSIO_DEV_STATE_INIT) {
257462306a36Sopenharmony_ci
257562306a36Sopenharmony_ci			hw->flags |= CSIO_HWF_USING_SOFT_PARAMS;
257662306a36Sopenharmony_ci
257762306a36Sopenharmony_ci			/* device parameters */
257862306a36Sopenharmony_ci			rv = csio_get_device_params(hw);
257962306a36Sopenharmony_ci			if (rv != 0)
258062306a36Sopenharmony_ci				goto out;
258162306a36Sopenharmony_ci
258262306a36Sopenharmony_ci			/* Get device capabilities */
258362306a36Sopenharmony_ci			rv = csio_config_device_caps(hw);
258462306a36Sopenharmony_ci			if (rv != 0)
258562306a36Sopenharmony_ci				goto out;
258662306a36Sopenharmony_ci
258762306a36Sopenharmony_ci			/* Configure SGE */
258862306a36Sopenharmony_ci			csio_wr_sge_init(hw);
258962306a36Sopenharmony_ci
259062306a36Sopenharmony_ci			/* Post event to notify completion of configuration */
259162306a36Sopenharmony_ci			csio_post_event(&hw->sm, CSIO_HWE_INIT);
259262306a36Sopenharmony_ci			goto out;
259362306a36Sopenharmony_ci		}
259462306a36Sopenharmony_ci	} /* if not master */
259562306a36Sopenharmony_ci
259662306a36Sopenharmony_ciout:
259762306a36Sopenharmony_ci	return;
259862306a36Sopenharmony_ci}
259962306a36Sopenharmony_ci
260062306a36Sopenharmony_ci/*
260162306a36Sopenharmony_ci * csio_hw_initialize - Initialize HW
260262306a36Sopenharmony_ci * @hw - HW module
260362306a36Sopenharmony_ci *
260462306a36Sopenharmony_ci */
260562306a36Sopenharmony_cistatic void
260662306a36Sopenharmony_cicsio_hw_initialize(struct csio_hw *hw)
260762306a36Sopenharmony_ci{
260862306a36Sopenharmony_ci	struct csio_mb	*mbp;
260962306a36Sopenharmony_ci	enum fw_retval retval;
261062306a36Sopenharmony_ci	int rv;
261162306a36Sopenharmony_ci	int i;
261262306a36Sopenharmony_ci
261362306a36Sopenharmony_ci	if (csio_is_hw_master(hw) && hw->fw_state != CSIO_DEV_STATE_INIT) {
261462306a36Sopenharmony_ci		mbp = mempool_alloc(hw->mb_mempool, GFP_ATOMIC);
261562306a36Sopenharmony_ci		if (!mbp)
261662306a36Sopenharmony_ci			goto out;
261762306a36Sopenharmony_ci
261862306a36Sopenharmony_ci		csio_mb_initialize(hw, mbp, CSIO_MB_DEFAULT_TMO, NULL);
261962306a36Sopenharmony_ci
262062306a36Sopenharmony_ci		if (csio_mb_issue(hw, mbp)) {
262162306a36Sopenharmony_ci			csio_err(hw, "Issue of FW_INITIALIZE_CMD failed!\n");
262262306a36Sopenharmony_ci			goto free_and_out;
262362306a36Sopenharmony_ci		}
262462306a36Sopenharmony_ci
262562306a36Sopenharmony_ci		retval = csio_mb_fw_retval(mbp);
262662306a36Sopenharmony_ci		if (retval != FW_SUCCESS) {
262762306a36Sopenharmony_ci			csio_err(hw, "FW_INITIALIZE_CMD returned 0x%x!\n",
262862306a36Sopenharmony_ci				 retval);
262962306a36Sopenharmony_ci			goto free_and_out;
263062306a36Sopenharmony_ci		}
263162306a36Sopenharmony_ci
263262306a36Sopenharmony_ci		mempool_free(mbp, hw->mb_mempool);
263362306a36Sopenharmony_ci	}
263462306a36Sopenharmony_ci
263562306a36Sopenharmony_ci	rv = csio_get_fcoe_resinfo(hw);
263662306a36Sopenharmony_ci	if (rv != 0) {
263762306a36Sopenharmony_ci		csio_err(hw, "Failed to read fcoe resource info: %d\n", rv);
263862306a36Sopenharmony_ci		goto out;
263962306a36Sopenharmony_ci	}
264062306a36Sopenharmony_ci
264162306a36Sopenharmony_ci	spin_unlock_irq(&hw->lock);
264262306a36Sopenharmony_ci	rv = csio_config_queues(hw);
264362306a36Sopenharmony_ci	spin_lock_irq(&hw->lock);
264462306a36Sopenharmony_ci
264562306a36Sopenharmony_ci	if (rv != 0) {
264662306a36Sopenharmony_ci		csio_err(hw, "Config of queues failed!: %d\n", rv);
264762306a36Sopenharmony_ci		goto out;
264862306a36Sopenharmony_ci	}
264962306a36Sopenharmony_ci
265062306a36Sopenharmony_ci	for (i = 0; i < hw->num_pports; i++)
265162306a36Sopenharmony_ci		hw->pport[i].mod_type = FW_PORT_MOD_TYPE_NA;
265262306a36Sopenharmony_ci
265362306a36Sopenharmony_ci	if (csio_is_hw_master(hw) && hw->fw_state != CSIO_DEV_STATE_INIT) {
265462306a36Sopenharmony_ci		rv = csio_enable_ports(hw);
265562306a36Sopenharmony_ci		if (rv != 0) {
265662306a36Sopenharmony_ci			csio_err(hw, "Failed to enable ports: %d\n", rv);
265762306a36Sopenharmony_ci			goto out;
265862306a36Sopenharmony_ci		}
265962306a36Sopenharmony_ci	}
266062306a36Sopenharmony_ci
266162306a36Sopenharmony_ci	csio_post_event(&hw->sm, CSIO_HWE_INIT_DONE);
266262306a36Sopenharmony_ci	return;
266362306a36Sopenharmony_ci
266462306a36Sopenharmony_cifree_and_out:
266562306a36Sopenharmony_ci	mempool_free(mbp, hw->mb_mempool);
266662306a36Sopenharmony_ciout:
266762306a36Sopenharmony_ci	return;
266862306a36Sopenharmony_ci}
266962306a36Sopenharmony_ci
267062306a36Sopenharmony_ci#define PF_INTR_MASK (PFSW_F | PFCIM_F)
267162306a36Sopenharmony_ci
267262306a36Sopenharmony_ci/*
267362306a36Sopenharmony_ci * csio_hw_intr_enable - Enable HW interrupts
267462306a36Sopenharmony_ci * @hw: Pointer to HW module.
267562306a36Sopenharmony_ci *
267662306a36Sopenharmony_ci * Enable interrupts in HW registers.
267762306a36Sopenharmony_ci */
267862306a36Sopenharmony_cistatic void
267962306a36Sopenharmony_cicsio_hw_intr_enable(struct csio_hw *hw)
268062306a36Sopenharmony_ci{
268162306a36Sopenharmony_ci	uint16_t vec = (uint16_t)csio_get_mb_intr_idx(csio_hw_to_mbm(hw));
268262306a36Sopenharmony_ci	u32 pf = 0;
268362306a36Sopenharmony_ci	uint32_t pl = csio_rd_reg32(hw, PL_INT_ENABLE_A);
268462306a36Sopenharmony_ci
268562306a36Sopenharmony_ci	if (csio_is_t5(hw->pdev->device & CSIO_HW_CHIP_MASK))
268662306a36Sopenharmony_ci		pf = SOURCEPF_G(csio_rd_reg32(hw, PL_WHOAMI_A));
268762306a36Sopenharmony_ci	else
268862306a36Sopenharmony_ci		pf = T6_SOURCEPF_G(csio_rd_reg32(hw, PL_WHOAMI_A));
268962306a36Sopenharmony_ci
269062306a36Sopenharmony_ci	/*
269162306a36Sopenharmony_ci	 * Set aivec for MSI/MSIX. PCIE_PF_CFG.INTXType is set up
269262306a36Sopenharmony_ci	 * by FW, so do nothing for INTX.
269362306a36Sopenharmony_ci	 */
269462306a36Sopenharmony_ci	if (hw->intr_mode == CSIO_IM_MSIX)
269562306a36Sopenharmony_ci		csio_set_reg_field(hw, MYPF_REG(PCIE_PF_CFG_A),
269662306a36Sopenharmony_ci				   AIVEC_V(AIVEC_M), vec);
269762306a36Sopenharmony_ci	else if (hw->intr_mode == CSIO_IM_MSI)
269862306a36Sopenharmony_ci		csio_set_reg_field(hw, MYPF_REG(PCIE_PF_CFG_A),
269962306a36Sopenharmony_ci				   AIVEC_V(AIVEC_M), 0);
270062306a36Sopenharmony_ci
270162306a36Sopenharmony_ci	csio_wr_reg32(hw, PF_INTR_MASK, MYPF_REG(PL_PF_INT_ENABLE_A));
270262306a36Sopenharmony_ci
270362306a36Sopenharmony_ci	/* Turn on MB interrupts - this will internally flush PIO as well */
270462306a36Sopenharmony_ci	csio_mb_intr_enable(hw);
270562306a36Sopenharmony_ci
270662306a36Sopenharmony_ci	/* These are common registers - only a master can modify them */
270762306a36Sopenharmony_ci	if (csio_is_hw_master(hw)) {
270862306a36Sopenharmony_ci		/*
270962306a36Sopenharmony_ci		 * Disable the Serial FLASH interrupt, if enabled!
271062306a36Sopenharmony_ci		 */
271162306a36Sopenharmony_ci		pl &= (~SF_F);
271262306a36Sopenharmony_ci		csio_wr_reg32(hw, pl, PL_INT_ENABLE_A);
271362306a36Sopenharmony_ci
271462306a36Sopenharmony_ci		csio_wr_reg32(hw, ERR_CPL_EXCEED_IQE_SIZE_F |
271562306a36Sopenharmony_ci			      EGRESS_SIZE_ERR_F | ERR_INVALID_CIDX_INC_F |
271662306a36Sopenharmony_ci			      ERR_CPL_OPCODE_0_F | ERR_DROPPED_DB_F |
271762306a36Sopenharmony_ci			      ERR_DATA_CPL_ON_HIGH_QID1_F |
271862306a36Sopenharmony_ci			      ERR_DATA_CPL_ON_HIGH_QID0_F | ERR_BAD_DB_PIDX3_F |
271962306a36Sopenharmony_ci			      ERR_BAD_DB_PIDX2_F | ERR_BAD_DB_PIDX1_F |
272062306a36Sopenharmony_ci			      ERR_BAD_DB_PIDX0_F | ERR_ING_CTXT_PRIO_F |
272162306a36Sopenharmony_ci			      ERR_EGR_CTXT_PRIO_F | INGRESS_SIZE_ERR_F,
272262306a36Sopenharmony_ci			      SGE_INT_ENABLE3_A);
272362306a36Sopenharmony_ci		csio_set_reg_field(hw, PL_INT_MAP0_A, 0, 1 << pf);
272462306a36Sopenharmony_ci	}
272562306a36Sopenharmony_ci
272662306a36Sopenharmony_ci	hw->flags |= CSIO_HWF_HW_INTR_ENABLED;
272762306a36Sopenharmony_ci
272862306a36Sopenharmony_ci}
272962306a36Sopenharmony_ci
273062306a36Sopenharmony_ci/*
273162306a36Sopenharmony_ci * csio_hw_intr_disable - Disable HW interrupts
273262306a36Sopenharmony_ci * @hw: Pointer to HW module.
273362306a36Sopenharmony_ci *
273462306a36Sopenharmony_ci * Turn off Mailbox and PCI_PF_CFG interrupts.
273562306a36Sopenharmony_ci */
273662306a36Sopenharmony_civoid
273762306a36Sopenharmony_cicsio_hw_intr_disable(struct csio_hw *hw)
273862306a36Sopenharmony_ci{
273962306a36Sopenharmony_ci	u32 pf = 0;
274062306a36Sopenharmony_ci
274162306a36Sopenharmony_ci	if (csio_is_t5(hw->pdev->device & CSIO_HW_CHIP_MASK))
274262306a36Sopenharmony_ci		pf = SOURCEPF_G(csio_rd_reg32(hw, PL_WHOAMI_A));
274362306a36Sopenharmony_ci	else
274462306a36Sopenharmony_ci		pf = T6_SOURCEPF_G(csio_rd_reg32(hw, PL_WHOAMI_A));
274562306a36Sopenharmony_ci
274662306a36Sopenharmony_ci	if (!(hw->flags & CSIO_HWF_HW_INTR_ENABLED))
274762306a36Sopenharmony_ci		return;
274862306a36Sopenharmony_ci
274962306a36Sopenharmony_ci	hw->flags &= ~CSIO_HWF_HW_INTR_ENABLED;
275062306a36Sopenharmony_ci
275162306a36Sopenharmony_ci	csio_wr_reg32(hw, 0, MYPF_REG(PL_PF_INT_ENABLE_A));
275262306a36Sopenharmony_ci	if (csio_is_hw_master(hw))
275362306a36Sopenharmony_ci		csio_set_reg_field(hw, PL_INT_MAP0_A, 1 << pf, 0);
275462306a36Sopenharmony_ci
275562306a36Sopenharmony_ci	/* Turn off MB interrupts */
275662306a36Sopenharmony_ci	csio_mb_intr_disable(hw);
275762306a36Sopenharmony_ci
275862306a36Sopenharmony_ci}
275962306a36Sopenharmony_ci
276062306a36Sopenharmony_civoid
276162306a36Sopenharmony_cicsio_hw_fatal_err(struct csio_hw *hw)
276262306a36Sopenharmony_ci{
276362306a36Sopenharmony_ci	csio_set_reg_field(hw, SGE_CONTROL_A, GLOBALENABLE_F, 0);
276462306a36Sopenharmony_ci	csio_hw_intr_disable(hw);
276562306a36Sopenharmony_ci
276662306a36Sopenharmony_ci	/* Do not reset HW, we may need FW state for debugging */
276762306a36Sopenharmony_ci	csio_fatal(hw, "HW Fatal error encountered!\n");
276862306a36Sopenharmony_ci}
276962306a36Sopenharmony_ci
277062306a36Sopenharmony_ci/*****************************************************************************/
277162306a36Sopenharmony_ci/* START: HW SM                                                              */
277262306a36Sopenharmony_ci/*****************************************************************************/
277362306a36Sopenharmony_ci/*
277462306a36Sopenharmony_ci * csio_hws_uninit - Uninit state
277562306a36Sopenharmony_ci * @hw - HW module
277662306a36Sopenharmony_ci * @evt - Event
277762306a36Sopenharmony_ci *
277862306a36Sopenharmony_ci */
277962306a36Sopenharmony_cistatic void
278062306a36Sopenharmony_cicsio_hws_uninit(struct csio_hw *hw, enum csio_hw_ev evt)
278162306a36Sopenharmony_ci{
278262306a36Sopenharmony_ci	hw->prev_evt = hw->cur_evt;
278362306a36Sopenharmony_ci	hw->cur_evt = evt;
278462306a36Sopenharmony_ci	CSIO_INC_STATS(hw, n_evt_sm[evt]);
278562306a36Sopenharmony_ci
278662306a36Sopenharmony_ci	switch (evt) {
278762306a36Sopenharmony_ci	case CSIO_HWE_CFG:
278862306a36Sopenharmony_ci		csio_set_state(&hw->sm, csio_hws_configuring);
278962306a36Sopenharmony_ci		csio_hw_configure(hw);
279062306a36Sopenharmony_ci		break;
279162306a36Sopenharmony_ci
279262306a36Sopenharmony_ci	default:
279362306a36Sopenharmony_ci		CSIO_INC_STATS(hw, n_evt_unexp);
279462306a36Sopenharmony_ci		break;
279562306a36Sopenharmony_ci	}
279662306a36Sopenharmony_ci}
279762306a36Sopenharmony_ci
279862306a36Sopenharmony_ci/*
279962306a36Sopenharmony_ci * csio_hws_configuring - Configuring state
280062306a36Sopenharmony_ci * @hw - HW module
280162306a36Sopenharmony_ci * @evt - Event
280262306a36Sopenharmony_ci *
280362306a36Sopenharmony_ci */
280462306a36Sopenharmony_cistatic void
280562306a36Sopenharmony_cicsio_hws_configuring(struct csio_hw *hw, enum csio_hw_ev evt)
280662306a36Sopenharmony_ci{
280762306a36Sopenharmony_ci	hw->prev_evt = hw->cur_evt;
280862306a36Sopenharmony_ci	hw->cur_evt = evt;
280962306a36Sopenharmony_ci	CSIO_INC_STATS(hw, n_evt_sm[evt]);
281062306a36Sopenharmony_ci
281162306a36Sopenharmony_ci	switch (evt) {
281262306a36Sopenharmony_ci	case CSIO_HWE_INIT:
281362306a36Sopenharmony_ci		csio_set_state(&hw->sm, csio_hws_initializing);
281462306a36Sopenharmony_ci		csio_hw_initialize(hw);
281562306a36Sopenharmony_ci		break;
281662306a36Sopenharmony_ci
281762306a36Sopenharmony_ci	case CSIO_HWE_INIT_DONE:
281862306a36Sopenharmony_ci		csio_set_state(&hw->sm, csio_hws_ready);
281962306a36Sopenharmony_ci		/* Fan out event to all lnode SMs */
282062306a36Sopenharmony_ci		csio_notify_lnodes(hw, CSIO_LN_NOTIFY_HWREADY);
282162306a36Sopenharmony_ci		break;
282262306a36Sopenharmony_ci
282362306a36Sopenharmony_ci	case CSIO_HWE_FATAL:
282462306a36Sopenharmony_ci		csio_set_state(&hw->sm, csio_hws_uninit);
282562306a36Sopenharmony_ci		break;
282662306a36Sopenharmony_ci
282762306a36Sopenharmony_ci	case CSIO_HWE_PCI_REMOVE:
282862306a36Sopenharmony_ci		csio_do_bye(hw);
282962306a36Sopenharmony_ci		break;
283062306a36Sopenharmony_ci	default:
283162306a36Sopenharmony_ci		CSIO_INC_STATS(hw, n_evt_unexp);
283262306a36Sopenharmony_ci		break;
283362306a36Sopenharmony_ci	}
283462306a36Sopenharmony_ci}
283562306a36Sopenharmony_ci
283662306a36Sopenharmony_ci/*
283762306a36Sopenharmony_ci * csio_hws_initializing - Initializing state
283862306a36Sopenharmony_ci * @hw - HW module
283962306a36Sopenharmony_ci * @evt - Event
284062306a36Sopenharmony_ci *
284162306a36Sopenharmony_ci */
284262306a36Sopenharmony_cistatic void
284362306a36Sopenharmony_cicsio_hws_initializing(struct csio_hw *hw, enum csio_hw_ev evt)
284462306a36Sopenharmony_ci{
284562306a36Sopenharmony_ci	hw->prev_evt = hw->cur_evt;
284662306a36Sopenharmony_ci	hw->cur_evt = evt;
284762306a36Sopenharmony_ci	CSIO_INC_STATS(hw, n_evt_sm[evt]);
284862306a36Sopenharmony_ci
284962306a36Sopenharmony_ci	switch (evt) {
285062306a36Sopenharmony_ci	case CSIO_HWE_INIT_DONE:
285162306a36Sopenharmony_ci		csio_set_state(&hw->sm, csio_hws_ready);
285262306a36Sopenharmony_ci
285362306a36Sopenharmony_ci		/* Fan out event to all lnode SMs */
285462306a36Sopenharmony_ci		csio_notify_lnodes(hw, CSIO_LN_NOTIFY_HWREADY);
285562306a36Sopenharmony_ci
285662306a36Sopenharmony_ci		/* Enable interrupts */
285762306a36Sopenharmony_ci		csio_hw_intr_enable(hw);
285862306a36Sopenharmony_ci		break;
285962306a36Sopenharmony_ci
286062306a36Sopenharmony_ci	case CSIO_HWE_FATAL:
286162306a36Sopenharmony_ci		csio_set_state(&hw->sm, csio_hws_uninit);
286262306a36Sopenharmony_ci		break;
286362306a36Sopenharmony_ci
286462306a36Sopenharmony_ci	case CSIO_HWE_PCI_REMOVE:
286562306a36Sopenharmony_ci		csio_do_bye(hw);
286662306a36Sopenharmony_ci		break;
286762306a36Sopenharmony_ci
286862306a36Sopenharmony_ci	default:
286962306a36Sopenharmony_ci		CSIO_INC_STATS(hw, n_evt_unexp);
287062306a36Sopenharmony_ci		break;
287162306a36Sopenharmony_ci	}
287262306a36Sopenharmony_ci}
287362306a36Sopenharmony_ci
287462306a36Sopenharmony_ci/*
287562306a36Sopenharmony_ci * csio_hws_ready - Ready state
287662306a36Sopenharmony_ci * @hw - HW module
287762306a36Sopenharmony_ci * @evt - Event
287862306a36Sopenharmony_ci *
287962306a36Sopenharmony_ci */
288062306a36Sopenharmony_cistatic void
288162306a36Sopenharmony_cicsio_hws_ready(struct csio_hw *hw, enum csio_hw_ev evt)
288262306a36Sopenharmony_ci{
288362306a36Sopenharmony_ci	/* Remember the event */
288462306a36Sopenharmony_ci	hw->evtflag = evt;
288562306a36Sopenharmony_ci
288662306a36Sopenharmony_ci	hw->prev_evt = hw->cur_evt;
288762306a36Sopenharmony_ci	hw->cur_evt = evt;
288862306a36Sopenharmony_ci	CSIO_INC_STATS(hw, n_evt_sm[evt]);
288962306a36Sopenharmony_ci
289062306a36Sopenharmony_ci	switch (evt) {
289162306a36Sopenharmony_ci	case CSIO_HWE_HBA_RESET:
289262306a36Sopenharmony_ci	case CSIO_HWE_FW_DLOAD:
289362306a36Sopenharmony_ci	case CSIO_HWE_SUSPEND:
289462306a36Sopenharmony_ci	case CSIO_HWE_PCI_REMOVE:
289562306a36Sopenharmony_ci	case CSIO_HWE_PCIERR_DETECTED:
289662306a36Sopenharmony_ci		csio_set_state(&hw->sm, csio_hws_quiescing);
289762306a36Sopenharmony_ci		/* cleanup all outstanding cmds */
289862306a36Sopenharmony_ci		if (evt == CSIO_HWE_HBA_RESET ||
289962306a36Sopenharmony_ci		    evt == CSIO_HWE_PCIERR_DETECTED)
290062306a36Sopenharmony_ci			csio_scsim_cleanup_io(csio_hw_to_scsim(hw), false);
290162306a36Sopenharmony_ci		else
290262306a36Sopenharmony_ci			csio_scsim_cleanup_io(csio_hw_to_scsim(hw), true);
290362306a36Sopenharmony_ci
290462306a36Sopenharmony_ci		csio_hw_intr_disable(hw);
290562306a36Sopenharmony_ci		csio_hw_mbm_cleanup(hw);
290662306a36Sopenharmony_ci		csio_evtq_stop(hw);
290762306a36Sopenharmony_ci		csio_notify_lnodes(hw, CSIO_LN_NOTIFY_HWSTOP);
290862306a36Sopenharmony_ci		csio_evtq_flush(hw);
290962306a36Sopenharmony_ci		csio_mgmtm_cleanup(csio_hw_to_mgmtm(hw));
291062306a36Sopenharmony_ci		csio_post_event(&hw->sm, CSIO_HWE_QUIESCED);
291162306a36Sopenharmony_ci		break;
291262306a36Sopenharmony_ci
291362306a36Sopenharmony_ci	case CSIO_HWE_FATAL:
291462306a36Sopenharmony_ci		csio_set_state(&hw->sm, csio_hws_uninit);
291562306a36Sopenharmony_ci		break;
291662306a36Sopenharmony_ci
291762306a36Sopenharmony_ci	default:
291862306a36Sopenharmony_ci		CSIO_INC_STATS(hw, n_evt_unexp);
291962306a36Sopenharmony_ci		break;
292062306a36Sopenharmony_ci	}
292162306a36Sopenharmony_ci}
292262306a36Sopenharmony_ci
292362306a36Sopenharmony_ci/*
292462306a36Sopenharmony_ci * csio_hws_quiescing - Quiescing state
292562306a36Sopenharmony_ci * @hw - HW module
292662306a36Sopenharmony_ci * @evt - Event
292762306a36Sopenharmony_ci *
292862306a36Sopenharmony_ci */
292962306a36Sopenharmony_cistatic void
293062306a36Sopenharmony_cicsio_hws_quiescing(struct csio_hw *hw, enum csio_hw_ev evt)
293162306a36Sopenharmony_ci{
293262306a36Sopenharmony_ci	hw->prev_evt = hw->cur_evt;
293362306a36Sopenharmony_ci	hw->cur_evt = evt;
293462306a36Sopenharmony_ci	CSIO_INC_STATS(hw, n_evt_sm[evt]);
293562306a36Sopenharmony_ci
293662306a36Sopenharmony_ci	switch (evt) {
293762306a36Sopenharmony_ci	case CSIO_HWE_QUIESCED:
293862306a36Sopenharmony_ci		switch (hw->evtflag) {
293962306a36Sopenharmony_ci		case CSIO_HWE_FW_DLOAD:
294062306a36Sopenharmony_ci			csio_set_state(&hw->sm, csio_hws_resetting);
294162306a36Sopenharmony_ci			/* Download firmware */
294262306a36Sopenharmony_ci			fallthrough;
294362306a36Sopenharmony_ci
294462306a36Sopenharmony_ci		case CSIO_HWE_HBA_RESET:
294562306a36Sopenharmony_ci			csio_set_state(&hw->sm, csio_hws_resetting);
294662306a36Sopenharmony_ci			/* Start reset of the HBA */
294762306a36Sopenharmony_ci			csio_notify_lnodes(hw, CSIO_LN_NOTIFY_HWRESET);
294862306a36Sopenharmony_ci			csio_wr_destroy_queues(hw, false);
294962306a36Sopenharmony_ci			csio_do_reset(hw, false);
295062306a36Sopenharmony_ci			csio_post_event(&hw->sm, CSIO_HWE_HBA_RESET_DONE);
295162306a36Sopenharmony_ci			break;
295262306a36Sopenharmony_ci
295362306a36Sopenharmony_ci		case CSIO_HWE_PCI_REMOVE:
295462306a36Sopenharmony_ci			csio_set_state(&hw->sm, csio_hws_removing);
295562306a36Sopenharmony_ci			csio_notify_lnodes(hw, CSIO_LN_NOTIFY_HWREMOVE);
295662306a36Sopenharmony_ci			csio_wr_destroy_queues(hw, true);
295762306a36Sopenharmony_ci			/* Now send the bye command */
295862306a36Sopenharmony_ci			csio_do_bye(hw);
295962306a36Sopenharmony_ci			break;
296062306a36Sopenharmony_ci
296162306a36Sopenharmony_ci		case CSIO_HWE_SUSPEND:
296262306a36Sopenharmony_ci			csio_set_state(&hw->sm, csio_hws_quiesced);
296362306a36Sopenharmony_ci			break;
296462306a36Sopenharmony_ci
296562306a36Sopenharmony_ci		case CSIO_HWE_PCIERR_DETECTED:
296662306a36Sopenharmony_ci			csio_set_state(&hw->sm, csio_hws_pcierr);
296762306a36Sopenharmony_ci			csio_wr_destroy_queues(hw, false);
296862306a36Sopenharmony_ci			break;
296962306a36Sopenharmony_ci
297062306a36Sopenharmony_ci		default:
297162306a36Sopenharmony_ci			CSIO_INC_STATS(hw, n_evt_unexp);
297262306a36Sopenharmony_ci			break;
297362306a36Sopenharmony_ci
297462306a36Sopenharmony_ci		}
297562306a36Sopenharmony_ci		break;
297662306a36Sopenharmony_ci
297762306a36Sopenharmony_ci	default:
297862306a36Sopenharmony_ci		CSIO_INC_STATS(hw, n_evt_unexp);
297962306a36Sopenharmony_ci		break;
298062306a36Sopenharmony_ci	}
298162306a36Sopenharmony_ci}
298262306a36Sopenharmony_ci
298362306a36Sopenharmony_ci/*
298462306a36Sopenharmony_ci * csio_hws_quiesced - Quiesced state
298562306a36Sopenharmony_ci * @hw - HW module
298662306a36Sopenharmony_ci * @evt - Event
298762306a36Sopenharmony_ci *
298862306a36Sopenharmony_ci */
298962306a36Sopenharmony_cistatic void
299062306a36Sopenharmony_cicsio_hws_quiesced(struct csio_hw *hw, enum csio_hw_ev evt)
299162306a36Sopenharmony_ci{
299262306a36Sopenharmony_ci	hw->prev_evt = hw->cur_evt;
299362306a36Sopenharmony_ci	hw->cur_evt = evt;
299462306a36Sopenharmony_ci	CSIO_INC_STATS(hw, n_evt_sm[evt]);
299562306a36Sopenharmony_ci
299662306a36Sopenharmony_ci	switch (evt) {
299762306a36Sopenharmony_ci	case CSIO_HWE_RESUME:
299862306a36Sopenharmony_ci		csio_set_state(&hw->sm, csio_hws_configuring);
299962306a36Sopenharmony_ci		csio_hw_configure(hw);
300062306a36Sopenharmony_ci		break;
300162306a36Sopenharmony_ci
300262306a36Sopenharmony_ci	default:
300362306a36Sopenharmony_ci		CSIO_INC_STATS(hw, n_evt_unexp);
300462306a36Sopenharmony_ci		break;
300562306a36Sopenharmony_ci	}
300662306a36Sopenharmony_ci}
300762306a36Sopenharmony_ci
300862306a36Sopenharmony_ci/*
300962306a36Sopenharmony_ci * csio_hws_resetting - HW Resetting state
301062306a36Sopenharmony_ci * @hw - HW module
301162306a36Sopenharmony_ci * @evt - Event
301262306a36Sopenharmony_ci *
301362306a36Sopenharmony_ci */
301462306a36Sopenharmony_cistatic void
301562306a36Sopenharmony_cicsio_hws_resetting(struct csio_hw *hw, enum csio_hw_ev evt)
301662306a36Sopenharmony_ci{
301762306a36Sopenharmony_ci	hw->prev_evt = hw->cur_evt;
301862306a36Sopenharmony_ci	hw->cur_evt = evt;
301962306a36Sopenharmony_ci	CSIO_INC_STATS(hw, n_evt_sm[evt]);
302062306a36Sopenharmony_ci
302162306a36Sopenharmony_ci	switch (evt) {
302262306a36Sopenharmony_ci	case CSIO_HWE_HBA_RESET_DONE:
302362306a36Sopenharmony_ci		csio_evtq_start(hw);
302462306a36Sopenharmony_ci		csio_set_state(&hw->sm, csio_hws_configuring);
302562306a36Sopenharmony_ci		csio_hw_configure(hw);
302662306a36Sopenharmony_ci		break;
302762306a36Sopenharmony_ci
302862306a36Sopenharmony_ci	default:
302962306a36Sopenharmony_ci		CSIO_INC_STATS(hw, n_evt_unexp);
303062306a36Sopenharmony_ci		break;
303162306a36Sopenharmony_ci	}
303262306a36Sopenharmony_ci}
303362306a36Sopenharmony_ci
303462306a36Sopenharmony_ci/*
303562306a36Sopenharmony_ci * csio_hws_removing - PCI Hotplug removing state
303662306a36Sopenharmony_ci * @hw - HW module
303762306a36Sopenharmony_ci * @evt - Event
303862306a36Sopenharmony_ci *
303962306a36Sopenharmony_ci */
304062306a36Sopenharmony_cistatic void
304162306a36Sopenharmony_cicsio_hws_removing(struct csio_hw *hw, enum csio_hw_ev evt)
304262306a36Sopenharmony_ci{
304362306a36Sopenharmony_ci	hw->prev_evt = hw->cur_evt;
304462306a36Sopenharmony_ci	hw->cur_evt = evt;
304562306a36Sopenharmony_ci	CSIO_INC_STATS(hw, n_evt_sm[evt]);
304662306a36Sopenharmony_ci
304762306a36Sopenharmony_ci	switch (evt) {
304862306a36Sopenharmony_ci	case CSIO_HWE_HBA_RESET:
304962306a36Sopenharmony_ci		if (!csio_is_hw_master(hw))
305062306a36Sopenharmony_ci			break;
305162306a36Sopenharmony_ci		/*
305262306a36Sopenharmony_ci		 * The BYE should have already been issued, so we can't
305362306a36Sopenharmony_ci		 * use the mailbox interface. Hence we use the PL_RST
305462306a36Sopenharmony_ci		 * register directly.
305562306a36Sopenharmony_ci		 */
305662306a36Sopenharmony_ci		csio_err(hw, "Resetting HW and waiting 2 seconds...\n");
305762306a36Sopenharmony_ci		csio_wr_reg32(hw, PIORSTMODE_F | PIORST_F, PL_RST_A);
305862306a36Sopenharmony_ci		mdelay(2000);
305962306a36Sopenharmony_ci		break;
306062306a36Sopenharmony_ci
306162306a36Sopenharmony_ci	/* Should never receive any new events */
306262306a36Sopenharmony_ci	default:
306362306a36Sopenharmony_ci		CSIO_INC_STATS(hw, n_evt_unexp);
306462306a36Sopenharmony_ci		break;
306562306a36Sopenharmony_ci
306662306a36Sopenharmony_ci	}
306762306a36Sopenharmony_ci}
306862306a36Sopenharmony_ci
306962306a36Sopenharmony_ci/*
307062306a36Sopenharmony_ci * csio_hws_pcierr - PCI Error state
307162306a36Sopenharmony_ci * @hw - HW module
307262306a36Sopenharmony_ci * @evt - Event
307362306a36Sopenharmony_ci *
307462306a36Sopenharmony_ci */
307562306a36Sopenharmony_cistatic void
307662306a36Sopenharmony_cicsio_hws_pcierr(struct csio_hw *hw, enum csio_hw_ev evt)
307762306a36Sopenharmony_ci{
307862306a36Sopenharmony_ci	hw->prev_evt = hw->cur_evt;
307962306a36Sopenharmony_ci	hw->cur_evt = evt;
308062306a36Sopenharmony_ci	CSIO_INC_STATS(hw, n_evt_sm[evt]);
308162306a36Sopenharmony_ci
308262306a36Sopenharmony_ci	switch (evt) {
308362306a36Sopenharmony_ci	case CSIO_HWE_PCIERR_SLOT_RESET:
308462306a36Sopenharmony_ci		csio_evtq_start(hw);
308562306a36Sopenharmony_ci		csio_set_state(&hw->sm, csio_hws_configuring);
308662306a36Sopenharmony_ci		csio_hw_configure(hw);
308762306a36Sopenharmony_ci		break;
308862306a36Sopenharmony_ci
308962306a36Sopenharmony_ci	default:
309062306a36Sopenharmony_ci		CSIO_INC_STATS(hw, n_evt_unexp);
309162306a36Sopenharmony_ci		break;
309262306a36Sopenharmony_ci	}
309362306a36Sopenharmony_ci}
309462306a36Sopenharmony_ci
309562306a36Sopenharmony_ci/*****************************************************************************/
309662306a36Sopenharmony_ci/* END: HW SM                                                                */
309762306a36Sopenharmony_ci/*****************************************************************************/
309862306a36Sopenharmony_ci
309962306a36Sopenharmony_ci/*
310062306a36Sopenharmony_ci *	csio_handle_intr_status - table driven interrupt handler
310162306a36Sopenharmony_ci *	@hw: HW instance
310262306a36Sopenharmony_ci *	@reg: the interrupt status register to process
310362306a36Sopenharmony_ci *	@acts: table of interrupt actions
310462306a36Sopenharmony_ci *
310562306a36Sopenharmony_ci *	A table driven interrupt handler that applies a set of masks to an
310662306a36Sopenharmony_ci *	interrupt status word and performs the corresponding actions if the
310762306a36Sopenharmony_ci *	interrupts described by the mask have occurred.  The actions include
310862306a36Sopenharmony_ci *	optionally emitting a warning or alert message. The table is terminated
310962306a36Sopenharmony_ci *	by an entry specifying mask 0.  Returns the number of fatal interrupt
311062306a36Sopenharmony_ci *	conditions.
311162306a36Sopenharmony_ci */
311262306a36Sopenharmony_ciint
311362306a36Sopenharmony_cicsio_handle_intr_status(struct csio_hw *hw, unsigned int reg,
311462306a36Sopenharmony_ci				 const struct intr_info *acts)
311562306a36Sopenharmony_ci{
311662306a36Sopenharmony_ci	int fatal = 0;
311762306a36Sopenharmony_ci	unsigned int mask = 0;
311862306a36Sopenharmony_ci	unsigned int status = csio_rd_reg32(hw, reg);
311962306a36Sopenharmony_ci
312062306a36Sopenharmony_ci	for ( ; acts->mask; ++acts) {
312162306a36Sopenharmony_ci		if (!(status & acts->mask))
312262306a36Sopenharmony_ci			continue;
312362306a36Sopenharmony_ci		if (acts->fatal) {
312462306a36Sopenharmony_ci			fatal++;
312562306a36Sopenharmony_ci			csio_fatal(hw, "Fatal %s (0x%x)\n",
312662306a36Sopenharmony_ci				    acts->msg, status & acts->mask);
312762306a36Sopenharmony_ci		} else if (acts->msg)
312862306a36Sopenharmony_ci			csio_info(hw, "%s (0x%x)\n",
312962306a36Sopenharmony_ci				    acts->msg, status & acts->mask);
313062306a36Sopenharmony_ci		mask |= acts->mask;
313162306a36Sopenharmony_ci	}
313262306a36Sopenharmony_ci	status &= mask;
313362306a36Sopenharmony_ci	if (status)                           /* clear processed interrupts */
313462306a36Sopenharmony_ci		csio_wr_reg32(hw, status, reg);
313562306a36Sopenharmony_ci	return fatal;
313662306a36Sopenharmony_ci}
313762306a36Sopenharmony_ci
313862306a36Sopenharmony_ci/*
313962306a36Sopenharmony_ci * TP interrupt handler.
314062306a36Sopenharmony_ci */
314162306a36Sopenharmony_cistatic void csio_tp_intr_handler(struct csio_hw *hw)
314262306a36Sopenharmony_ci{
314362306a36Sopenharmony_ci	static struct intr_info tp_intr_info[] = {
314462306a36Sopenharmony_ci		{ 0x3fffffff, "TP parity error", -1, 1 },
314562306a36Sopenharmony_ci		{ FLMTXFLSTEMPTY_F, "TP out of Tx pages", -1, 1 },
314662306a36Sopenharmony_ci		{ 0, NULL, 0, 0 }
314762306a36Sopenharmony_ci	};
314862306a36Sopenharmony_ci
314962306a36Sopenharmony_ci	if (csio_handle_intr_status(hw, TP_INT_CAUSE_A, tp_intr_info))
315062306a36Sopenharmony_ci		csio_hw_fatal_err(hw);
315162306a36Sopenharmony_ci}
315262306a36Sopenharmony_ci
315362306a36Sopenharmony_ci/*
315462306a36Sopenharmony_ci * SGE interrupt handler.
315562306a36Sopenharmony_ci */
315662306a36Sopenharmony_cistatic void csio_sge_intr_handler(struct csio_hw *hw)
315762306a36Sopenharmony_ci{
315862306a36Sopenharmony_ci	uint64_t v;
315962306a36Sopenharmony_ci
316062306a36Sopenharmony_ci	static struct intr_info sge_intr_info[] = {
316162306a36Sopenharmony_ci		{ ERR_CPL_EXCEED_IQE_SIZE_F,
316262306a36Sopenharmony_ci		  "SGE received CPL exceeding IQE size", -1, 1 },
316362306a36Sopenharmony_ci		{ ERR_INVALID_CIDX_INC_F,
316462306a36Sopenharmony_ci		  "SGE GTS CIDX increment too large", -1, 0 },
316562306a36Sopenharmony_ci		{ ERR_CPL_OPCODE_0_F, "SGE received 0-length CPL", -1, 0 },
316662306a36Sopenharmony_ci		{ ERR_DROPPED_DB_F, "SGE doorbell dropped", -1, 0 },
316762306a36Sopenharmony_ci		{ ERR_DATA_CPL_ON_HIGH_QID1_F | ERR_DATA_CPL_ON_HIGH_QID0_F,
316862306a36Sopenharmony_ci		  "SGE IQID > 1023 received CPL for FL", -1, 0 },
316962306a36Sopenharmony_ci		{ ERR_BAD_DB_PIDX3_F, "SGE DBP 3 pidx increment too large", -1,
317062306a36Sopenharmony_ci		  0 },
317162306a36Sopenharmony_ci		{ ERR_BAD_DB_PIDX2_F, "SGE DBP 2 pidx increment too large", -1,
317262306a36Sopenharmony_ci		  0 },
317362306a36Sopenharmony_ci		{ ERR_BAD_DB_PIDX1_F, "SGE DBP 1 pidx increment too large", -1,
317462306a36Sopenharmony_ci		  0 },
317562306a36Sopenharmony_ci		{ ERR_BAD_DB_PIDX0_F, "SGE DBP 0 pidx increment too large", -1,
317662306a36Sopenharmony_ci		  0 },
317762306a36Sopenharmony_ci		{ ERR_ING_CTXT_PRIO_F,
317862306a36Sopenharmony_ci		  "SGE too many priority ingress contexts", -1, 0 },
317962306a36Sopenharmony_ci		{ ERR_EGR_CTXT_PRIO_F,
318062306a36Sopenharmony_ci		  "SGE too many priority egress contexts", -1, 0 },
318162306a36Sopenharmony_ci		{ INGRESS_SIZE_ERR_F, "SGE illegal ingress QID", -1, 0 },
318262306a36Sopenharmony_ci		{ EGRESS_SIZE_ERR_F, "SGE illegal egress QID", -1, 0 },
318362306a36Sopenharmony_ci		{ 0, NULL, 0, 0 }
318462306a36Sopenharmony_ci	};
318562306a36Sopenharmony_ci
318662306a36Sopenharmony_ci	v = (uint64_t)csio_rd_reg32(hw, SGE_INT_CAUSE1_A) |
318762306a36Sopenharmony_ci	    ((uint64_t)csio_rd_reg32(hw, SGE_INT_CAUSE2_A) << 32);
318862306a36Sopenharmony_ci	if (v) {
318962306a36Sopenharmony_ci		csio_fatal(hw, "SGE parity error (%#llx)\n",
319062306a36Sopenharmony_ci			    (unsigned long long)v);
319162306a36Sopenharmony_ci		csio_wr_reg32(hw, (uint32_t)(v & 0xFFFFFFFF),
319262306a36Sopenharmony_ci						SGE_INT_CAUSE1_A);
319362306a36Sopenharmony_ci		csio_wr_reg32(hw, (uint32_t)(v >> 32), SGE_INT_CAUSE2_A);
319462306a36Sopenharmony_ci	}
319562306a36Sopenharmony_ci
319662306a36Sopenharmony_ci	v |= csio_handle_intr_status(hw, SGE_INT_CAUSE3_A, sge_intr_info);
319762306a36Sopenharmony_ci
319862306a36Sopenharmony_ci	if (csio_handle_intr_status(hw, SGE_INT_CAUSE3_A, sge_intr_info) ||
319962306a36Sopenharmony_ci	    v != 0)
320062306a36Sopenharmony_ci		csio_hw_fatal_err(hw);
320162306a36Sopenharmony_ci}
320262306a36Sopenharmony_ci
320362306a36Sopenharmony_ci#define CIM_OBQ_INTR (OBQULP0PARERR_F | OBQULP1PARERR_F | OBQULP2PARERR_F |\
320462306a36Sopenharmony_ci		      OBQULP3PARERR_F | OBQSGEPARERR_F | OBQNCSIPARERR_F)
320562306a36Sopenharmony_ci#define CIM_IBQ_INTR (IBQTP0PARERR_F | IBQTP1PARERR_F | IBQULPPARERR_F |\
320662306a36Sopenharmony_ci		      IBQSGEHIPARERR_F | IBQSGELOPARERR_F | IBQNCSIPARERR_F)
320762306a36Sopenharmony_ci
320862306a36Sopenharmony_ci/*
320962306a36Sopenharmony_ci * CIM interrupt handler.
321062306a36Sopenharmony_ci */
321162306a36Sopenharmony_cistatic void csio_cim_intr_handler(struct csio_hw *hw)
321262306a36Sopenharmony_ci{
321362306a36Sopenharmony_ci	static struct intr_info cim_intr_info[] = {
321462306a36Sopenharmony_ci		{ PREFDROPINT_F, "CIM control register prefetch drop", -1, 1 },
321562306a36Sopenharmony_ci		{ CIM_OBQ_INTR, "CIM OBQ parity error", -1, 1 },
321662306a36Sopenharmony_ci		{ CIM_IBQ_INTR, "CIM IBQ parity error", -1, 1 },
321762306a36Sopenharmony_ci		{ MBUPPARERR_F, "CIM mailbox uP parity error", -1, 1 },
321862306a36Sopenharmony_ci		{ MBHOSTPARERR_F, "CIM mailbox host parity error", -1, 1 },
321962306a36Sopenharmony_ci		{ TIEQINPARERRINT_F, "CIM TIEQ outgoing parity error", -1, 1 },
322062306a36Sopenharmony_ci		{ TIEQOUTPARERRINT_F, "CIM TIEQ incoming parity error", -1, 1 },
322162306a36Sopenharmony_ci		{ 0, NULL, 0, 0 }
322262306a36Sopenharmony_ci	};
322362306a36Sopenharmony_ci	static struct intr_info cim_upintr_info[] = {
322462306a36Sopenharmony_ci		{ RSVDSPACEINT_F, "CIM reserved space access", -1, 1 },
322562306a36Sopenharmony_ci		{ ILLTRANSINT_F, "CIM illegal transaction", -1, 1 },
322662306a36Sopenharmony_ci		{ ILLWRINT_F, "CIM illegal write", -1, 1 },
322762306a36Sopenharmony_ci		{ ILLRDINT_F, "CIM illegal read", -1, 1 },
322862306a36Sopenharmony_ci		{ ILLRDBEINT_F, "CIM illegal read BE", -1, 1 },
322962306a36Sopenharmony_ci		{ ILLWRBEINT_F, "CIM illegal write BE", -1, 1 },
323062306a36Sopenharmony_ci		{ SGLRDBOOTINT_F, "CIM single read from boot space", -1, 1 },
323162306a36Sopenharmony_ci		{ SGLWRBOOTINT_F, "CIM single write to boot space", -1, 1 },
323262306a36Sopenharmony_ci		{ BLKWRBOOTINT_F, "CIM block write to boot space", -1, 1 },
323362306a36Sopenharmony_ci		{ SGLRDFLASHINT_F, "CIM single read from flash space", -1, 1 },
323462306a36Sopenharmony_ci		{ SGLWRFLASHINT_F, "CIM single write to flash space", -1, 1 },
323562306a36Sopenharmony_ci		{ BLKWRFLASHINT_F, "CIM block write to flash space", -1, 1 },
323662306a36Sopenharmony_ci		{ SGLRDEEPROMINT_F, "CIM single EEPROM read", -1, 1 },
323762306a36Sopenharmony_ci		{ SGLWREEPROMINT_F, "CIM single EEPROM write", -1, 1 },
323862306a36Sopenharmony_ci		{ BLKRDEEPROMINT_F, "CIM block EEPROM read", -1, 1 },
323962306a36Sopenharmony_ci		{ BLKWREEPROMINT_F, "CIM block EEPROM write", -1, 1 },
324062306a36Sopenharmony_ci		{ SGLRDCTLINT_F, "CIM single read from CTL space", -1, 1 },
324162306a36Sopenharmony_ci		{ SGLWRCTLINT_F, "CIM single write to CTL space", -1, 1 },
324262306a36Sopenharmony_ci		{ BLKRDCTLINT_F, "CIM block read from CTL space", -1, 1 },
324362306a36Sopenharmony_ci		{ BLKWRCTLINT_F, "CIM block write to CTL space", -1, 1 },
324462306a36Sopenharmony_ci		{ SGLRDPLINT_F, "CIM single read from PL space", -1, 1 },
324562306a36Sopenharmony_ci		{ SGLWRPLINT_F, "CIM single write to PL space", -1, 1 },
324662306a36Sopenharmony_ci		{ BLKRDPLINT_F, "CIM block read from PL space", -1, 1 },
324762306a36Sopenharmony_ci		{ BLKWRPLINT_F, "CIM block write to PL space", -1, 1 },
324862306a36Sopenharmony_ci		{ REQOVRLOOKUPINT_F, "CIM request FIFO overwrite", -1, 1 },
324962306a36Sopenharmony_ci		{ RSPOVRLOOKUPINT_F, "CIM response FIFO overwrite", -1, 1 },
325062306a36Sopenharmony_ci		{ TIMEOUTINT_F, "CIM PIF timeout", -1, 1 },
325162306a36Sopenharmony_ci		{ TIMEOUTMAINT_F, "CIM PIF MA timeout", -1, 1 },
325262306a36Sopenharmony_ci		{ 0, NULL, 0, 0 }
325362306a36Sopenharmony_ci	};
325462306a36Sopenharmony_ci
325562306a36Sopenharmony_ci	int fat;
325662306a36Sopenharmony_ci
325762306a36Sopenharmony_ci	fat = csio_handle_intr_status(hw, CIM_HOST_INT_CAUSE_A,
325862306a36Sopenharmony_ci				      cim_intr_info) +
325962306a36Sopenharmony_ci	      csio_handle_intr_status(hw, CIM_HOST_UPACC_INT_CAUSE_A,
326062306a36Sopenharmony_ci				      cim_upintr_info);
326162306a36Sopenharmony_ci	if (fat)
326262306a36Sopenharmony_ci		csio_hw_fatal_err(hw);
326362306a36Sopenharmony_ci}
326462306a36Sopenharmony_ci
326562306a36Sopenharmony_ci/*
326662306a36Sopenharmony_ci * ULP RX interrupt handler.
326762306a36Sopenharmony_ci */
326862306a36Sopenharmony_cistatic void csio_ulprx_intr_handler(struct csio_hw *hw)
326962306a36Sopenharmony_ci{
327062306a36Sopenharmony_ci	static struct intr_info ulprx_intr_info[] = {
327162306a36Sopenharmony_ci		{ 0x1800000, "ULPRX context error", -1, 1 },
327262306a36Sopenharmony_ci		{ 0x7fffff, "ULPRX parity error", -1, 1 },
327362306a36Sopenharmony_ci		{ 0, NULL, 0, 0 }
327462306a36Sopenharmony_ci	};
327562306a36Sopenharmony_ci
327662306a36Sopenharmony_ci	if (csio_handle_intr_status(hw, ULP_RX_INT_CAUSE_A, ulprx_intr_info))
327762306a36Sopenharmony_ci		csio_hw_fatal_err(hw);
327862306a36Sopenharmony_ci}
327962306a36Sopenharmony_ci
328062306a36Sopenharmony_ci/*
328162306a36Sopenharmony_ci * ULP TX interrupt handler.
328262306a36Sopenharmony_ci */
328362306a36Sopenharmony_cistatic void csio_ulptx_intr_handler(struct csio_hw *hw)
328462306a36Sopenharmony_ci{
328562306a36Sopenharmony_ci	static struct intr_info ulptx_intr_info[] = {
328662306a36Sopenharmony_ci		{ PBL_BOUND_ERR_CH3_F, "ULPTX channel 3 PBL out of bounds", -1,
328762306a36Sopenharmony_ci		  0 },
328862306a36Sopenharmony_ci		{ PBL_BOUND_ERR_CH2_F, "ULPTX channel 2 PBL out of bounds", -1,
328962306a36Sopenharmony_ci		  0 },
329062306a36Sopenharmony_ci		{ PBL_BOUND_ERR_CH1_F, "ULPTX channel 1 PBL out of bounds", -1,
329162306a36Sopenharmony_ci		  0 },
329262306a36Sopenharmony_ci		{ PBL_BOUND_ERR_CH0_F, "ULPTX channel 0 PBL out of bounds", -1,
329362306a36Sopenharmony_ci		  0 },
329462306a36Sopenharmony_ci		{ 0xfffffff, "ULPTX parity error", -1, 1 },
329562306a36Sopenharmony_ci		{ 0, NULL, 0, 0 }
329662306a36Sopenharmony_ci	};
329762306a36Sopenharmony_ci
329862306a36Sopenharmony_ci	if (csio_handle_intr_status(hw, ULP_TX_INT_CAUSE_A, ulptx_intr_info))
329962306a36Sopenharmony_ci		csio_hw_fatal_err(hw);
330062306a36Sopenharmony_ci}
330162306a36Sopenharmony_ci
330262306a36Sopenharmony_ci/*
330362306a36Sopenharmony_ci * PM TX interrupt handler.
330462306a36Sopenharmony_ci */
330562306a36Sopenharmony_cistatic void csio_pmtx_intr_handler(struct csio_hw *hw)
330662306a36Sopenharmony_ci{
330762306a36Sopenharmony_ci	static struct intr_info pmtx_intr_info[] = {
330862306a36Sopenharmony_ci		{ PCMD_LEN_OVFL0_F, "PMTX channel 0 pcmd too large", -1, 1 },
330962306a36Sopenharmony_ci		{ PCMD_LEN_OVFL1_F, "PMTX channel 1 pcmd too large", -1, 1 },
331062306a36Sopenharmony_ci		{ PCMD_LEN_OVFL2_F, "PMTX channel 2 pcmd too large", -1, 1 },
331162306a36Sopenharmony_ci		{ ZERO_C_CMD_ERROR_F, "PMTX 0-length pcmd", -1, 1 },
331262306a36Sopenharmony_ci		{ 0xffffff0, "PMTX framing error", -1, 1 },
331362306a36Sopenharmony_ci		{ OESPI_PAR_ERROR_F, "PMTX oespi parity error", -1, 1 },
331462306a36Sopenharmony_ci		{ DB_OPTIONS_PAR_ERROR_F, "PMTX db_options parity error", -1,
331562306a36Sopenharmony_ci		  1 },
331662306a36Sopenharmony_ci		{ ICSPI_PAR_ERROR_F, "PMTX icspi parity error", -1, 1 },
331762306a36Sopenharmony_ci		{ PMTX_C_PCMD_PAR_ERROR_F, "PMTX c_pcmd parity error", -1, 1},
331862306a36Sopenharmony_ci		{ 0, NULL, 0, 0 }
331962306a36Sopenharmony_ci	};
332062306a36Sopenharmony_ci
332162306a36Sopenharmony_ci	if (csio_handle_intr_status(hw, PM_TX_INT_CAUSE_A, pmtx_intr_info))
332262306a36Sopenharmony_ci		csio_hw_fatal_err(hw);
332362306a36Sopenharmony_ci}
332462306a36Sopenharmony_ci
332562306a36Sopenharmony_ci/*
332662306a36Sopenharmony_ci * PM RX interrupt handler.
332762306a36Sopenharmony_ci */
332862306a36Sopenharmony_cistatic void csio_pmrx_intr_handler(struct csio_hw *hw)
332962306a36Sopenharmony_ci{
333062306a36Sopenharmony_ci	static struct intr_info pmrx_intr_info[] = {
333162306a36Sopenharmony_ci		{ ZERO_E_CMD_ERROR_F, "PMRX 0-length pcmd", -1, 1 },
333262306a36Sopenharmony_ci		{ 0x3ffff0, "PMRX framing error", -1, 1 },
333362306a36Sopenharmony_ci		{ OCSPI_PAR_ERROR_F, "PMRX ocspi parity error", -1, 1 },
333462306a36Sopenharmony_ci		{ DB_OPTIONS_PAR_ERROR_F, "PMRX db_options parity error", -1,
333562306a36Sopenharmony_ci		  1 },
333662306a36Sopenharmony_ci		{ IESPI_PAR_ERROR_F, "PMRX iespi parity error", -1, 1 },
333762306a36Sopenharmony_ci		{ PMRX_E_PCMD_PAR_ERROR_F, "PMRX e_pcmd parity error", -1, 1},
333862306a36Sopenharmony_ci		{ 0, NULL, 0, 0 }
333962306a36Sopenharmony_ci	};
334062306a36Sopenharmony_ci
334162306a36Sopenharmony_ci	if (csio_handle_intr_status(hw, PM_RX_INT_CAUSE_A, pmrx_intr_info))
334262306a36Sopenharmony_ci		csio_hw_fatal_err(hw);
334362306a36Sopenharmony_ci}
334462306a36Sopenharmony_ci
334562306a36Sopenharmony_ci/*
334662306a36Sopenharmony_ci * CPL switch interrupt handler.
334762306a36Sopenharmony_ci */
334862306a36Sopenharmony_cistatic void csio_cplsw_intr_handler(struct csio_hw *hw)
334962306a36Sopenharmony_ci{
335062306a36Sopenharmony_ci	static struct intr_info cplsw_intr_info[] = {
335162306a36Sopenharmony_ci		{ CIM_OP_MAP_PERR_F, "CPLSW CIM op_map parity error", -1, 1 },
335262306a36Sopenharmony_ci		{ CIM_OVFL_ERROR_F, "CPLSW CIM overflow", -1, 1 },
335362306a36Sopenharmony_ci		{ TP_FRAMING_ERROR_F, "CPLSW TP framing error", -1, 1 },
335462306a36Sopenharmony_ci		{ SGE_FRAMING_ERROR_F, "CPLSW SGE framing error", -1, 1 },
335562306a36Sopenharmony_ci		{ CIM_FRAMING_ERROR_F, "CPLSW CIM framing error", -1, 1 },
335662306a36Sopenharmony_ci		{ ZERO_SWITCH_ERROR_F, "CPLSW no-switch error", -1, 1 },
335762306a36Sopenharmony_ci		{ 0, NULL, 0, 0 }
335862306a36Sopenharmony_ci	};
335962306a36Sopenharmony_ci
336062306a36Sopenharmony_ci	if (csio_handle_intr_status(hw, CPL_INTR_CAUSE_A, cplsw_intr_info))
336162306a36Sopenharmony_ci		csio_hw_fatal_err(hw);
336262306a36Sopenharmony_ci}
336362306a36Sopenharmony_ci
336462306a36Sopenharmony_ci/*
336562306a36Sopenharmony_ci * LE interrupt handler.
336662306a36Sopenharmony_ci */
336762306a36Sopenharmony_cistatic void csio_le_intr_handler(struct csio_hw *hw)
336862306a36Sopenharmony_ci{
336962306a36Sopenharmony_ci	enum chip_type chip = CHELSIO_CHIP_VERSION(hw->chip_id);
337062306a36Sopenharmony_ci
337162306a36Sopenharmony_ci	static struct intr_info le_intr_info[] = {
337262306a36Sopenharmony_ci		{ LIPMISS_F, "LE LIP miss", -1, 0 },
337362306a36Sopenharmony_ci		{ LIP0_F, "LE 0 LIP error", -1, 0 },
337462306a36Sopenharmony_ci		{ PARITYERR_F, "LE parity error", -1, 1 },
337562306a36Sopenharmony_ci		{ UNKNOWNCMD_F, "LE unknown command", -1, 1 },
337662306a36Sopenharmony_ci		{ REQQPARERR_F, "LE request queue parity error", -1, 1 },
337762306a36Sopenharmony_ci		{ 0, NULL, 0, 0 }
337862306a36Sopenharmony_ci	};
337962306a36Sopenharmony_ci
338062306a36Sopenharmony_ci	static struct intr_info t6_le_intr_info[] = {
338162306a36Sopenharmony_ci		{ T6_LIPMISS_F, "LE LIP miss", -1, 0 },
338262306a36Sopenharmony_ci		{ T6_LIP0_F, "LE 0 LIP error", -1, 0 },
338362306a36Sopenharmony_ci		{ TCAMINTPERR_F, "LE parity error", -1, 1 },
338462306a36Sopenharmony_ci		{ T6_UNKNOWNCMD_F, "LE unknown command", -1, 1 },
338562306a36Sopenharmony_ci		{ SSRAMINTPERR_F, "LE request queue parity error", -1, 1 },
338662306a36Sopenharmony_ci		{ 0, NULL, 0, 0 }
338762306a36Sopenharmony_ci	};
338862306a36Sopenharmony_ci
338962306a36Sopenharmony_ci	if (csio_handle_intr_status(hw, LE_DB_INT_CAUSE_A,
339062306a36Sopenharmony_ci				    (chip == CHELSIO_T5) ?
339162306a36Sopenharmony_ci				    le_intr_info : t6_le_intr_info))
339262306a36Sopenharmony_ci		csio_hw_fatal_err(hw);
339362306a36Sopenharmony_ci}
339462306a36Sopenharmony_ci
339562306a36Sopenharmony_ci/*
339662306a36Sopenharmony_ci * MPS interrupt handler.
339762306a36Sopenharmony_ci */
339862306a36Sopenharmony_cistatic void csio_mps_intr_handler(struct csio_hw *hw)
339962306a36Sopenharmony_ci{
340062306a36Sopenharmony_ci	static struct intr_info mps_rx_intr_info[] = {
340162306a36Sopenharmony_ci		{ 0xffffff, "MPS Rx parity error", -1, 1 },
340262306a36Sopenharmony_ci		{ 0, NULL, 0, 0 }
340362306a36Sopenharmony_ci	};
340462306a36Sopenharmony_ci	static struct intr_info mps_tx_intr_info[] = {
340562306a36Sopenharmony_ci		{ TPFIFO_V(TPFIFO_M), "MPS Tx TP FIFO parity error", -1, 1 },
340662306a36Sopenharmony_ci		{ NCSIFIFO_F, "MPS Tx NC-SI FIFO parity error", -1, 1 },
340762306a36Sopenharmony_ci		{ TXDATAFIFO_V(TXDATAFIFO_M), "MPS Tx data FIFO parity error",
340862306a36Sopenharmony_ci		  -1, 1 },
340962306a36Sopenharmony_ci		{ TXDESCFIFO_V(TXDESCFIFO_M), "MPS Tx desc FIFO parity error",
341062306a36Sopenharmony_ci		  -1, 1 },
341162306a36Sopenharmony_ci		{ BUBBLE_F, "MPS Tx underflow", -1, 1 },
341262306a36Sopenharmony_ci		{ SECNTERR_F, "MPS Tx SOP/EOP error", -1, 1 },
341362306a36Sopenharmony_ci		{ FRMERR_F, "MPS Tx framing error", -1, 1 },
341462306a36Sopenharmony_ci		{ 0, NULL, 0, 0 }
341562306a36Sopenharmony_ci	};
341662306a36Sopenharmony_ci	static struct intr_info mps_trc_intr_info[] = {
341762306a36Sopenharmony_ci		{ FILTMEM_V(FILTMEM_M), "MPS TRC filter parity error", -1, 1 },
341862306a36Sopenharmony_ci		{ PKTFIFO_V(PKTFIFO_M), "MPS TRC packet FIFO parity error",
341962306a36Sopenharmony_ci		  -1, 1 },
342062306a36Sopenharmony_ci		{ MISCPERR_F, "MPS TRC misc parity error", -1, 1 },
342162306a36Sopenharmony_ci		{ 0, NULL, 0, 0 }
342262306a36Sopenharmony_ci	};
342362306a36Sopenharmony_ci	static struct intr_info mps_stat_sram_intr_info[] = {
342462306a36Sopenharmony_ci		{ 0x1fffff, "MPS statistics SRAM parity error", -1, 1 },
342562306a36Sopenharmony_ci		{ 0, NULL, 0, 0 }
342662306a36Sopenharmony_ci	};
342762306a36Sopenharmony_ci	static struct intr_info mps_stat_tx_intr_info[] = {
342862306a36Sopenharmony_ci		{ 0xfffff, "MPS statistics Tx FIFO parity error", -1, 1 },
342962306a36Sopenharmony_ci		{ 0, NULL, 0, 0 }
343062306a36Sopenharmony_ci	};
343162306a36Sopenharmony_ci	static struct intr_info mps_stat_rx_intr_info[] = {
343262306a36Sopenharmony_ci		{ 0xffffff, "MPS statistics Rx FIFO parity error", -1, 1 },
343362306a36Sopenharmony_ci		{ 0, NULL, 0, 0 }
343462306a36Sopenharmony_ci	};
343562306a36Sopenharmony_ci	static struct intr_info mps_cls_intr_info[] = {
343662306a36Sopenharmony_ci		{ MATCHSRAM_F, "MPS match SRAM parity error", -1, 1 },
343762306a36Sopenharmony_ci		{ MATCHTCAM_F, "MPS match TCAM parity error", -1, 1 },
343862306a36Sopenharmony_ci		{ HASHSRAM_F, "MPS hash SRAM parity error", -1, 1 },
343962306a36Sopenharmony_ci		{ 0, NULL, 0, 0 }
344062306a36Sopenharmony_ci	};
344162306a36Sopenharmony_ci
344262306a36Sopenharmony_ci	int fat;
344362306a36Sopenharmony_ci
344462306a36Sopenharmony_ci	fat = csio_handle_intr_status(hw, MPS_RX_PERR_INT_CAUSE_A,
344562306a36Sopenharmony_ci				      mps_rx_intr_info) +
344662306a36Sopenharmony_ci	      csio_handle_intr_status(hw, MPS_TX_INT_CAUSE_A,
344762306a36Sopenharmony_ci				      mps_tx_intr_info) +
344862306a36Sopenharmony_ci	      csio_handle_intr_status(hw, MPS_TRC_INT_CAUSE_A,
344962306a36Sopenharmony_ci				      mps_trc_intr_info) +
345062306a36Sopenharmony_ci	      csio_handle_intr_status(hw, MPS_STAT_PERR_INT_CAUSE_SRAM_A,
345162306a36Sopenharmony_ci				      mps_stat_sram_intr_info) +
345262306a36Sopenharmony_ci	      csio_handle_intr_status(hw, MPS_STAT_PERR_INT_CAUSE_TX_FIFO_A,
345362306a36Sopenharmony_ci				      mps_stat_tx_intr_info) +
345462306a36Sopenharmony_ci	      csio_handle_intr_status(hw, MPS_STAT_PERR_INT_CAUSE_RX_FIFO_A,
345562306a36Sopenharmony_ci				      mps_stat_rx_intr_info) +
345662306a36Sopenharmony_ci	      csio_handle_intr_status(hw, MPS_CLS_INT_CAUSE_A,
345762306a36Sopenharmony_ci				      mps_cls_intr_info);
345862306a36Sopenharmony_ci
345962306a36Sopenharmony_ci	csio_wr_reg32(hw, 0, MPS_INT_CAUSE_A);
346062306a36Sopenharmony_ci	csio_rd_reg32(hw, MPS_INT_CAUSE_A);                    /* flush */
346162306a36Sopenharmony_ci	if (fat)
346262306a36Sopenharmony_ci		csio_hw_fatal_err(hw);
346362306a36Sopenharmony_ci}
346462306a36Sopenharmony_ci
346562306a36Sopenharmony_ci#define MEM_INT_MASK (PERR_INT_CAUSE_F | ECC_CE_INT_CAUSE_F | \
346662306a36Sopenharmony_ci		      ECC_UE_INT_CAUSE_F)
346762306a36Sopenharmony_ci
346862306a36Sopenharmony_ci/*
346962306a36Sopenharmony_ci * EDC/MC interrupt handler.
347062306a36Sopenharmony_ci */
347162306a36Sopenharmony_cistatic void csio_mem_intr_handler(struct csio_hw *hw, int idx)
347262306a36Sopenharmony_ci{
347362306a36Sopenharmony_ci	static const char name[3][5] = { "EDC0", "EDC1", "MC" };
347462306a36Sopenharmony_ci
347562306a36Sopenharmony_ci	unsigned int addr, cnt_addr, v;
347662306a36Sopenharmony_ci
347762306a36Sopenharmony_ci	if (idx <= MEM_EDC1) {
347862306a36Sopenharmony_ci		addr = EDC_REG(EDC_INT_CAUSE_A, idx);
347962306a36Sopenharmony_ci		cnt_addr = EDC_REG(EDC_ECC_STATUS_A, idx);
348062306a36Sopenharmony_ci	} else {
348162306a36Sopenharmony_ci		addr = MC_INT_CAUSE_A;
348262306a36Sopenharmony_ci		cnt_addr = MC_ECC_STATUS_A;
348362306a36Sopenharmony_ci	}
348462306a36Sopenharmony_ci
348562306a36Sopenharmony_ci	v = csio_rd_reg32(hw, addr) & MEM_INT_MASK;
348662306a36Sopenharmony_ci	if (v & PERR_INT_CAUSE_F)
348762306a36Sopenharmony_ci		csio_fatal(hw, "%s FIFO parity error\n", name[idx]);
348862306a36Sopenharmony_ci	if (v & ECC_CE_INT_CAUSE_F) {
348962306a36Sopenharmony_ci		uint32_t cnt = ECC_CECNT_G(csio_rd_reg32(hw, cnt_addr));
349062306a36Sopenharmony_ci
349162306a36Sopenharmony_ci		csio_wr_reg32(hw, ECC_CECNT_V(ECC_CECNT_M), cnt_addr);
349262306a36Sopenharmony_ci		csio_warn(hw, "%u %s correctable ECC data error%s\n",
349362306a36Sopenharmony_ci			    cnt, name[idx], cnt > 1 ? "s" : "");
349462306a36Sopenharmony_ci	}
349562306a36Sopenharmony_ci	if (v & ECC_UE_INT_CAUSE_F)
349662306a36Sopenharmony_ci		csio_fatal(hw, "%s uncorrectable ECC data error\n", name[idx]);
349762306a36Sopenharmony_ci
349862306a36Sopenharmony_ci	csio_wr_reg32(hw, v, addr);
349962306a36Sopenharmony_ci	if (v & (PERR_INT_CAUSE_F | ECC_UE_INT_CAUSE_F))
350062306a36Sopenharmony_ci		csio_hw_fatal_err(hw);
350162306a36Sopenharmony_ci}
350262306a36Sopenharmony_ci
350362306a36Sopenharmony_ci/*
350462306a36Sopenharmony_ci * MA interrupt handler.
350562306a36Sopenharmony_ci */
350662306a36Sopenharmony_cistatic void csio_ma_intr_handler(struct csio_hw *hw)
350762306a36Sopenharmony_ci{
350862306a36Sopenharmony_ci	uint32_t v, status = csio_rd_reg32(hw, MA_INT_CAUSE_A);
350962306a36Sopenharmony_ci
351062306a36Sopenharmony_ci	if (status & MEM_PERR_INT_CAUSE_F)
351162306a36Sopenharmony_ci		csio_fatal(hw, "MA parity error, parity status %#x\n",
351262306a36Sopenharmony_ci			    csio_rd_reg32(hw, MA_PARITY_ERROR_STATUS_A));
351362306a36Sopenharmony_ci	if (status & MEM_WRAP_INT_CAUSE_F) {
351462306a36Sopenharmony_ci		v = csio_rd_reg32(hw, MA_INT_WRAP_STATUS_A);
351562306a36Sopenharmony_ci		csio_fatal(hw,
351662306a36Sopenharmony_ci		   "MA address wrap-around error by client %u to address %#x\n",
351762306a36Sopenharmony_ci		   MEM_WRAP_CLIENT_NUM_G(v), MEM_WRAP_ADDRESS_G(v) << 4);
351862306a36Sopenharmony_ci	}
351962306a36Sopenharmony_ci	csio_wr_reg32(hw, status, MA_INT_CAUSE_A);
352062306a36Sopenharmony_ci	csio_hw_fatal_err(hw);
352162306a36Sopenharmony_ci}
352262306a36Sopenharmony_ci
352362306a36Sopenharmony_ci/*
352462306a36Sopenharmony_ci * SMB interrupt handler.
352562306a36Sopenharmony_ci */
352662306a36Sopenharmony_cistatic void csio_smb_intr_handler(struct csio_hw *hw)
352762306a36Sopenharmony_ci{
352862306a36Sopenharmony_ci	static struct intr_info smb_intr_info[] = {
352962306a36Sopenharmony_ci		{ MSTTXFIFOPARINT_F, "SMB master Tx FIFO parity error", -1, 1 },
353062306a36Sopenharmony_ci		{ MSTRXFIFOPARINT_F, "SMB master Rx FIFO parity error", -1, 1 },
353162306a36Sopenharmony_ci		{ SLVFIFOPARINT_F, "SMB slave FIFO parity error", -1, 1 },
353262306a36Sopenharmony_ci		{ 0, NULL, 0, 0 }
353362306a36Sopenharmony_ci	};
353462306a36Sopenharmony_ci
353562306a36Sopenharmony_ci	if (csio_handle_intr_status(hw, SMB_INT_CAUSE_A, smb_intr_info))
353662306a36Sopenharmony_ci		csio_hw_fatal_err(hw);
353762306a36Sopenharmony_ci}
353862306a36Sopenharmony_ci
353962306a36Sopenharmony_ci/*
354062306a36Sopenharmony_ci * NC-SI interrupt handler.
354162306a36Sopenharmony_ci */
354262306a36Sopenharmony_cistatic void csio_ncsi_intr_handler(struct csio_hw *hw)
354362306a36Sopenharmony_ci{
354462306a36Sopenharmony_ci	static struct intr_info ncsi_intr_info[] = {
354562306a36Sopenharmony_ci		{ CIM_DM_PRTY_ERR_F, "NC-SI CIM parity error", -1, 1 },
354662306a36Sopenharmony_ci		{ MPS_DM_PRTY_ERR_F, "NC-SI MPS parity error", -1, 1 },
354762306a36Sopenharmony_ci		{ TXFIFO_PRTY_ERR_F, "NC-SI Tx FIFO parity error", -1, 1 },
354862306a36Sopenharmony_ci		{ RXFIFO_PRTY_ERR_F, "NC-SI Rx FIFO parity error", -1, 1 },
354962306a36Sopenharmony_ci		{ 0, NULL, 0, 0 }
355062306a36Sopenharmony_ci	};
355162306a36Sopenharmony_ci
355262306a36Sopenharmony_ci	if (csio_handle_intr_status(hw, NCSI_INT_CAUSE_A, ncsi_intr_info))
355362306a36Sopenharmony_ci		csio_hw_fatal_err(hw);
355462306a36Sopenharmony_ci}
355562306a36Sopenharmony_ci
355662306a36Sopenharmony_ci/*
355762306a36Sopenharmony_ci * XGMAC interrupt handler.
355862306a36Sopenharmony_ci */
355962306a36Sopenharmony_cistatic void csio_xgmac_intr_handler(struct csio_hw *hw, int port)
356062306a36Sopenharmony_ci{
356162306a36Sopenharmony_ci	uint32_t v = csio_rd_reg32(hw, T5_PORT_REG(port, MAC_PORT_INT_CAUSE_A));
356262306a36Sopenharmony_ci
356362306a36Sopenharmony_ci	v &= TXFIFO_PRTY_ERR_F | RXFIFO_PRTY_ERR_F;
356462306a36Sopenharmony_ci	if (!v)
356562306a36Sopenharmony_ci		return;
356662306a36Sopenharmony_ci
356762306a36Sopenharmony_ci	if (v & TXFIFO_PRTY_ERR_F)
356862306a36Sopenharmony_ci		csio_fatal(hw, "XGMAC %d Tx FIFO parity error\n", port);
356962306a36Sopenharmony_ci	if (v & RXFIFO_PRTY_ERR_F)
357062306a36Sopenharmony_ci		csio_fatal(hw, "XGMAC %d Rx FIFO parity error\n", port);
357162306a36Sopenharmony_ci	csio_wr_reg32(hw, v, T5_PORT_REG(port, MAC_PORT_INT_CAUSE_A));
357262306a36Sopenharmony_ci	csio_hw_fatal_err(hw);
357362306a36Sopenharmony_ci}
357462306a36Sopenharmony_ci
357562306a36Sopenharmony_ci/*
357662306a36Sopenharmony_ci * PL interrupt handler.
357762306a36Sopenharmony_ci */
357862306a36Sopenharmony_cistatic void csio_pl_intr_handler(struct csio_hw *hw)
357962306a36Sopenharmony_ci{
358062306a36Sopenharmony_ci	static struct intr_info pl_intr_info[] = {
358162306a36Sopenharmony_ci		{ FATALPERR_F, "T4 fatal parity error", -1, 1 },
358262306a36Sopenharmony_ci		{ PERRVFID_F, "PL VFID_MAP parity error", -1, 1 },
358362306a36Sopenharmony_ci		{ 0, NULL, 0, 0 }
358462306a36Sopenharmony_ci	};
358562306a36Sopenharmony_ci
358662306a36Sopenharmony_ci	if (csio_handle_intr_status(hw, PL_PL_INT_CAUSE_A, pl_intr_info))
358762306a36Sopenharmony_ci		csio_hw_fatal_err(hw);
358862306a36Sopenharmony_ci}
358962306a36Sopenharmony_ci
359062306a36Sopenharmony_ci/*
359162306a36Sopenharmony_ci *	csio_hw_slow_intr_handler - control path interrupt handler
359262306a36Sopenharmony_ci *	@hw: HW module
359362306a36Sopenharmony_ci *
359462306a36Sopenharmony_ci *	Interrupt handler for non-data global interrupt events, e.g., errors.
359562306a36Sopenharmony_ci *	The designation 'slow' is because it involves register reads, while
359662306a36Sopenharmony_ci *	data interrupts typically don't involve any MMIOs.
359762306a36Sopenharmony_ci */
359862306a36Sopenharmony_ciint
359962306a36Sopenharmony_cicsio_hw_slow_intr_handler(struct csio_hw *hw)
360062306a36Sopenharmony_ci{
360162306a36Sopenharmony_ci	uint32_t cause = csio_rd_reg32(hw, PL_INT_CAUSE_A);
360262306a36Sopenharmony_ci
360362306a36Sopenharmony_ci	if (!(cause & CSIO_GLBL_INTR_MASK)) {
360462306a36Sopenharmony_ci		CSIO_INC_STATS(hw, n_plint_unexp);
360562306a36Sopenharmony_ci		return 0;
360662306a36Sopenharmony_ci	}
360762306a36Sopenharmony_ci
360862306a36Sopenharmony_ci	csio_dbg(hw, "Slow interrupt! cause: 0x%x\n", cause);
360962306a36Sopenharmony_ci
361062306a36Sopenharmony_ci	CSIO_INC_STATS(hw, n_plint_cnt);
361162306a36Sopenharmony_ci
361262306a36Sopenharmony_ci	if (cause & CIM_F)
361362306a36Sopenharmony_ci		csio_cim_intr_handler(hw);
361462306a36Sopenharmony_ci
361562306a36Sopenharmony_ci	if (cause & MPS_F)
361662306a36Sopenharmony_ci		csio_mps_intr_handler(hw);
361762306a36Sopenharmony_ci
361862306a36Sopenharmony_ci	if (cause & NCSI_F)
361962306a36Sopenharmony_ci		csio_ncsi_intr_handler(hw);
362062306a36Sopenharmony_ci
362162306a36Sopenharmony_ci	if (cause & PL_F)
362262306a36Sopenharmony_ci		csio_pl_intr_handler(hw);
362362306a36Sopenharmony_ci
362462306a36Sopenharmony_ci	if (cause & SMB_F)
362562306a36Sopenharmony_ci		csio_smb_intr_handler(hw);
362662306a36Sopenharmony_ci
362762306a36Sopenharmony_ci	if (cause & XGMAC0_F)
362862306a36Sopenharmony_ci		csio_xgmac_intr_handler(hw, 0);
362962306a36Sopenharmony_ci
363062306a36Sopenharmony_ci	if (cause & XGMAC1_F)
363162306a36Sopenharmony_ci		csio_xgmac_intr_handler(hw, 1);
363262306a36Sopenharmony_ci
363362306a36Sopenharmony_ci	if (cause & XGMAC_KR0_F)
363462306a36Sopenharmony_ci		csio_xgmac_intr_handler(hw, 2);
363562306a36Sopenharmony_ci
363662306a36Sopenharmony_ci	if (cause & XGMAC_KR1_F)
363762306a36Sopenharmony_ci		csio_xgmac_intr_handler(hw, 3);
363862306a36Sopenharmony_ci
363962306a36Sopenharmony_ci	if (cause & PCIE_F)
364062306a36Sopenharmony_ci		hw->chip_ops->chip_pcie_intr_handler(hw);
364162306a36Sopenharmony_ci
364262306a36Sopenharmony_ci	if (cause & MC_F)
364362306a36Sopenharmony_ci		csio_mem_intr_handler(hw, MEM_MC);
364462306a36Sopenharmony_ci
364562306a36Sopenharmony_ci	if (cause & EDC0_F)
364662306a36Sopenharmony_ci		csio_mem_intr_handler(hw, MEM_EDC0);
364762306a36Sopenharmony_ci
364862306a36Sopenharmony_ci	if (cause & EDC1_F)
364962306a36Sopenharmony_ci		csio_mem_intr_handler(hw, MEM_EDC1);
365062306a36Sopenharmony_ci
365162306a36Sopenharmony_ci	if (cause & LE_F)
365262306a36Sopenharmony_ci		csio_le_intr_handler(hw);
365362306a36Sopenharmony_ci
365462306a36Sopenharmony_ci	if (cause & TP_F)
365562306a36Sopenharmony_ci		csio_tp_intr_handler(hw);
365662306a36Sopenharmony_ci
365762306a36Sopenharmony_ci	if (cause & MA_F)
365862306a36Sopenharmony_ci		csio_ma_intr_handler(hw);
365962306a36Sopenharmony_ci
366062306a36Sopenharmony_ci	if (cause & PM_TX_F)
366162306a36Sopenharmony_ci		csio_pmtx_intr_handler(hw);
366262306a36Sopenharmony_ci
366362306a36Sopenharmony_ci	if (cause & PM_RX_F)
366462306a36Sopenharmony_ci		csio_pmrx_intr_handler(hw);
366562306a36Sopenharmony_ci
366662306a36Sopenharmony_ci	if (cause & ULP_RX_F)
366762306a36Sopenharmony_ci		csio_ulprx_intr_handler(hw);
366862306a36Sopenharmony_ci
366962306a36Sopenharmony_ci	if (cause & CPL_SWITCH_F)
367062306a36Sopenharmony_ci		csio_cplsw_intr_handler(hw);
367162306a36Sopenharmony_ci
367262306a36Sopenharmony_ci	if (cause & SGE_F)
367362306a36Sopenharmony_ci		csio_sge_intr_handler(hw);
367462306a36Sopenharmony_ci
367562306a36Sopenharmony_ci	if (cause & ULP_TX_F)
367662306a36Sopenharmony_ci		csio_ulptx_intr_handler(hw);
367762306a36Sopenharmony_ci
367862306a36Sopenharmony_ci	/* Clear the interrupts just processed for which we are the master. */
367962306a36Sopenharmony_ci	csio_wr_reg32(hw, cause & CSIO_GLBL_INTR_MASK, PL_INT_CAUSE_A);
368062306a36Sopenharmony_ci	csio_rd_reg32(hw, PL_INT_CAUSE_A); /* flush */
368162306a36Sopenharmony_ci
368262306a36Sopenharmony_ci	return 1;
368362306a36Sopenharmony_ci}
368462306a36Sopenharmony_ci
368562306a36Sopenharmony_ci/*****************************************************************************
368662306a36Sopenharmony_ci * HW <--> mailbox interfacing routines.
368762306a36Sopenharmony_ci ****************************************************************************/
368862306a36Sopenharmony_ci/*
368962306a36Sopenharmony_ci * csio_mberr_worker - Worker thread (dpc) for mailbox/error completions
369062306a36Sopenharmony_ci *
369162306a36Sopenharmony_ci * @data: Private data pointer.
369262306a36Sopenharmony_ci *
369362306a36Sopenharmony_ci * Called from worker thread context.
369462306a36Sopenharmony_ci */
369562306a36Sopenharmony_cistatic void
369662306a36Sopenharmony_cicsio_mberr_worker(void *data)
369762306a36Sopenharmony_ci{
369862306a36Sopenharmony_ci	struct csio_hw *hw = (struct csio_hw *)data;
369962306a36Sopenharmony_ci	struct csio_mbm *mbm = &hw->mbm;
370062306a36Sopenharmony_ci	LIST_HEAD(cbfn_q);
370162306a36Sopenharmony_ci	struct csio_mb *mbp_next;
370262306a36Sopenharmony_ci	int rv;
370362306a36Sopenharmony_ci
370462306a36Sopenharmony_ci	del_timer_sync(&mbm->timer);
370562306a36Sopenharmony_ci
370662306a36Sopenharmony_ci	spin_lock_irq(&hw->lock);
370762306a36Sopenharmony_ci	if (list_empty(&mbm->cbfn_q)) {
370862306a36Sopenharmony_ci		spin_unlock_irq(&hw->lock);
370962306a36Sopenharmony_ci		return;
371062306a36Sopenharmony_ci	}
371162306a36Sopenharmony_ci
371262306a36Sopenharmony_ci	list_splice_tail_init(&mbm->cbfn_q, &cbfn_q);
371362306a36Sopenharmony_ci	mbm->stats.n_cbfnq = 0;
371462306a36Sopenharmony_ci
371562306a36Sopenharmony_ci	/* Try to start waiting mailboxes */
371662306a36Sopenharmony_ci	if (!list_empty(&mbm->req_q)) {
371762306a36Sopenharmony_ci		mbp_next = list_first_entry(&mbm->req_q, struct csio_mb, list);
371862306a36Sopenharmony_ci		list_del_init(&mbp_next->list);
371962306a36Sopenharmony_ci
372062306a36Sopenharmony_ci		rv = csio_mb_issue(hw, mbp_next);
372162306a36Sopenharmony_ci		if (rv != 0)
372262306a36Sopenharmony_ci			list_add_tail(&mbp_next->list, &mbm->req_q);
372362306a36Sopenharmony_ci		else
372462306a36Sopenharmony_ci			CSIO_DEC_STATS(mbm, n_activeq);
372562306a36Sopenharmony_ci	}
372662306a36Sopenharmony_ci	spin_unlock_irq(&hw->lock);
372762306a36Sopenharmony_ci
372862306a36Sopenharmony_ci	/* Now callback completions */
372962306a36Sopenharmony_ci	csio_mb_completions(hw, &cbfn_q);
373062306a36Sopenharmony_ci}
373162306a36Sopenharmony_ci
373262306a36Sopenharmony_ci/*
373362306a36Sopenharmony_ci * csio_hw_mb_timer - Top-level Mailbox timeout handler.
373462306a36Sopenharmony_ci *
373562306a36Sopenharmony_ci * @data: private data pointer
373662306a36Sopenharmony_ci *
373762306a36Sopenharmony_ci **/
373862306a36Sopenharmony_cistatic void
373962306a36Sopenharmony_cicsio_hw_mb_timer(struct timer_list *t)
374062306a36Sopenharmony_ci{
374162306a36Sopenharmony_ci	struct csio_mbm *mbm = from_timer(mbm, t, timer);
374262306a36Sopenharmony_ci	struct csio_hw *hw = mbm->hw;
374362306a36Sopenharmony_ci	struct csio_mb *mbp = NULL;
374462306a36Sopenharmony_ci
374562306a36Sopenharmony_ci	spin_lock_irq(&hw->lock);
374662306a36Sopenharmony_ci	mbp = csio_mb_tmo_handler(hw);
374762306a36Sopenharmony_ci	spin_unlock_irq(&hw->lock);
374862306a36Sopenharmony_ci
374962306a36Sopenharmony_ci	/* Call back the function for the timed-out Mailbox */
375062306a36Sopenharmony_ci	if (mbp)
375162306a36Sopenharmony_ci		mbp->mb_cbfn(hw, mbp);
375262306a36Sopenharmony_ci
375362306a36Sopenharmony_ci}
375462306a36Sopenharmony_ci
375562306a36Sopenharmony_ci/*
375662306a36Sopenharmony_ci * csio_hw_mbm_cleanup - Cleanup Mailbox module.
375762306a36Sopenharmony_ci * @hw: HW module
375862306a36Sopenharmony_ci *
375962306a36Sopenharmony_ci * Called with lock held, should exit with lock held.
376062306a36Sopenharmony_ci * Cancels outstanding mailboxes (waiting, in-flight) and gathers them
376162306a36Sopenharmony_ci * into a local queue. Drops lock and calls the completions. Holds
376262306a36Sopenharmony_ci * lock and returns.
376362306a36Sopenharmony_ci */
376462306a36Sopenharmony_cistatic void
376562306a36Sopenharmony_cicsio_hw_mbm_cleanup(struct csio_hw *hw)
376662306a36Sopenharmony_ci{
376762306a36Sopenharmony_ci	LIST_HEAD(cbfn_q);
376862306a36Sopenharmony_ci
376962306a36Sopenharmony_ci	csio_mb_cancel_all(hw, &cbfn_q);
377062306a36Sopenharmony_ci
377162306a36Sopenharmony_ci	spin_unlock_irq(&hw->lock);
377262306a36Sopenharmony_ci	csio_mb_completions(hw, &cbfn_q);
377362306a36Sopenharmony_ci	spin_lock_irq(&hw->lock);
377462306a36Sopenharmony_ci}
377562306a36Sopenharmony_ci
377662306a36Sopenharmony_ci/*****************************************************************************
377762306a36Sopenharmony_ci * Event handling
377862306a36Sopenharmony_ci ****************************************************************************/
377962306a36Sopenharmony_ciint
378062306a36Sopenharmony_cicsio_enqueue_evt(struct csio_hw *hw, enum csio_evt type, void *evt_msg,
378162306a36Sopenharmony_ci			uint16_t len)
378262306a36Sopenharmony_ci{
378362306a36Sopenharmony_ci	struct csio_evt_msg *evt_entry = NULL;
378462306a36Sopenharmony_ci
378562306a36Sopenharmony_ci	if (type >= CSIO_EVT_MAX)
378662306a36Sopenharmony_ci		return -EINVAL;
378762306a36Sopenharmony_ci
378862306a36Sopenharmony_ci	if (len > CSIO_EVT_MSG_SIZE)
378962306a36Sopenharmony_ci		return -EINVAL;
379062306a36Sopenharmony_ci
379162306a36Sopenharmony_ci	if (hw->flags & CSIO_HWF_FWEVT_STOP)
379262306a36Sopenharmony_ci		return -EINVAL;
379362306a36Sopenharmony_ci
379462306a36Sopenharmony_ci	if (list_empty(&hw->evt_free_q)) {
379562306a36Sopenharmony_ci		csio_err(hw, "Failed to alloc evt entry, msg type %d len %d\n",
379662306a36Sopenharmony_ci			 type, len);
379762306a36Sopenharmony_ci		return -ENOMEM;
379862306a36Sopenharmony_ci	}
379962306a36Sopenharmony_ci
380062306a36Sopenharmony_ci	evt_entry = list_first_entry(&hw->evt_free_q,
380162306a36Sopenharmony_ci				     struct csio_evt_msg, list);
380262306a36Sopenharmony_ci	list_del_init(&evt_entry->list);
380362306a36Sopenharmony_ci
380462306a36Sopenharmony_ci	/* copy event msg and queue the event */
380562306a36Sopenharmony_ci	evt_entry->type = type;
380662306a36Sopenharmony_ci	memcpy((void *)evt_entry->data, evt_msg, len);
380762306a36Sopenharmony_ci	list_add_tail(&evt_entry->list, &hw->evt_active_q);
380862306a36Sopenharmony_ci
380962306a36Sopenharmony_ci	CSIO_DEC_STATS(hw, n_evt_freeq);
381062306a36Sopenharmony_ci	CSIO_INC_STATS(hw, n_evt_activeq);
381162306a36Sopenharmony_ci
381262306a36Sopenharmony_ci	return 0;
381362306a36Sopenharmony_ci}
381462306a36Sopenharmony_ci
381562306a36Sopenharmony_cistatic int
381662306a36Sopenharmony_cicsio_enqueue_evt_lock(struct csio_hw *hw, enum csio_evt type, void *evt_msg,
381762306a36Sopenharmony_ci			uint16_t len, bool msg_sg)
381862306a36Sopenharmony_ci{
381962306a36Sopenharmony_ci	struct csio_evt_msg *evt_entry = NULL;
382062306a36Sopenharmony_ci	struct csio_fl_dma_buf *fl_sg;
382162306a36Sopenharmony_ci	uint32_t off = 0;
382262306a36Sopenharmony_ci	unsigned long flags;
382362306a36Sopenharmony_ci	int n, ret = 0;
382462306a36Sopenharmony_ci
382562306a36Sopenharmony_ci	if (type >= CSIO_EVT_MAX)
382662306a36Sopenharmony_ci		return -EINVAL;
382762306a36Sopenharmony_ci
382862306a36Sopenharmony_ci	if (len > CSIO_EVT_MSG_SIZE)
382962306a36Sopenharmony_ci		return -EINVAL;
383062306a36Sopenharmony_ci
383162306a36Sopenharmony_ci	spin_lock_irqsave(&hw->lock, flags);
383262306a36Sopenharmony_ci	if (hw->flags & CSIO_HWF_FWEVT_STOP) {
383362306a36Sopenharmony_ci		ret = -EINVAL;
383462306a36Sopenharmony_ci		goto out;
383562306a36Sopenharmony_ci	}
383662306a36Sopenharmony_ci
383762306a36Sopenharmony_ci	if (list_empty(&hw->evt_free_q)) {
383862306a36Sopenharmony_ci		csio_err(hw, "Failed to alloc evt entry, msg type %d len %d\n",
383962306a36Sopenharmony_ci			 type, len);
384062306a36Sopenharmony_ci		ret = -ENOMEM;
384162306a36Sopenharmony_ci		goto out;
384262306a36Sopenharmony_ci	}
384362306a36Sopenharmony_ci
384462306a36Sopenharmony_ci	evt_entry = list_first_entry(&hw->evt_free_q,
384562306a36Sopenharmony_ci				     struct csio_evt_msg, list);
384662306a36Sopenharmony_ci	list_del_init(&evt_entry->list);
384762306a36Sopenharmony_ci
384862306a36Sopenharmony_ci	/* copy event msg and queue the event */
384962306a36Sopenharmony_ci	evt_entry->type = type;
385062306a36Sopenharmony_ci
385162306a36Sopenharmony_ci	/* If Payload in SG list*/
385262306a36Sopenharmony_ci	if (msg_sg) {
385362306a36Sopenharmony_ci		fl_sg = (struct csio_fl_dma_buf *) evt_msg;
385462306a36Sopenharmony_ci		for (n = 0; (n < CSIO_MAX_FLBUF_PER_IQWR && off < len); n++) {
385562306a36Sopenharmony_ci			memcpy((void *)((uintptr_t)evt_entry->data + off),
385662306a36Sopenharmony_ci				fl_sg->flbufs[n].vaddr,
385762306a36Sopenharmony_ci				fl_sg->flbufs[n].len);
385862306a36Sopenharmony_ci			off += fl_sg->flbufs[n].len;
385962306a36Sopenharmony_ci		}
386062306a36Sopenharmony_ci	} else
386162306a36Sopenharmony_ci		memcpy((void *)evt_entry->data, evt_msg, len);
386262306a36Sopenharmony_ci
386362306a36Sopenharmony_ci	list_add_tail(&evt_entry->list, &hw->evt_active_q);
386462306a36Sopenharmony_ci	CSIO_DEC_STATS(hw, n_evt_freeq);
386562306a36Sopenharmony_ci	CSIO_INC_STATS(hw, n_evt_activeq);
386662306a36Sopenharmony_ciout:
386762306a36Sopenharmony_ci	spin_unlock_irqrestore(&hw->lock, flags);
386862306a36Sopenharmony_ci	return ret;
386962306a36Sopenharmony_ci}
387062306a36Sopenharmony_ci
387162306a36Sopenharmony_cistatic void
387262306a36Sopenharmony_cicsio_free_evt(struct csio_hw *hw, struct csio_evt_msg *evt_entry)
387362306a36Sopenharmony_ci{
387462306a36Sopenharmony_ci	if (evt_entry) {
387562306a36Sopenharmony_ci		spin_lock_irq(&hw->lock);
387662306a36Sopenharmony_ci		list_del_init(&evt_entry->list);
387762306a36Sopenharmony_ci		list_add_tail(&evt_entry->list, &hw->evt_free_q);
387862306a36Sopenharmony_ci		CSIO_DEC_STATS(hw, n_evt_activeq);
387962306a36Sopenharmony_ci		CSIO_INC_STATS(hw, n_evt_freeq);
388062306a36Sopenharmony_ci		spin_unlock_irq(&hw->lock);
388162306a36Sopenharmony_ci	}
388262306a36Sopenharmony_ci}
388362306a36Sopenharmony_ci
388462306a36Sopenharmony_civoid
388562306a36Sopenharmony_cicsio_evtq_flush(struct csio_hw *hw)
388662306a36Sopenharmony_ci{
388762306a36Sopenharmony_ci	uint32_t count;
388862306a36Sopenharmony_ci	count = 30;
388962306a36Sopenharmony_ci	while (hw->flags & CSIO_HWF_FWEVT_PENDING && count--) {
389062306a36Sopenharmony_ci		spin_unlock_irq(&hw->lock);
389162306a36Sopenharmony_ci		msleep(2000);
389262306a36Sopenharmony_ci		spin_lock_irq(&hw->lock);
389362306a36Sopenharmony_ci	}
389462306a36Sopenharmony_ci
389562306a36Sopenharmony_ci	CSIO_DB_ASSERT(!(hw->flags & CSIO_HWF_FWEVT_PENDING));
389662306a36Sopenharmony_ci}
389762306a36Sopenharmony_ci
389862306a36Sopenharmony_cistatic void
389962306a36Sopenharmony_cicsio_evtq_stop(struct csio_hw *hw)
390062306a36Sopenharmony_ci{
390162306a36Sopenharmony_ci	hw->flags |= CSIO_HWF_FWEVT_STOP;
390262306a36Sopenharmony_ci}
390362306a36Sopenharmony_ci
390462306a36Sopenharmony_cistatic void
390562306a36Sopenharmony_cicsio_evtq_start(struct csio_hw *hw)
390662306a36Sopenharmony_ci{
390762306a36Sopenharmony_ci	hw->flags &= ~CSIO_HWF_FWEVT_STOP;
390862306a36Sopenharmony_ci}
390962306a36Sopenharmony_ci
391062306a36Sopenharmony_cistatic void
391162306a36Sopenharmony_cicsio_evtq_cleanup(struct csio_hw *hw)
391262306a36Sopenharmony_ci{
391362306a36Sopenharmony_ci	struct list_head *evt_entry, *next_entry;
391462306a36Sopenharmony_ci
391562306a36Sopenharmony_ci	/* Release outstanding events from activeq to freeq*/
391662306a36Sopenharmony_ci	if (!list_empty(&hw->evt_active_q))
391762306a36Sopenharmony_ci		list_splice_tail_init(&hw->evt_active_q, &hw->evt_free_q);
391862306a36Sopenharmony_ci
391962306a36Sopenharmony_ci	hw->stats.n_evt_activeq = 0;
392062306a36Sopenharmony_ci	hw->flags &= ~CSIO_HWF_FWEVT_PENDING;
392162306a36Sopenharmony_ci
392262306a36Sopenharmony_ci	/* Freeup event entry */
392362306a36Sopenharmony_ci	list_for_each_safe(evt_entry, next_entry, &hw->evt_free_q) {
392462306a36Sopenharmony_ci		kfree(evt_entry);
392562306a36Sopenharmony_ci		CSIO_DEC_STATS(hw, n_evt_freeq);
392662306a36Sopenharmony_ci	}
392762306a36Sopenharmony_ci
392862306a36Sopenharmony_ci	hw->stats.n_evt_freeq = 0;
392962306a36Sopenharmony_ci}
393062306a36Sopenharmony_ci
393162306a36Sopenharmony_ci
393262306a36Sopenharmony_cistatic void
393362306a36Sopenharmony_cicsio_process_fwevtq_entry(struct csio_hw *hw, void *wr, uint32_t len,
393462306a36Sopenharmony_ci			  struct csio_fl_dma_buf *flb, void *priv)
393562306a36Sopenharmony_ci{
393662306a36Sopenharmony_ci	__u8 op;
393762306a36Sopenharmony_ci	void *msg = NULL;
393862306a36Sopenharmony_ci	uint32_t msg_len = 0;
393962306a36Sopenharmony_ci	bool msg_sg = 0;
394062306a36Sopenharmony_ci
394162306a36Sopenharmony_ci	op = ((struct rss_header *) wr)->opcode;
394262306a36Sopenharmony_ci	if (op == CPL_FW6_PLD) {
394362306a36Sopenharmony_ci		CSIO_INC_STATS(hw, n_cpl_fw6_pld);
394462306a36Sopenharmony_ci		if (!flb || !flb->totlen) {
394562306a36Sopenharmony_ci			CSIO_INC_STATS(hw, n_cpl_unexp);
394662306a36Sopenharmony_ci			return;
394762306a36Sopenharmony_ci		}
394862306a36Sopenharmony_ci
394962306a36Sopenharmony_ci		msg = (void *) flb;
395062306a36Sopenharmony_ci		msg_len = flb->totlen;
395162306a36Sopenharmony_ci		msg_sg = 1;
395262306a36Sopenharmony_ci	} else if (op == CPL_FW6_MSG || op == CPL_FW4_MSG) {
395362306a36Sopenharmony_ci
395462306a36Sopenharmony_ci		CSIO_INC_STATS(hw, n_cpl_fw6_msg);
395562306a36Sopenharmony_ci		/* skip RSS header */
395662306a36Sopenharmony_ci		msg = (void *)((uintptr_t)wr + sizeof(__be64));
395762306a36Sopenharmony_ci		msg_len = (op == CPL_FW6_MSG) ? sizeof(struct cpl_fw6_msg) :
395862306a36Sopenharmony_ci			   sizeof(struct cpl_fw4_msg);
395962306a36Sopenharmony_ci	} else {
396062306a36Sopenharmony_ci		csio_warn(hw, "unexpected CPL %#x on FW event queue\n", op);
396162306a36Sopenharmony_ci		CSIO_INC_STATS(hw, n_cpl_unexp);
396262306a36Sopenharmony_ci		return;
396362306a36Sopenharmony_ci	}
396462306a36Sopenharmony_ci
396562306a36Sopenharmony_ci	/*
396662306a36Sopenharmony_ci	 * Enqueue event to EventQ. Events processing happens
396762306a36Sopenharmony_ci	 * in Event worker thread context
396862306a36Sopenharmony_ci	 */
396962306a36Sopenharmony_ci	if (csio_enqueue_evt_lock(hw, CSIO_EVT_FW, msg,
397062306a36Sopenharmony_ci				  (uint16_t)msg_len, msg_sg))
397162306a36Sopenharmony_ci		CSIO_INC_STATS(hw, n_evt_drop);
397262306a36Sopenharmony_ci}
397362306a36Sopenharmony_ci
397462306a36Sopenharmony_civoid
397562306a36Sopenharmony_cicsio_evtq_worker(struct work_struct *work)
397662306a36Sopenharmony_ci{
397762306a36Sopenharmony_ci	struct csio_hw *hw = container_of(work, struct csio_hw, evtq_work);
397862306a36Sopenharmony_ci	struct list_head *evt_entry, *next_entry;
397962306a36Sopenharmony_ci	LIST_HEAD(evt_q);
398062306a36Sopenharmony_ci	struct csio_evt_msg	*evt_msg;
398162306a36Sopenharmony_ci	struct cpl_fw6_msg *msg;
398262306a36Sopenharmony_ci	struct csio_rnode *rn;
398362306a36Sopenharmony_ci	int rv = 0;
398462306a36Sopenharmony_ci	uint8_t evtq_stop = 0;
398562306a36Sopenharmony_ci
398662306a36Sopenharmony_ci	csio_dbg(hw, "event worker thread active evts#%d\n",
398762306a36Sopenharmony_ci		 hw->stats.n_evt_activeq);
398862306a36Sopenharmony_ci
398962306a36Sopenharmony_ci	spin_lock_irq(&hw->lock);
399062306a36Sopenharmony_ci	while (!list_empty(&hw->evt_active_q)) {
399162306a36Sopenharmony_ci		list_splice_tail_init(&hw->evt_active_q, &evt_q);
399262306a36Sopenharmony_ci		spin_unlock_irq(&hw->lock);
399362306a36Sopenharmony_ci
399462306a36Sopenharmony_ci		list_for_each_safe(evt_entry, next_entry, &evt_q) {
399562306a36Sopenharmony_ci			evt_msg = (struct csio_evt_msg *) evt_entry;
399662306a36Sopenharmony_ci
399762306a36Sopenharmony_ci			/* Drop events if queue is STOPPED */
399862306a36Sopenharmony_ci			spin_lock_irq(&hw->lock);
399962306a36Sopenharmony_ci			if (hw->flags & CSIO_HWF_FWEVT_STOP)
400062306a36Sopenharmony_ci				evtq_stop = 1;
400162306a36Sopenharmony_ci			spin_unlock_irq(&hw->lock);
400262306a36Sopenharmony_ci			if (evtq_stop) {
400362306a36Sopenharmony_ci				CSIO_INC_STATS(hw, n_evt_drop);
400462306a36Sopenharmony_ci				goto free_evt;
400562306a36Sopenharmony_ci			}
400662306a36Sopenharmony_ci
400762306a36Sopenharmony_ci			switch (evt_msg->type) {
400862306a36Sopenharmony_ci			case CSIO_EVT_FW:
400962306a36Sopenharmony_ci				msg = (struct cpl_fw6_msg *)(evt_msg->data);
401062306a36Sopenharmony_ci
401162306a36Sopenharmony_ci				if ((msg->opcode == CPL_FW6_MSG ||
401262306a36Sopenharmony_ci				     msg->opcode == CPL_FW4_MSG) &&
401362306a36Sopenharmony_ci				    !msg->type) {
401462306a36Sopenharmony_ci					rv = csio_mb_fwevt_handler(hw,
401562306a36Sopenharmony_ci								msg->data);
401662306a36Sopenharmony_ci					if (!rv)
401762306a36Sopenharmony_ci						break;
401862306a36Sopenharmony_ci					/* Handle any remaining fw events */
401962306a36Sopenharmony_ci					csio_fcoe_fwevt_handler(hw,
402062306a36Sopenharmony_ci							msg->opcode, msg->data);
402162306a36Sopenharmony_ci				} else if (msg->opcode == CPL_FW6_PLD) {
402262306a36Sopenharmony_ci
402362306a36Sopenharmony_ci					csio_fcoe_fwevt_handler(hw,
402462306a36Sopenharmony_ci							msg->opcode, msg->data);
402562306a36Sopenharmony_ci				} else {
402662306a36Sopenharmony_ci					csio_warn(hw,
402762306a36Sopenharmony_ci					     "Unhandled FW msg op %x type %x\n",
402862306a36Sopenharmony_ci						  msg->opcode, msg->type);
402962306a36Sopenharmony_ci					CSIO_INC_STATS(hw, n_evt_drop);
403062306a36Sopenharmony_ci				}
403162306a36Sopenharmony_ci				break;
403262306a36Sopenharmony_ci
403362306a36Sopenharmony_ci			case CSIO_EVT_MBX:
403462306a36Sopenharmony_ci				csio_mberr_worker(hw);
403562306a36Sopenharmony_ci				break;
403662306a36Sopenharmony_ci
403762306a36Sopenharmony_ci			case CSIO_EVT_DEV_LOSS:
403862306a36Sopenharmony_ci				memcpy(&rn, evt_msg->data, sizeof(rn));
403962306a36Sopenharmony_ci				csio_rnode_devloss_handler(rn);
404062306a36Sopenharmony_ci				break;
404162306a36Sopenharmony_ci
404262306a36Sopenharmony_ci			default:
404362306a36Sopenharmony_ci				csio_warn(hw, "Unhandled event %x on evtq\n",
404462306a36Sopenharmony_ci					  evt_msg->type);
404562306a36Sopenharmony_ci				CSIO_INC_STATS(hw, n_evt_unexp);
404662306a36Sopenharmony_ci				break;
404762306a36Sopenharmony_ci			}
404862306a36Sopenharmony_cifree_evt:
404962306a36Sopenharmony_ci			csio_free_evt(hw, evt_msg);
405062306a36Sopenharmony_ci		}
405162306a36Sopenharmony_ci
405262306a36Sopenharmony_ci		spin_lock_irq(&hw->lock);
405362306a36Sopenharmony_ci	}
405462306a36Sopenharmony_ci	hw->flags &= ~CSIO_HWF_FWEVT_PENDING;
405562306a36Sopenharmony_ci	spin_unlock_irq(&hw->lock);
405662306a36Sopenharmony_ci}
405762306a36Sopenharmony_ci
405862306a36Sopenharmony_ciint
405962306a36Sopenharmony_cicsio_fwevtq_handler(struct csio_hw *hw)
406062306a36Sopenharmony_ci{
406162306a36Sopenharmony_ci	int rv;
406262306a36Sopenharmony_ci
406362306a36Sopenharmony_ci	if (csio_q_iqid(hw, hw->fwevt_iq_idx) == CSIO_MAX_QID) {
406462306a36Sopenharmony_ci		CSIO_INC_STATS(hw, n_int_stray);
406562306a36Sopenharmony_ci		return -EINVAL;
406662306a36Sopenharmony_ci	}
406762306a36Sopenharmony_ci
406862306a36Sopenharmony_ci	rv = csio_wr_process_iq_idx(hw, hw->fwevt_iq_idx,
406962306a36Sopenharmony_ci			   csio_process_fwevtq_entry, NULL);
407062306a36Sopenharmony_ci	return rv;
407162306a36Sopenharmony_ci}
407262306a36Sopenharmony_ci
407362306a36Sopenharmony_ci/****************************************************************************
407462306a36Sopenharmony_ci * Entry points
407562306a36Sopenharmony_ci ****************************************************************************/
407662306a36Sopenharmony_ci
407762306a36Sopenharmony_ci/* Management module */
407862306a36Sopenharmony_ci/*
407962306a36Sopenharmony_ci * csio_mgmt_req_lookup - Lookup the given IO req exist in Active Q.
408062306a36Sopenharmony_ci * mgmt - mgmt module
408162306a36Sopenharmony_ci * @io_req - io request
408262306a36Sopenharmony_ci *
408362306a36Sopenharmony_ci * Return - 0:if given IO Req exists in active Q.
408462306a36Sopenharmony_ci *          -EINVAL  :if lookup fails.
408562306a36Sopenharmony_ci */
408662306a36Sopenharmony_ciint
408762306a36Sopenharmony_cicsio_mgmt_req_lookup(struct csio_mgmtm *mgmtm, struct csio_ioreq *io_req)
408862306a36Sopenharmony_ci{
408962306a36Sopenharmony_ci	struct list_head *tmp;
409062306a36Sopenharmony_ci
409162306a36Sopenharmony_ci	/* Lookup ioreq in the ACTIVEQ */
409262306a36Sopenharmony_ci	list_for_each(tmp, &mgmtm->active_q) {
409362306a36Sopenharmony_ci		if (io_req == (struct csio_ioreq *)tmp)
409462306a36Sopenharmony_ci			return 0;
409562306a36Sopenharmony_ci	}
409662306a36Sopenharmony_ci	return -EINVAL;
409762306a36Sopenharmony_ci}
409862306a36Sopenharmony_ci
409962306a36Sopenharmony_ci#define	ECM_MIN_TMO	1000	/* Minimum timeout value for req */
410062306a36Sopenharmony_ci
410162306a36Sopenharmony_ci/*
410262306a36Sopenharmony_ci * csio_mgmts_tmo_handler - MGMT IO Timeout handler.
410362306a36Sopenharmony_ci * @data - Event data.
410462306a36Sopenharmony_ci *
410562306a36Sopenharmony_ci * Return - none.
410662306a36Sopenharmony_ci */
410762306a36Sopenharmony_cistatic void
410862306a36Sopenharmony_cicsio_mgmt_tmo_handler(struct timer_list *t)
410962306a36Sopenharmony_ci{
411062306a36Sopenharmony_ci	struct csio_mgmtm *mgmtm = from_timer(mgmtm, t, mgmt_timer);
411162306a36Sopenharmony_ci	struct list_head *tmp;
411262306a36Sopenharmony_ci	struct csio_ioreq *io_req;
411362306a36Sopenharmony_ci
411462306a36Sopenharmony_ci	csio_dbg(mgmtm->hw, "Mgmt timer invoked!\n");
411562306a36Sopenharmony_ci
411662306a36Sopenharmony_ci	spin_lock_irq(&mgmtm->hw->lock);
411762306a36Sopenharmony_ci
411862306a36Sopenharmony_ci	list_for_each(tmp, &mgmtm->active_q) {
411962306a36Sopenharmony_ci		io_req = (struct csio_ioreq *) tmp;
412062306a36Sopenharmony_ci		io_req->tmo -= min_t(uint32_t, io_req->tmo, ECM_MIN_TMO);
412162306a36Sopenharmony_ci
412262306a36Sopenharmony_ci		if (!io_req->tmo) {
412362306a36Sopenharmony_ci			/* Dequeue the request from retry Q. */
412462306a36Sopenharmony_ci			tmp = csio_list_prev(tmp);
412562306a36Sopenharmony_ci			list_del_init(&io_req->sm.sm_list);
412662306a36Sopenharmony_ci			if (io_req->io_cbfn) {
412762306a36Sopenharmony_ci				/* io_req will be freed by completion handler */
412862306a36Sopenharmony_ci				io_req->wr_status = -ETIMEDOUT;
412962306a36Sopenharmony_ci				io_req->io_cbfn(mgmtm->hw, io_req);
413062306a36Sopenharmony_ci			} else {
413162306a36Sopenharmony_ci				CSIO_DB_ASSERT(0);
413262306a36Sopenharmony_ci			}
413362306a36Sopenharmony_ci		}
413462306a36Sopenharmony_ci	}
413562306a36Sopenharmony_ci
413662306a36Sopenharmony_ci	/* If retry queue is not empty, re-arm timer */
413762306a36Sopenharmony_ci	if (!list_empty(&mgmtm->active_q))
413862306a36Sopenharmony_ci		mod_timer(&mgmtm->mgmt_timer,
413962306a36Sopenharmony_ci			  jiffies + msecs_to_jiffies(ECM_MIN_TMO));
414062306a36Sopenharmony_ci	spin_unlock_irq(&mgmtm->hw->lock);
414162306a36Sopenharmony_ci}
414262306a36Sopenharmony_ci
414362306a36Sopenharmony_cistatic void
414462306a36Sopenharmony_cicsio_mgmtm_cleanup(struct csio_mgmtm *mgmtm)
414562306a36Sopenharmony_ci{
414662306a36Sopenharmony_ci	struct csio_hw *hw = mgmtm->hw;
414762306a36Sopenharmony_ci	struct csio_ioreq *io_req;
414862306a36Sopenharmony_ci	struct list_head *tmp;
414962306a36Sopenharmony_ci	uint32_t count;
415062306a36Sopenharmony_ci
415162306a36Sopenharmony_ci	count = 30;
415262306a36Sopenharmony_ci	/* Wait for all outstanding req to complete gracefully */
415362306a36Sopenharmony_ci	while ((!list_empty(&mgmtm->active_q)) && count--) {
415462306a36Sopenharmony_ci		spin_unlock_irq(&hw->lock);
415562306a36Sopenharmony_ci		msleep(2000);
415662306a36Sopenharmony_ci		spin_lock_irq(&hw->lock);
415762306a36Sopenharmony_ci	}
415862306a36Sopenharmony_ci
415962306a36Sopenharmony_ci	/* release outstanding req from ACTIVEQ */
416062306a36Sopenharmony_ci	list_for_each(tmp, &mgmtm->active_q) {
416162306a36Sopenharmony_ci		io_req = (struct csio_ioreq *) tmp;
416262306a36Sopenharmony_ci		tmp = csio_list_prev(tmp);
416362306a36Sopenharmony_ci		list_del_init(&io_req->sm.sm_list);
416462306a36Sopenharmony_ci		mgmtm->stats.n_active--;
416562306a36Sopenharmony_ci		if (io_req->io_cbfn) {
416662306a36Sopenharmony_ci			/* io_req will be freed by completion handler */
416762306a36Sopenharmony_ci			io_req->wr_status = -ETIMEDOUT;
416862306a36Sopenharmony_ci			io_req->io_cbfn(mgmtm->hw, io_req);
416962306a36Sopenharmony_ci		}
417062306a36Sopenharmony_ci	}
417162306a36Sopenharmony_ci}
417262306a36Sopenharmony_ci
417362306a36Sopenharmony_ci/*
417462306a36Sopenharmony_ci * csio_mgmt_init - Mgmt module init entry point
417562306a36Sopenharmony_ci * @mgmtsm - mgmt module
417662306a36Sopenharmony_ci * @hw	 - HW module
417762306a36Sopenharmony_ci *
417862306a36Sopenharmony_ci * Initialize mgmt timer, resource wait queue, active queue,
417962306a36Sopenharmony_ci * completion q. Allocate Egress and Ingress
418062306a36Sopenharmony_ci * WR queues and save off the queue index returned by the WR
418162306a36Sopenharmony_ci * module for future use. Allocate and save off mgmt reqs in the
418262306a36Sopenharmony_ci * mgmt_req_freelist for future use. Make sure their SM is initialized
418362306a36Sopenharmony_ci * to uninit state.
418462306a36Sopenharmony_ci * Returns: 0 - on success
418562306a36Sopenharmony_ci *          -ENOMEM   - on error.
418662306a36Sopenharmony_ci */
418762306a36Sopenharmony_cistatic int
418862306a36Sopenharmony_cicsio_mgmtm_init(struct csio_mgmtm *mgmtm, struct csio_hw *hw)
418962306a36Sopenharmony_ci{
419062306a36Sopenharmony_ci	timer_setup(&mgmtm->mgmt_timer, csio_mgmt_tmo_handler, 0);
419162306a36Sopenharmony_ci
419262306a36Sopenharmony_ci	INIT_LIST_HEAD(&mgmtm->active_q);
419362306a36Sopenharmony_ci	INIT_LIST_HEAD(&mgmtm->cbfn_q);
419462306a36Sopenharmony_ci
419562306a36Sopenharmony_ci	mgmtm->hw = hw;
419662306a36Sopenharmony_ci	/*mgmtm->iq_idx = hw->fwevt_iq_idx;*/
419762306a36Sopenharmony_ci
419862306a36Sopenharmony_ci	return 0;
419962306a36Sopenharmony_ci}
420062306a36Sopenharmony_ci
420162306a36Sopenharmony_ci/*
420262306a36Sopenharmony_ci * csio_mgmtm_exit - MGMT module exit entry point
420362306a36Sopenharmony_ci * @mgmtsm - mgmt module
420462306a36Sopenharmony_ci *
420562306a36Sopenharmony_ci * This function called during MGMT module uninit.
420662306a36Sopenharmony_ci * Stop timers, free ioreqs allocated.
420762306a36Sopenharmony_ci * Returns: None
420862306a36Sopenharmony_ci *
420962306a36Sopenharmony_ci */
421062306a36Sopenharmony_cistatic void
421162306a36Sopenharmony_cicsio_mgmtm_exit(struct csio_mgmtm *mgmtm)
421262306a36Sopenharmony_ci{
421362306a36Sopenharmony_ci	del_timer_sync(&mgmtm->mgmt_timer);
421462306a36Sopenharmony_ci}
421562306a36Sopenharmony_ci
421662306a36Sopenharmony_ci
421762306a36Sopenharmony_ci/**
421862306a36Sopenharmony_ci * csio_hw_start - Kicks off the HW State machine
421962306a36Sopenharmony_ci * @hw:		Pointer to HW module.
422062306a36Sopenharmony_ci *
422162306a36Sopenharmony_ci * It is assumed that the initialization is a synchronous operation.
422262306a36Sopenharmony_ci * So when we return after posting the event, the HW SM should be in
422362306a36Sopenharmony_ci * the ready state, if there were no errors during init.
422462306a36Sopenharmony_ci */
422562306a36Sopenharmony_ciint
422662306a36Sopenharmony_cicsio_hw_start(struct csio_hw *hw)
422762306a36Sopenharmony_ci{
422862306a36Sopenharmony_ci	spin_lock_irq(&hw->lock);
422962306a36Sopenharmony_ci	csio_post_event(&hw->sm, CSIO_HWE_CFG);
423062306a36Sopenharmony_ci	spin_unlock_irq(&hw->lock);
423162306a36Sopenharmony_ci
423262306a36Sopenharmony_ci	if (csio_is_hw_ready(hw))
423362306a36Sopenharmony_ci		return 0;
423462306a36Sopenharmony_ci	else if (csio_match_state(hw, csio_hws_uninit))
423562306a36Sopenharmony_ci		return -EINVAL;
423662306a36Sopenharmony_ci	else
423762306a36Sopenharmony_ci		return -ENODEV;
423862306a36Sopenharmony_ci}
423962306a36Sopenharmony_ci
424062306a36Sopenharmony_ciint
424162306a36Sopenharmony_cicsio_hw_stop(struct csio_hw *hw)
424262306a36Sopenharmony_ci{
424362306a36Sopenharmony_ci	csio_post_event(&hw->sm, CSIO_HWE_PCI_REMOVE);
424462306a36Sopenharmony_ci
424562306a36Sopenharmony_ci	if (csio_is_hw_removing(hw))
424662306a36Sopenharmony_ci		return 0;
424762306a36Sopenharmony_ci	else
424862306a36Sopenharmony_ci		return -EINVAL;
424962306a36Sopenharmony_ci}
425062306a36Sopenharmony_ci
425162306a36Sopenharmony_ci/* Max reset retries */
425262306a36Sopenharmony_ci#define CSIO_MAX_RESET_RETRIES	3
425362306a36Sopenharmony_ci
425462306a36Sopenharmony_ci/**
425562306a36Sopenharmony_ci * csio_hw_reset - Reset the hardware
425662306a36Sopenharmony_ci * @hw:		HW module.
425762306a36Sopenharmony_ci *
425862306a36Sopenharmony_ci * Caller should hold lock across this function.
425962306a36Sopenharmony_ci */
426062306a36Sopenharmony_ciint
426162306a36Sopenharmony_cicsio_hw_reset(struct csio_hw *hw)
426262306a36Sopenharmony_ci{
426362306a36Sopenharmony_ci	if (!csio_is_hw_master(hw))
426462306a36Sopenharmony_ci		return -EPERM;
426562306a36Sopenharmony_ci
426662306a36Sopenharmony_ci	if (hw->rst_retries >= CSIO_MAX_RESET_RETRIES) {
426762306a36Sopenharmony_ci		csio_dbg(hw, "Max hw reset attempts reached..");
426862306a36Sopenharmony_ci		return -EINVAL;
426962306a36Sopenharmony_ci	}
427062306a36Sopenharmony_ci
427162306a36Sopenharmony_ci	hw->rst_retries++;
427262306a36Sopenharmony_ci	csio_post_event(&hw->sm, CSIO_HWE_HBA_RESET);
427362306a36Sopenharmony_ci
427462306a36Sopenharmony_ci	if (csio_is_hw_ready(hw)) {
427562306a36Sopenharmony_ci		hw->rst_retries = 0;
427662306a36Sopenharmony_ci		hw->stats.n_reset_start = jiffies_to_msecs(jiffies);
427762306a36Sopenharmony_ci		return 0;
427862306a36Sopenharmony_ci	} else
427962306a36Sopenharmony_ci		return -EINVAL;
428062306a36Sopenharmony_ci}
428162306a36Sopenharmony_ci
428262306a36Sopenharmony_ci/*
428362306a36Sopenharmony_ci * csio_hw_get_device_id - Caches the Adapter's vendor & device id.
428462306a36Sopenharmony_ci * @hw: HW module.
428562306a36Sopenharmony_ci */
428662306a36Sopenharmony_cistatic void
428762306a36Sopenharmony_cicsio_hw_get_device_id(struct csio_hw *hw)
428862306a36Sopenharmony_ci{
428962306a36Sopenharmony_ci	/* Is the adapter device id cached already ?*/
429062306a36Sopenharmony_ci	if (csio_is_dev_id_cached(hw))
429162306a36Sopenharmony_ci		return;
429262306a36Sopenharmony_ci
429362306a36Sopenharmony_ci	/* Get the PCI vendor & device id */
429462306a36Sopenharmony_ci	pci_read_config_word(hw->pdev, PCI_VENDOR_ID,
429562306a36Sopenharmony_ci			     &hw->params.pci.vendor_id);
429662306a36Sopenharmony_ci	pci_read_config_word(hw->pdev, PCI_DEVICE_ID,
429762306a36Sopenharmony_ci			     &hw->params.pci.device_id);
429862306a36Sopenharmony_ci
429962306a36Sopenharmony_ci	csio_dev_id_cached(hw);
430062306a36Sopenharmony_ci	hw->chip_id = (hw->params.pci.device_id & CSIO_HW_CHIP_MASK);
430162306a36Sopenharmony_ci
430262306a36Sopenharmony_ci} /* csio_hw_get_device_id */
430362306a36Sopenharmony_ci
430462306a36Sopenharmony_ci/*
430562306a36Sopenharmony_ci * csio_hw_set_description - Set the model, description of the hw.
430662306a36Sopenharmony_ci * @hw: HW module.
430762306a36Sopenharmony_ci * @ven_id: PCI Vendor ID
430862306a36Sopenharmony_ci * @dev_id: PCI Device ID
430962306a36Sopenharmony_ci */
431062306a36Sopenharmony_cistatic void
431162306a36Sopenharmony_cicsio_hw_set_description(struct csio_hw *hw, uint16_t ven_id, uint16_t dev_id)
431262306a36Sopenharmony_ci{
431362306a36Sopenharmony_ci	uint32_t adap_type, prot_type;
431462306a36Sopenharmony_ci
431562306a36Sopenharmony_ci	if (ven_id == CSIO_VENDOR_ID) {
431662306a36Sopenharmony_ci		prot_type = (dev_id & CSIO_ASIC_DEVID_PROTO_MASK);
431762306a36Sopenharmony_ci		adap_type = (dev_id & CSIO_ASIC_DEVID_TYPE_MASK);
431862306a36Sopenharmony_ci
431962306a36Sopenharmony_ci		if (prot_type == CSIO_T5_FCOE_ASIC) {
432062306a36Sopenharmony_ci			memcpy(hw->hw_ver,
432162306a36Sopenharmony_ci			       csio_t5_fcoe_adapters[adap_type].model_no, 16);
432262306a36Sopenharmony_ci			memcpy(hw->model_desc,
432362306a36Sopenharmony_ci			       csio_t5_fcoe_adapters[adap_type].description,
432462306a36Sopenharmony_ci			       32);
432562306a36Sopenharmony_ci		} else {
432662306a36Sopenharmony_ci			char tempName[32] = "Chelsio FCoE Controller";
432762306a36Sopenharmony_ci			memcpy(hw->model_desc, tempName, 32);
432862306a36Sopenharmony_ci		}
432962306a36Sopenharmony_ci	}
433062306a36Sopenharmony_ci} /* csio_hw_set_description */
433162306a36Sopenharmony_ci
433262306a36Sopenharmony_ci/**
433362306a36Sopenharmony_ci * csio_hw_init - Initialize HW module.
433462306a36Sopenharmony_ci * @hw:		Pointer to HW module.
433562306a36Sopenharmony_ci *
433662306a36Sopenharmony_ci * Initialize the members of the HW module.
433762306a36Sopenharmony_ci */
433862306a36Sopenharmony_ciint
433962306a36Sopenharmony_cicsio_hw_init(struct csio_hw *hw)
434062306a36Sopenharmony_ci{
434162306a36Sopenharmony_ci	int rv = -EINVAL;
434262306a36Sopenharmony_ci	uint32_t i;
434362306a36Sopenharmony_ci	uint16_t ven_id, dev_id;
434462306a36Sopenharmony_ci	struct csio_evt_msg	*evt_entry;
434562306a36Sopenharmony_ci
434662306a36Sopenharmony_ci	INIT_LIST_HEAD(&hw->sm.sm_list);
434762306a36Sopenharmony_ci	csio_init_state(&hw->sm, csio_hws_uninit);
434862306a36Sopenharmony_ci	spin_lock_init(&hw->lock);
434962306a36Sopenharmony_ci	INIT_LIST_HEAD(&hw->sln_head);
435062306a36Sopenharmony_ci
435162306a36Sopenharmony_ci	/* Get the PCI vendor & device id */
435262306a36Sopenharmony_ci	csio_hw_get_device_id(hw);
435362306a36Sopenharmony_ci
435462306a36Sopenharmony_ci	strcpy(hw->name, CSIO_HW_NAME);
435562306a36Sopenharmony_ci
435662306a36Sopenharmony_ci	/* Initialize the HW chip ops T5 specific ops */
435762306a36Sopenharmony_ci	hw->chip_ops = &t5_ops;
435862306a36Sopenharmony_ci
435962306a36Sopenharmony_ci	/* Set the model & its description */
436062306a36Sopenharmony_ci
436162306a36Sopenharmony_ci	ven_id = hw->params.pci.vendor_id;
436262306a36Sopenharmony_ci	dev_id = hw->params.pci.device_id;
436362306a36Sopenharmony_ci
436462306a36Sopenharmony_ci	csio_hw_set_description(hw, ven_id, dev_id);
436562306a36Sopenharmony_ci
436662306a36Sopenharmony_ci	/* Initialize default log level */
436762306a36Sopenharmony_ci	hw->params.log_level = (uint32_t) csio_dbg_level;
436862306a36Sopenharmony_ci
436962306a36Sopenharmony_ci	csio_set_fwevt_intr_idx(hw, -1);
437062306a36Sopenharmony_ci	csio_set_nondata_intr_idx(hw, -1);
437162306a36Sopenharmony_ci
437262306a36Sopenharmony_ci	/* Init all the modules: Mailbox, WorkRequest and Transport */
437362306a36Sopenharmony_ci	if (csio_mbm_init(csio_hw_to_mbm(hw), hw, csio_hw_mb_timer))
437462306a36Sopenharmony_ci		goto err;
437562306a36Sopenharmony_ci
437662306a36Sopenharmony_ci	rv = csio_wrm_init(csio_hw_to_wrm(hw), hw);
437762306a36Sopenharmony_ci	if (rv)
437862306a36Sopenharmony_ci		goto err_mbm_exit;
437962306a36Sopenharmony_ci
438062306a36Sopenharmony_ci	rv = csio_scsim_init(csio_hw_to_scsim(hw), hw);
438162306a36Sopenharmony_ci	if (rv)
438262306a36Sopenharmony_ci		goto err_wrm_exit;
438362306a36Sopenharmony_ci
438462306a36Sopenharmony_ci	rv = csio_mgmtm_init(csio_hw_to_mgmtm(hw), hw);
438562306a36Sopenharmony_ci	if (rv)
438662306a36Sopenharmony_ci		goto err_scsim_exit;
438762306a36Sopenharmony_ci	/* Pre-allocate evtq and initialize them */
438862306a36Sopenharmony_ci	INIT_LIST_HEAD(&hw->evt_active_q);
438962306a36Sopenharmony_ci	INIT_LIST_HEAD(&hw->evt_free_q);
439062306a36Sopenharmony_ci	for (i = 0; i < csio_evtq_sz; i++) {
439162306a36Sopenharmony_ci
439262306a36Sopenharmony_ci		evt_entry = kzalloc(sizeof(struct csio_evt_msg), GFP_KERNEL);
439362306a36Sopenharmony_ci		if (!evt_entry) {
439462306a36Sopenharmony_ci			rv = -ENOMEM;
439562306a36Sopenharmony_ci			csio_err(hw, "Failed to initialize eventq");
439662306a36Sopenharmony_ci			goto err_evtq_cleanup;
439762306a36Sopenharmony_ci		}
439862306a36Sopenharmony_ci
439962306a36Sopenharmony_ci		list_add_tail(&evt_entry->list, &hw->evt_free_q);
440062306a36Sopenharmony_ci		CSIO_INC_STATS(hw, n_evt_freeq);
440162306a36Sopenharmony_ci	}
440262306a36Sopenharmony_ci
440362306a36Sopenharmony_ci	hw->dev_num = dev_num;
440462306a36Sopenharmony_ci	dev_num++;
440562306a36Sopenharmony_ci
440662306a36Sopenharmony_ci	return 0;
440762306a36Sopenharmony_ci
440862306a36Sopenharmony_cierr_evtq_cleanup:
440962306a36Sopenharmony_ci	csio_evtq_cleanup(hw);
441062306a36Sopenharmony_ci	csio_mgmtm_exit(csio_hw_to_mgmtm(hw));
441162306a36Sopenharmony_cierr_scsim_exit:
441262306a36Sopenharmony_ci	csio_scsim_exit(csio_hw_to_scsim(hw));
441362306a36Sopenharmony_cierr_wrm_exit:
441462306a36Sopenharmony_ci	csio_wrm_exit(csio_hw_to_wrm(hw), hw);
441562306a36Sopenharmony_cierr_mbm_exit:
441662306a36Sopenharmony_ci	csio_mbm_exit(csio_hw_to_mbm(hw));
441762306a36Sopenharmony_cierr:
441862306a36Sopenharmony_ci	return rv;
441962306a36Sopenharmony_ci}
442062306a36Sopenharmony_ci
442162306a36Sopenharmony_ci/**
442262306a36Sopenharmony_ci * csio_hw_exit - Un-initialize HW module.
442362306a36Sopenharmony_ci * @hw:		Pointer to HW module.
442462306a36Sopenharmony_ci *
442562306a36Sopenharmony_ci */
442662306a36Sopenharmony_civoid
442762306a36Sopenharmony_cicsio_hw_exit(struct csio_hw *hw)
442862306a36Sopenharmony_ci{
442962306a36Sopenharmony_ci	csio_evtq_cleanup(hw);
443062306a36Sopenharmony_ci	csio_mgmtm_exit(csio_hw_to_mgmtm(hw));
443162306a36Sopenharmony_ci	csio_scsim_exit(csio_hw_to_scsim(hw));
443262306a36Sopenharmony_ci	csio_wrm_exit(csio_hw_to_wrm(hw), hw);
443362306a36Sopenharmony_ci	csio_mbm_exit(csio_hw_to_mbm(hw));
443462306a36Sopenharmony_ci}
4435