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