162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci#include <linux/delay.h>
362306a36Sopenharmony_ci#include <linux/firmware.h>
462306a36Sopenharmony_ci#include <linux/list.h>
562306a36Sopenharmony_ci#include <linux/module.h>
662306a36Sopenharmony_ci#include <linux/mutex.h>
762306a36Sopenharmony_ci#include <linux/pci.h>
862306a36Sopenharmony_ci#include <linux/pci_ids.h>
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include "nitrox_dev.h"
1162306a36Sopenharmony_ci#include "nitrox_common.h"
1262306a36Sopenharmony_ci#include "nitrox_csr.h"
1362306a36Sopenharmony_ci#include "nitrox_hal.h"
1462306a36Sopenharmony_ci#include "nitrox_isr.h"
1562306a36Sopenharmony_ci#include "nitrox_debugfs.h"
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#define CNN55XX_DEV_ID	0x12
1862306a36Sopenharmony_ci#define UCODE_HLEN 48
1962306a36Sopenharmony_ci#define DEFAULT_SE_GROUP 0
2062306a36Sopenharmony_ci#define DEFAULT_AE_GROUP 0
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#define DRIVER_VERSION "1.2"
2362306a36Sopenharmony_ci#define CNN55XX_UCD_BLOCK_SIZE 32768
2462306a36Sopenharmony_ci#define CNN55XX_MAX_UCODE_SIZE (CNN55XX_UCD_BLOCK_SIZE * 2)
2562306a36Sopenharmony_ci#define FW_DIR "cavium/"
2662306a36Sopenharmony_ci/* SE microcode */
2762306a36Sopenharmony_ci#define SE_FW	FW_DIR "cnn55xx_se.fw"
2862306a36Sopenharmony_ci/* AE microcode */
2962306a36Sopenharmony_ci#define AE_FW	FW_DIR "cnn55xx_ae.fw"
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cistatic const char nitrox_driver_name[] = "CNN55XX";
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cistatic LIST_HEAD(ndevlist);
3462306a36Sopenharmony_cistatic DEFINE_MUTEX(devlist_lock);
3562306a36Sopenharmony_cistatic unsigned int num_devices;
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci/*
3862306a36Sopenharmony_ci * nitrox_pci_tbl - PCI Device ID Table
3962306a36Sopenharmony_ci */
4062306a36Sopenharmony_cistatic const struct pci_device_id nitrox_pci_tbl[] = {
4162306a36Sopenharmony_ci	{PCI_VDEVICE(CAVIUM, CNN55XX_DEV_ID), 0},
4262306a36Sopenharmony_ci	/* required last entry */
4362306a36Sopenharmony_ci	{0, }
4462306a36Sopenharmony_ci};
4562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, nitrox_pci_tbl);
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_cistatic unsigned int qlen = DEFAULT_CMD_QLEN;
4862306a36Sopenharmony_cimodule_param(qlen, uint, 0644);
4962306a36Sopenharmony_ciMODULE_PARM_DESC(qlen, "Command queue length - default 2048");
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci/**
5262306a36Sopenharmony_ci * struct ucode - Firmware Header
5362306a36Sopenharmony_ci * @id: microcode ID
5462306a36Sopenharmony_ci * @version: firmware version
5562306a36Sopenharmony_ci * @code_size: code section size
5662306a36Sopenharmony_ci * @raz: alignment
5762306a36Sopenharmony_ci * @code: code section
5862306a36Sopenharmony_ci */
5962306a36Sopenharmony_cistruct ucode {
6062306a36Sopenharmony_ci	u8 id;
6162306a36Sopenharmony_ci	char version[VERSION_LEN - 1];
6262306a36Sopenharmony_ci	__be32 code_size;
6362306a36Sopenharmony_ci	u8 raz[12];
6462306a36Sopenharmony_ci	u64 code[];
6562306a36Sopenharmony_ci};
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci/*
6862306a36Sopenharmony_ci * write_to_ucd_unit - Write Firmware to NITROX UCD unit
6962306a36Sopenharmony_ci */
7062306a36Sopenharmony_cistatic void write_to_ucd_unit(struct nitrox_device *ndev, u32 ucode_size,
7162306a36Sopenharmony_ci			      u64 *ucode_data, int block_num)
7262306a36Sopenharmony_ci{
7362306a36Sopenharmony_ci	u32 code_size;
7462306a36Sopenharmony_ci	u64 offset, data;
7562306a36Sopenharmony_ci	int i = 0;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	/*
7862306a36Sopenharmony_ci	 * UCD structure
7962306a36Sopenharmony_ci	 *
8062306a36Sopenharmony_ci	 *  -------------
8162306a36Sopenharmony_ci	 *  |    BLK 7  |
8262306a36Sopenharmony_ci	 *  -------------
8362306a36Sopenharmony_ci	 *  |    BLK 6  |
8462306a36Sopenharmony_ci	 *  -------------
8562306a36Sopenharmony_ci	 *  |    ...    |
8662306a36Sopenharmony_ci	 *  -------------
8762306a36Sopenharmony_ci	 *  |    BLK 0  |
8862306a36Sopenharmony_ci	 *  -------------
8962306a36Sopenharmony_ci	 *  Total of 8 blocks, each size 32KB
9062306a36Sopenharmony_ci	 */
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	/* set the block number */
9362306a36Sopenharmony_ci	offset = UCD_UCODE_LOAD_BLOCK_NUM;
9462306a36Sopenharmony_ci	nitrox_write_csr(ndev, offset, block_num);
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	code_size = roundup(ucode_size, 16);
9762306a36Sopenharmony_ci	while (code_size) {
9862306a36Sopenharmony_ci		data = ucode_data[i];
9962306a36Sopenharmony_ci		/* write 8 bytes at a time */
10062306a36Sopenharmony_ci		offset = UCD_UCODE_LOAD_IDX_DATAX(i);
10162306a36Sopenharmony_ci		nitrox_write_csr(ndev, offset, data);
10262306a36Sopenharmony_ci		code_size -= 8;
10362306a36Sopenharmony_ci		i++;
10462306a36Sopenharmony_ci	}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	usleep_range(300, 400);
10762306a36Sopenharmony_ci}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_cistatic int nitrox_load_fw(struct nitrox_device *ndev)
11062306a36Sopenharmony_ci{
11162306a36Sopenharmony_ci	const struct firmware *fw;
11262306a36Sopenharmony_ci	const char *fw_name;
11362306a36Sopenharmony_ci	struct ucode *ucode;
11462306a36Sopenharmony_ci	u64 *ucode_data;
11562306a36Sopenharmony_ci	u64 offset;
11662306a36Sopenharmony_ci	union ucd_core_eid_ucode_block_num core_2_eid_val;
11762306a36Sopenharmony_ci	union aqm_grp_execmsk_lo aqm_grp_execmask_lo;
11862306a36Sopenharmony_ci	union aqm_grp_execmsk_hi aqm_grp_execmask_hi;
11962306a36Sopenharmony_ci	u32 ucode_size;
12062306a36Sopenharmony_ci	int ret, i = 0;
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	fw_name = SE_FW;
12362306a36Sopenharmony_ci	dev_info(DEV(ndev), "Loading firmware \"%s\"\n", fw_name);
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	ret = request_firmware(&fw, fw_name, DEV(ndev));
12662306a36Sopenharmony_ci	if (ret < 0) {
12762306a36Sopenharmony_ci		dev_err(DEV(ndev), "failed to get firmware %s\n", fw_name);
12862306a36Sopenharmony_ci		return ret;
12962306a36Sopenharmony_ci	}
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	ucode = (struct ucode *)fw->data;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	ucode_size = be32_to_cpu(ucode->code_size) * 2;
13462306a36Sopenharmony_ci	if (!ucode_size || ucode_size > CNN55XX_MAX_UCODE_SIZE) {
13562306a36Sopenharmony_ci		dev_err(DEV(ndev), "Invalid ucode size: %u for firmware %s\n",
13662306a36Sopenharmony_ci			ucode_size, fw_name);
13762306a36Sopenharmony_ci		release_firmware(fw);
13862306a36Sopenharmony_ci		return -EINVAL;
13962306a36Sopenharmony_ci	}
14062306a36Sopenharmony_ci	ucode_data = ucode->code;
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	/* copy the firmware version */
14362306a36Sopenharmony_ci	memcpy(&ndev->hw.fw_name[0][0], ucode->version, (VERSION_LEN - 2));
14462306a36Sopenharmony_ci	ndev->hw.fw_name[0][VERSION_LEN - 1] = '\0';
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	/* Load SE Firmware on UCD Block 0 */
14762306a36Sopenharmony_ci	write_to_ucd_unit(ndev, ucode_size, ucode_data, 0);
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	release_firmware(fw);
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	/* put all SE cores in DEFAULT_SE_GROUP */
15262306a36Sopenharmony_ci	offset = POM_GRP_EXECMASKX(DEFAULT_SE_GROUP);
15362306a36Sopenharmony_ci	nitrox_write_csr(ndev, offset, (~0ULL));
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	/* write block number and firmware length
15662306a36Sopenharmony_ci	 * bit:<2:0> block number
15762306a36Sopenharmony_ci	 * bit:3 is set SE uses 32KB microcode
15862306a36Sopenharmony_ci	 * bit:3 is clear SE uses 64KB microcode
15962306a36Sopenharmony_ci	 */
16062306a36Sopenharmony_ci	core_2_eid_val.value = 0ULL;
16162306a36Sopenharmony_ci	core_2_eid_val.ucode_blk = 0;
16262306a36Sopenharmony_ci	if (ucode_size <= CNN55XX_UCD_BLOCK_SIZE)
16362306a36Sopenharmony_ci		core_2_eid_val.ucode_len = 1;
16462306a36Sopenharmony_ci	else
16562306a36Sopenharmony_ci		core_2_eid_val.ucode_len = 0;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	for (i = 0; i < ndev->hw.se_cores; i++) {
16862306a36Sopenharmony_ci		offset = UCD_SE_EID_UCODE_BLOCK_NUMX(i);
16962306a36Sopenharmony_ci		nitrox_write_csr(ndev, offset, core_2_eid_val.value);
17062306a36Sopenharmony_ci	}
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	fw_name = AE_FW;
17462306a36Sopenharmony_ci	dev_info(DEV(ndev), "Loading firmware \"%s\"\n", fw_name);
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	ret = request_firmware(&fw, fw_name, DEV(ndev));
17762306a36Sopenharmony_ci	if (ret < 0) {
17862306a36Sopenharmony_ci		dev_err(DEV(ndev), "failed to get firmware %s\n", fw_name);
17962306a36Sopenharmony_ci		return ret;
18062306a36Sopenharmony_ci	}
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	ucode = (struct ucode *)fw->data;
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	ucode_size = be32_to_cpu(ucode->code_size) * 2;
18562306a36Sopenharmony_ci	if (!ucode_size || ucode_size > CNN55XX_MAX_UCODE_SIZE) {
18662306a36Sopenharmony_ci		dev_err(DEV(ndev), "Invalid ucode size: %u for firmware %s\n",
18762306a36Sopenharmony_ci			ucode_size, fw_name);
18862306a36Sopenharmony_ci		release_firmware(fw);
18962306a36Sopenharmony_ci		return -EINVAL;
19062306a36Sopenharmony_ci	}
19162306a36Sopenharmony_ci	ucode_data = ucode->code;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	/* copy the firmware version */
19462306a36Sopenharmony_ci	memcpy(&ndev->hw.fw_name[1][0], ucode->version, (VERSION_LEN - 2));
19562306a36Sopenharmony_ci	ndev->hw.fw_name[1][VERSION_LEN - 1] = '\0';
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	/* Load AE Firmware on UCD Block 2 */
19862306a36Sopenharmony_ci	write_to_ucd_unit(ndev, ucode_size, ucode_data, 2);
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	release_firmware(fw);
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	/* put all AE cores in DEFAULT_AE_GROUP */
20362306a36Sopenharmony_ci	offset = AQM_GRP_EXECMSK_LOX(DEFAULT_AE_GROUP);
20462306a36Sopenharmony_ci	aqm_grp_execmask_lo.exec_0_to_39 = 0xFFFFFFFFFFULL;
20562306a36Sopenharmony_ci	nitrox_write_csr(ndev, offset, aqm_grp_execmask_lo.value);
20662306a36Sopenharmony_ci	offset = AQM_GRP_EXECMSK_HIX(DEFAULT_AE_GROUP);
20762306a36Sopenharmony_ci	aqm_grp_execmask_hi.exec_40_to_79 = 0xFFFFFFFFFFULL;
20862306a36Sopenharmony_ci	nitrox_write_csr(ndev, offset, aqm_grp_execmask_hi.value);
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	/* write block number and firmware length
21162306a36Sopenharmony_ci	 * bit:<2:0> block number
21262306a36Sopenharmony_ci	 * bit:3 is set AE uses 32KB microcode
21362306a36Sopenharmony_ci	 * bit:3 is clear AE uses 64KB microcode
21462306a36Sopenharmony_ci	 */
21562306a36Sopenharmony_ci	core_2_eid_val.value = 0ULL;
21662306a36Sopenharmony_ci	core_2_eid_val.ucode_blk = 2;
21762306a36Sopenharmony_ci	if (ucode_size <= CNN55XX_UCD_BLOCK_SIZE)
21862306a36Sopenharmony_ci		core_2_eid_val.ucode_len = 1;
21962306a36Sopenharmony_ci	else
22062306a36Sopenharmony_ci		core_2_eid_val.ucode_len = 0;
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	for (i = 0; i < ndev->hw.ae_cores; i++) {
22362306a36Sopenharmony_ci		offset = UCD_AE_EID_UCODE_BLOCK_NUMX(i);
22462306a36Sopenharmony_ci		nitrox_write_csr(ndev, offset, core_2_eid_val.value);
22562306a36Sopenharmony_ci	}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	return 0;
22862306a36Sopenharmony_ci}
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci/**
23162306a36Sopenharmony_ci * nitrox_add_to_devlist - add NITROX device to global device list
23262306a36Sopenharmony_ci * @ndev: NITROX device
23362306a36Sopenharmony_ci */
23462306a36Sopenharmony_cistatic int nitrox_add_to_devlist(struct nitrox_device *ndev)
23562306a36Sopenharmony_ci{
23662306a36Sopenharmony_ci	struct nitrox_device *dev;
23762306a36Sopenharmony_ci	int ret = 0;
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	INIT_LIST_HEAD(&ndev->list);
24062306a36Sopenharmony_ci	refcount_set(&ndev->refcnt, 1);
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	mutex_lock(&devlist_lock);
24362306a36Sopenharmony_ci	list_for_each_entry(dev, &ndevlist, list) {
24462306a36Sopenharmony_ci		if (dev == ndev) {
24562306a36Sopenharmony_ci			ret = -EEXIST;
24662306a36Sopenharmony_ci			goto unlock;
24762306a36Sopenharmony_ci		}
24862306a36Sopenharmony_ci	}
24962306a36Sopenharmony_ci	ndev->idx = num_devices++;
25062306a36Sopenharmony_ci	list_add_tail(&ndev->list, &ndevlist);
25162306a36Sopenharmony_ciunlock:
25262306a36Sopenharmony_ci	mutex_unlock(&devlist_lock);
25362306a36Sopenharmony_ci	return ret;
25462306a36Sopenharmony_ci}
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci/**
25762306a36Sopenharmony_ci * nitrox_remove_from_devlist - remove NITROX device from
25862306a36Sopenharmony_ci *   global device list
25962306a36Sopenharmony_ci * @ndev: NITROX device
26062306a36Sopenharmony_ci */
26162306a36Sopenharmony_cistatic void nitrox_remove_from_devlist(struct nitrox_device *ndev)
26262306a36Sopenharmony_ci{
26362306a36Sopenharmony_ci	mutex_lock(&devlist_lock);
26462306a36Sopenharmony_ci	list_del(&ndev->list);
26562306a36Sopenharmony_ci	num_devices--;
26662306a36Sopenharmony_ci	mutex_unlock(&devlist_lock);
26762306a36Sopenharmony_ci}
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_cistruct nitrox_device *nitrox_get_first_device(void)
27062306a36Sopenharmony_ci{
27162306a36Sopenharmony_ci	struct nitrox_device *ndev = NULL, *iter;
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	mutex_lock(&devlist_lock);
27462306a36Sopenharmony_ci	list_for_each_entry(iter, &ndevlist, list) {
27562306a36Sopenharmony_ci		if (nitrox_ready(iter)) {
27662306a36Sopenharmony_ci			ndev = iter;
27762306a36Sopenharmony_ci			break;
27862306a36Sopenharmony_ci		}
27962306a36Sopenharmony_ci	}
28062306a36Sopenharmony_ci	mutex_unlock(&devlist_lock);
28162306a36Sopenharmony_ci	if (!ndev)
28262306a36Sopenharmony_ci		return NULL;
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	refcount_inc(&ndev->refcnt);
28562306a36Sopenharmony_ci	/* barrier to sync with other cpus */
28662306a36Sopenharmony_ci	smp_mb__after_atomic();
28762306a36Sopenharmony_ci	return ndev;
28862306a36Sopenharmony_ci}
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_civoid nitrox_put_device(struct nitrox_device *ndev)
29162306a36Sopenharmony_ci{
29262306a36Sopenharmony_ci	if (!ndev)
29362306a36Sopenharmony_ci		return;
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	refcount_dec(&ndev->refcnt);
29662306a36Sopenharmony_ci	/* barrier to sync with other cpus */
29762306a36Sopenharmony_ci	smp_mb__after_atomic();
29862306a36Sopenharmony_ci}
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_cistatic int nitrox_device_flr(struct pci_dev *pdev)
30162306a36Sopenharmony_ci{
30262306a36Sopenharmony_ci	int pos = 0;
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	pos = pci_save_state(pdev);
30562306a36Sopenharmony_ci	if (pos) {
30662306a36Sopenharmony_ci		dev_err(&pdev->dev, "Failed to save pci state\n");
30762306a36Sopenharmony_ci		return -ENOMEM;
30862306a36Sopenharmony_ci	}
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	pcie_reset_flr(pdev, PCI_RESET_DO_RESET);
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	pci_restore_state(pdev);
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	return 0;
31562306a36Sopenharmony_ci}
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_cistatic int nitrox_pf_sw_init(struct nitrox_device *ndev)
31862306a36Sopenharmony_ci{
31962306a36Sopenharmony_ci	int err;
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	err = nitrox_common_sw_init(ndev);
32262306a36Sopenharmony_ci	if (err)
32362306a36Sopenharmony_ci		return err;
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	err = nitrox_register_interrupts(ndev);
32662306a36Sopenharmony_ci	if (err)
32762306a36Sopenharmony_ci		nitrox_common_sw_cleanup(ndev);
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	return err;
33062306a36Sopenharmony_ci}
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_cistatic void nitrox_pf_sw_cleanup(struct nitrox_device *ndev)
33362306a36Sopenharmony_ci{
33462306a36Sopenharmony_ci	nitrox_unregister_interrupts(ndev);
33562306a36Sopenharmony_ci	nitrox_common_sw_cleanup(ndev);
33662306a36Sopenharmony_ci}
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci/**
33962306a36Sopenharmony_ci * nitrox_bist_check - Check NITROX BIST registers status
34062306a36Sopenharmony_ci * @ndev: NITROX device
34162306a36Sopenharmony_ci */
34262306a36Sopenharmony_cistatic int nitrox_bist_check(struct nitrox_device *ndev)
34362306a36Sopenharmony_ci{
34462306a36Sopenharmony_ci	u64 value = 0;
34562306a36Sopenharmony_ci	int i;
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	for (i = 0; i < NR_CLUSTERS; i++) {
34862306a36Sopenharmony_ci		value += nitrox_read_csr(ndev, EMU_BIST_STATUSX(i));
34962306a36Sopenharmony_ci		value += nitrox_read_csr(ndev, EFL_CORE_BIST_REGX(i));
35062306a36Sopenharmony_ci	}
35162306a36Sopenharmony_ci	value += nitrox_read_csr(ndev, UCD_BIST_STATUS);
35262306a36Sopenharmony_ci	value += nitrox_read_csr(ndev, NPS_CORE_BIST_REG);
35362306a36Sopenharmony_ci	value += nitrox_read_csr(ndev, NPS_CORE_NPC_BIST_REG);
35462306a36Sopenharmony_ci	value += nitrox_read_csr(ndev, NPS_PKT_SLC_BIST_REG);
35562306a36Sopenharmony_ci	value += nitrox_read_csr(ndev, NPS_PKT_IN_BIST_REG);
35662306a36Sopenharmony_ci	value += nitrox_read_csr(ndev, POM_BIST_REG);
35762306a36Sopenharmony_ci	value += nitrox_read_csr(ndev, BMI_BIST_REG);
35862306a36Sopenharmony_ci	value += nitrox_read_csr(ndev, EFL_TOP_BIST_STAT);
35962306a36Sopenharmony_ci	value += nitrox_read_csr(ndev, BMO_BIST_REG);
36062306a36Sopenharmony_ci	value += nitrox_read_csr(ndev, LBC_BIST_STATUS);
36162306a36Sopenharmony_ci	value += nitrox_read_csr(ndev, PEM_BIST_STATUSX(0));
36262306a36Sopenharmony_ci	if (value)
36362306a36Sopenharmony_ci		return -EIO;
36462306a36Sopenharmony_ci	return 0;
36562306a36Sopenharmony_ci}
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_cistatic int nitrox_pf_hw_init(struct nitrox_device *ndev)
36862306a36Sopenharmony_ci{
36962306a36Sopenharmony_ci	int err;
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	err = nitrox_bist_check(ndev);
37262306a36Sopenharmony_ci	if (err) {
37362306a36Sopenharmony_ci		dev_err(&ndev->pdev->dev, "BIST check failed\n");
37462306a36Sopenharmony_ci		return err;
37562306a36Sopenharmony_ci	}
37662306a36Sopenharmony_ci	/* get cores information */
37762306a36Sopenharmony_ci	nitrox_get_hwinfo(ndev);
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	nitrox_config_nps_core_unit(ndev);
38062306a36Sopenharmony_ci	nitrox_config_aqm_unit(ndev);
38162306a36Sopenharmony_ci	nitrox_config_nps_pkt_unit(ndev);
38262306a36Sopenharmony_ci	nitrox_config_pom_unit(ndev);
38362306a36Sopenharmony_ci	nitrox_config_efl_unit(ndev);
38462306a36Sopenharmony_ci	/* configure IO units */
38562306a36Sopenharmony_ci	nitrox_config_bmi_unit(ndev);
38662306a36Sopenharmony_ci	nitrox_config_bmo_unit(ndev);
38762306a36Sopenharmony_ci	/* configure Local Buffer Cache */
38862306a36Sopenharmony_ci	nitrox_config_lbc_unit(ndev);
38962306a36Sopenharmony_ci	nitrox_config_rand_unit(ndev);
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	/* load firmware on cores */
39262306a36Sopenharmony_ci	err = nitrox_load_fw(ndev);
39362306a36Sopenharmony_ci	if (err)
39462306a36Sopenharmony_ci		return err;
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	nitrox_config_emu_unit(ndev);
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	return 0;
39962306a36Sopenharmony_ci}
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci/**
40262306a36Sopenharmony_ci * nitrox_probe - NITROX Initialization function.
40362306a36Sopenharmony_ci * @pdev: PCI device information struct
40462306a36Sopenharmony_ci * @id: entry in nitrox_pci_tbl
40562306a36Sopenharmony_ci *
40662306a36Sopenharmony_ci * Return: 0, if the driver is bound to the device, or
40762306a36Sopenharmony_ci *         a negative error if there is failure.
40862306a36Sopenharmony_ci */
40962306a36Sopenharmony_cistatic int nitrox_probe(struct pci_dev *pdev,
41062306a36Sopenharmony_ci			const struct pci_device_id *id)
41162306a36Sopenharmony_ci{
41262306a36Sopenharmony_ci	struct nitrox_device *ndev;
41362306a36Sopenharmony_ci	int err;
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	dev_info_once(&pdev->dev, "%s driver version %s\n",
41662306a36Sopenharmony_ci		      nitrox_driver_name, DRIVER_VERSION);
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	err = pci_enable_device_mem(pdev);
41962306a36Sopenharmony_ci	if (err)
42062306a36Sopenharmony_ci		return err;
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	/* do FLR */
42362306a36Sopenharmony_ci	err = nitrox_device_flr(pdev);
42462306a36Sopenharmony_ci	if (err) {
42562306a36Sopenharmony_ci		dev_err(&pdev->dev, "FLR failed\n");
42662306a36Sopenharmony_ci		goto flr_fail;
42762306a36Sopenharmony_ci	}
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	if (!dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64))) {
43062306a36Sopenharmony_ci		dev_dbg(&pdev->dev, "DMA to 64-BIT address\n");
43162306a36Sopenharmony_ci	} else {
43262306a36Sopenharmony_ci		err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
43362306a36Sopenharmony_ci		if (err) {
43462306a36Sopenharmony_ci			dev_err(&pdev->dev, "DMA configuration failed\n");
43562306a36Sopenharmony_ci			goto flr_fail;
43662306a36Sopenharmony_ci		}
43762306a36Sopenharmony_ci	}
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	err = pci_request_mem_regions(pdev, nitrox_driver_name);
44062306a36Sopenharmony_ci	if (err)
44162306a36Sopenharmony_ci		goto flr_fail;
44262306a36Sopenharmony_ci	pci_set_master(pdev);
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci	ndev = kzalloc(sizeof(*ndev), GFP_KERNEL);
44562306a36Sopenharmony_ci	if (!ndev) {
44662306a36Sopenharmony_ci		err = -ENOMEM;
44762306a36Sopenharmony_ci		goto ndev_fail;
44862306a36Sopenharmony_ci	}
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	pci_set_drvdata(pdev, ndev);
45162306a36Sopenharmony_ci	ndev->pdev = pdev;
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	/* add to device list */
45462306a36Sopenharmony_ci	nitrox_add_to_devlist(ndev);
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	ndev->hw.vendor_id = pdev->vendor;
45762306a36Sopenharmony_ci	ndev->hw.device_id = pdev->device;
45862306a36Sopenharmony_ci	ndev->hw.revision_id = pdev->revision;
45962306a36Sopenharmony_ci	/* command timeout in jiffies */
46062306a36Sopenharmony_ci	ndev->timeout = msecs_to_jiffies(CMD_TIMEOUT);
46162306a36Sopenharmony_ci	ndev->node = dev_to_node(&pdev->dev);
46262306a36Sopenharmony_ci	if (ndev->node == NUMA_NO_NODE)
46362306a36Sopenharmony_ci		ndev->node = 0;
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	ndev->bar_addr = ioremap(pci_resource_start(pdev, 0),
46662306a36Sopenharmony_ci				 pci_resource_len(pdev, 0));
46762306a36Sopenharmony_ci	if (!ndev->bar_addr) {
46862306a36Sopenharmony_ci		err = -EIO;
46962306a36Sopenharmony_ci		goto ioremap_err;
47062306a36Sopenharmony_ci	}
47162306a36Sopenharmony_ci	/* allocate command queus based on cpus, max queues are 64 */
47262306a36Sopenharmony_ci	ndev->nr_queues = min_t(u32, MAX_PF_QUEUES, num_online_cpus());
47362306a36Sopenharmony_ci	ndev->qlen = qlen;
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	err = nitrox_pf_sw_init(ndev);
47662306a36Sopenharmony_ci	if (err)
47762306a36Sopenharmony_ci		goto pf_sw_fail;
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	err = nitrox_pf_hw_init(ndev);
48062306a36Sopenharmony_ci	if (err)
48162306a36Sopenharmony_ci		goto pf_hw_fail;
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci	nitrox_debugfs_init(ndev);
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	/* clear the statistics */
48662306a36Sopenharmony_ci	atomic64_set(&ndev->stats.posted, 0);
48762306a36Sopenharmony_ci	atomic64_set(&ndev->stats.completed, 0);
48862306a36Sopenharmony_ci	atomic64_set(&ndev->stats.dropped, 0);
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	atomic_set(&ndev->state, __NDEV_READY);
49162306a36Sopenharmony_ci	/* barrier to sync with other cpus */
49262306a36Sopenharmony_ci	smp_mb__after_atomic();
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	err = nitrox_crypto_register();
49562306a36Sopenharmony_ci	if (err)
49662306a36Sopenharmony_ci		goto crypto_fail;
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci	return 0;
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_cicrypto_fail:
50162306a36Sopenharmony_ci	nitrox_debugfs_exit(ndev);
50262306a36Sopenharmony_ci	atomic_set(&ndev->state, __NDEV_NOT_READY);
50362306a36Sopenharmony_ci	/* barrier to sync with other cpus */
50462306a36Sopenharmony_ci	smp_mb__after_atomic();
50562306a36Sopenharmony_cipf_hw_fail:
50662306a36Sopenharmony_ci	nitrox_pf_sw_cleanup(ndev);
50762306a36Sopenharmony_cipf_sw_fail:
50862306a36Sopenharmony_ci	iounmap(ndev->bar_addr);
50962306a36Sopenharmony_ciioremap_err:
51062306a36Sopenharmony_ci	nitrox_remove_from_devlist(ndev);
51162306a36Sopenharmony_ci	kfree(ndev);
51262306a36Sopenharmony_ci	pci_set_drvdata(pdev, NULL);
51362306a36Sopenharmony_cindev_fail:
51462306a36Sopenharmony_ci	pci_release_mem_regions(pdev);
51562306a36Sopenharmony_ciflr_fail:
51662306a36Sopenharmony_ci	pci_disable_device(pdev);
51762306a36Sopenharmony_ci	return err;
51862306a36Sopenharmony_ci}
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci/**
52162306a36Sopenharmony_ci * nitrox_remove - Unbind the driver from the device.
52262306a36Sopenharmony_ci * @pdev: PCI device information struct
52362306a36Sopenharmony_ci */
52462306a36Sopenharmony_cistatic void nitrox_remove(struct pci_dev *pdev)
52562306a36Sopenharmony_ci{
52662306a36Sopenharmony_ci	struct nitrox_device *ndev = pci_get_drvdata(pdev);
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci	if (!ndev)
52962306a36Sopenharmony_ci		return;
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	if (!refcount_dec_and_test(&ndev->refcnt)) {
53262306a36Sopenharmony_ci		dev_err(DEV(ndev), "Device refcnt not zero (%d)\n",
53362306a36Sopenharmony_ci			refcount_read(&ndev->refcnt));
53462306a36Sopenharmony_ci		return;
53562306a36Sopenharmony_ci	}
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci	dev_info(DEV(ndev), "Removing Device %x:%x\n",
53862306a36Sopenharmony_ci		 ndev->hw.vendor_id, ndev->hw.device_id);
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	atomic_set(&ndev->state, __NDEV_NOT_READY);
54162306a36Sopenharmony_ci	/* barrier to sync with other cpus */
54262306a36Sopenharmony_ci	smp_mb__after_atomic();
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	nitrox_remove_from_devlist(ndev);
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci	/* disable SR-IOV */
54762306a36Sopenharmony_ci	nitrox_sriov_configure(pdev, 0);
54862306a36Sopenharmony_ci	nitrox_crypto_unregister();
54962306a36Sopenharmony_ci	nitrox_debugfs_exit(ndev);
55062306a36Sopenharmony_ci	nitrox_pf_sw_cleanup(ndev);
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	iounmap(ndev->bar_addr);
55362306a36Sopenharmony_ci	kfree(ndev);
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	pci_set_drvdata(pdev, NULL);
55662306a36Sopenharmony_ci	pci_release_mem_regions(pdev);
55762306a36Sopenharmony_ci	pci_disable_device(pdev);
55862306a36Sopenharmony_ci}
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_cistatic void nitrox_shutdown(struct pci_dev *pdev)
56162306a36Sopenharmony_ci{
56262306a36Sopenharmony_ci	pci_set_drvdata(pdev, NULL);
56362306a36Sopenharmony_ci	pci_release_mem_regions(pdev);
56462306a36Sopenharmony_ci	pci_disable_device(pdev);
56562306a36Sopenharmony_ci}
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_cistatic struct pci_driver nitrox_driver = {
56862306a36Sopenharmony_ci	.name = nitrox_driver_name,
56962306a36Sopenharmony_ci	.id_table = nitrox_pci_tbl,
57062306a36Sopenharmony_ci	.probe = nitrox_probe,
57162306a36Sopenharmony_ci	.remove	= nitrox_remove,
57262306a36Sopenharmony_ci	.shutdown = nitrox_shutdown,
57362306a36Sopenharmony_ci	.sriov_configure = nitrox_sriov_configure,
57462306a36Sopenharmony_ci};
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_cimodule_pci_driver(nitrox_driver);
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ciMODULE_AUTHOR("Srikanth Jampala <Jampala.Srikanth@cavium.com>");
57962306a36Sopenharmony_ciMODULE_DESCRIPTION("Cavium CNN55XX PF Driver" DRIVER_VERSION " ");
58062306a36Sopenharmony_ciMODULE_LICENSE("GPL");
58162306a36Sopenharmony_ciMODULE_VERSION(DRIVER_VERSION);
58262306a36Sopenharmony_ciMODULE_FIRMWARE(SE_FW);
583