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 
25865e665e6SIgor Russkikh int hw_atl_utils_mpi_read_mbox(struct aq_hw_s *self,
25965e665e6SIgor Russkikh 			       struct hw_aq_atl_utils_mbox_header *pmbox)
26065e665e6SIgor Russkikh {
26165e665e6SIgor Russkikh 	return hw_atl_utils_fw_downld_dwords(self,
26265e665e6SIgor Russkikh 				      PHAL_ATLANTIC->mbox_addr,
26365e665e6SIgor Russkikh 				      (u32 *)(void *)pmbox,
26465e665e6SIgor Russkikh 				      sizeof(*pmbox) / sizeof(u32));
26565e665e6SIgor Russkikh }
26665e665e6SIgor Russkikh 
26798c4c201SDavid VomLehn void hw_atl_utils_mpi_read_stats(struct aq_hw_s *self,
26898c4c201SDavid VomLehn 				 struct hw_aq_atl_utils_mbox *pmbox)
26998c4c201SDavid VomLehn {
27098c4c201SDavid VomLehn 	int err = 0;
27198c4c201SDavid VomLehn 
27298c4c201SDavid VomLehn 	err = hw_atl_utils_fw_downld_dwords(self,
27398c4c201SDavid VomLehn 					    PHAL_ATLANTIC->mbox_addr,
27498c4c201SDavid VomLehn 					    (u32 *)(void *)pmbox,
27598c4c201SDavid VomLehn 					    sizeof(*pmbox) / sizeof(u32));
27698c4c201SDavid VomLehn 	if (err < 0)
27798c4c201SDavid VomLehn 		goto err_exit;
27898c4c201SDavid VomLehn 
27998c4c201SDavid VomLehn 	if (IS_CHIP_FEATURE(REVISION_A0)) {
28098c4c201SDavid VomLehn 		unsigned int mtu = self->aq_nic_cfg ?
28198c4c201SDavid VomLehn 					self->aq_nic_cfg->mtu : 1514U;
28298c4c201SDavid VomLehn 		pmbox->stats.ubrc = pmbox->stats.uprc * mtu;
28398c4c201SDavid VomLehn 		pmbox->stats.ubtc = pmbox->stats.uptc * mtu;
28498c4c201SDavid VomLehn 		pmbox->stats.dpc = atomic_read(&PHAL_ATLANTIC_A0->dpc);
28598c4c201SDavid VomLehn 	} else {
28698c4c201SDavid VomLehn 		pmbox->stats.dpc = reg_rx_dma_stat_counter7get(self);
28798c4c201SDavid VomLehn 	}
28898c4c201SDavid VomLehn 
28998c4c201SDavid VomLehn err_exit:;
29098c4c201SDavid VomLehn }
29198c4c201SDavid VomLehn 
29298c4c201SDavid VomLehn int hw_atl_utils_mpi_set_speed(struct aq_hw_s *self, u32 speed,
29398c4c201SDavid VomLehn 			       enum hal_atl_utils_fw_state_e state)
29498c4c201SDavid VomLehn {
29598c4c201SDavid VomLehn 	u32 ucp_0x368 = 0;
29698c4c201SDavid VomLehn 
29798c4c201SDavid VomLehn 	ucp_0x368 = (speed << HW_ATL_MPI_SPEED_SHIFT) | state;
29898c4c201SDavid VomLehn 	aq_hw_write_reg(self, HW_ATL_MPI_CONTROL_ADR, ucp_0x368);
29998c4c201SDavid VomLehn 
30098c4c201SDavid VomLehn 	return 0;
30198c4c201SDavid VomLehn }
30298c4c201SDavid VomLehn 
30398c4c201SDavid VomLehn void hw_atl_utils_mpi_set(struct aq_hw_s *self,
30498c4c201SDavid VomLehn 			  enum hal_atl_utils_fw_state_e state, u32 speed)
30598c4c201SDavid VomLehn {
30698c4c201SDavid VomLehn 	int err = 0;
30798c4c201SDavid VomLehn 	u32 transaction_id = 0;
30865e665e6SIgor Russkikh 	struct hw_aq_atl_utils_mbox_header mbox;
30998c4c201SDavid VomLehn 
31098c4c201SDavid VomLehn 	if (state == MPI_RESET) {
31165e665e6SIgor Russkikh 		hw_atl_utils_mpi_read_mbox(self, &mbox);
31298c4c201SDavid VomLehn 
31365e665e6SIgor Russkikh 		transaction_id = mbox.transaction_id;
31498c4c201SDavid VomLehn 
31598c4c201SDavid VomLehn 		AQ_HW_WAIT_FOR(transaction_id !=
31665e665e6SIgor Russkikh 				(hw_atl_utils_mpi_read_mbox(self, &mbox),
31765e665e6SIgor Russkikh 				 mbox.transaction_id),
31898c4c201SDavid VomLehn 			       1000U, 100U);
31998c4c201SDavid VomLehn 		if (err < 0)
32098c4c201SDavid VomLehn 			goto err_exit;
32198c4c201SDavid VomLehn 	}
32298c4c201SDavid VomLehn 
32398c4c201SDavid VomLehn 	err = hw_atl_utils_mpi_set_speed(self, speed, state);
32498c4c201SDavid VomLehn 
32598c4c201SDavid VomLehn err_exit:;
32698c4c201SDavid VomLehn }
32798c4c201SDavid VomLehn 
328bd8ed441SPavel Belous int hw_atl_utils_mpi_get_link_status(struct aq_hw_s *self)
32998c4c201SDavid VomLehn {
33098c4c201SDavid VomLehn 	u32 cp0x036C = aq_hw_read_reg(self, HW_ATL_MPI_STATE_ADR);
33198c4c201SDavid VomLehn 	u32 link_speed_mask = cp0x036C >> HW_ATL_MPI_SPEED_SHIFT;
332bd8ed441SPavel Belous 	struct aq_hw_link_status_s *link_status = &self->aq_link_status;
33398c4c201SDavid VomLehn 
33498c4c201SDavid VomLehn 	if (!link_speed_mask) {
33598c4c201SDavid VomLehn 		link_status->mbps = 0U;
33698c4c201SDavid VomLehn 	} else {
33798c4c201SDavid VomLehn 		switch (link_speed_mask) {
33898c4c201SDavid VomLehn 		case HAL_ATLANTIC_RATE_10G:
33998c4c201SDavid VomLehn 			link_status->mbps = 10000U;
34098c4c201SDavid VomLehn 			break;
34198c4c201SDavid VomLehn 
34298c4c201SDavid VomLehn 		case HAL_ATLANTIC_RATE_5G:
34398c4c201SDavid VomLehn 		case HAL_ATLANTIC_RATE_5GSR:
34498c4c201SDavid VomLehn 			link_status->mbps = 5000U;
34598c4c201SDavid VomLehn 			break;
34698c4c201SDavid VomLehn 
34798c4c201SDavid VomLehn 		case HAL_ATLANTIC_RATE_2GS:
34898c4c201SDavid VomLehn 			link_status->mbps = 2500U;
34998c4c201SDavid VomLehn 			break;
35098c4c201SDavid VomLehn 
35198c4c201SDavid VomLehn 		case HAL_ATLANTIC_RATE_1G:
35298c4c201SDavid VomLehn 			link_status->mbps = 1000U;
35398c4c201SDavid VomLehn 			break;
35498c4c201SDavid VomLehn 
35598c4c201SDavid VomLehn 		case HAL_ATLANTIC_RATE_100M:
35698c4c201SDavid VomLehn 			link_status->mbps = 100U;
35798c4c201SDavid VomLehn 			break;
35898c4c201SDavid VomLehn 
35998c4c201SDavid VomLehn 		default:
360a7bb1beaSIgor Russkikh 			return -EBUSY;
36198c4c201SDavid VomLehn 		}
36298c4c201SDavid VomLehn 	}
36398c4c201SDavid VomLehn 
36498c4c201SDavid VomLehn 	return 0;
36598c4c201SDavid VomLehn }
36698c4c201SDavid VomLehn 
36798c4c201SDavid VomLehn int hw_atl_utils_get_mac_permanent(struct aq_hw_s *self,
36898c4c201SDavid VomLehn 				   struct aq_hw_caps_s *aq_hw_caps,
36998c4c201SDavid VomLehn 				   u8 *mac)
37098c4c201SDavid VomLehn {
37198c4c201SDavid VomLehn 	int err = 0;
37298c4c201SDavid VomLehn 	u32 h = 0U;
37398c4c201SDavid VomLehn 	u32 l = 0U;
37498c4c201SDavid VomLehn 	u32 mac_addr[2];
37598c4c201SDavid VomLehn 
37698c4c201SDavid VomLehn 	self->mmio = aq_pci_func_get_mmio(self->aq_pci_func);
37798c4c201SDavid VomLehn 
37898c4c201SDavid VomLehn 	hw_atl_utils_hw_chip_features_init(self,
37998c4c201SDavid VomLehn 					   &PHAL_ATLANTIC_A0->chip_features);
38098c4c201SDavid VomLehn 
38198c4c201SDavid VomLehn 	err = hw_atl_utils_mpi_create(self, aq_hw_caps);
38298c4c201SDavid VomLehn 	if (err < 0)
38398c4c201SDavid VomLehn 		goto err_exit;
38498c4c201SDavid VomLehn 
38598c4c201SDavid VomLehn 	if (!aq_hw_read_reg(self, HW_ATL_UCP_0X370_REG)) {
38698c4c201SDavid VomLehn 		unsigned int rnd = 0;
38798c4c201SDavid VomLehn 		unsigned int ucp_0x370 = 0;
38898c4c201SDavid VomLehn 
38998c4c201SDavid VomLehn 		get_random_bytes(&rnd, sizeof(unsigned int));
39098c4c201SDavid VomLehn 
39198c4c201SDavid VomLehn 		ucp_0x370 = 0x02020202 | (0xFEFEFEFE & rnd);
39298c4c201SDavid VomLehn 		aq_hw_write_reg(self, HW_ATL_UCP_0X370_REG, ucp_0x370);
39398c4c201SDavid VomLehn 	}
39498c4c201SDavid VomLehn 
39598c4c201SDavid VomLehn 	err = hw_atl_utils_fw_downld_dwords(self,
39698c4c201SDavid VomLehn 					    aq_hw_read_reg(self, 0x00000374U) +
39798c4c201SDavid VomLehn 					    (40U * 4U),
39898c4c201SDavid VomLehn 					    mac_addr,
39908b5cf08SIgor Russkikh 					    ARRAY_SIZE(mac_addr));
40098c4c201SDavid VomLehn 	if (err < 0) {
40198c4c201SDavid VomLehn 		mac_addr[0] = 0U;
40298c4c201SDavid VomLehn 		mac_addr[1] = 0U;
40398c4c201SDavid VomLehn 		err = 0;
40498c4c201SDavid VomLehn 	} else {
40598c4c201SDavid VomLehn 		mac_addr[0] = __swab32(mac_addr[0]);
40698c4c201SDavid VomLehn 		mac_addr[1] = __swab32(mac_addr[1]);
40798c4c201SDavid VomLehn 	}
40898c4c201SDavid VomLehn 
40998c4c201SDavid VomLehn 	ether_addr_copy(mac, (u8 *)mac_addr);
41098c4c201SDavid VomLehn 
41198c4c201SDavid VomLehn 	if ((mac[0] & 0x01U) || ((mac[0] | mac[1] | mac[2]) == 0x00U)) {
41298c4c201SDavid VomLehn 		/* chip revision */
41398c4c201SDavid VomLehn 		l = 0xE3000000U
41498c4c201SDavid VomLehn 			| (0xFFFFU & aq_hw_read_reg(self, HW_ATL_UCP_0X370_REG))
41598c4c201SDavid VomLehn 			| (0x00 << 16);
41698c4c201SDavid VomLehn 		h = 0x8001300EU;
41798c4c201SDavid VomLehn 
41898c4c201SDavid VomLehn 		mac[5] = (u8)(0xFFU & l);
41998c4c201SDavid VomLehn 		l >>= 8;
42098c4c201SDavid VomLehn 		mac[4] = (u8)(0xFFU & l);
42198c4c201SDavid VomLehn 		l >>= 8;
42298c4c201SDavid VomLehn 		mac[3] = (u8)(0xFFU & l);
42398c4c201SDavid VomLehn 		l >>= 8;
42498c4c201SDavid VomLehn 		mac[2] = (u8)(0xFFU & l);
42598c4c201SDavid VomLehn 		mac[1] = (u8)(0xFFU & h);
42698c4c201SDavid VomLehn 		h >>= 8;
42798c4c201SDavid VomLehn 		mac[0] = (u8)(0xFFU & h);
42898c4c201SDavid VomLehn 	}
42998c4c201SDavid VomLehn 
43098c4c201SDavid VomLehn err_exit:
43198c4c201SDavid VomLehn 	return err;
43298c4c201SDavid VomLehn }
43398c4c201SDavid VomLehn 
43498c4c201SDavid VomLehn unsigned int hw_atl_utils_mbps_2_speed_index(unsigned int mbps)
43598c4c201SDavid VomLehn {
43698c4c201SDavid VomLehn 	unsigned int ret = 0U;
43798c4c201SDavid VomLehn 
43898c4c201SDavid VomLehn 	switch (mbps) {
43998c4c201SDavid VomLehn 	case 100U:
44098c4c201SDavid VomLehn 		ret = 5U;
44198c4c201SDavid VomLehn 		break;
44298c4c201SDavid VomLehn 
44398c4c201SDavid VomLehn 	case 1000U:
44498c4c201SDavid VomLehn 		ret = 4U;
44598c4c201SDavid VomLehn 		break;
44698c4c201SDavid VomLehn 
44798c4c201SDavid VomLehn 	case 2500U:
44898c4c201SDavid VomLehn 		ret = 3U;
44998c4c201SDavid VomLehn 		break;
45098c4c201SDavid VomLehn 
45198c4c201SDavid VomLehn 	case 5000U:
45298c4c201SDavid VomLehn 		ret = 1U;
45398c4c201SDavid VomLehn 		break;
45498c4c201SDavid VomLehn 
45598c4c201SDavid VomLehn 	case 10000U:
45698c4c201SDavid VomLehn 		ret = 0U;
45798c4c201SDavid VomLehn 		break;
45898c4c201SDavid VomLehn 
45998c4c201SDavid VomLehn 	default:
46098c4c201SDavid VomLehn 		break;
46198c4c201SDavid VomLehn 	}
46298c4c201SDavid VomLehn 	return ret;
46398c4c201SDavid VomLehn }
46498c4c201SDavid VomLehn 
46598c4c201SDavid VomLehn void hw_atl_utils_hw_chip_features_init(struct aq_hw_s *self, u32 *p)
46698c4c201SDavid VomLehn {
46798c4c201SDavid VomLehn 	u32 chip_features = 0U;
46898c4c201SDavid VomLehn 	u32 val = reg_glb_mif_id_get(self);
46998c4c201SDavid VomLehn 	u32 mif_rev = val & 0xFFU;
47098c4c201SDavid VomLehn 
47198c4c201SDavid VomLehn 	if ((3U & mif_rev) == 1U) {
47298c4c201SDavid VomLehn 		chip_features |=
47398c4c201SDavid VomLehn 			HAL_ATLANTIC_UTILS_CHIP_REVISION_A0 |
47498c4c201SDavid VomLehn 			HAL_ATLANTIC_UTILS_CHIP_MPI_AQ |
47598c4c201SDavid VomLehn 			HAL_ATLANTIC_UTILS_CHIP_MIPS;
47698c4c201SDavid VomLehn 	} else if ((3U & mif_rev) == 2U) {
47798c4c201SDavid VomLehn 		chip_features |=
47898c4c201SDavid VomLehn 			HAL_ATLANTIC_UTILS_CHIP_REVISION_B0 |
47998c4c201SDavid VomLehn 			HAL_ATLANTIC_UTILS_CHIP_MPI_AQ |
48098c4c201SDavid VomLehn 			HAL_ATLANTIC_UTILS_CHIP_MIPS |
48198c4c201SDavid VomLehn 			HAL_ATLANTIC_UTILS_CHIP_TPO2 |
48298c4c201SDavid VomLehn 			HAL_ATLANTIC_UTILS_CHIP_RPF2;
48398c4c201SDavid VomLehn 	}
48498c4c201SDavid VomLehn 
48598c4c201SDavid VomLehn 	*p = chip_features;
48698c4c201SDavid VomLehn }
48798c4c201SDavid VomLehn 
48898c4c201SDavid VomLehn int hw_atl_utils_hw_deinit(struct aq_hw_s *self)
48998c4c201SDavid VomLehn {
49098c4c201SDavid VomLehn 	hw_atl_utils_mpi_set(self, MPI_DEINIT, 0x0U);
49198c4c201SDavid VomLehn 	return 0;
49298c4c201SDavid VomLehn }
49398c4c201SDavid VomLehn 
49498c4c201SDavid VomLehn int hw_atl_utils_hw_set_power(struct aq_hw_s *self,
49598c4c201SDavid VomLehn 			      unsigned int power_state)
49698c4c201SDavid VomLehn {
49798c4c201SDavid VomLehn 	hw_atl_utils_mpi_set(self, MPI_POWER, 0x0U);
49898c4c201SDavid VomLehn 	return 0;
49998c4c201SDavid VomLehn }
50098c4c201SDavid VomLehn 
50165e665e6SIgor Russkikh int hw_atl_utils_update_stats(struct aq_hw_s *self)
50265e665e6SIgor Russkikh {
50365e665e6SIgor Russkikh 	struct hw_atl_s *hw_self = PHAL_ATLANTIC;
50465e665e6SIgor Russkikh 	struct hw_aq_atl_utils_mbox mbox;
50565e665e6SIgor Russkikh 
50665e665e6SIgor Russkikh 	hw_atl_utils_mpi_read_stats(self, &mbox);
50765e665e6SIgor Russkikh 
50865e665e6SIgor Russkikh #define AQ_SDELTA(_N_) (hw_self->curr_stats._N_ += \
50965e665e6SIgor Russkikh 			mbox.stats._N_ - hw_self->last_stats._N_)
510be08d839SIgor Russkikh 	if (self->aq_link_status.mbps) {
51165e665e6SIgor Russkikh 		AQ_SDELTA(uprc);
51265e665e6SIgor Russkikh 		AQ_SDELTA(mprc);
51365e665e6SIgor Russkikh 		AQ_SDELTA(bprc);
51465e665e6SIgor Russkikh 		AQ_SDELTA(erpt);
51565e665e6SIgor Russkikh 
51665e665e6SIgor Russkikh 		AQ_SDELTA(uptc);
51765e665e6SIgor Russkikh 		AQ_SDELTA(mptc);
51865e665e6SIgor Russkikh 		AQ_SDELTA(bptc);
51965e665e6SIgor Russkikh 		AQ_SDELTA(erpr);
52065e665e6SIgor Russkikh 
52165e665e6SIgor Russkikh 		AQ_SDELTA(ubrc);
52265e665e6SIgor Russkikh 		AQ_SDELTA(ubtc);
52365e665e6SIgor Russkikh 		AQ_SDELTA(mbrc);
52465e665e6SIgor Russkikh 		AQ_SDELTA(mbtc);
52565e665e6SIgor Russkikh 		AQ_SDELTA(bbrc);
52665e665e6SIgor Russkikh 		AQ_SDELTA(bbtc);
52765e665e6SIgor Russkikh 		AQ_SDELTA(dpc);
528be08d839SIgor Russkikh 	}
52965e665e6SIgor Russkikh #undef AQ_SDELTA
530be08d839SIgor Russkikh 	hw_self->curr_stats.dma_pkt_rc = stats_rx_dma_good_pkt_counterlsw_get(self);
531be08d839SIgor Russkikh 	hw_self->curr_stats.dma_pkt_tc = stats_tx_dma_good_pkt_counterlsw_get(self);
532be08d839SIgor Russkikh 	hw_self->curr_stats.dma_oct_rc = stats_rx_dma_good_octet_counterlsw_get(self);
533be08d839SIgor Russkikh 	hw_self->curr_stats.dma_oct_tc = stats_tx_dma_good_octet_counterlsw_get(self);
53465e665e6SIgor Russkikh 
53565e665e6SIgor Russkikh 	memcpy(&hw_self->last_stats, &mbox.stats, sizeof(mbox.stats));
53665e665e6SIgor Russkikh 
53765e665e6SIgor Russkikh 	return 0;
53865e665e6SIgor Russkikh }
53965e665e6SIgor Russkikh 
540be08d839SIgor Russkikh struct aq_stats_s *hw_atl_utils_get_hw_stats(struct aq_hw_s *self)
54198c4c201SDavid VomLehn {
542be08d839SIgor Russkikh 	return &PHAL_ATLANTIC->curr_stats;
54398c4c201SDavid VomLehn }
54498c4c201SDavid VomLehn 
54598c4c201SDavid VomLehn static const u32 hw_atl_utils_hw_mac_regs[] = {
54698c4c201SDavid VomLehn 	0x00005580U, 0x00005590U, 0x000055B0U, 0x000055B4U,
54798c4c201SDavid VomLehn 	0x000055C0U, 0x00005B00U, 0x00005B04U, 0x00005B08U,
54898c4c201SDavid VomLehn 	0x00005B0CU, 0x00005B10U, 0x00005B14U, 0x00005B18U,
54998c4c201SDavid VomLehn 	0x00005B1CU, 0x00005B20U, 0x00005B24U, 0x00005B28U,
55098c4c201SDavid VomLehn 	0x00005B2CU, 0x00005B30U, 0x00005B34U, 0x00005B38U,
55198c4c201SDavid VomLehn 	0x00005B3CU, 0x00005B40U, 0x00005B44U, 0x00005B48U,
55298c4c201SDavid VomLehn 	0x00005B4CU, 0x00005B50U, 0x00005B54U, 0x00005B58U,
55398c4c201SDavid VomLehn 	0x00005B5CU, 0x00005B60U, 0x00005B64U, 0x00005B68U,
55498c4c201SDavid VomLehn 	0x00005B6CU, 0x00005B70U, 0x00005B74U, 0x00005B78U,
55598c4c201SDavid VomLehn 	0x00005B7CU, 0x00007C00U, 0x00007C04U, 0x00007C08U,
55698c4c201SDavid VomLehn 	0x00007C0CU, 0x00007C10U, 0x00007C14U, 0x00007C18U,
55798c4c201SDavid VomLehn 	0x00007C1CU, 0x00007C20U, 0x00007C40U, 0x00007C44U,
55898c4c201SDavid VomLehn 	0x00007C48U, 0x00007C4CU, 0x00007C50U, 0x00007C54U,
55998c4c201SDavid VomLehn 	0x00007C58U, 0x00007C5CU, 0x00007C60U, 0x00007C80U,
56098c4c201SDavid VomLehn 	0x00007C84U, 0x00007C88U, 0x00007C8CU, 0x00007C90U,
56198c4c201SDavid VomLehn 	0x00007C94U, 0x00007C98U, 0x00007C9CU, 0x00007CA0U,
56298c4c201SDavid VomLehn 	0x00007CC0U, 0x00007CC4U, 0x00007CC8U, 0x00007CCCU,
56398c4c201SDavid VomLehn 	0x00007CD0U, 0x00007CD4U, 0x00007CD8U, 0x00007CDCU,
56498c4c201SDavid VomLehn 	0x00007CE0U, 0x00000300U, 0x00000304U, 0x00000308U,
56598c4c201SDavid VomLehn 	0x0000030cU, 0x00000310U, 0x00000314U, 0x00000318U,
56698c4c201SDavid VomLehn 	0x0000031cU, 0x00000360U, 0x00000364U, 0x00000368U,
56798c4c201SDavid VomLehn 	0x0000036cU, 0x00000370U, 0x00000374U, 0x00006900U,
56898c4c201SDavid VomLehn };
56998c4c201SDavid VomLehn 
57098c4c201SDavid VomLehn int hw_atl_utils_hw_get_regs(struct aq_hw_s *self,
57198c4c201SDavid VomLehn 			     struct aq_hw_caps_s *aq_hw_caps,
57298c4c201SDavid VomLehn 			     u32 *regs_buff)
57398c4c201SDavid VomLehn {
57498c4c201SDavid VomLehn 	unsigned int i = 0U;
57598c4c201SDavid VomLehn 
57698c4c201SDavid VomLehn 	for (i = 0; i < aq_hw_caps->mac_regs_count; i++)
57798c4c201SDavid VomLehn 		regs_buff[i] = aq_hw_read_reg(self,
57898c4c201SDavid VomLehn 			hw_atl_utils_hw_mac_regs[i]);
57998c4c201SDavid VomLehn 	return 0;
58098c4c201SDavid VomLehn }
58198c4c201SDavid VomLehn 
58298c4c201SDavid VomLehn int hw_atl_utils_get_fw_version(struct aq_hw_s *self, u32 *fw_version)
58398c4c201SDavid VomLehn {
58498c4c201SDavid VomLehn 	*fw_version = aq_hw_read_reg(self, 0x18U);
58598c4c201SDavid VomLehn 	return 0;
58698c4c201SDavid VomLehn }
587