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 141a713f87SIgor Russkikh #include "../aq_nic.h" 1598c4c201SDavid VomLehn #include "../aq_hw_utils.h" 1698c4c201SDavid VomLehn #include "hw_atl_utils.h" 1798c4c201SDavid VomLehn #include "hw_atl_llh.h" 180c58c35fSIgor Russkikh #include "hw_atl_llh_internal.h" 1998c4c201SDavid VomLehn 2098c4c201SDavid VomLehn #include <linux/random.h> 2198c4c201SDavid VomLehn 2298c4c201SDavid VomLehn #define HW_ATL_UCP_0X370_REG 0x0370U 2398c4c201SDavid VomLehn 2447203b34SIgor Russkikh #define HW_ATL_MIF_CMD 0x0200U 2547203b34SIgor Russkikh #define HW_ATL_MIF_ADDR 0x0208U 2647203b34SIgor Russkikh #define HW_ATL_MIF_VAL 0x020CU 2747203b34SIgor Russkikh 286a7f2277SNikita Danilov #define HW_ATL_RPC_CONTROL_ADR 0x0338U 296a7f2277SNikita Danilov #define HW_ATL_RPC_STATE_ADR 0x033CU 306a7f2277SNikita Danilov 310c58c35fSIgor Russkikh #define HW_ATL_MPI_FW_VERSION 0x18 3298c4c201SDavid VomLehn #define HW_ATL_MPI_CONTROL_ADR 0x0368U 3398c4c201SDavid VomLehn #define HW_ATL_MPI_STATE_ADR 0x036CU 3498c4c201SDavid VomLehn 3598c4c201SDavid VomLehn #define HW_ATL_MPI_STATE_MSK 0x00FFU 3698c4c201SDavid VomLehn #define HW_ATL_MPI_STATE_SHIFT 0U 3744e00dd8SIgor Russkikh #define HW_ATL_MPI_SPEED_MSK 0x00FF0000U 3898c4c201SDavid VomLehn #define HW_ATL_MPI_SPEED_SHIFT 16U 3944e00dd8SIgor Russkikh #define HW_ATL_MPI_DIRTY_WAKE_MSK 0x02000000U 4098c4c201SDavid VomLehn 41c8c82eb3SIgor Russkikh #define HW_ATL_MPI_DAISY_CHAIN_STATUS 0x704 42c8c82eb3SIgor Russkikh #define HW_ATL_MPI_BOOT_EXIT_CODE 0x388 43c8c82eb3SIgor Russkikh 44c8c82eb3SIgor Russkikh #define HW_ATL_MAC_PHY_CONTROL 0x4000 45c8c82eb3SIgor Russkikh #define HW_ATL_MAC_PHY_MPI_RESET_BIT 0x1D 46c8c82eb3SIgor Russkikh 470c58c35fSIgor Russkikh #define HW_ATL_FW_VER_1X 0x01050006U 48a57d3929SIgor Russkikh #define HW_ATL_FW_VER_2X 0x02000000U 49a57d3929SIgor Russkikh #define HW_ATL_FW_VER_3X 0x03000000U 500c58c35fSIgor Russkikh 51c8c82eb3SIgor Russkikh #define FORCE_FLASHLESS 0 52c8c82eb3SIgor Russkikh 530c58c35fSIgor Russkikh static int hw_atl_utils_ver_match(u32 ver_expected, u32 ver_actual); 54e9157848SNikita Danilov 55cce96d18SIgor Russkikh static int hw_atl_utils_mpi_set_state(struct aq_hw_s *self, 56cce96d18SIgor Russkikh enum hal_atl_utils_fw_state_e state); 570c58c35fSIgor Russkikh 586a7f2277SNikita Danilov static u32 hw_atl_utils_get_mpi_mbox_tid(struct aq_hw_s *self); 596a7f2277SNikita Danilov static u32 hw_atl_utils_mpi_get_state(struct aq_hw_s *self); 606a7f2277SNikita Danilov static u32 hw_atl_utils_mif_cmd_get(struct aq_hw_s *self); 616a7f2277SNikita Danilov static u32 hw_atl_utils_mif_addr_get(struct aq_hw_s *self); 626a7f2277SNikita Danilov static u32 hw_atl_utils_rpc_state_get(struct aq_hw_s *self); 636a7f2277SNikita Danilov 640c58c35fSIgor Russkikh int hw_atl_utils_initfw(struct aq_hw_s *self, const struct aq_fw_ops **fw_ops) 650c58c35fSIgor Russkikh { 660c58c35fSIgor Russkikh int err = 0; 670c58c35fSIgor Russkikh 68c8c82eb3SIgor Russkikh err = hw_atl_utils_soft_reset(self); 69c8c82eb3SIgor Russkikh if (err) 70c8c82eb3SIgor Russkikh return err; 71c8c82eb3SIgor Russkikh 720c58c35fSIgor Russkikh hw_atl_utils_hw_chip_features_init(self, 730c58c35fSIgor Russkikh &self->chip_features); 740c58c35fSIgor Russkikh 750c58c35fSIgor Russkikh hw_atl_utils_get_fw_version(self, &self->fw_ver_actual); 760c58c35fSIgor Russkikh 77c8c82eb3SIgor Russkikh if (hw_atl_utils_ver_match(HW_ATL_FW_VER_1X, 78c8c82eb3SIgor Russkikh self->fw_ver_actual) == 0) { 790c58c35fSIgor Russkikh *fw_ops = &aq_fw_1x_ops; 80c8c82eb3SIgor Russkikh } else if (hw_atl_utils_ver_match(HW_ATL_FW_VER_2X, 81c8c82eb3SIgor Russkikh self->fw_ver_actual) == 0) { 82a57d3929SIgor Russkikh *fw_ops = &aq_fw_2x_ops; 83c8c82eb3SIgor Russkikh } else if (hw_atl_utils_ver_match(HW_ATL_FW_VER_3X, 84c8c82eb3SIgor Russkikh self->fw_ver_actual) == 0) { 85a57d3929SIgor Russkikh *fw_ops = &aq_fw_2x_ops; 86c8c82eb3SIgor Russkikh } else { 870c58c35fSIgor Russkikh aq_pr_err("Bad FW version detected: %x\n", 880c58c35fSIgor Russkikh self->fw_ver_actual); 890c58c35fSIgor Russkikh return -EOPNOTSUPP; 900c58c35fSIgor Russkikh } 910c58c35fSIgor Russkikh self->aq_fw_ops = *fw_ops; 920c58c35fSIgor Russkikh err = self->aq_fw_ops->init(self); 930c58c35fSIgor Russkikh return err; 940c58c35fSIgor Russkikh } 950c58c35fSIgor Russkikh 96c8c82eb3SIgor Russkikh static int hw_atl_utils_soft_reset_flb(struct aq_hw_s *self) 97c8c82eb3SIgor Russkikh { 981bf9a752SIgor Russkikh u32 gsr, val; 99c8c82eb3SIgor Russkikh int k = 0; 100c8c82eb3SIgor Russkikh 101c8c82eb3SIgor Russkikh aq_hw_write_reg(self, 0x404, 0x40e1); 102c8c82eb3SIgor Russkikh AQ_HW_SLEEP(50); 103c8c82eb3SIgor Russkikh 104c8c82eb3SIgor Russkikh /* Cleanup SPI */ 1051bf9a752SIgor Russkikh val = aq_hw_read_reg(self, 0x53C); 1061bf9a752SIgor Russkikh aq_hw_write_reg(self, 0x53C, val | 0x10); 107c8c82eb3SIgor Russkikh 108c8c82eb3SIgor Russkikh gsr = aq_hw_read_reg(self, HW_ATL_GLB_SOFT_RES_ADR); 109c8c82eb3SIgor Russkikh aq_hw_write_reg(self, HW_ATL_GLB_SOFT_RES_ADR, (gsr & 0xBFFF) | 0x8000); 110c8c82eb3SIgor Russkikh 111c8c82eb3SIgor Russkikh /* Kickstart MAC */ 112c8c82eb3SIgor Russkikh aq_hw_write_reg(self, 0x404, 0x80e0); 113c8c82eb3SIgor Russkikh aq_hw_write_reg(self, 0x32a8, 0x0); 114c8c82eb3SIgor Russkikh aq_hw_write_reg(self, 0x520, 0x1); 1151bf9a752SIgor Russkikh 1161bf9a752SIgor Russkikh /* Reset SPI again because of possible interrupted SPI burst */ 1171bf9a752SIgor Russkikh val = aq_hw_read_reg(self, 0x53C); 1181bf9a752SIgor Russkikh aq_hw_write_reg(self, 0x53C, val | 0x10); 119c8c82eb3SIgor Russkikh AQ_HW_SLEEP(10); 1201bf9a752SIgor Russkikh /* Clear SPI reset state */ 1211bf9a752SIgor Russkikh aq_hw_write_reg(self, 0x53C, val & ~0x10); 1221bf9a752SIgor Russkikh 123c8c82eb3SIgor Russkikh aq_hw_write_reg(self, 0x404, 0x180e0); 124c8c82eb3SIgor Russkikh 125c8c82eb3SIgor Russkikh for (k = 0; k < 1000; k++) { 126c8c82eb3SIgor Russkikh u32 flb_status = aq_hw_read_reg(self, 127c8c82eb3SIgor Russkikh HW_ATL_MPI_DAISY_CHAIN_STATUS); 128c8c82eb3SIgor Russkikh 129c8c82eb3SIgor Russkikh flb_status = flb_status & 0x10; 130c8c82eb3SIgor Russkikh if (flb_status) 131c8c82eb3SIgor Russkikh break; 132c8c82eb3SIgor Russkikh AQ_HW_SLEEP(10); 133c8c82eb3SIgor Russkikh } 134c8c82eb3SIgor Russkikh if (k == 1000) { 135c8c82eb3SIgor Russkikh aq_pr_err("MAC kickstart failed\n"); 136c8c82eb3SIgor Russkikh return -EIO; 137c8c82eb3SIgor Russkikh } 138c8c82eb3SIgor Russkikh 139c8c82eb3SIgor Russkikh /* FW reset */ 140c8c82eb3SIgor Russkikh aq_hw_write_reg(self, 0x404, 0x80e0); 141c8c82eb3SIgor Russkikh AQ_HW_SLEEP(50); 142c8c82eb3SIgor Russkikh aq_hw_write_reg(self, 0x3a0, 0x1); 143c8c82eb3SIgor Russkikh 144c8c82eb3SIgor Russkikh /* Kickstart PHY - skipped */ 145c8c82eb3SIgor Russkikh 146c8c82eb3SIgor Russkikh /* Global software reset*/ 147c8c82eb3SIgor Russkikh hw_atl_rx_rx_reg_res_dis_set(self, 0U); 148c8c82eb3SIgor Russkikh hw_atl_tx_tx_reg_res_dis_set(self, 0U); 149c8c82eb3SIgor Russkikh aq_hw_write_reg_bit(self, HW_ATL_MAC_PHY_CONTROL, 150c8c82eb3SIgor Russkikh BIT(HW_ATL_MAC_PHY_MPI_RESET_BIT), 151c8c82eb3SIgor Russkikh HW_ATL_MAC_PHY_MPI_RESET_BIT, 0x0); 152c8c82eb3SIgor Russkikh gsr = aq_hw_read_reg(self, HW_ATL_GLB_SOFT_RES_ADR); 153c8c82eb3SIgor Russkikh aq_hw_write_reg(self, HW_ATL_GLB_SOFT_RES_ADR, (gsr & 0xBFFF) | 0x8000); 154c8c82eb3SIgor Russkikh 155c8c82eb3SIgor Russkikh for (k = 0; k < 1000; k++) { 156c8c82eb3SIgor Russkikh u32 fw_state = aq_hw_read_reg(self, HW_ATL_MPI_FW_VERSION); 157c8c82eb3SIgor Russkikh 158c8c82eb3SIgor Russkikh if (fw_state) 159c8c82eb3SIgor Russkikh break; 160c8c82eb3SIgor Russkikh AQ_HW_SLEEP(10); 161c8c82eb3SIgor Russkikh } 162c8c82eb3SIgor Russkikh if (k == 1000) { 163c8c82eb3SIgor Russkikh aq_pr_err("FW kickstart failed\n"); 164c8c82eb3SIgor Russkikh return -EIO; 165c8c82eb3SIgor Russkikh } 166d0f0fb25SIgor Russkikh /* Old FW requires fixed delay after init */ 167d0f0fb25SIgor Russkikh AQ_HW_SLEEP(15); 168c8c82eb3SIgor Russkikh 169c8c82eb3SIgor Russkikh return 0; 170c8c82eb3SIgor Russkikh } 171c8c82eb3SIgor Russkikh 172c8c82eb3SIgor Russkikh static int hw_atl_utils_soft_reset_rbl(struct aq_hw_s *self) 173c8c82eb3SIgor Russkikh { 1741bf9a752SIgor Russkikh u32 gsr, val, rbl_status; 175c8c82eb3SIgor Russkikh int k; 176c8c82eb3SIgor Russkikh 177c8c82eb3SIgor Russkikh aq_hw_write_reg(self, 0x404, 0x40e1); 178c8c82eb3SIgor Russkikh aq_hw_write_reg(self, 0x3a0, 0x1); 179c8c82eb3SIgor Russkikh aq_hw_write_reg(self, 0x32a8, 0x0); 180c8c82eb3SIgor Russkikh 181c8c82eb3SIgor Russkikh /* Alter RBL status */ 182c8c82eb3SIgor Russkikh aq_hw_write_reg(self, 0x388, 0xDEAD); 183c8c82eb3SIgor Russkikh 1841bf9a752SIgor Russkikh /* Cleanup SPI */ 1851bf9a752SIgor Russkikh val = aq_hw_read_reg(self, 0x53C); 1861bf9a752SIgor Russkikh aq_hw_write_reg(self, 0x53C, val | 0x10); 1871bf9a752SIgor Russkikh 188c8c82eb3SIgor Russkikh /* Global software reset*/ 189c8c82eb3SIgor Russkikh hw_atl_rx_rx_reg_res_dis_set(self, 0U); 190c8c82eb3SIgor Russkikh hw_atl_tx_tx_reg_res_dis_set(self, 0U); 191c8c82eb3SIgor Russkikh aq_hw_write_reg_bit(self, HW_ATL_MAC_PHY_CONTROL, 192c8c82eb3SIgor Russkikh BIT(HW_ATL_MAC_PHY_MPI_RESET_BIT), 193c8c82eb3SIgor Russkikh HW_ATL_MAC_PHY_MPI_RESET_BIT, 0x0); 194c8c82eb3SIgor Russkikh gsr = aq_hw_read_reg(self, HW_ATL_GLB_SOFT_RES_ADR); 195c8c82eb3SIgor Russkikh aq_hw_write_reg(self, HW_ATL_GLB_SOFT_RES_ADR, 196c8c82eb3SIgor Russkikh (gsr & 0xFFFFBFFF) | 0x8000); 197c8c82eb3SIgor Russkikh 198c8c82eb3SIgor Russkikh if (FORCE_FLASHLESS) 199c8c82eb3SIgor Russkikh aq_hw_write_reg(self, 0x534, 0x0); 200c8c82eb3SIgor Russkikh 201c8c82eb3SIgor Russkikh aq_hw_write_reg(self, 0x404, 0x40e0); 202c8c82eb3SIgor Russkikh 203c8c82eb3SIgor Russkikh /* Wait for RBL boot */ 204c8c82eb3SIgor Russkikh for (k = 0; k < 1000; k++) { 205c8c82eb3SIgor Russkikh rbl_status = aq_hw_read_reg(self, 0x388) & 0xFFFF; 206c8c82eb3SIgor Russkikh if (rbl_status && rbl_status != 0xDEAD) 207c8c82eb3SIgor Russkikh break; 208c8c82eb3SIgor Russkikh AQ_HW_SLEEP(10); 209c8c82eb3SIgor Russkikh } 210c8c82eb3SIgor Russkikh if (!rbl_status || rbl_status == 0xDEAD) { 211c8c82eb3SIgor Russkikh aq_pr_err("RBL Restart failed"); 212c8c82eb3SIgor Russkikh return -EIO; 213c8c82eb3SIgor Russkikh } 214c8c82eb3SIgor Russkikh 215c8c82eb3SIgor Russkikh /* Restore NVR */ 216c8c82eb3SIgor Russkikh if (FORCE_FLASHLESS) 217c8c82eb3SIgor Russkikh aq_hw_write_reg(self, 0x534, 0xA0); 218c8c82eb3SIgor Russkikh 219c8c82eb3SIgor Russkikh if (rbl_status == 0xF1A7) { 220c8c82eb3SIgor Russkikh aq_pr_err("No FW detected. Dynamic FW load not implemented\n"); 221c8c82eb3SIgor Russkikh return -ENOTSUPP; 222c8c82eb3SIgor Russkikh } 223c8c82eb3SIgor Russkikh 224c8c82eb3SIgor Russkikh for (k = 0; k < 1000; k++) { 225c8c82eb3SIgor Russkikh u32 fw_state = aq_hw_read_reg(self, HW_ATL_MPI_FW_VERSION); 226c8c82eb3SIgor Russkikh 227c8c82eb3SIgor Russkikh if (fw_state) 228c8c82eb3SIgor Russkikh break; 229c8c82eb3SIgor Russkikh AQ_HW_SLEEP(10); 230c8c82eb3SIgor Russkikh } 231c8c82eb3SIgor Russkikh if (k == 1000) { 232c8c82eb3SIgor Russkikh aq_pr_err("FW kickstart failed\n"); 233c8c82eb3SIgor Russkikh return -EIO; 234c8c82eb3SIgor Russkikh } 235d0f0fb25SIgor Russkikh /* Old FW requires fixed delay after init */ 236d0f0fb25SIgor Russkikh AQ_HW_SLEEP(15); 237c8c82eb3SIgor Russkikh 238c8c82eb3SIgor Russkikh return 0; 239c8c82eb3SIgor Russkikh } 240c8c82eb3SIgor Russkikh 241c8c82eb3SIgor Russkikh int hw_atl_utils_soft_reset(struct aq_hw_s *self) 242c8c82eb3SIgor Russkikh { 243c8c82eb3SIgor Russkikh int k; 244c8c82eb3SIgor Russkikh u32 boot_exit_code = 0; 2456a7f2277SNikita Danilov u32 val; 246c8c82eb3SIgor Russkikh 247c8c82eb3SIgor Russkikh for (k = 0; k < 1000; ++k) { 248c8c82eb3SIgor Russkikh u32 flb_status = aq_hw_read_reg(self, 249c8c82eb3SIgor Russkikh HW_ATL_MPI_DAISY_CHAIN_STATUS); 250c8c82eb3SIgor Russkikh boot_exit_code = aq_hw_read_reg(self, 251c8c82eb3SIgor Russkikh HW_ATL_MPI_BOOT_EXIT_CODE); 252c8c82eb3SIgor Russkikh if (flb_status != 0x06000000 || boot_exit_code != 0) 253c8c82eb3SIgor Russkikh break; 254c8c82eb3SIgor Russkikh } 255c8c82eb3SIgor Russkikh 256c8c82eb3SIgor Russkikh if (k == 1000) { 257c8c82eb3SIgor Russkikh aq_pr_err("Neither RBL nor FLB firmware started\n"); 258c8c82eb3SIgor Russkikh return -EOPNOTSUPP; 259c8c82eb3SIgor Russkikh } 260c8c82eb3SIgor Russkikh 261c8c82eb3SIgor Russkikh self->rbl_enabled = (boot_exit_code != 0); 262c8c82eb3SIgor Russkikh 263cce96d18SIgor Russkikh /* FW 1.x may bootup in an invalid POWER state (WOL feature). 264cce96d18SIgor Russkikh * We should work around this by forcing its state back to DEINIT 265cce96d18SIgor Russkikh */ 266cce96d18SIgor Russkikh if (!hw_atl_utils_ver_match(HW_ATL_FW_VER_1X, 267cce96d18SIgor Russkikh aq_hw_read_reg(self, 268cce96d18SIgor Russkikh HW_ATL_MPI_FW_VERSION))) { 269cce96d18SIgor Russkikh int err = 0; 270cce96d18SIgor Russkikh 271cce96d18SIgor Russkikh hw_atl_utils_mpi_set_state(self, MPI_DEINIT); 2726a7f2277SNikita Danilov err = readx_poll_timeout_atomic(hw_atl_utils_mpi_get_state, 2736a7f2277SNikita Danilov self, val, 2746a7f2277SNikita Danilov (val & HW_ATL_MPI_STATE_MSK) == 2756a7f2277SNikita Danilov MPI_DEINIT, 2766a7f2277SNikita Danilov 10, 10000U); 2774e3c7c00SYueHaibing if (err) 2784e3c7c00SYueHaibing return err; 279cce96d18SIgor Russkikh } 280cce96d18SIgor Russkikh 281c8c82eb3SIgor Russkikh if (self->rbl_enabled) 282c8c82eb3SIgor Russkikh return hw_atl_utils_soft_reset_rbl(self); 283c8c82eb3SIgor Russkikh else 284c8c82eb3SIgor Russkikh return hw_atl_utils_soft_reset_flb(self); 285c8c82eb3SIgor Russkikh } 286c8c82eb3SIgor Russkikh 287a57d3929SIgor Russkikh int hw_atl_utils_fw_downld_dwords(struct aq_hw_s *self, u32 a, 28898c4c201SDavid VomLehn u32 *p, u32 cnt) 28998c4c201SDavid VomLehn { 29098c4c201SDavid VomLehn int err = 0; 2916a7f2277SNikita Danilov u32 val; 29298c4c201SDavid VomLehn 2936a7f2277SNikita Danilov err = readx_poll_timeout_atomic(hw_atl_sem_ram_get, 2946a7f2277SNikita Danilov self, val, val == 1U, 29598c4c201SDavid VomLehn 1U, 10000U); 29698c4c201SDavid VomLehn 29798c4c201SDavid VomLehn if (err < 0) { 29898c4c201SDavid VomLehn bool is_locked; 29998c4c201SDavid VomLehn 3008e1c072fSIgor Russkikh hw_atl_reg_glb_cpu_sem_set(self, 1U, HW_ATL_FW_SM_RAM); 3010b926d46SNikita Danilov is_locked = hw_atl_sem_ram_get(self); 30298c4c201SDavid VomLehn if (!is_locked) { 30398c4c201SDavid VomLehn err = -ETIME; 30498c4c201SDavid VomLehn goto err_exit; 30598c4c201SDavid VomLehn } 30698c4c201SDavid VomLehn } 30798c4c201SDavid VomLehn 30847203b34SIgor Russkikh aq_hw_write_reg(self, HW_ATL_MIF_ADDR, a); 30998c4c201SDavid VomLehn 31047203b34SIgor Russkikh for (++cnt; --cnt && !err;) { 31147203b34SIgor Russkikh aq_hw_write_reg(self, HW_ATL_MIF_CMD, 0x00008000U); 31298c4c201SDavid VomLehn 31347203b34SIgor Russkikh if (IS_CHIP_FEATURE(REVISION_B1)) 3146a7f2277SNikita Danilov err = readx_poll_timeout_atomic(hw_atl_utils_mif_addr_get, 3156a7f2277SNikita Danilov self, val, val != a, 3166a7f2277SNikita Danilov 1U, 1000U); 31747203b34SIgor Russkikh else 3186a7f2277SNikita Danilov err = readx_poll_timeout_atomic(hw_atl_utils_mif_cmd_get, 3196a7f2277SNikita Danilov self, val, 3206a7f2277SNikita Danilov !(val & 0x100), 3216a7f2277SNikita Danilov 1U, 1000U); 32298c4c201SDavid VomLehn 32347203b34SIgor Russkikh *(p++) = aq_hw_read_reg(self, HW_ATL_MIF_VAL); 32447203b34SIgor Russkikh a += 4; 32598c4c201SDavid VomLehn } 32698c4c201SDavid VomLehn 3278e1c072fSIgor Russkikh hw_atl_reg_glb_cpu_sem_set(self, 1U, HW_ATL_FW_SM_RAM); 32898c4c201SDavid VomLehn 32998c4c201SDavid VomLehn err_exit: 33098c4c201SDavid VomLehn return err; 33198c4c201SDavid VomLehn } 33298c4c201SDavid VomLehn 33398c4c201SDavid VomLehn static int hw_atl_utils_fw_upload_dwords(struct aq_hw_s *self, u32 a, u32 *p, 33498c4c201SDavid VomLehn u32 cnt) 33598c4c201SDavid VomLehn { 3366a7f2277SNikita Danilov u32 val; 33798c4c201SDavid VomLehn int err = 0; 33898c4c201SDavid VomLehn bool is_locked; 33998c4c201SDavid VomLehn 3400b926d46SNikita Danilov is_locked = hw_atl_sem_ram_get(self); 34198c4c201SDavid VomLehn if (!is_locked) { 34298c4c201SDavid VomLehn err = -ETIME; 34398c4c201SDavid VomLehn goto err_exit; 34498c4c201SDavid VomLehn } 3453ee5c887SYana Esina if (IS_CHIP_FEATURE(REVISION_B1)) { 3463ee5c887SYana Esina u32 offset = 0; 34798c4c201SDavid VomLehn 3483ee5c887SYana Esina for (; offset < cnt; ++offset) { 3493ee5c887SYana Esina aq_hw_write_reg(self, 0x328, p[offset]); 3503ee5c887SYana Esina aq_hw_write_reg(self, 0x32C, 3513ee5c887SYana Esina (0x80000000 | (0xFFFF & (offset * 4)))); 3523ee5c887SYana Esina hw_atl_mcp_up_force_intr_set(self, 1); 3533ee5c887SYana Esina /* 1000 times by 10us = 10ms */ 3546a7f2277SNikita Danilov err = readx_poll_timeout_atomic(hw_atl_scrpad12_get, 3556a7f2277SNikita Danilov self, val, 3566a7f2277SNikita Danilov (val & 0xF0000000) == 3573ee5c887SYana Esina 0x80000000, 3586a7f2277SNikita Danilov 10U, 10000U); 3593ee5c887SYana Esina } 3603ee5c887SYana Esina } else { 3613ee5c887SYana Esina u32 offset = 0; 36298c4c201SDavid VomLehn 3633ee5c887SYana Esina aq_hw_write_reg(self, 0x208, a); 36498c4c201SDavid VomLehn 3653ee5c887SYana Esina for (; offset < cnt; ++offset) { 3663ee5c887SYana Esina aq_hw_write_reg(self, 0x20C, p[offset]); 3673ee5c887SYana Esina aq_hw_write_reg(self, 0x200, 0xC000); 36898c4c201SDavid VomLehn 3696a7f2277SNikita Danilov err = readx_poll_timeout_atomic(hw_atl_utils_mif_cmd_get, 3706a7f2277SNikita Danilov self, val, 3716a7f2277SNikita Danilov (val & 0x100) == 0, 3726a7f2277SNikita Danilov 1000U, 10000U); 37398c4c201SDavid VomLehn } 37498c4c201SDavid VomLehn } 37598c4c201SDavid VomLehn 3768e1c072fSIgor Russkikh hw_atl_reg_glb_cpu_sem_set(self, 1U, HW_ATL_FW_SM_RAM); 37798c4c201SDavid VomLehn 37898c4c201SDavid VomLehn err_exit: 37998c4c201SDavid VomLehn return err; 38098c4c201SDavid VomLehn } 38198c4c201SDavid VomLehn 38298c4c201SDavid VomLehn static int hw_atl_utils_ver_match(u32 ver_expected, u32 ver_actual) 38398c4c201SDavid VomLehn { 38498c4c201SDavid VomLehn int err = 0; 38598c4c201SDavid VomLehn const u32 dw_major_mask = 0xff000000U; 38698c4c201SDavid VomLehn const u32 dw_minor_mask = 0x00ffffffU; 38798c4c201SDavid VomLehn 38898c4c201SDavid VomLehn err = (dw_major_mask & (ver_expected ^ ver_actual)) ? -EOPNOTSUPP : 0; 38998c4c201SDavid VomLehn if (err < 0) 39098c4c201SDavid VomLehn goto err_exit; 39198c4c201SDavid VomLehn err = ((dw_minor_mask & ver_expected) > (dw_minor_mask & ver_actual)) ? 39298c4c201SDavid VomLehn -EOPNOTSUPP : 0; 39398c4c201SDavid VomLehn err_exit: 39498c4c201SDavid VomLehn return err; 39598c4c201SDavid VomLehn } 39698c4c201SDavid VomLehn 39798c4c201SDavid VomLehn static int hw_atl_utils_init_ucp(struct aq_hw_s *self, 3984cbc9f92SIgor Russkikh const struct aq_hw_caps_s *aq_hw_caps) 39998c4c201SDavid VomLehn { 40098c4c201SDavid VomLehn int err = 0; 40198c4c201SDavid VomLehn 40298c4c201SDavid VomLehn if (!aq_hw_read_reg(self, 0x370U)) { 40398c4c201SDavid VomLehn unsigned int rnd = 0U; 40498c4c201SDavid VomLehn unsigned int ucp_0x370 = 0U; 40598c4c201SDavid VomLehn 40698c4c201SDavid VomLehn get_random_bytes(&rnd, sizeof(unsigned int)); 40798c4c201SDavid VomLehn 40898c4c201SDavid VomLehn ucp_0x370 = 0x02020202U | (0xFEFEFEFEU & rnd); 40998c4c201SDavid VomLehn aq_hw_write_reg(self, HW_ATL_UCP_0X370_REG, ucp_0x370); 41098c4c201SDavid VomLehn } 41198c4c201SDavid VomLehn 4128e1c072fSIgor Russkikh hw_atl_reg_glb_cpu_scratch_scp_set(self, 0x00000000U, 25U); 41398c4c201SDavid VomLehn 41498c4c201SDavid VomLehn /* check 10 times by 1ms */ 4156a7f2277SNikita Danilov err = readx_poll_timeout_atomic(hw_atl_scrpad25_get, 4166a7f2277SNikita Danilov self, self->mbox_addr, 4176a7f2277SNikita Danilov self->mbox_addr != 0U, 4186a7f2277SNikita Danilov 1000U, 10000U); 41998c4c201SDavid VomLehn 42098c4c201SDavid VomLehn return err; 42198c4c201SDavid VomLehn } 42298c4c201SDavid VomLehn 42398c4c201SDavid VomLehn struct aq_hw_atl_utils_fw_rpc_tid_s { 42498c4c201SDavid VomLehn union { 42598c4c201SDavid VomLehn u32 val; 42698c4c201SDavid VomLehn struct { 42798c4c201SDavid VomLehn u16 tid; 42898c4c201SDavid VomLehn u16 len; 42998c4c201SDavid VomLehn }; 43098c4c201SDavid VomLehn }; 43198c4c201SDavid VomLehn }; 43298c4c201SDavid VomLehn 43398c4c201SDavid VomLehn #define hw_atl_utils_fw_rpc_init(_H_) hw_atl_utils_fw_rpc_wait(_H_, NULL) 43498c4c201SDavid VomLehn 4353ee5c887SYana Esina int hw_atl_utils_fw_rpc_call(struct aq_hw_s *self, unsigned int rpc_size) 43698c4c201SDavid VomLehn { 43798c4c201SDavid VomLehn int err = 0; 43898c4c201SDavid VomLehn struct aq_hw_atl_utils_fw_rpc_tid_s sw; 43998c4c201SDavid VomLehn 44098c4c201SDavid VomLehn if (!IS_CHIP_FEATURE(MIPS)) { 44198c4c201SDavid VomLehn err = -1; 44298c4c201SDavid VomLehn goto err_exit; 44398c4c201SDavid VomLehn } 4441a713f87SIgor Russkikh err = hw_atl_utils_fw_upload_dwords(self, self->rpc_addr, 4451a713f87SIgor Russkikh (u32 *)(void *)&self->rpc, 44698c4c201SDavid VomLehn (rpc_size + sizeof(u32) - 44798c4c201SDavid VomLehn sizeof(u8)) / sizeof(u32)); 44898c4c201SDavid VomLehn if (err < 0) 44998c4c201SDavid VomLehn goto err_exit; 45098c4c201SDavid VomLehn 4511a713f87SIgor Russkikh sw.tid = 0xFFFFU & (++self->rpc_tid); 45298c4c201SDavid VomLehn sw.len = (u16)rpc_size; 45398c4c201SDavid VomLehn aq_hw_write_reg(self, HW_ATL_RPC_CONTROL_ADR, sw.val); 45498c4c201SDavid VomLehn 45598c4c201SDavid VomLehn err_exit: 45698c4c201SDavid VomLehn return err; 45798c4c201SDavid VomLehn } 45898c4c201SDavid VomLehn 4593ee5c887SYana Esina int hw_atl_utils_fw_rpc_wait(struct aq_hw_s *self, 4608f60f762SNikita Danilov struct hw_atl_utils_fw_rpc **rpc) 46198c4c201SDavid VomLehn { 46298c4c201SDavid VomLehn int err = 0; 46398c4c201SDavid VomLehn struct aq_hw_atl_utils_fw_rpc_tid_s sw; 46498c4c201SDavid VomLehn struct aq_hw_atl_utils_fw_rpc_tid_s fw; 46598c4c201SDavid VomLehn 46698c4c201SDavid VomLehn do { 46798c4c201SDavid VomLehn sw.val = aq_hw_read_reg(self, HW_ATL_RPC_CONTROL_ADR); 46898c4c201SDavid VomLehn 4691a713f87SIgor Russkikh self->rpc_tid = sw.tid; 47098c4c201SDavid VomLehn 4716a7f2277SNikita Danilov err = readx_poll_timeout_atomic(hw_atl_utils_rpc_state_get, 4726a7f2277SNikita Danilov self, fw.val, 4736a7f2277SNikita Danilov sw.tid == fw.tid, 4746a7f2277SNikita Danilov 1000U, 100000U); 47598c4c201SDavid VomLehn 47698c4c201SDavid VomLehn if (fw.len == 0xFFFFU) { 47798c4c201SDavid VomLehn err = hw_atl_utils_fw_rpc_call(self, sw.len); 47898c4c201SDavid VomLehn if (err < 0) 47998c4c201SDavid VomLehn goto err_exit; 48098c4c201SDavid VomLehn } 48198c4c201SDavid VomLehn } while (sw.tid != fw.tid || 0xFFFFU == fw.len); 48298c4c201SDavid VomLehn 48398c4c201SDavid VomLehn if (rpc) { 48498c4c201SDavid VomLehn if (fw.len) { 48598c4c201SDavid VomLehn err = 48698c4c201SDavid VomLehn hw_atl_utils_fw_downld_dwords(self, 4871a713f87SIgor Russkikh self->rpc_addr, 48898c4c201SDavid VomLehn (u32 *)(void *) 4891a713f87SIgor Russkikh &self->rpc, 49098c4c201SDavid VomLehn (fw.len + sizeof(u32) - 49198c4c201SDavid VomLehn sizeof(u8)) / 49298c4c201SDavid VomLehn sizeof(u32)); 49398c4c201SDavid VomLehn if (err < 0) 49498c4c201SDavid VomLehn goto err_exit; 49598c4c201SDavid VomLehn } 49698c4c201SDavid VomLehn 4971a713f87SIgor Russkikh *rpc = &self->rpc; 49898c4c201SDavid VomLehn } 49998c4c201SDavid VomLehn 50098c4c201SDavid VomLehn err_exit: 50198c4c201SDavid VomLehn return err; 50298c4c201SDavid VomLehn } 50398c4c201SDavid VomLehn 5041a713f87SIgor Russkikh static int hw_atl_utils_mpi_create(struct aq_hw_s *self) 50598c4c201SDavid VomLehn { 50698c4c201SDavid VomLehn int err = 0; 50798c4c201SDavid VomLehn 5081a713f87SIgor Russkikh err = hw_atl_utils_init_ucp(self, self->aq_nic_cfg->aq_hw_caps); 50998c4c201SDavid VomLehn if (err < 0) 51098c4c201SDavid VomLehn goto err_exit; 51198c4c201SDavid VomLehn 51298c4c201SDavid VomLehn err = hw_atl_utils_fw_rpc_init(self); 51398c4c201SDavid VomLehn if (err < 0) 51498c4c201SDavid VomLehn goto err_exit; 51598c4c201SDavid VomLehn 51698c4c201SDavid VomLehn err_exit: 51798c4c201SDavid VomLehn return err; 51898c4c201SDavid VomLehn } 51998c4c201SDavid VomLehn 52065e665e6SIgor Russkikh int hw_atl_utils_mpi_read_mbox(struct aq_hw_s *self, 5218f60f762SNikita Danilov struct hw_atl_utils_mbox_header *pmbox) 52265e665e6SIgor Russkikh { 52365e665e6SIgor Russkikh return hw_atl_utils_fw_downld_dwords(self, 5241a713f87SIgor Russkikh self->mbox_addr, 52565e665e6SIgor Russkikh (u32 *)(void *)pmbox, 52665e665e6SIgor Russkikh sizeof(*pmbox) / sizeof(u32)); 52765e665e6SIgor Russkikh } 52865e665e6SIgor Russkikh 52998c4c201SDavid VomLehn void hw_atl_utils_mpi_read_stats(struct aq_hw_s *self, 5308f60f762SNikita Danilov struct hw_atl_utils_mbox *pmbox) 53198c4c201SDavid VomLehn { 53298c4c201SDavid VomLehn int err = 0; 53398c4c201SDavid VomLehn 53498c4c201SDavid VomLehn err = hw_atl_utils_fw_downld_dwords(self, 5351a713f87SIgor Russkikh self->mbox_addr, 53698c4c201SDavid VomLehn (u32 *)(void *)pmbox, 53798c4c201SDavid VomLehn sizeof(*pmbox) / sizeof(u32)); 53898c4c201SDavid VomLehn if (err < 0) 53998c4c201SDavid VomLehn goto err_exit; 54098c4c201SDavid VomLehn 54198c4c201SDavid VomLehn if (IS_CHIP_FEATURE(REVISION_A0)) { 54298c4c201SDavid VomLehn unsigned int mtu = self->aq_nic_cfg ? 54398c4c201SDavid VomLehn self->aq_nic_cfg->mtu : 1514U; 54498c4c201SDavid VomLehn pmbox->stats.ubrc = pmbox->stats.uprc * mtu; 54598c4c201SDavid VomLehn pmbox->stats.ubtc = pmbox->stats.uptc * mtu; 5461a713f87SIgor Russkikh pmbox->stats.dpc = atomic_read(&self->dpc); 54798c4c201SDavid VomLehn } else { 5488e1c072fSIgor Russkikh pmbox->stats.dpc = hw_atl_reg_rx_dma_stat_counter7get(self); 54998c4c201SDavid VomLehn } 55098c4c201SDavid VomLehn 55198c4c201SDavid VomLehn err_exit:; 55298c4c201SDavid VomLehn } 55398c4c201SDavid VomLehn 554dfbd0749SWei Yongjun static int hw_atl_utils_mpi_set_speed(struct aq_hw_s *self, u32 speed) 55598c4c201SDavid VomLehn { 5560c58c35fSIgor Russkikh u32 val = aq_hw_read_reg(self, HW_ATL_MPI_CONTROL_ADR); 55798c4c201SDavid VomLehn 55844e00dd8SIgor Russkikh val = val & ~HW_ATL_MPI_SPEED_MSK; 55944e00dd8SIgor Russkikh val |= speed << HW_ATL_MPI_SPEED_SHIFT; 5600c58c35fSIgor Russkikh aq_hw_write_reg(self, HW_ATL_MPI_CONTROL_ADR, val); 56198c4c201SDavid VomLehn 56298c4c201SDavid VomLehn return 0; 56398c4c201SDavid VomLehn } 56498c4c201SDavid VomLehn 565dfbd0749SWei Yongjun static int hw_atl_utils_mpi_set_state(struct aq_hw_s *self, 56644e00dd8SIgor Russkikh enum hal_atl_utils_fw_state_e state) 56798c4c201SDavid VomLehn { 56898c4c201SDavid VomLehn int err = 0; 56998c4c201SDavid VomLehn u32 transaction_id = 0; 5708f60f762SNikita Danilov struct hw_atl_utils_mbox_header mbox; 57144e00dd8SIgor Russkikh u32 val = aq_hw_read_reg(self, HW_ATL_MPI_CONTROL_ADR); 57298c4c201SDavid VomLehn 57398c4c201SDavid VomLehn if (state == MPI_RESET) { 57465e665e6SIgor Russkikh hw_atl_utils_mpi_read_mbox(self, &mbox); 57598c4c201SDavid VomLehn 57665e665e6SIgor Russkikh transaction_id = mbox.transaction_id; 57798c4c201SDavid VomLehn 5786a7f2277SNikita Danilov err = readx_poll_timeout_atomic(hw_atl_utils_get_mpi_mbox_tid, 5796a7f2277SNikita Danilov self, mbox.transaction_id, 5806a7f2277SNikita Danilov transaction_id != 5816a7f2277SNikita Danilov mbox.transaction_id, 5826a7f2277SNikita Danilov 1000U, 100000U); 58398c4c201SDavid VomLehn if (err < 0) 58498c4c201SDavid VomLehn goto err_exit; 58598c4c201SDavid VomLehn } 58644e00dd8SIgor Russkikh /* On interface DEINIT we disable DW (raise bit) 58744e00dd8SIgor Russkikh * Otherwise enable DW (clear bit) 58844e00dd8SIgor Russkikh */ 58944e00dd8SIgor Russkikh if (state == MPI_DEINIT || state == MPI_POWER) 59044e00dd8SIgor Russkikh val |= HW_ATL_MPI_DIRTY_WAKE_MSK; 59144e00dd8SIgor Russkikh else 59244e00dd8SIgor Russkikh val &= ~HW_ATL_MPI_DIRTY_WAKE_MSK; 59398c4c201SDavid VomLehn 59444e00dd8SIgor Russkikh /* Set new state bits */ 59544e00dd8SIgor Russkikh val = val & ~HW_ATL_MPI_STATE_MSK; 59644e00dd8SIgor Russkikh val |= state & HW_ATL_MPI_STATE_MSK; 59798c4c201SDavid VomLehn 5980c58c35fSIgor Russkikh aq_hw_write_reg(self, HW_ATL_MPI_CONTROL_ADR, val); 59944e00dd8SIgor Russkikh err_exit: 60044e00dd8SIgor Russkikh return err; 6010c58c35fSIgor Russkikh } 6020c58c35fSIgor Russkikh 603bd8ed441SPavel Belous int hw_atl_utils_mpi_get_link_status(struct aq_hw_s *self) 60498c4c201SDavid VomLehn { 6050b926d46SNikita Danilov u32 cp0x036C = hw_atl_utils_mpi_get_state(self); 60698c4c201SDavid VomLehn u32 link_speed_mask = cp0x036C >> HW_ATL_MPI_SPEED_SHIFT; 607bd8ed441SPavel Belous struct aq_hw_link_status_s *link_status = &self->aq_link_status; 60898c4c201SDavid VomLehn 60998c4c201SDavid VomLehn if (!link_speed_mask) { 61098c4c201SDavid VomLehn link_status->mbps = 0U; 61198c4c201SDavid VomLehn } else { 61298c4c201SDavid VomLehn switch (link_speed_mask) { 61398c4c201SDavid VomLehn case HAL_ATLANTIC_RATE_10G: 61498c4c201SDavid VomLehn link_status->mbps = 10000U; 61598c4c201SDavid VomLehn break; 61698c4c201SDavid VomLehn 61798c4c201SDavid VomLehn case HAL_ATLANTIC_RATE_5G: 61898c4c201SDavid VomLehn case HAL_ATLANTIC_RATE_5GSR: 61998c4c201SDavid VomLehn link_status->mbps = 5000U; 62098c4c201SDavid VomLehn break; 62198c4c201SDavid VomLehn 62298c4c201SDavid VomLehn case HAL_ATLANTIC_RATE_2GS: 62398c4c201SDavid VomLehn link_status->mbps = 2500U; 62498c4c201SDavid VomLehn break; 62598c4c201SDavid VomLehn 62698c4c201SDavid VomLehn case HAL_ATLANTIC_RATE_1G: 62798c4c201SDavid VomLehn link_status->mbps = 1000U; 62898c4c201SDavid VomLehn break; 62998c4c201SDavid VomLehn 63098c4c201SDavid VomLehn case HAL_ATLANTIC_RATE_100M: 63198c4c201SDavid VomLehn link_status->mbps = 100U; 63298c4c201SDavid VomLehn break; 63398c4c201SDavid VomLehn 63498c4c201SDavid VomLehn default: 635a7bb1beaSIgor Russkikh return -EBUSY; 63698c4c201SDavid VomLehn } 63798c4c201SDavid VomLehn } 63898c4c201SDavid VomLehn 63998c4c201SDavid VomLehn return 0; 64098c4c201SDavid VomLehn } 64198c4c201SDavid VomLehn 64298c4c201SDavid VomLehn int hw_atl_utils_get_mac_permanent(struct aq_hw_s *self, 64398c4c201SDavid VomLehn u8 *mac) 64498c4c201SDavid VomLehn { 64598c4c201SDavid VomLehn int err = 0; 64698c4c201SDavid VomLehn u32 h = 0U; 64798c4c201SDavid VomLehn u32 l = 0U; 64898c4c201SDavid VomLehn u32 mac_addr[2]; 64998c4c201SDavid VomLehn 65098c4c201SDavid VomLehn if (!aq_hw_read_reg(self, HW_ATL_UCP_0X370_REG)) { 65198c4c201SDavid VomLehn unsigned int rnd = 0; 65298c4c201SDavid VomLehn unsigned int ucp_0x370 = 0; 65398c4c201SDavid VomLehn 65498c4c201SDavid VomLehn get_random_bytes(&rnd, sizeof(unsigned int)); 65598c4c201SDavid VomLehn 65698c4c201SDavid VomLehn ucp_0x370 = 0x02020202 | (0xFEFEFEFE & rnd); 65798c4c201SDavid VomLehn aq_hw_write_reg(self, HW_ATL_UCP_0X370_REG, ucp_0x370); 65898c4c201SDavid VomLehn } 65998c4c201SDavid VomLehn 66098c4c201SDavid VomLehn err = hw_atl_utils_fw_downld_dwords(self, 66198c4c201SDavid VomLehn aq_hw_read_reg(self, 0x00000374U) + 66298c4c201SDavid VomLehn (40U * 4U), 66398c4c201SDavid VomLehn mac_addr, 66408b5cf08SIgor Russkikh ARRAY_SIZE(mac_addr)); 66598c4c201SDavid VomLehn if (err < 0) { 66698c4c201SDavid VomLehn mac_addr[0] = 0U; 66798c4c201SDavid VomLehn mac_addr[1] = 0U; 66898c4c201SDavid VomLehn err = 0; 66998c4c201SDavid VomLehn } else { 67098c4c201SDavid VomLehn mac_addr[0] = __swab32(mac_addr[0]); 67198c4c201SDavid VomLehn mac_addr[1] = __swab32(mac_addr[1]); 67298c4c201SDavid VomLehn } 67398c4c201SDavid VomLehn 67498c4c201SDavid VomLehn ether_addr_copy(mac, (u8 *)mac_addr); 67598c4c201SDavid VomLehn 67698c4c201SDavid VomLehn if ((mac[0] & 0x01U) || ((mac[0] | mac[1] | mac[2]) == 0x00U)) { 67798c4c201SDavid VomLehn /* chip revision */ 678e9157848SNikita Danilov l = 0xE3000000U | 679e9157848SNikita Danilov (0xFFFFU & aq_hw_read_reg(self, HW_ATL_UCP_0X370_REG)) | 680e9157848SNikita Danilov (0x00 << 16); 68198c4c201SDavid VomLehn h = 0x8001300EU; 68298c4c201SDavid VomLehn 68398c4c201SDavid VomLehn mac[5] = (u8)(0xFFU & l); 68498c4c201SDavid VomLehn l >>= 8; 68598c4c201SDavid VomLehn mac[4] = (u8)(0xFFU & l); 68698c4c201SDavid VomLehn l >>= 8; 68798c4c201SDavid VomLehn mac[3] = (u8)(0xFFU & l); 68898c4c201SDavid VomLehn l >>= 8; 68998c4c201SDavid VomLehn mac[2] = (u8)(0xFFU & l); 69098c4c201SDavid VomLehn mac[1] = (u8)(0xFFU & h); 69198c4c201SDavid VomLehn h >>= 8; 69298c4c201SDavid VomLehn mac[0] = (u8)(0xFFU & h); 69398c4c201SDavid VomLehn } 69498c4c201SDavid VomLehn 69598c4c201SDavid VomLehn return err; 69698c4c201SDavid VomLehn } 69798c4c201SDavid VomLehn 69898c4c201SDavid VomLehn unsigned int hw_atl_utils_mbps_2_speed_index(unsigned int mbps) 69998c4c201SDavid VomLehn { 70098c4c201SDavid VomLehn unsigned int ret = 0U; 70198c4c201SDavid VomLehn 70298c4c201SDavid VomLehn switch (mbps) { 70398c4c201SDavid VomLehn case 100U: 70498c4c201SDavid VomLehn ret = 5U; 70598c4c201SDavid VomLehn break; 70698c4c201SDavid VomLehn 70798c4c201SDavid VomLehn case 1000U: 70898c4c201SDavid VomLehn ret = 4U; 70998c4c201SDavid VomLehn break; 71098c4c201SDavid VomLehn 71198c4c201SDavid VomLehn case 2500U: 71298c4c201SDavid VomLehn ret = 3U; 71398c4c201SDavid VomLehn break; 71498c4c201SDavid VomLehn 71598c4c201SDavid VomLehn case 5000U: 71698c4c201SDavid VomLehn ret = 1U; 71798c4c201SDavid VomLehn break; 71898c4c201SDavid VomLehn 71998c4c201SDavid VomLehn case 10000U: 72098c4c201SDavid VomLehn ret = 0U; 72198c4c201SDavid VomLehn break; 72298c4c201SDavid VomLehn 72398c4c201SDavid VomLehn default: 72498c4c201SDavid VomLehn break; 72598c4c201SDavid VomLehn } 72698c4c201SDavid VomLehn return ret; 72798c4c201SDavid VomLehn } 72898c4c201SDavid VomLehn 72998c4c201SDavid VomLehn void hw_atl_utils_hw_chip_features_init(struct aq_hw_s *self, u32 *p) 73098c4c201SDavid VomLehn { 73198c4c201SDavid VomLehn u32 chip_features = 0U; 7328e1c072fSIgor Russkikh u32 val = hw_atl_reg_glb_mif_id_get(self); 73398c4c201SDavid VomLehn u32 mif_rev = val & 0xFFU; 73498c4c201SDavid VomLehn 73547203b34SIgor Russkikh if ((0xFU & mif_rev) == 1U) { 73647203b34SIgor Russkikh chip_features |= HAL_ATLANTIC_UTILS_CHIP_REVISION_A0 | 73798c4c201SDavid VomLehn HAL_ATLANTIC_UTILS_CHIP_MPI_AQ | 73898c4c201SDavid VomLehn HAL_ATLANTIC_UTILS_CHIP_MIPS; 73947203b34SIgor Russkikh } else if ((0xFU & mif_rev) == 2U) { 74047203b34SIgor Russkikh chip_features |= HAL_ATLANTIC_UTILS_CHIP_REVISION_B0 | 74147203b34SIgor Russkikh HAL_ATLANTIC_UTILS_CHIP_MPI_AQ | 74247203b34SIgor Russkikh HAL_ATLANTIC_UTILS_CHIP_MIPS | 74347203b34SIgor Russkikh HAL_ATLANTIC_UTILS_CHIP_TPO2 | 74447203b34SIgor Russkikh HAL_ATLANTIC_UTILS_CHIP_RPF2; 74547203b34SIgor Russkikh } else if ((0xFU & mif_rev) == 0xAU) { 74647203b34SIgor Russkikh chip_features |= HAL_ATLANTIC_UTILS_CHIP_REVISION_B1 | 74798c4c201SDavid VomLehn HAL_ATLANTIC_UTILS_CHIP_MPI_AQ | 74898c4c201SDavid VomLehn HAL_ATLANTIC_UTILS_CHIP_MIPS | 74998c4c201SDavid VomLehn HAL_ATLANTIC_UTILS_CHIP_TPO2 | 75098c4c201SDavid VomLehn HAL_ATLANTIC_UTILS_CHIP_RPF2; 75198c4c201SDavid VomLehn } 75298c4c201SDavid VomLehn 75398c4c201SDavid VomLehn *p = chip_features; 75498c4c201SDavid VomLehn } 75598c4c201SDavid VomLehn 75644e00dd8SIgor Russkikh static int hw_atl_fw1x_deinit(struct aq_hw_s *self) 75798c4c201SDavid VomLehn { 75844e00dd8SIgor Russkikh hw_atl_utils_mpi_set_speed(self, 0); 75944e00dd8SIgor Russkikh hw_atl_utils_mpi_set_state(self, MPI_DEINIT); 76098c4c201SDavid VomLehn return 0; 76198c4c201SDavid VomLehn } 76298c4c201SDavid VomLehn 76365e665e6SIgor Russkikh int hw_atl_utils_update_stats(struct aq_hw_s *self) 76465e665e6SIgor Russkikh { 7658f60f762SNikita Danilov struct hw_atl_utils_mbox mbox; 76665e665e6SIgor Russkikh 76765e665e6SIgor Russkikh hw_atl_utils_mpi_read_stats(self, &mbox); 76865e665e6SIgor Russkikh 7691a713f87SIgor Russkikh #define AQ_SDELTA(_N_) (self->curr_stats._N_ += \ 7701a713f87SIgor Russkikh mbox.stats._N_ - self->last_stats._N_) 7711a713f87SIgor Russkikh 772be08d839SIgor Russkikh if (self->aq_link_status.mbps) { 77365e665e6SIgor Russkikh AQ_SDELTA(uprc); 77465e665e6SIgor Russkikh AQ_SDELTA(mprc); 77565e665e6SIgor Russkikh AQ_SDELTA(bprc); 77665e665e6SIgor Russkikh AQ_SDELTA(erpt); 77765e665e6SIgor Russkikh 77865e665e6SIgor Russkikh AQ_SDELTA(uptc); 77965e665e6SIgor Russkikh AQ_SDELTA(mptc); 78065e665e6SIgor Russkikh AQ_SDELTA(bptc); 78165e665e6SIgor Russkikh AQ_SDELTA(erpr); 78265e665e6SIgor Russkikh 78365e665e6SIgor Russkikh AQ_SDELTA(ubrc); 78465e665e6SIgor Russkikh AQ_SDELTA(ubtc); 78565e665e6SIgor Russkikh AQ_SDELTA(mbrc); 78665e665e6SIgor Russkikh AQ_SDELTA(mbtc); 78765e665e6SIgor Russkikh AQ_SDELTA(bbrc); 78865e665e6SIgor Russkikh AQ_SDELTA(bbtc); 78965e665e6SIgor Russkikh AQ_SDELTA(dpc); 790be08d839SIgor Russkikh } 79165e665e6SIgor Russkikh #undef AQ_SDELTA 7928e1c072fSIgor Russkikh self->curr_stats.dma_pkt_rc = hw_atl_stats_rx_dma_good_pkt_counterlsw_get(self); 7938e1c072fSIgor Russkikh self->curr_stats.dma_pkt_tc = hw_atl_stats_tx_dma_good_pkt_counterlsw_get(self); 7948e1c072fSIgor Russkikh self->curr_stats.dma_oct_rc = hw_atl_stats_rx_dma_good_octet_counterlsw_get(self); 7958e1c072fSIgor Russkikh self->curr_stats.dma_oct_tc = hw_atl_stats_tx_dma_good_octet_counterlsw_get(self); 79665e665e6SIgor Russkikh 7971a713f87SIgor Russkikh memcpy(&self->last_stats, &mbox.stats, sizeof(mbox.stats)); 79865e665e6SIgor Russkikh 79965e665e6SIgor Russkikh return 0; 80065e665e6SIgor Russkikh } 80165e665e6SIgor Russkikh 802be08d839SIgor Russkikh struct aq_stats_s *hw_atl_utils_get_hw_stats(struct aq_hw_s *self) 80398c4c201SDavid VomLehn { 8041a713f87SIgor Russkikh return &self->curr_stats; 80598c4c201SDavid VomLehn } 80698c4c201SDavid VomLehn 80798c4c201SDavid VomLehn static const u32 hw_atl_utils_hw_mac_regs[] = { 80898c4c201SDavid VomLehn 0x00005580U, 0x00005590U, 0x000055B0U, 0x000055B4U, 80998c4c201SDavid VomLehn 0x000055C0U, 0x00005B00U, 0x00005B04U, 0x00005B08U, 81098c4c201SDavid VomLehn 0x00005B0CU, 0x00005B10U, 0x00005B14U, 0x00005B18U, 81198c4c201SDavid VomLehn 0x00005B1CU, 0x00005B20U, 0x00005B24U, 0x00005B28U, 81298c4c201SDavid VomLehn 0x00005B2CU, 0x00005B30U, 0x00005B34U, 0x00005B38U, 81398c4c201SDavid VomLehn 0x00005B3CU, 0x00005B40U, 0x00005B44U, 0x00005B48U, 81498c4c201SDavid VomLehn 0x00005B4CU, 0x00005B50U, 0x00005B54U, 0x00005B58U, 81598c4c201SDavid VomLehn 0x00005B5CU, 0x00005B60U, 0x00005B64U, 0x00005B68U, 81698c4c201SDavid VomLehn 0x00005B6CU, 0x00005B70U, 0x00005B74U, 0x00005B78U, 81798c4c201SDavid VomLehn 0x00005B7CU, 0x00007C00U, 0x00007C04U, 0x00007C08U, 81898c4c201SDavid VomLehn 0x00007C0CU, 0x00007C10U, 0x00007C14U, 0x00007C18U, 81998c4c201SDavid VomLehn 0x00007C1CU, 0x00007C20U, 0x00007C40U, 0x00007C44U, 82098c4c201SDavid VomLehn 0x00007C48U, 0x00007C4CU, 0x00007C50U, 0x00007C54U, 82198c4c201SDavid VomLehn 0x00007C58U, 0x00007C5CU, 0x00007C60U, 0x00007C80U, 82298c4c201SDavid VomLehn 0x00007C84U, 0x00007C88U, 0x00007C8CU, 0x00007C90U, 82398c4c201SDavid VomLehn 0x00007C94U, 0x00007C98U, 0x00007C9CU, 0x00007CA0U, 82498c4c201SDavid VomLehn 0x00007CC0U, 0x00007CC4U, 0x00007CC8U, 0x00007CCCU, 82598c4c201SDavid VomLehn 0x00007CD0U, 0x00007CD4U, 0x00007CD8U, 0x00007CDCU, 82698c4c201SDavid VomLehn 0x00007CE0U, 0x00000300U, 0x00000304U, 0x00000308U, 82798c4c201SDavid VomLehn 0x0000030cU, 0x00000310U, 0x00000314U, 0x00000318U, 82898c4c201SDavid VomLehn 0x0000031cU, 0x00000360U, 0x00000364U, 0x00000368U, 82998c4c201SDavid VomLehn 0x0000036cU, 0x00000370U, 0x00000374U, 0x00006900U, 83098c4c201SDavid VomLehn }; 83198c4c201SDavid VomLehn 83298c4c201SDavid VomLehn int hw_atl_utils_hw_get_regs(struct aq_hw_s *self, 8334cbc9f92SIgor Russkikh const struct aq_hw_caps_s *aq_hw_caps, 83498c4c201SDavid VomLehn u32 *regs_buff) 83598c4c201SDavid VomLehn { 83698c4c201SDavid VomLehn unsigned int i = 0U; 83798c4c201SDavid VomLehn 83898c4c201SDavid VomLehn for (i = 0; i < aq_hw_caps->mac_regs_count; i++) 83998c4c201SDavid VomLehn regs_buff[i] = aq_hw_read_reg(self, 84098c4c201SDavid VomLehn hw_atl_utils_hw_mac_regs[i]); 84198c4c201SDavid VomLehn return 0; 84298c4c201SDavid VomLehn } 84398c4c201SDavid VomLehn 84498c4c201SDavid VomLehn int hw_atl_utils_get_fw_version(struct aq_hw_s *self, u32 *fw_version) 84598c4c201SDavid VomLehn { 84698c4c201SDavid VomLehn *fw_version = aq_hw_read_reg(self, 0x18U); 84798c4c201SDavid VomLehn return 0; 84898c4c201SDavid VomLehn } 8490c58c35fSIgor Russkikh 850a0da96c0SYana Esina static int aq_fw1x_set_wol(struct aq_hw_s *self, bool wol_enabled, u8 *mac) 851a0da96c0SYana Esina { 8528f60f762SNikita Danilov struct hw_atl_utils_fw_rpc *prpc = NULL; 853a0da96c0SYana Esina unsigned int rpc_size = 0U; 854a0da96c0SYana Esina int err = 0; 855a0da96c0SYana Esina 856a0da96c0SYana Esina err = hw_atl_utils_fw_rpc_wait(self, &prpc); 857a0da96c0SYana Esina if (err < 0) 858a0da96c0SYana Esina goto err_exit; 859a0da96c0SYana Esina 860a0da96c0SYana Esina memset(prpc, 0, sizeof(*prpc)); 861a0da96c0SYana Esina 862a0da96c0SYana Esina if (wol_enabled) { 863a0da96c0SYana Esina rpc_size = sizeof(prpc->msg_id) + sizeof(prpc->msg_wol); 864a0da96c0SYana Esina 865a0da96c0SYana Esina prpc->msg_id = HAL_ATLANTIC_UTILS_FW_MSG_WOL_ADD; 866a0da96c0SYana Esina prpc->msg_wol.priority = 867a0da96c0SYana Esina HAL_ATLANTIC_UTILS_FW_MSG_WOL_PRIOR; 868a0da96c0SYana Esina prpc->msg_wol.pattern_id = 869a0da96c0SYana Esina HAL_ATLANTIC_UTILS_FW_MSG_WOL_PATTERN; 870a0da96c0SYana Esina prpc->msg_wol.wol_packet_type = 871a0da96c0SYana Esina HAL_ATLANTIC_UTILS_FW_MSG_WOL_MAG_PKT; 872a0da96c0SYana Esina 873a0da96c0SYana Esina ether_addr_copy((u8 *)&prpc->msg_wol.wol_pattern, mac); 874a0da96c0SYana Esina } else { 875a0da96c0SYana Esina rpc_size = sizeof(prpc->msg_id) + sizeof(prpc->msg_del_id); 876a0da96c0SYana Esina 877a0da96c0SYana Esina prpc->msg_id = HAL_ATLANTIC_UTILS_FW_MSG_WOL_DEL; 878a0da96c0SYana Esina prpc->msg_wol.pattern_id = 879a0da96c0SYana Esina HAL_ATLANTIC_UTILS_FW_MSG_WOL_PATTERN; 880a0da96c0SYana Esina } 881a0da96c0SYana Esina 882a0da96c0SYana Esina err = hw_atl_utils_fw_rpc_call(self, rpc_size); 883a0da96c0SYana Esina 884a0da96c0SYana Esina err_exit: 885a0da96c0SYana Esina return err; 886a0da96c0SYana Esina } 887a0da96c0SYana Esina 8883d5537f9SWei Yongjun static int aq_fw1x_set_power(struct aq_hw_s *self, unsigned int power_state, 889a0da96c0SYana Esina u8 *mac) 890a0da96c0SYana Esina { 8918f60f762SNikita Danilov struct hw_atl_utils_fw_rpc *prpc = NULL; 892a0da96c0SYana Esina unsigned int rpc_size = 0U; 893a0da96c0SYana Esina int err = 0; 894a0da96c0SYana Esina 895a0da96c0SYana Esina if (self->aq_nic_cfg->wol & AQ_NIC_WOL_ENABLED) { 896a0da96c0SYana Esina err = aq_fw1x_set_wol(self, 1, mac); 897a0da96c0SYana Esina 898a0da96c0SYana Esina if (err < 0) 899a0da96c0SYana Esina goto err_exit; 900a0da96c0SYana Esina 901a0da96c0SYana Esina rpc_size = sizeof(prpc->msg_id) + 902a0da96c0SYana Esina sizeof(prpc->msg_enable_wakeup); 903a0da96c0SYana Esina 904a0da96c0SYana Esina err = hw_atl_utils_fw_rpc_wait(self, &prpc); 905a0da96c0SYana Esina 906a0da96c0SYana Esina if (err < 0) 907a0da96c0SYana Esina goto err_exit; 908a0da96c0SYana Esina 909a0da96c0SYana Esina memset(prpc, 0, rpc_size); 910a0da96c0SYana Esina 911a0da96c0SYana Esina prpc->msg_id = HAL_ATLANTIC_UTILS_FW_MSG_ENABLE_WAKEUP; 912a0da96c0SYana Esina prpc->msg_enable_wakeup.pattern_mask = 0x00000002; 913a0da96c0SYana Esina 914a0da96c0SYana Esina err = hw_atl_utils_fw_rpc_call(self, rpc_size); 915a0da96c0SYana Esina if (err < 0) 916a0da96c0SYana Esina goto err_exit; 917a0da96c0SYana Esina } 918a0da96c0SYana Esina hw_atl_utils_mpi_set_speed(self, 0); 919a0da96c0SYana Esina hw_atl_utils_mpi_set_state(self, MPI_POWER); 920a0da96c0SYana Esina 921a0da96c0SYana Esina err_exit: 922a0da96c0SYana Esina return err; 923a0da96c0SYana Esina } 924a0da96c0SYana Esina 9256a7f2277SNikita Danilov static u32 hw_atl_utils_get_mpi_mbox_tid(struct aq_hw_s *self) 9266a7f2277SNikita Danilov { 9276a7f2277SNikita Danilov struct hw_atl_utils_mbox_header mbox; 9286a7f2277SNikita Danilov 9296a7f2277SNikita Danilov hw_atl_utils_mpi_read_mbox(self, &mbox); 9306a7f2277SNikita Danilov 9316a7f2277SNikita Danilov return mbox.transaction_id; 9326a7f2277SNikita Danilov } 9336a7f2277SNikita Danilov 9346a7f2277SNikita Danilov static u32 hw_atl_utils_mpi_get_state(struct aq_hw_s *self) 9356a7f2277SNikita Danilov { 9366a7f2277SNikita Danilov return aq_hw_read_reg(self, HW_ATL_MPI_STATE_ADR); 9376a7f2277SNikita Danilov } 9386a7f2277SNikita Danilov 9396a7f2277SNikita Danilov static u32 hw_atl_utils_mif_cmd_get(struct aq_hw_s *self) 9406a7f2277SNikita Danilov { 9416a7f2277SNikita Danilov return aq_hw_read_reg(self, HW_ATL_MIF_CMD); 9426a7f2277SNikita Danilov } 9436a7f2277SNikita Danilov 9446a7f2277SNikita Danilov static u32 hw_atl_utils_mif_addr_get(struct aq_hw_s *self) 9456a7f2277SNikita Danilov { 9466a7f2277SNikita Danilov return aq_hw_read_reg(self, HW_ATL_MIF_ADDR); 9476a7f2277SNikita Danilov } 9486a7f2277SNikita Danilov 9496a7f2277SNikita Danilov static u32 hw_atl_utils_rpc_state_get(struct aq_hw_s *self) 9506a7f2277SNikita Danilov { 9516a7f2277SNikita Danilov return aq_hw_read_reg(self, HW_ATL_RPC_STATE_ADR); 9526a7f2277SNikita Danilov } 9536a7f2277SNikita Danilov 9540c58c35fSIgor Russkikh const struct aq_fw_ops aq_fw_1x_ops = { 9550c58c35fSIgor Russkikh .init = hw_atl_utils_mpi_create, 95644e00dd8SIgor Russkikh .deinit = hw_atl_fw1x_deinit, 9570c58c35fSIgor Russkikh .reset = NULL, 9580c58c35fSIgor Russkikh .get_mac_permanent = hw_atl_utils_get_mac_permanent, 9590c58c35fSIgor Russkikh .set_link_speed = hw_atl_utils_mpi_set_speed, 9600c58c35fSIgor Russkikh .set_state = hw_atl_utils_mpi_set_state, 9610c58c35fSIgor Russkikh .update_link_status = hw_atl_utils_mpi_get_link_status, 9620c58c35fSIgor Russkikh .update_stats = hw_atl_utils_update_stats, 9638f894011SYana Esina .get_phy_temp = NULL, 964a0da96c0SYana Esina .set_power = aq_fw1x_set_power, 96592ab6407SYana Esina .set_eee_rate = NULL, 96692ab6407SYana Esina .get_eee_rate = NULL, 967288551deSIgor Russkikh .set_flow_control = NULL, 9680c58c35fSIgor Russkikh }; 969