198c4c201SDavid VomLehn /*
298c4c201SDavid VomLehn  * aQuantia Corporation Network Driver
398c4c201SDavid VomLehn  * Copyright (C) 2014-2017 aQuantia Corporation. All rights reserved
498c4c201SDavid VomLehn  *
598c4c201SDavid VomLehn  * This program is free software; you can redistribute it and/or modify it
698c4c201SDavid VomLehn  * under the terms and conditions of the GNU General Public License,
798c4c201SDavid VomLehn  * version 2, as published by the Free Software Foundation.
898c4c201SDavid VomLehn  */
998c4c201SDavid VomLehn 
1098c4c201SDavid VomLehn /* File hw_atl_utils.c: Definition of common functions for Atlantic hardware
1198c4c201SDavid VomLehn  * abstraction layer.
1298c4c201SDavid VomLehn  */
1398c4c201SDavid VomLehn 
1498c4c201SDavid VomLehn #include "../aq_hw.h"
1598c4c201SDavid VomLehn #include "../aq_hw_utils.h"
1698c4c201SDavid VomLehn #include "../aq_pci_func.h"
1798c4c201SDavid VomLehn #include "../aq_ring.h"
1898c4c201SDavid VomLehn #include "../aq_vec.h"
1998c4c201SDavid VomLehn #include "hw_atl_utils.h"
2098c4c201SDavid VomLehn #include "hw_atl_llh.h"
2198c4c201SDavid VomLehn 
2298c4c201SDavid VomLehn #include <linux/random.h>
2398c4c201SDavid VomLehn 
2498c4c201SDavid VomLehn #define HW_ATL_UCP_0X370_REG    0x0370U
2598c4c201SDavid VomLehn 
2698c4c201SDavid VomLehn #define HW_ATL_FW_SM_RAM        0x2U
2798c4c201SDavid VomLehn #define HW_ATL_MPI_CONTROL_ADR  0x0368U
2898c4c201SDavid VomLehn #define HW_ATL_MPI_STATE_ADR    0x036CU
2998c4c201SDavid VomLehn 
3098c4c201SDavid VomLehn #define HW_ATL_MPI_STATE_MSK    0x00FFU
3198c4c201SDavid VomLehn #define HW_ATL_MPI_STATE_SHIFT  0U
3298c4c201SDavid VomLehn #define HW_ATL_MPI_SPEED_MSK    0xFFFFU
3398c4c201SDavid VomLehn #define HW_ATL_MPI_SPEED_SHIFT  16U
3498c4c201SDavid VomLehn 
3598c4c201SDavid VomLehn static int hw_atl_utils_fw_downld_dwords(struct aq_hw_s *self, u32 a,
3698c4c201SDavid VomLehn 					 u32 *p, u32 cnt)
3798c4c201SDavid VomLehn {
3898c4c201SDavid VomLehn 	int err = 0;
3998c4c201SDavid VomLehn 
4098c4c201SDavid VomLehn 	AQ_HW_WAIT_FOR(reg_glb_cpu_sem_get(self,
4198c4c201SDavid VomLehn 					   HW_ATL_FW_SM_RAM) == 1U,
4298c4c201SDavid VomLehn 					   1U, 10000U);
4398c4c201SDavid VomLehn 
4498c4c201SDavid VomLehn 	if (err < 0) {
4598c4c201SDavid VomLehn 		bool is_locked;
4698c4c201SDavid VomLehn 
4798c4c201SDavid VomLehn 		reg_glb_cpu_sem_set(self, 1U, HW_ATL_FW_SM_RAM);
4898c4c201SDavid VomLehn 		is_locked = reg_glb_cpu_sem_get(self, HW_ATL_FW_SM_RAM);
4998c4c201SDavid VomLehn 		if (!is_locked) {
5098c4c201SDavid VomLehn 			err = -ETIME;
5198c4c201SDavid VomLehn 			goto err_exit;
5298c4c201SDavid VomLehn 		}
5398c4c201SDavid VomLehn 	}
5498c4c201SDavid VomLehn 
5598c4c201SDavid VomLehn 	aq_hw_write_reg(self, 0x00000208U, a);
5698c4c201SDavid VomLehn 
5798c4c201SDavid VomLehn 	for (++cnt; --cnt;) {
5898c4c201SDavid VomLehn 		u32 i = 0U;
5998c4c201SDavid VomLehn 
6098c4c201SDavid VomLehn 		aq_hw_write_reg(self, 0x00000200U, 0x00008000U);
6198c4c201SDavid VomLehn 
6298c4c201SDavid VomLehn 		for (i = 1024U;
6398c4c201SDavid VomLehn 			(0x100U & aq_hw_read_reg(self, 0x00000200U)) && --i;) {
6498c4c201SDavid VomLehn 		}
6598c4c201SDavid VomLehn 
6698c4c201SDavid VomLehn 		*(p++) = aq_hw_read_reg(self, 0x0000020CU);
6798c4c201SDavid VomLehn 	}
6898c4c201SDavid VomLehn 
6998c4c201SDavid VomLehn 	reg_glb_cpu_sem_set(self, 1U, HW_ATL_FW_SM_RAM);
7098c4c201SDavid VomLehn 
7198c4c201SDavid VomLehn err_exit:
7298c4c201SDavid VomLehn 	return err;
7398c4c201SDavid VomLehn }
7498c4c201SDavid VomLehn 
7598c4c201SDavid VomLehn static int hw_atl_utils_fw_upload_dwords(struct aq_hw_s *self, u32 a, u32 *p,
7698c4c201SDavid VomLehn 					 u32 cnt)
7798c4c201SDavid VomLehn {
7898c4c201SDavid VomLehn 	int err = 0;
7998c4c201SDavid VomLehn 	bool is_locked;
8098c4c201SDavid VomLehn 
8198c4c201SDavid VomLehn 	is_locked = reg_glb_cpu_sem_get(self, HW_ATL_FW_SM_RAM);
8298c4c201SDavid VomLehn 	if (!is_locked) {
8398c4c201SDavid VomLehn 		err = -ETIME;
8498c4c201SDavid VomLehn 		goto err_exit;
8598c4c201SDavid VomLehn 	}
8698c4c201SDavid VomLehn 
8798c4c201SDavid VomLehn 	aq_hw_write_reg(self, 0x00000208U, a);
8898c4c201SDavid VomLehn 
8998c4c201SDavid VomLehn 	for (++cnt; --cnt;) {
9098c4c201SDavid VomLehn 		u32 i = 0U;
9198c4c201SDavid VomLehn 
9298c4c201SDavid VomLehn 		aq_hw_write_reg(self, 0x0000020CU, *(p++));
9398c4c201SDavid VomLehn 		aq_hw_write_reg(self, 0x00000200U, 0xC000U);
9498c4c201SDavid VomLehn 
9598c4c201SDavid VomLehn 		for (i = 1024U;
9698c4c201SDavid VomLehn 			(0x100U & aq_hw_read_reg(self, 0x00000200U)) && --i;) {
9798c4c201SDavid VomLehn 		}
9898c4c201SDavid VomLehn 	}
9998c4c201SDavid VomLehn 
10098c4c201SDavid VomLehn 	reg_glb_cpu_sem_set(self, 1U, HW_ATL_FW_SM_RAM);
10198c4c201SDavid VomLehn 
10298c4c201SDavid VomLehn err_exit:
10398c4c201SDavid VomLehn 	return err;
10498c4c201SDavid VomLehn }
10598c4c201SDavid VomLehn 
10698c4c201SDavid VomLehn static int hw_atl_utils_ver_match(u32 ver_expected, u32 ver_actual)
10798c4c201SDavid VomLehn {
10898c4c201SDavid VomLehn 	int err = 0;
10998c4c201SDavid VomLehn 	const u32 dw_major_mask = 0xff000000U;
11098c4c201SDavid VomLehn 	const u32 dw_minor_mask = 0x00ffffffU;
11198c4c201SDavid VomLehn 
11298c4c201SDavid VomLehn 	err = (dw_major_mask & (ver_expected ^ ver_actual)) ? -EOPNOTSUPP : 0;
11398c4c201SDavid VomLehn 	if (err < 0)
11498c4c201SDavid VomLehn 		goto err_exit;
11598c4c201SDavid VomLehn 	err = ((dw_minor_mask & ver_expected) > (dw_minor_mask & ver_actual)) ?
11698c4c201SDavid VomLehn 		-EOPNOTSUPP : 0;
11798c4c201SDavid VomLehn err_exit:
11898c4c201SDavid VomLehn 	return err;
11998c4c201SDavid VomLehn }
12098c4c201SDavid VomLehn 
12198c4c201SDavid VomLehn static int hw_atl_utils_init_ucp(struct aq_hw_s *self,
12298c4c201SDavid VomLehn 				 struct aq_hw_caps_s *aq_hw_caps)
12398c4c201SDavid VomLehn {
12498c4c201SDavid VomLehn 	int err = 0;
12598c4c201SDavid VomLehn 
12698c4c201SDavid VomLehn 	if (!aq_hw_read_reg(self, 0x370U)) {
12798c4c201SDavid VomLehn 		unsigned int rnd = 0U;
12898c4c201SDavid VomLehn 		unsigned int ucp_0x370 = 0U;
12998c4c201SDavid VomLehn 
13098c4c201SDavid VomLehn 		get_random_bytes(&rnd, sizeof(unsigned int));
13198c4c201SDavid VomLehn 
13298c4c201SDavid VomLehn 		ucp_0x370 = 0x02020202U | (0xFEFEFEFEU & rnd);
13398c4c201SDavid VomLehn 		aq_hw_write_reg(self, HW_ATL_UCP_0X370_REG, ucp_0x370);
13498c4c201SDavid VomLehn 	}
13598c4c201SDavid VomLehn 
13698c4c201SDavid VomLehn 	reg_glb_cpu_scratch_scp_set(self, 0x00000000U, 25U);
13798c4c201SDavid VomLehn 
13898c4c201SDavid VomLehn 	/* check 10 times by 1ms */
13998c4c201SDavid VomLehn 	AQ_HW_WAIT_FOR(0U != (PHAL_ATLANTIC_A0->mbox_addr =
14098c4c201SDavid VomLehn 			aq_hw_read_reg(self, 0x360U)), 1000U, 10U);
14198c4c201SDavid VomLehn 
14298c4c201SDavid VomLehn 	err = hw_atl_utils_ver_match(aq_hw_caps->fw_ver_expected,
14398c4c201SDavid VomLehn 				     aq_hw_read_reg(self, 0x18U));
1446d3f58e0SPavel Belous 
1456d3f58e0SPavel Belous 	if (err < 0)
1466d3f58e0SPavel Belous 		pr_err("%s: Bad FW version detected: expected=%x, actual=%x\n",
1476d3f58e0SPavel Belous 		       AQ_CFG_DRV_NAME,
1486d3f58e0SPavel Belous 		       aq_hw_caps->fw_ver_expected,
1496d3f58e0SPavel Belous 		       aq_hw_read_reg(self, 0x18U));
15098c4c201SDavid VomLehn 	return err;
15198c4c201SDavid VomLehn }
15298c4c201SDavid VomLehn 
15398c4c201SDavid VomLehn #define HW_ATL_RPC_CONTROL_ADR 0x0338U
15498c4c201SDavid VomLehn #define HW_ATL_RPC_STATE_ADR   0x033CU
15598c4c201SDavid VomLehn 
15698c4c201SDavid VomLehn struct aq_hw_atl_utils_fw_rpc_tid_s {
15798c4c201SDavid VomLehn 	union {
15898c4c201SDavid VomLehn 		u32 val;
15998c4c201SDavid VomLehn 		struct {
16098c4c201SDavid VomLehn 			u16 tid;
16198c4c201SDavid VomLehn 			u16 len;
16298c4c201SDavid VomLehn 		};
16398c4c201SDavid VomLehn 	};
16498c4c201SDavid VomLehn };
16598c4c201SDavid VomLehn 
16698c4c201SDavid VomLehn #define hw_atl_utils_fw_rpc_init(_H_) hw_atl_utils_fw_rpc_wait(_H_, NULL)
16798c4c201SDavid VomLehn 
16898c4c201SDavid VomLehn static int hw_atl_utils_fw_rpc_call(struct aq_hw_s *self, unsigned int rpc_size)
16998c4c201SDavid VomLehn {
17098c4c201SDavid VomLehn 	int err = 0;
17198c4c201SDavid VomLehn 	struct aq_hw_atl_utils_fw_rpc_tid_s sw;
17298c4c201SDavid VomLehn 
17398c4c201SDavid VomLehn 	if (!IS_CHIP_FEATURE(MIPS)) {
17498c4c201SDavid VomLehn 		err = -1;
17598c4c201SDavid VomLehn 		goto err_exit;
17698c4c201SDavid VomLehn 	}
17798c4c201SDavid VomLehn 	err = hw_atl_utils_fw_upload_dwords(self, PHAL_ATLANTIC->rpc_addr,
17898c4c201SDavid VomLehn 					    (u32 *)(void *)&PHAL_ATLANTIC->rpc,
17998c4c201SDavid VomLehn 					    (rpc_size + sizeof(u32) -
18098c4c201SDavid VomLehn 					    sizeof(u8)) / sizeof(u32));
18198c4c201SDavid VomLehn 	if (err < 0)
18298c4c201SDavid VomLehn 		goto err_exit;
18398c4c201SDavid VomLehn 
18498c4c201SDavid VomLehn 	sw.tid = 0xFFFFU & (++PHAL_ATLANTIC->rpc_tid);
18598c4c201SDavid VomLehn 	sw.len = (u16)rpc_size;
18698c4c201SDavid VomLehn 	aq_hw_write_reg(self, HW_ATL_RPC_CONTROL_ADR, sw.val);
18798c4c201SDavid VomLehn 
18898c4c201SDavid VomLehn err_exit:
18998c4c201SDavid VomLehn 	return err;
19098c4c201SDavid VomLehn }
19198c4c201SDavid VomLehn 
19298c4c201SDavid VomLehn static int hw_atl_utils_fw_rpc_wait(struct aq_hw_s *self,
19398c4c201SDavid VomLehn 				    struct hw_aq_atl_utils_fw_rpc **rpc)
19498c4c201SDavid VomLehn {
19598c4c201SDavid VomLehn 	int err = 0;
19698c4c201SDavid VomLehn 	struct aq_hw_atl_utils_fw_rpc_tid_s sw;
19798c4c201SDavid VomLehn 	struct aq_hw_atl_utils_fw_rpc_tid_s fw;
19898c4c201SDavid VomLehn 
19998c4c201SDavid VomLehn 	do {
20098c4c201SDavid VomLehn 		sw.val = aq_hw_read_reg(self, HW_ATL_RPC_CONTROL_ADR);
20198c4c201SDavid VomLehn 
20298c4c201SDavid VomLehn 		PHAL_ATLANTIC->rpc_tid = sw.tid;
20398c4c201SDavid VomLehn 
20498c4c201SDavid VomLehn 		AQ_HW_WAIT_FOR(sw.tid ==
20598c4c201SDavid VomLehn 				(fw.val =
20698c4c201SDavid VomLehn 				aq_hw_read_reg(self, HW_ATL_RPC_STATE_ADR),
20798c4c201SDavid VomLehn 				fw.tid), 1000U, 100U);
20898c4c201SDavid VomLehn 		if (err < 0)
20998c4c201SDavid VomLehn 			goto err_exit;
21098c4c201SDavid VomLehn 
21198c4c201SDavid VomLehn 		if (fw.len == 0xFFFFU) {
21298c4c201SDavid VomLehn 			err = hw_atl_utils_fw_rpc_call(self, sw.len);
21398c4c201SDavid VomLehn 			if (err < 0)
21498c4c201SDavid VomLehn 				goto err_exit;
21598c4c201SDavid VomLehn 		}
21698c4c201SDavid VomLehn 	} while (sw.tid != fw.tid || 0xFFFFU == fw.len);
21798c4c201SDavid VomLehn 	if (err < 0)
21898c4c201SDavid VomLehn 		goto err_exit;
21998c4c201SDavid VomLehn 
22098c4c201SDavid VomLehn 	if (rpc) {
22198c4c201SDavid VomLehn 		if (fw.len) {
22298c4c201SDavid VomLehn 			err =
22398c4c201SDavid VomLehn 			hw_atl_utils_fw_downld_dwords(self,
22498c4c201SDavid VomLehn 						      PHAL_ATLANTIC->rpc_addr,
22598c4c201SDavid VomLehn 						      (u32 *)(void *)
22698c4c201SDavid VomLehn 						      &PHAL_ATLANTIC->rpc,
22798c4c201SDavid VomLehn 						      (fw.len + sizeof(u32) -
22898c4c201SDavid VomLehn 						      sizeof(u8)) /
22998c4c201SDavid VomLehn 						      sizeof(u32));
23098c4c201SDavid VomLehn 			if (err < 0)
23198c4c201SDavid VomLehn 				goto err_exit;
23298c4c201SDavid VomLehn 		}
23398c4c201SDavid VomLehn 
23498c4c201SDavid VomLehn 		*rpc = &PHAL_ATLANTIC->rpc;
23598c4c201SDavid VomLehn 	}
23698c4c201SDavid VomLehn 
23798c4c201SDavid VomLehn err_exit:
23898c4c201SDavid VomLehn 	return err;
23998c4c201SDavid VomLehn }
24098c4c201SDavid VomLehn 
24198c4c201SDavid VomLehn static int hw_atl_utils_mpi_create(struct aq_hw_s *self,
24298c4c201SDavid VomLehn 				   struct aq_hw_caps_s *aq_hw_caps)
24398c4c201SDavid VomLehn {
24498c4c201SDavid VomLehn 	int err = 0;
24598c4c201SDavid VomLehn 
24698c4c201SDavid VomLehn 	err = hw_atl_utils_init_ucp(self, aq_hw_caps);
24798c4c201SDavid VomLehn 	if (err < 0)
24898c4c201SDavid VomLehn 		goto err_exit;
24998c4c201SDavid VomLehn 
25098c4c201SDavid VomLehn 	err = hw_atl_utils_fw_rpc_init(self);
25198c4c201SDavid VomLehn 	if (err < 0)
25298c4c201SDavid VomLehn 		goto err_exit;
25398c4c201SDavid VomLehn 
25498c4c201SDavid VomLehn err_exit:
25598c4c201SDavid VomLehn 	return err;
25698c4c201SDavid VomLehn }
25798c4c201SDavid VomLehn 
25898c4c201SDavid VomLehn void hw_atl_utils_mpi_read_stats(struct aq_hw_s *self,
25998c4c201SDavid VomLehn 				 struct hw_aq_atl_utils_mbox *pmbox)
26098c4c201SDavid VomLehn {
26198c4c201SDavid VomLehn 	int err = 0;
26298c4c201SDavid VomLehn 
26398c4c201SDavid VomLehn 	err = hw_atl_utils_fw_downld_dwords(self,
26498c4c201SDavid VomLehn 					    PHAL_ATLANTIC->mbox_addr,
26598c4c201SDavid VomLehn 					    (u32 *)(void *)pmbox,
26698c4c201SDavid VomLehn 					    sizeof(*pmbox) / sizeof(u32));
26798c4c201SDavid VomLehn 	if (err < 0)
26898c4c201SDavid VomLehn 		goto err_exit;
26998c4c201SDavid VomLehn 
27098c4c201SDavid VomLehn 	if (pmbox != &PHAL_ATLANTIC->mbox)
27198c4c201SDavid VomLehn 		memcpy(pmbox, &PHAL_ATLANTIC->mbox, sizeof(*pmbox));
27298c4c201SDavid VomLehn 
27398c4c201SDavid VomLehn 	if (IS_CHIP_FEATURE(REVISION_A0)) {
27498c4c201SDavid VomLehn 		unsigned int mtu = self->aq_nic_cfg ?
27598c4c201SDavid VomLehn 					self->aq_nic_cfg->mtu : 1514U;
27698c4c201SDavid VomLehn 		pmbox->stats.ubrc = pmbox->stats.uprc * mtu;
27798c4c201SDavid VomLehn 		pmbox->stats.ubtc = pmbox->stats.uptc * mtu;
27898c4c201SDavid VomLehn 		pmbox->stats.dpc = atomic_read(&PHAL_ATLANTIC_A0->dpc);
27998c4c201SDavid VomLehn 	} else {
28098c4c201SDavid VomLehn 		pmbox->stats.dpc = reg_rx_dma_stat_counter7get(self);
28198c4c201SDavid VomLehn 	}
28298c4c201SDavid VomLehn 
28398c4c201SDavid VomLehn err_exit:;
28498c4c201SDavid VomLehn }
28598c4c201SDavid VomLehn 
28698c4c201SDavid VomLehn int hw_atl_utils_mpi_set_speed(struct aq_hw_s *self, u32 speed,
28798c4c201SDavid VomLehn 			       enum hal_atl_utils_fw_state_e state)
28898c4c201SDavid VomLehn {
28998c4c201SDavid VomLehn 	u32 ucp_0x368 = 0;
29098c4c201SDavid VomLehn 
29198c4c201SDavid VomLehn 	ucp_0x368 = (speed << HW_ATL_MPI_SPEED_SHIFT) | state;
29298c4c201SDavid VomLehn 	aq_hw_write_reg(self, HW_ATL_MPI_CONTROL_ADR, ucp_0x368);
29398c4c201SDavid VomLehn 
29498c4c201SDavid VomLehn 	return 0;
29598c4c201SDavid VomLehn }
29698c4c201SDavid VomLehn 
29798c4c201SDavid VomLehn void hw_atl_utils_mpi_set(struct aq_hw_s *self,
29898c4c201SDavid VomLehn 			  enum hal_atl_utils_fw_state_e state, u32 speed)
29998c4c201SDavid VomLehn {
30098c4c201SDavid VomLehn 	int err = 0;
30198c4c201SDavid VomLehn 	u32 transaction_id = 0;
30298c4c201SDavid VomLehn 
30398c4c201SDavid VomLehn 	if (state == MPI_RESET) {
30498c4c201SDavid VomLehn 		hw_atl_utils_mpi_read_stats(self, &PHAL_ATLANTIC->mbox);
30598c4c201SDavid VomLehn 
30698c4c201SDavid VomLehn 		transaction_id = PHAL_ATLANTIC->mbox.transaction_id;
30798c4c201SDavid VomLehn 
30898c4c201SDavid VomLehn 		AQ_HW_WAIT_FOR(transaction_id !=
30998c4c201SDavid VomLehn 				(hw_atl_utils_mpi_read_stats
31098c4c201SDavid VomLehn 					(self, &PHAL_ATLANTIC->mbox),
31198c4c201SDavid VomLehn 					PHAL_ATLANTIC->mbox.transaction_id),
31298c4c201SDavid VomLehn 					1000U, 100U);
31398c4c201SDavid VomLehn 		if (err < 0)
31498c4c201SDavid VomLehn 			goto err_exit;
31598c4c201SDavid VomLehn 	}
31698c4c201SDavid VomLehn 
31798c4c201SDavid VomLehn 	err = hw_atl_utils_mpi_set_speed(self, speed, state);
31898c4c201SDavid VomLehn 
31998c4c201SDavid VomLehn err_exit:;
32098c4c201SDavid VomLehn }
32198c4c201SDavid VomLehn 
322bd8ed441SPavel Belous int hw_atl_utils_mpi_get_link_status(struct aq_hw_s *self)
32398c4c201SDavid VomLehn {
32498c4c201SDavid VomLehn 	u32 cp0x036C = aq_hw_read_reg(self, HW_ATL_MPI_STATE_ADR);
32598c4c201SDavid VomLehn 	u32 link_speed_mask = cp0x036C >> HW_ATL_MPI_SPEED_SHIFT;
326bd8ed441SPavel Belous 	struct aq_hw_link_status_s *link_status = &self->aq_link_status;
32798c4c201SDavid VomLehn 
32898c4c201SDavid VomLehn 	if (!link_speed_mask) {
32998c4c201SDavid VomLehn 		link_status->mbps = 0U;
33098c4c201SDavid VomLehn 	} else {
33198c4c201SDavid VomLehn 		switch (link_speed_mask) {
33298c4c201SDavid VomLehn 		case HAL_ATLANTIC_RATE_10G:
33398c4c201SDavid VomLehn 			link_status->mbps = 10000U;
33498c4c201SDavid VomLehn 			break;
33598c4c201SDavid VomLehn 
33698c4c201SDavid VomLehn 		case HAL_ATLANTIC_RATE_5G:
33798c4c201SDavid VomLehn 		case HAL_ATLANTIC_RATE_5GSR:
33898c4c201SDavid VomLehn 			link_status->mbps = 5000U;
33998c4c201SDavid VomLehn 			break;
34098c4c201SDavid VomLehn 
34198c4c201SDavid VomLehn 		case HAL_ATLANTIC_RATE_2GS:
34298c4c201SDavid VomLehn 			link_status->mbps = 2500U;
34398c4c201SDavid VomLehn 			break;
34498c4c201SDavid VomLehn 
34598c4c201SDavid VomLehn 		case HAL_ATLANTIC_RATE_1G:
34698c4c201SDavid VomLehn 			link_status->mbps = 1000U;
34798c4c201SDavid VomLehn 			break;
34898c4c201SDavid VomLehn 
34998c4c201SDavid VomLehn 		case HAL_ATLANTIC_RATE_100M:
35098c4c201SDavid VomLehn 			link_status->mbps = 100U;
35198c4c201SDavid VomLehn 			break;
35298c4c201SDavid VomLehn 
35398c4c201SDavid VomLehn 		default:
35498c4c201SDavid VomLehn 			link_status->mbps = 0U;
35598c4c201SDavid VomLehn 			break;
35698c4c201SDavid VomLehn 		}
35798c4c201SDavid VomLehn 	}
35898c4c201SDavid VomLehn 
35998c4c201SDavid VomLehn 	return 0;
36098c4c201SDavid VomLehn }
36198c4c201SDavid VomLehn 
36298c4c201SDavid VomLehn int hw_atl_utils_get_mac_permanent(struct aq_hw_s *self,
36398c4c201SDavid VomLehn 				   struct aq_hw_caps_s *aq_hw_caps,
36498c4c201SDavid VomLehn 				   u8 *mac)
36598c4c201SDavid VomLehn {
36698c4c201SDavid VomLehn 	int err = 0;
36798c4c201SDavid VomLehn 	u32 h = 0U;
36898c4c201SDavid VomLehn 	u32 l = 0U;
36998c4c201SDavid VomLehn 	u32 mac_addr[2];
37098c4c201SDavid VomLehn 
37198c4c201SDavid VomLehn 	self->mmio = aq_pci_func_get_mmio(self->aq_pci_func);
37298c4c201SDavid VomLehn 
37398c4c201SDavid VomLehn 	hw_atl_utils_hw_chip_features_init(self,
37498c4c201SDavid VomLehn 					   &PHAL_ATLANTIC_A0->chip_features);
37598c4c201SDavid VomLehn 
37698c4c201SDavid VomLehn 	err = hw_atl_utils_mpi_create(self, aq_hw_caps);
37798c4c201SDavid VomLehn 	if (err < 0)
37898c4c201SDavid VomLehn 		goto err_exit;
37998c4c201SDavid VomLehn 
38098c4c201SDavid VomLehn 	if (!aq_hw_read_reg(self, HW_ATL_UCP_0X370_REG)) {
38198c4c201SDavid VomLehn 		unsigned int rnd = 0;
38298c4c201SDavid VomLehn 		unsigned int ucp_0x370 = 0;
38398c4c201SDavid VomLehn 
38498c4c201SDavid VomLehn 		get_random_bytes(&rnd, sizeof(unsigned int));
38598c4c201SDavid VomLehn 
38698c4c201SDavid VomLehn 		ucp_0x370 = 0x02020202 | (0xFEFEFEFE & rnd);
38798c4c201SDavid VomLehn 		aq_hw_write_reg(self, HW_ATL_UCP_0X370_REG, ucp_0x370);
38898c4c201SDavid VomLehn 	}
38998c4c201SDavid VomLehn 
39098c4c201SDavid VomLehn 	err = hw_atl_utils_fw_downld_dwords(self,
39198c4c201SDavid VomLehn 					    aq_hw_read_reg(self, 0x00000374U) +
39298c4c201SDavid VomLehn 					    (40U * 4U),
39398c4c201SDavid VomLehn 					    mac_addr,
39498c4c201SDavid VomLehn 					    AQ_DIMOF(mac_addr));
39598c4c201SDavid VomLehn 	if (err < 0) {
39698c4c201SDavid VomLehn 		mac_addr[0] = 0U;
39798c4c201SDavid VomLehn 		mac_addr[1] = 0U;
39898c4c201SDavid VomLehn 		err = 0;
39998c4c201SDavid VomLehn 	} else {
40098c4c201SDavid VomLehn 		mac_addr[0] = __swab32(mac_addr[0]);
40198c4c201SDavid VomLehn 		mac_addr[1] = __swab32(mac_addr[1]);
40298c4c201SDavid VomLehn 	}
40398c4c201SDavid VomLehn 
40498c4c201SDavid VomLehn 	ether_addr_copy(mac, (u8 *)mac_addr);
40598c4c201SDavid VomLehn 
40698c4c201SDavid VomLehn 	if ((mac[0] & 0x01U) || ((mac[0] | mac[1] | mac[2]) == 0x00U)) {
40798c4c201SDavid VomLehn 		/* chip revision */
40898c4c201SDavid VomLehn 		l = 0xE3000000U
40998c4c201SDavid VomLehn 			| (0xFFFFU & aq_hw_read_reg(self, HW_ATL_UCP_0X370_REG))
41098c4c201SDavid VomLehn 			| (0x00 << 16);
41198c4c201SDavid VomLehn 		h = 0x8001300EU;
41298c4c201SDavid VomLehn 
41398c4c201SDavid VomLehn 		mac[5] = (u8)(0xFFU & l);
41498c4c201SDavid VomLehn 		l >>= 8;
41598c4c201SDavid VomLehn 		mac[4] = (u8)(0xFFU & l);
41698c4c201SDavid VomLehn 		l >>= 8;
41798c4c201SDavid VomLehn 		mac[3] = (u8)(0xFFU & l);
41898c4c201SDavid VomLehn 		l >>= 8;
41998c4c201SDavid VomLehn 		mac[2] = (u8)(0xFFU & l);
42098c4c201SDavid VomLehn 		mac[1] = (u8)(0xFFU & h);
42198c4c201SDavid VomLehn 		h >>= 8;
42298c4c201SDavid VomLehn 		mac[0] = (u8)(0xFFU & h);
42398c4c201SDavid VomLehn 	}
42498c4c201SDavid VomLehn 
42598c4c201SDavid VomLehn err_exit:
42698c4c201SDavid VomLehn 	return err;
42798c4c201SDavid VomLehn }
42898c4c201SDavid VomLehn 
42998c4c201SDavid VomLehn unsigned int hw_atl_utils_mbps_2_speed_index(unsigned int mbps)
43098c4c201SDavid VomLehn {
43198c4c201SDavid VomLehn 	unsigned int ret = 0U;
43298c4c201SDavid VomLehn 
43398c4c201SDavid VomLehn 	switch (mbps) {
43498c4c201SDavid VomLehn 	case 100U:
43598c4c201SDavid VomLehn 		ret = 5U;
43698c4c201SDavid VomLehn 		break;
43798c4c201SDavid VomLehn 
43898c4c201SDavid VomLehn 	case 1000U:
43998c4c201SDavid VomLehn 		ret = 4U;
44098c4c201SDavid VomLehn 		break;
44198c4c201SDavid VomLehn 
44298c4c201SDavid VomLehn 	case 2500U:
44398c4c201SDavid VomLehn 		ret = 3U;
44498c4c201SDavid VomLehn 		break;
44598c4c201SDavid VomLehn 
44698c4c201SDavid VomLehn 	case 5000U:
44798c4c201SDavid VomLehn 		ret = 1U;
44898c4c201SDavid VomLehn 		break;
44998c4c201SDavid VomLehn 
45098c4c201SDavid VomLehn 	case 10000U:
45198c4c201SDavid VomLehn 		ret = 0U;
45298c4c201SDavid VomLehn 		break;
45398c4c201SDavid VomLehn 
45498c4c201SDavid VomLehn 	default:
45598c4c201SDavid VomLehn 		break;
45698c4c201SDavid VomLehn 	}
45798c4c201SDavid VomLehn 	return ret;
45898c4c201SDavid VomLehn }
45998c4c201SDavid VomLehn 
46098c4c201SDavid VomLehn void hw_atl_utils_hw_chip_features_init(struct aq_hw_s *self, u32 *p)
46198c4c201SDavid VomLehn {
46298c4c201SDavid VomLehn 	u32 chip_features = 0U;
46398c4c201SDavid VomLehn 	u32 val = reg_glb_mif_id_get(self);
46498c4c201SDavid VomLehn 	u32 mif_rev = val & 0xFFU;
46598c4c201SDavid VomLehn 
46698c4c201SDavid VomLehn 	if ((3U & mif_rev) == 1U) {
46798c4c201SDavid VomLehn 		chip_features |=
46898c4c201SDavid VomLehn 			HAL_ATLANTIC_UTILS_CHIP_REVISION_A0 |
46998c4c201SDavid VomLehn 			HAL_ATLANTIC_UTILS_CHIP_MPI_AQ |
47098c4c201SDavid VomLehn 			HAL_ATLANTIC_UTILS_CHIP_MIPS;
47198c4c201SDavid VomLehn 	} else if ((3U & mif_rev) == 2U) {
47298c4c201SDavid VomLehn 		chip_features |=
47398c4c201SDavid VomLehn 			HAL_ATLANTIC_UTILS_CHIP_REVISION_B0 |
47498c4c201SDavid VomLehn 			HAL_ATLANTIC_UTILS_CHIP_MPI_AQ |
47598c4c201SDavid VomLehn 			HAL_ATLANTIC_UTILS_CHIP_MIPS |
47698c4c201SDavid VomLehn 			HAL_ATLANTIC_UTILS_CHIP_TPO2 |
47798c4c201SDavid VomLehn 			HAL_ATLANTIC_UTILS_CHIP_RPF2;
47898c4c201SDavid VomLehn 	}
47998c4c201SDavid VomLehn 
48098c4c201SDavid VomLehn 	*p = chip_features;
48198c4c201SDavid VomLehn }
48298c4c201SDavid VomLehn 
48398c4c201SDavid VomLehn int hw_atl_utils_hw_deinit(struct aq_hw_s *self)
48498c4c201SDavid VomLehn {
48598c4c201SDavid VomLehn 	hw_atl_utils_mpi_set(self, MPI_DEINIT, 0x0U);
48698c4c201SDavid VomLehn 	return 0;
48798c4c201SDavid VomLehn }
48898c4c201SDavid VomLehn 
48998c4c201SDavid VomLehn int hw_atl_utils_hw_set_power(struct aq_hw_s *self,
49098c4c201SDavid VomLehn 			      unsigned int power_state)
49198c4c201SDavid VomLehn {
49298c4c201SDavid VomLehn 	hw_atl_utils_mpi_set(self, MPI_POWER, 0x0U);
49398c4c201SDavid VomLehn 	return 0;
49498c4c201SDavid VomLehn }
49598c4c201SDavid VomLehn 
49698c4c201SDavid VomLehn int hw_atl_utils_get_hw_stats(struct aq_hw_s *self,
49798c4c201SDavid VomLehn 			      u64 *data, unsigned int *p_count)
49898c4c201SDavid VomLehn {
49998c4c201SDavid VomLehn 	struct hw_atl_stats_s *stats = NULL;
50098c4c201SDavid VomLehn 	int i = 0;
50198c4c201SDavid VomLehn 
50298c4c201SDavid VomLehn 	hw_atl_utils_mpi_read_stats(self, &PHAL_ATLANTIC->mbox);
50398c4c201SDavid VomLehn 
50498c4c201SDavid VomLehn 	stats = &PHAL_ATLANTIC->mbox.stats;
50598c4c201SDavid VomLehn 
50698c4c201SDavid VomLehn 	data[i] = stats->uprc + stats->mprc + stats->bprc;
50798c4c201SDavid VomLehn 	data[++i] = stats->uprc;
50898c4c201SDavid VomLehn 	data[++i] = stats->mprc;
50998c4c201SDavid VomLehn 	data[++i] = stats->bprc;
51098c4c201SDavid VomLehn 	data[++i] = stats->erpt;
51198c4c201SDavid VomLehn 	data[++i] = stats->uptc + stats->mptc + stats->bptc;
51298c4c201SDavid VomLehn 	data[++i] = stats->uptc;
51398c4c201SDavid VomLehn 	data[++i] = stats->mptc;
51498c4c201SDavid VomLehn 	data[++i] = stats->bptc;
51598c4c201SDavid VomLehn 	data[++i] = stats->ubrc;
51698c4c201SDavid VomLehn 	data[++i] = stats->ubtc;
51798c4c201SDavid VomLehn 	data[++i] = stats->mbrc;
51898c4c201SDavid VomLehn 	data[++i] = stats->mbtc;
51998c4c201SDavid VomLehn 	data[++i] = stats->bbrc;
52098c4c201SDavid VomLehn 	data[++i] = stats->bbtc;
52198c4c201SDavid VomLehn 	data[++i] = stats->ubrc + stats->mbrc + stats->bbrc;
52298c4c201SDavid VomLehn 	data[++i] = stats->ubtc + stats->mbtc + stats->bbtc;
52398c4c201SDavid VomLehn 	data[++i] = stats_rx_dma_good_pkt_counterlsw_get(self);
52498c4c201SDavid VomLehn 	data[++i] = stats_tx_dma_good_pkt_counterlsw_get(self);
52598c4c201SDavid VomLehn 	data[++i] = stats_rx_dma_good_octet_counterlsw_get(self);
52698c4c201SDavid VomLehn 	data[++i] = stats_tx_dma_good_octet_counterlsw_get(self);
52798c4c201SDavid VomLehn 	data[++i] = stats->dpc;
52898c4c201SDavid VomLehn 
52998c4c201SDavid VomLehn 	if (p_count)
53098c4c201SDavid VomLehn 		*p_count = ++i;
53198c4c201SDavid VomLehn 
53298c4c201SDavid VomLehn 	return 0;
53398c4c201SDavid VomLehn }
53498c4c201SDavid VomLehn 
53598c4c201SDavid VomLehn static const u32 hw_atl_utils_hw_mac_regs[] = {
53698c4c201SDavid VomLehn 	0x00005580U, 0x00005590U, 0x000055B0U, 0x000055B4U,
53798c4c201SDavid VomLehn 	0x000055C0U, 0x00005B00U, 0x00005B04U, 0x00005B08U,
53898c4c201SDavid VomLehn 	0x00005B0CU, 0x00005B10U, 0x00005B14U, 0x00005B18U,
53998c4c201SDavid VomLehn 	0x00005B1CU, 0x00005B20U, 0x00005B24U, 0x00005B28U,
54098c4c201SDavid VomLehn 	0x00005B2CU, 0x00005B30U, 0x00005B34U, 0x00005B38U,
54198c4c201SDavid VomLehn 	0x00005B3CU, 0x00005B40U, 0x00005B44U, 0x00005B48U,
54298c4c201SDavid VomLehn 	0x00005B4CU, 0x00005B50U, 0x00005B54U, 0x00005B58U,
54398c4c201SDavid VomLehn 	0x00005B5CU, 0x00005B60U, 0x00005B64U, 0x00005B68U,
54498c4c201SDavid VomLehn 	0x00005B6CU, 0x00005B70U, 0x00005B74U, 0x00005B78U,
54598c4c201SDavid VomLehn 	0x00005B7CU, 0x00007C00U, 0x00007C04U, 0x00007C08U,
54698c4c201SDavid VomLehn 	0x00007C0CU, 0x00007C10U, 0x00007C14U, 0x00007C18U,
54798c4c201SDavid VomLehn 	0x00007C1CU, 0x00007C20U, 0x00007C40U, 0x00007C44U,
54898c4c201SDavid VomLehn 	0x00007C48U, 0x00007C4CU, 0x00007C50U, 0x00007C54U,
54998c4c201SDavid VomLehn 	0x00007C58U, 0x00007C5CU, 0x00007C60U, 0x00007C80U,
55098c4c201SDavid VomLehn 	0x00007C84U, 0x00007C88U, 0x00007C8CU, 0x00007C90U,
55198c4c201SDavid VomLehn 	0x00007C94U, 0x00007C98U, 0x00007C9CU, 0x00007CA0U,
55298c4c201SDavid VomLehn 	0x00007CC0U, 0x00007CC4U, 0x00007CC8U, 0x00007CCCU,
55398c4c201SDavid VomLehn 	0x00007CD0U, 0x00007CD4U, 0x00007CD8U, 0x00007CDCU,
55498c4c201SDavid VomLehn 	0x00007CE0U, 0x00000300U, 0x00000304U, 0x00000308U,
55598c4c201SDavid VomLehn 	0x0000030cU, 0x00000310U, 0x00000314U, 0x00000318U,
55698c4c201SDavid VomLehn 	0x0000031cU, 0x00000360U, 0x00000364U, 0x00000368U,
55798c4c201SDavid VomLehn 	0x0000036cU, 0x00000370U, 0x00000374U, 0x00006900U,
55898c4c201SDavid VomLehn };
55998c4c201SDavid VomLehn 
56098c4c201SDavid VomLehn int hw_atl_utils_hw_get_regs(struct aq_hw_s *self,
56198c4c201SDavid VomLehn 			     struct aq_hw_caps_s *aq_hw_caps,
56298c4c201SDavid VomLehn 			     u32 *regs_buff)
56398c4c201SDavid VomLehn {
56498c4c201SDavid VomLehn 	unsigned int i = 0U;
56598c4c201SDavid VomLehn 
56698c4c201SDavid VomLehn 	for (i = 0; i < aq_hw_caps->mac_regs_count; i++)
56798c4c201SDavid VomLehn 		regs_buff[i] = aq_hw_read_reg(self,
56898c4c201SDavid VomLehn 			hw_atl_utils_hw_mac_regs[i]);
56998c4c201SDavid VomLehn 	return 0;
57098c4c201SDavid VomLehn }
57198c4c201SDavid VomLehn 
57298c4c201SDavid VomLehn int hw_atl_utils_get_fw_version(struct aq_hw_s *self, u32 *fw_version)
57398c4c201SDavid VomLehn {
57498c4c201SDavid VomLehn 	*fw_version = aq_hw_read_reg(self, 0x18U);
57598c4c201SDavid VomLehn 	return 0;
57698c4c201SDavid VomLehn }
577