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 2898c4c201SDavid VomLehn #define HW_ATL_FW_SM_RAM 0x2U 290c58c35fSIgor Russkikh #define HW_ATL_MPI_FW_VERSION 0x18 3098c4c201SDavid VomLehn #define HW_ATL_MPI_CONTROL_ADR 0x0368U 3198c4c201SDavid VomLehn #define HW_ATL_MPI_STATE_ADR 0x036CU 3298c4c201SDavid VomLehn 3398c4c201SDavid VomLehn #define HW_ATL_MPI_STATE_MSK 0x00FFU 3498c4c201SDavid VomLehn #define HW_ATL_MPI_STATE_SHIFT 0U 3544e00dd8SIgor Russkikh #define HW_ATL_MPI_SPEED_MSK 0x00FF0000U 3698c4c201SDavid VomLehn #define HW_ATL_MPI_SPEED_SHIFT 16U 3744e00dd8SIgor Russkikh #define HW_ATL_MPI_DIRTY_WAKE_MSK 0x02000000U 3898c4c201SDavid VomLehn 39c8c82eb3SIgor Russkikh #define HW_ATL_MPI_DAISY_CHAIN_STATUS 0x704 40c8c82eb3SIgor Russkikh #define HW_ATL_MPI_BOOT_EXIT_CODE 0x388 41c8c82eb3SIgor Russkikh 42c8c82eb3SIgor Russkikh #define HW_ATL_MAC_PHY_CONTROL 0x4000 43c8c82eb3SIgor Russkikh #define HW_ATL_MAC_PHY_MPI_RESET_BIT 0x1D 44c8c82eb3SIgor Russkikh 450c58c35fSIgor Russkikh #define HW_ATL_FW_VER_1X 0x01050006U 46a57d3929SIgor Russkikh #define HW_ATL_FW_VER_2X 0x02000000U 47a57d3929SIgor Russkikh #define HW_ATL_FW_VER_3X 0x03000000U 480c58c35fSIgor Russkikh 49c8c82eb3SIgor Russkikh #define FORCE_FLASHLESS 0 50c8c82eb3SIgor Russkikh 510c58c35fSIgor Russkikh static int hw_atl_utils_ver_match(u32 ver_expected, u32 ver_actual); 52e9157848SNikita Danilov 53cce96d18SIgor Russkikh static int hw_atl_utils_mpi_set_state(struct aq_hw_s *self, 54cce96d18SIgor Russkikh enum hal_atl_utils_fw_state_e state); 550c58c35fSIgor Russkikh 560c58c35fSIgor Russkikh int hw_atl_utils_initfw(struct aq_hw_s *self, const struct aq_fw_ops **fw_ops) 570c58c35fSIgor Russkikh { 580c58c35fSIgor Russkikh int err = 0; 590c58c35fSIgor Russkikh 60c8c82eb3SIgor Russkikh err = hw_atl_utils_soft_reset(self); 61c8c82eb3SIgor Russkikh if (err) 62c8c82eb3SIgor Russkikh return err; 63c8c82eb3SIgor Russkikh 640c58c35fSIgor Russkikh hw_atl_utils_hw_chip_features_init(self, 650c58c35fSIgor Russkikh &self->chip_features); 660c58c35fSIgor Russkikh 670c58c35fSIgor Russkikh hw_atl_utils_get_fw_version(self, &self->fw_ver_actual); 680c58c35fSIgor Russkikh 69c8c82eb3SIgor Russkikh if (hw_atl_utils_ver_match(HW_ATL_FW_VER_1X, 70c8c82eb3SIgor Russkikh self->fw_ver_actual) == 0) { 710c58c35fSIgor Russkikh *fw_ops = &aq_fw_1x_ops; 72c8c82eb3SIgor Russkikh } else if (hw_atl_utils_ver_match(HW_ATL_FW_VER_2X, 73c8c82eb3SIgor Russkikh self->fw_ver_actual) == 0) { 74a57d3929SIgor Russkikh *fw_ops = &aq_fw_2x_ops; 75c8c82eb3SIgor Russkikh } else if (hw_atl_utils_ver_match(HW_ATL_FW_VER_3X, 76c8c82eb3SIgor Russkikh self->fw_ver_actual) == 0) { 77a57d3929SIgor Russkikh *fw_ops = &aq_fw_2x_ops; 78c8c82eb3SIgor Russkikh } else { 790c58c35fSIgor Russkikh aq_pr_err("Bad FW version detected: %x\n", 800c58c35fSIgor Russkikh self->fw_ver_actual); 810c58c35fSIgor Russkikh return -EOPNOTSUPP; 820c58c35fSIgor Russkikh } 830c58c35fSIgor Russkikh self->aq_fw_ops = *fw_ops; 840c58c35fSIgor Russkikh err = self->aq_fw_ops->init(self); 850c58c35fSIgor Russkikh return err; 860c58c35fSIgor Russkikh } 870c58c35fSIgor Russkikh 88c8c82eb3SIgor Russkikh static int hw_atl_utils_soft_reset_flb(struct aq_hw_s *self) 89c8c82eb3SIgor Russkikh { 901bf9a752SIgor Russkikh u32 gsr, val; 91c8c82eb3SIgor Russkikh int k = 0; 92c8c82eb3SIgor Russkikh 93c8c82eb3SIgor Russkikh aq_hw_write_reg(self, 0x404, 0x40e1); 94c8c82eb3SIgor Russkikh AQ_HW_SLEEP(50); 95c8c82eb3SIgor Russkikh 96c8c82eb3SIgor Russkikh /* Cleanup SPI */ 971bf9a752SIgor Russkikh val = aq_hw_read_reg(self, 0x53C); 981bf9a752SIgor Russkikh aq_hw_write_reg(self, 0x53C, val | 0x10); 99c8c82eb3SIgor Russkikh 100c8c82eb3SIgor Russkikh gsr = aq_hw_read_reg(self, HW_ATL_GLB_SOFT_RES_ADR); 101c8c82eb3SIgor Russkikh aq_hw_write_reg(self, HW_ATL_GLB_SOFT_RES_ADR, (gsr & 0xBFFF) | 0x8000); 102c8c82eb3SIgor Russkikh 103c8c82eb3SIgor Russkikh /* Kickstart MAC */ 104c8c82eb3SIgor Russkikh aq_hw_write_reg(self, 0x404, 0x80e0); 105c8c82eb3SIgor Russkikh aq_hw_write_reg(self, 0x32a8, 0x0); 106c8c82eb3SIgor Russkikh aq_hw_write_reg(self, 0x520, 0x1); 1071bf9a752SIgor Russkikh 1081bf9a752SIgor Russkikh /* Reset SPI again because of possible interrupted SPI burst */ 1091bf9a752SIgor Russkikh val = aq_hw_read_reg(self, 0x53C); 1101bf9a752SIgor Russkikh aq_hw_write_reg(self, 0x53C, val | 0x10); 111c8c82eb3SIgor Russkikh AQ_HW_SLEEP(10); 1121bf9a752SIgor Russkikh /* Clear SPI reset state */ 1131bf9a752SIgor Russkikh aq_hw_write_reg(self, 0x53C, val & ~0x10); 1141bf9a752SIgor Russkikh 115c8c82eb3SIgor Russkikh aq_hw_write_reg(self, 0x404, 0x180e0); 116c8c82eb3SIgor Russkikh 117c8c82eb3SIgor Russkikh for (k = 0; k < 1000; k++) { 118c8c82eb3SIgor Russkikh u32 flb_status = aq_hw_read_reg(self, 119c8c82eb3SIgor Russkikh HW_ATL_MPI_DAISY_CHAIN_STATUS); 120c8c82eb3SIgor Russkikh 121c8c82eb3SIgor Russkikh flb_status = flb_status & 0x10; 122c8c82eb3SIgor Russkikh if (flb_status) 123c8c82eb3SIgor Russkikh break; 124c8c82eb3SIgor Russkikh AQ_HW_SLEEP(10); 125c8c82eb3SIgor Russkikh } 126c8c82eb3SIgor Russkikh if (k == 1000) { 127c8c82eb3SIgor Russkikh aq_pr_err("MAC kickstart failed\n"); 128c8c82eb3SIgor Russkikh return -EIO; 129c8c82eb3SIgor Russkikh } 130c8c82eb3SIgor Russkikh 131c8c82eb3SIgor Russkikh /* FW reset */ 132c8c82eb3SIgor Russkikh aq_hw_write_reg(self, 0x404, 0x80e0); 133c8c82eb3SIgor Russkikh AQ_HW_SLEEP(50); 134c8c82eb3SIgor Russkikh aq_hw_write_reg(self, 0x3a0, 0x1); 135c8c82eb3SIgor Russkikh 136c8c82eb3SIgor Russkikh /* Kickstart PHY - skipped */ 137c8c82eb3SIgor Russkikh 138c8c82eb3SIgor Russkikh /* Global software reset*/ 139c8c82eb3SIgor Russkikh hw_atl_rx_rx_reg_res_dis_set(self, 0U); 140c8c82eb3SIgor Russkikh hw_atl_tx_tx_reg_res_dis_set(self, 0U); 141c8c82eb3SIgor Russkikh aq_hw_write_reg_bit(self, HW_ATL_MAC_PHY_CONTROL, 142c8c82eb3SIgor Russkikh BIT(HW_ATL_MAC_PHY_MPI_RESET_BIT), 143c8c82eb3SIgor Russkikh HW_ATL_MAC_PHY_MPI_RESET_BIT, 0x0); 144c8c82eb3SIgor Russkikh gsr = aq_hw_read_reg(self, HW_ATL_GLB_SOFT_RES_ADR); 145c8c82eb3SIgor Russkikh aq_hw_write_reg(self, HW_ATL_GLB_SOFT_RES_ADR, (gsr & 0xBFFF) | 0x8000); 146c8c82eb3SIgor Russkikh 147c8c82eb3SIgor Russkikh for (k = 0; k < 1000; k++) { 148c8c82eb3SIgor Russkikh u32 fw_state = aq_hw_read_reg(self, HW_ATL_MPI_FW_VERSION); 149c8c82eb3SIgor Russkikh 150c8c82eb3SIgor Russkikh if (fw_state) 151c8c82eb3SIgor Russkikh break; 152c8c82eb3SIgor Russkikh AQ_HW_SLEEP(10); 153c8c82eb3SIgor Russkikh } 154c8c82eb3SIgor Russkikh if (k == 1000) { 155c8c82eb3SIgor Russkikh aq_pr_err("FW kickstart failed\n"); 156c8c82eb3SIgor Russkikh return -EIO; 157c8c82eb3SIgor Russkikh } 158d0f0fb25SIgor Russkikh /* Old FW requires fixed delay after init */ 159d0f0fb25SIgor Russkikh AQ_HW_SLEEP(15); 160c8c82eb3SIgor Russkikh 161c8c82eb3SIgor Russkikh return 0; 162c8c82eb3SIgor Russkikh } 163c8c82eb3SIgor Russkikh 164c8c82eb3SIgor Russkikh static int hw_atl_utils_soft_reset_rbl(struct aq_hw_s *self) 165c8c82eb3SIgor Russkikh { 1661bf9a752SIgor Russkikh u32 gsr, val, rbl_status; 167c8c82eb3SIgor Russkikh int k; 168c8c82eb3SIgor Russkikh 169c8c82eb3SIgor Russkikh aq_hw_write_reg(self, 0x404, 0x40e1); 170c8c82eb3SIgor Russkikh aq_hw_write_reg(self, 0x3a0, 0x1); 171c8c82eb3SIgor Russkikh aq_hw_write_reg(self, 0x32a8, 0x0); 172c8c82eb3SIgor Russkikh 173c8c82eb3SIgor Russkikh /* Alter RBL status */ 174c8c82eb3SIgor Russkikh aq_hw_write_reg(self, 0x388, 0xDEAD); 175c8c82eb3SIgor Russkikh 1761bf9a752SIgor Russkikh /* Cleanup SPI */ 1771bf9a752SIgor Russkikh val = aq_hw_read_reg(self, 0x53C); 1781bf9a752SIgor Russkikh aq_hw_write_reg(self, 0x53C, val | 0x10); 1791bf9a752SIgor Russkikh 180c8c82eb3SIgor Russkikh /* Global software reset*/ 181c8c82eb3SIgor Russkikh hw_atl_rx_rx_reg_res_dis_set(self, 0U); 182c8c82eb3SIgor Russkikh hw_atl_tx_tx_reg_res_dis_set(self, 0U); 183c8c82eb3SIgor Russkikh aq_hw_write_reg_bit(self, HW_ATL_MAC_PHY_CONTROL, 184c8c82eb3SIgor Russkikh BIT(HW_ATL_MAC_PHY_MPI_RESET_BIT), 185c8c82eb3SIgor Russkikh HW_ATL_MAC_PHY_MPI_RESET_BIT, 0x0); 186c8c82eb3SIgor Russkikh gsr = aq_hw_read_reg(self, HW_ATL_GLB_SOFT_RES_ADR); 187c8c82eb3SIgor Russkikh aq_hw_write_reg(self, HW_ATL_GLB_SOFT_RES_ADR, 188c8c82eb3SIgor Russkikh (gsr & 0xFFFFBFFF) | 0x8000); 189c8c82eb3SIgor Russkikh 190c8c82eb3SIgor Russkikh if (FORCE_FLASHLESS) 191c8c82eb3SIgor Russkikh aq_hw_write_reg(self, 0x534, 0x0); 192c8c82eb3SIgor Russkikh 193c8c82eb3SIgor Russkikh aq_hw_write_reg(self, 0x404, 0x40e0); 194c8c82eb3SIgor Russkikh 195c8c82eb3SIgor Russkikh /* Wait for RBL boot */ 196c8c82eb3SIgor Russkikh for (k = 0; k < 1000; k++) { 197c8c82eb3SIgor Russkikh rbl_status = aq_hw_read_reg(self, 0x388) & 0xFFFF; 198c8c82eb3SIgor Russkikh if (rbl_status && rbl_status != 0xDEAD) 199c8c82eb3SIgor Russkikh break; 200c8c82eb3SIgor Russkikh AQ_HW_SLEEP(10); 201c8c82eb3SIgor Russkikh } 202c8c82eb3SIgor Russkikh if (!rbl_status || rbl_status == 0xDEAD) { 203c8c82eb3SIgor Russkikh aq_pr_err("RBL Restart failed"); 204c8c82eb3SIgor Russkikh return -EIO; 205c8c82eb3SIgor Russkikh } 206c8c82eb3SIgor Russkikh 207c8c82eb3SIgor Russkikh /* Restore NVR */ 208c8c82eb3SIgor Russkikh if (FORCE_FLASHLESS) 209c8c82eb3SIgor Russkikh aq_hw_write_reg(self, 0x534, 0xA0); 210c8c82eb3SIgor Russkikh 211c8c82eb3SIgor Russkikh if (rbl_status == 0xF1A7) { 212c8c82eb3SIgor Russkikh aq_pr_err("No FW detected. Dynamic FW load not implemented\n"); 213c8c82eb3SIgor Russkikh return -ENOTSUPP; 214c8c82eb3SIgor Russkikh } 215c8c82eb3SIgor Russkikh 216c8c82eb3SIgor Russkikh for (k = 0; k < 1000; k++) { 217c8c82eb3SIgor Russkikh u32 fw_state = aq_hw_read_reg(self, HW_ATL_MPI_FW_VERSION); 218c8c82eb3SIgor Russkikh 219c8c82eb3SIgor Russkikh if (fw_state) 220c8c82eb3SIgor Russkikh break; 221c8c82eb3SIgor Russkikh AQ_HW_SLEEP(10); 222c8c82eb3SIgor Russkikh } 223c8c82eb3SIgor Russkikh if (k == 1000) { 224c8c82eb3SIgor Russkikh aq_pr_err("FW kickstart failed\n"); 225c8c82eb3SIgor Russkikh return -EIO; 226c8c82eb3SIgor Russkikh } 227d0f0fb25SIgor Russkikh /* Old FW requires fixed delay after init */ 228d0f0fb25SIgor Russkikh AQ_HW_SLEEP(15); 229c8c82eb3SIgor Russkikh 230c8c82eb3SIgor Russkikh return 0; 231c8c82eb3SIgor Russkikh } 232c8c82eb3SIgor Russkikh 233c8c82eb3SIgor Russkikh int hw_atl_utils_soft_reset(struct aq_hw_s *self) 234c8c82eb3SIgor Russkikh { 235c8c82eb3SIgor Russkikh int k; 236c8c82eb3SIgor Russkikh u32 boot_exit_code = 0; 237c8c82eb3SIgor Russkikh 238c8c82eb3SIgor Russkikh for (k = 0; k < 1000; ++k) { 239c8c82eb3SIgor Russkikh u32 flb_status = aq_hw_read_reg(self, 240c8c82eb3SIgor Russkikh HW_ATL_MPI_DAISY_CHAIN_STATUS); 241c8c82eb3SIgor Russkikh boot_exit_code = aq_hw_read_reg(self, 242c8c82eb3SIgor Russkikh HW_ATL_MPI_BOOT_EXIT_CODE); 243c8c82eb3SIgor Russkikh if (flb_status != 0x06000000 || boot_exit_code != 0) 244c8c82eb3SIgor Russkikh break; 245c8c82eb3SIgor Russkikh } 246c8c82eb3SIgor Russkikh 247c8c82eb3SIgor Russkikh if (k == 1000) { 248c8c82eb3SIgor Russkikh aq_pr_err("Neither RBL nor FLB firmware started\n"); 249c8c82eb3SIgor Russkikh return -EOPNOTSUPP; 250c8c82eb3SIgor Russkikh } 251c8c82eb3SIgor Russkikh 252c8c82eb3SIgor Russkikh self->rbl_enabled = (boot_exit_code != 0); 253c8c82eb3SIgor Russkikh 254cce96d18SIgor Russkikh /* FW 1.x may bootup in an invalid POWER state (WOL feature). 255cce96d18SIgor Russkikh * We should work around this by forcing its state back to DEINIT 256cce96d18SIgor Russkikh */ 257cce96d18SIgor Russkikh if (!hw_atl_utils_ver_match(HW_ATL_FW_VER_1X, 258cce96d18SIgor Russkikh aq_hw_read_reg(self, 259cce96d18SIgor Russkikh HW_ATL_MPI_FW_VERSION))) { 260cce96d18SIgor Russkikh int err = 0; 261cce96d18SIgor Russkikh 262cce96d18SIgor Russkikh hw_atl_utils_mpi_set_state(self, MPI_DEINIT); 263cce96d18SIgor Russkikh AQ_HW_WAIT_FOR((aq_hw_read_reg(self, HW_ATL_MPI_STATE_ADR) & 264cce96d18SIgor Russkikh HW_ATL_MPI_STATE_MSK) == MPI_DEINIT, 265cce96d18SIgor Russkikh 10, 1000U); 266cce96d18SIgor Russkikh } 267cce96d18SIgor Russkikh 268c8c82eb3SIgor Russkikh if (self->rbl_enabled) 269c8c82eb3SIgor Russkikh return hw_atl_utils_soft_reset_rbl(self); 270c8c82eb3SIgor Russkikh else 271c8c82eb3SIgor Russkikh return hw_atl_utils_soft_reset_flb(self); 272c8c82eb3SIgor Russkikh } 273c8c82eb3SIgor Russkikh 274a57d3929SIgor Russkikh int hw_atl_utils_fw_downld_dwords(struct aq_hw_s *self, u32 a, 27598c4c201SDavid VomLehn u32 *p, u32 cnt) 27698c4c201SDavid VomLehn { 27798c4c201SDavid VomLehn int err = 0; 27898c4c201SDavid VomLehn 2798e1c072fSIgor Russkikh AQ_HW_WAIT_FOR(hw_atl_reg_glb_cpu_sem_get(self, 28098c4c201SDavid VomLehn HW_ATL_FW_SM_RAM) == 1U, 28198c4c201SDavid VomLehn 1U, 10000U); 28298c4c201SDavid VomLehn 28398c4c201SDavid VomLehn if (err < 0) { 28498c4c201SDavid VomLehn bool is_locked; 28598c4c201SDavid VomLehn 2868e1c072fSIgor Russkikh hw_atl_reg_glb_cpu_sem_set(self, 1U, HW_ATL_FW_SM_RAM); 2878e1c072fSIgor Russkikh is_locked = hw_atl_reg_glb_cpu_sem_get(self, HW_ATL_FW_SM_RAM); 28898c4c201SDavid VomLehn if (!is_locked) { 28998c4c201SDavid VomLehn err = -ETIME; 29098c4c201SDavid VomLehn goto err_exit; 29198c4c201SDavid VomLehn } 29298c4c201SDavid VomLehn } 29398c4c201SDavid VomLehn 29447203b34SIgor Russkikh aq_hw_write_reg(self, HW_ATL_MIF_ADDR, a); 29598c4c201SDavid VomLehn 29647203b34SIgor Russkikh for (++cnt; --cnt && !err;) { 29747203b34SIgor Russkikh aq_hw_write_reg(self, HW_ATL_MIF_CMD, 0x00008000U); 29898c4c201SDavid VomLehn 29947203b34SIgor Russkikh if (IS_CHIP_FEATURE(REVISION_B1)) 30047203b34SIgor Russkikh AQ_HW_WAIT_FOR(a != aq_hw_read_reg(self, 30147203b34SIgor Russkikh HW_ATL_MIF_ADDR), 30247203b34SIgor Russkikh 1, 1000U); 30347203b34SIgor Russkikh else 30447203b34SIgor Russkikh AQ_HW_WAIT_FOR(!(0x100 & aq_hw_read_reg(self, 30547203b34SIgor Russkikh HW_ATL_MIF_CMD)), 30647203b34SIgor Russkikh 1, 1000U); 30798c4c201SDavid VomLehn 30847203b34SIgor Russkikh *(p++) = aq_hw_read_reg(self, HW_ATL_MIF_VAL); 30947203b34SIgor Russkikh a += 4; 31098c4c201SDavid VomLehn } 31198c4c201SDavid VomLehn 3128e1c072fSIgor Russkikh hw_atl_reg_glb_cpu_sem_set(self, 1U, HW_ATL_FW_SM_RAM); 31398c4c201SDavid VomLehn 31498c4c201SDavid VomLehn err_exit: 31598c4c201SDavid VomLehn return err; 31698c4c201SDavid VomLehn } 31798c4c201SDavid VomLehn 31898c4c201SDavid VomLehn static int hw_atl_utils_fw_upload_dwords(struct aq_hw_s *self, u32 a, u32 *p, 31998c4c201SDavid VomLehn u32 cnt) 32098c4c201SDavid VomLehn { 32198c4c201SDavid VomLehn int err = 0; 32298c4c201SDavid VomLehn bool is_locked; 32398c4c201SDavid VomLehn 3248e1c072fSIgor Russkikh is_locked = hw_atl_reg_glb_cpu_sem_get(self, HW_ATL_FW_SM_RAM); 32598c4c201SDavid VomLehn if (!is_locked) { 32698c4c201SDavid VomLehn err = -ETIME; 32798c4c201SDavid VomLehn goto err_exit; 32898c4c201SDavid VomLehn } 3293ee5c887SYana Esina if (IS_CHIP_FEATURE(REVISION_B1)) { 3303ee5c887SYana Esina u32 offset = 0; 33198c4c201SDavid VomLehn 3323ee5c887SYana Esina for (; offset < cnt; ++offset) { 3333ee5c887SYana Esina aq_hw_write_reg(self, 0x328, p[offset]); 3343ee5c887SYana Esina aq_hw_write_reg(self, 0x32C, 3353ee5c887SYana Esina (0x80000000 | (0xFFFF & (offset * 4)))); 3363ee5c887SYana Esina hw_atl_mcp_up_force_intr_set(self, 1); 3373ee5c887SYana Esina /* 1000 times by 10us = 10ms */ 3383ee5c887SYana Esina AQ_HW_WAIT_FOR((aq_hw_read_reg(self, 3393ee5c887SYana Esina 0x32C) & 0xF0000000) != 3403ee5c887SYana Esina 0x80000000, 3413ee5c887SYana Esina 10, 1000); 3423ee5c887SYana Esina } 3433ee5c887SYana Esina } else { 3443ee5c887SYana Esina u32 offset = 0; 34598c4c201SDavid VomLehn 3463ee5c887SYana Esina aq_hw_write_reg(self, 0x208, a); 34798c4c201SDavid VomLehn 3483ee5c887SYana Esina for (; offset < cnt; ++offset) { 3493ee5c887SYana Esina aq_hw_write_reg(self, 0x20C, p[offset]); 3503ee5c887SYana Esina aq_hw_write_reg(self, 0x200, 0xC000); 35198c4c201SDavid VomLehn 3523ee5c887SYana Esina AQ_HW_WAIT_FOR((aq_hw_read_reg(self, 0x200U) & 3533ee5c887SYana Esina 0x100) == 0, 10, 1000); 35498c4c201SDavid VomLehn } 35598c4c201SDavid VomLehn } 35698c4c201SDavid VomLehn 3578e1c072fSIgor Russkikh hw_atl_reg_glb_cpu_sem_set(self, 1U, HW_ATL_FW_SM_RAM); 35898c4c201SDavid VomLehn 35998c4c201SDavid VomLehn err_exit: 36098c4c201SDavid VomLehn return err; 36198c4c201SDavid VomLehn } 36298c4c201SDavid VomLehn 36398c4c201SDavid VomLehn static int hw_atl_utils_ver_match(u32 ver_expected, u32 ver_actual) 36498c4c201SDavid VomLehn { 36598c4c201SDavid VomLehn int err = 0; 36698c4c201SDavid VomLehn const u32 dw_major_mask = 0xff000000U; 36798c4c201SDavid VomLehn const u32 dw_minor_mask = 0x00ffffffU; 36898c4c201SDavid VomLehn 36998c4c201SDavid VomLehn err = (dw_major_mask & (ver_expected ^ ver_actual)) ? -EOPNOTSUPP : 0; 37098c4c201SDavid VomLehn if (err < 0) 37198c4c201SDavid VomLehn goto err_exit; 37298c4c201SDavid VomLehn err = ((dw_minor_mask & ver_expected) > (dw_minor_mask & ver_actual)) ? 37398c4c201SDavid VomLehn -EOPNOTSUPP : 0; 37498c4c201SDavid VomLehn err_exit: 37598c4c201SDavid VomLehn return err; 37698c4c201SDavid VomLehn } 37798c4c201SDavid VomLehn 37898c4c201SDavid VomLehn static int hw_atl_utils_init_ucp(struct aq_hw_s *self, 3794cbc9f92SIgor Russkikh const struct aq_hw_caps_s *aq_hw_caps) 38098c4c201SDavid VomLehn { 38198c4c201SDavid VomLehn int err = 0; 38298c4c201SDavid VomLehn 38398c4c201SDavid VomLehn if (!aq_hw_read_reg(self, 0x370U)) { 38498c4c201SDavid VomLehn unsigned int rnd = 0U; 38598c4c201SDavid VomLehn unsigned int ucp_0x370 = 0U; 38698c4c201SDavid VomLehn 38798c4c201SDavid VomLehn get_random_bytes(&rnd, sizeof(unsigned int)); 38898c4c201SDavid VomLehn 38998c4c201SDavid VomLehn ucp_0x370 = 0x02020202U | (0xFEFEFEFEU & rnd); 39098c4c201SDavid VomLehn aq_hw_write_reg(self, HW_ATL_UCP_0X370_REG, ucp_0x370); 39198c4c201SDavid VomLehn } 39298c4c201SDavid VomLehn 3938e1c072fSIgor Russkikh hw_atl_reg_glb_cpu_scratch_scp_set(self, 0x00000000U, 25U); 39498c4c201SDavid VomLehn 39598c4c201SDavid VomLehn /* check 10 times by 1ms */ 3961a713f87SIgor Russkikh AQ_HW_WAIT_FOR(0U != (self->mbox_addr = 39798c4c201SDavid VomLehn aq_hw_read_reg(self, 0x360U)), 1000U, 10U); 39898c4c201SDavid VomLehn 39998c4c201SDavid VomLehn return err; 40098c4c201SDavid VomLehn } 40198c4c201SDavid VomLehn 40298c4c201SDavid VomLehn #define HW_ATL_RPC_CONTROL_ADR 0x0338U 40398c4c201SDavid VomLehn #define HW_ATL_RPC_STATE_ADR 0x033CU 40498c4c201SDavid VomLehn 40598c4c201SDavid VomLehn struct aq_hw_atl_utils_fw_rpc_tid_s { 40698c4c201SDavid VomLehn union { 40798c4c201SDavid VomLehn u32 val; 40898c4c201SDavid VomLehn struct { 40998c4c201SDavid VomLehn u16 tid; 41098c4c201SDavid VomLehn u16 len; 41198c4c201SDavid VomLehn }; 41298c4c201SDavid VomLehn }; 41398c4c201SDavid VomLehn }; 41498c4c201SDavid VomLehn 41598c4c201SDavid VomLehn #define hw_atl_utils_fw_rpc_init(_H_) hw_atl_utils_fw_rpc_wait(_H_, NULL) 41698c4c201SDavid VomLehn 4173ee5c887SYana Esina int hw_atl_utils_fw_rpc_call(struct aq_hw_s *self, unsigned int rpc_size) 41898c4c201SDavid VomLehn { 41998c4c201SDavid VomLehn int err = 0; 42098c4c201SDavid VomLehn struct aq_hw_atl_utils_fw_rpc_tid_s sw; 42198c4c201SDavid VomLehn 42298c4c201SDavid VomLehn if (!IS_CHIP_FEATURE(MIPS)) { 42398c4c201SDavid VomLehn err = -1; 42498c4c201SDavid VomLehn goto err_exit; 42598c4c201SDavid VomLehn } 4261a713f87SIgor Russkikh err = hw_atl_utils_fw_upload_dwords(self, self->rpc_addr, 4271a713f87SIgor Russkikh (u32 *)(void *)&self->rpc, 42898c4c201SDavid VomLehn (rpc_size + sizeof(u32) - 42998c4c201SDavid VomLehn sizeof(u8)) / sizeof(u32)); 43098c4c201SDavid VomLehn if (err < 0) 43198c4c201SDavid VomLehn goto err_exit; 43298c4c201SDavid VomLehn 4331a713f87SIgor Russkikh sw.tid = 0xFFFFU & (++self->rpc_tid); 43498c4c201SDavid VomLehn sw.len = (u16)rpc_size; 43598c4c201SDavid VomLehn aq_hw_write_reg(self, HW_ATL_RPC_CONTROL_ADR, sw.val); 43698c4c201SDavid VomLehn 43798c4c201SDavid VomLehn err_exit: 43898c4c201SDavid VomLehn return err; 43998c4c201SDavid VomLehn } 44098c4c201SDavid VomLehn 4413ee5c887SYana Esina int hw_atl_utils_fw_rpc_wait(struct aq_hw_s *self, 44298c4c201SDavid VomLehn struct hw_aq_atl_utils_fw_rpc **rpc) 44398c4c201SDavid VomLehn { 44498c4c201SDavid VomLehn int err = 0; 44598c4c201SDavid VomLehn struct aq_hw_atl_utils_fw_rpc_tid_s sw; 44698c4c201SDavid VomLehn struct aq_hw_atl_utils_fw_rpc_tid_s fw; 44798c4c201SDavid VomLehn 44898c4c201SDavid VomLehn do { 44998c4c201SDavid VomLehn sw.val = aq_hw_read_reg(self, HW_ATL_RPC_CONTROL_ADR); 45098c4c201SDavid VomLehn 4511a713f87SIgor Russkikh self->rpc_tid = sw.tid; 45298c4c201SDavid VomLehn 45398c4c201SDavid VomLehn AQ_HW_WAIT_FOR(sw.tid == 45498c4c201SDavid VomLehn (fw.val = 45598c4c201SDavid VomLehn aq_hw_read_reg(self, HW_ATL_RPC_STATE_ADR), 45698c4c201SDavid VomLehn fw.tid), 1000U, 100U); 45798c4c201SDavid VomLehn if (err < 0) 45898c4c201SDavid VomLehn goto err_exit; 45998c4c201SDavid VomLehn 46098c4c201SDavid VomLehn if (fw.len == 0xFFFFU) { 46198c4c201SDavid VomLehn err = hw_atl_utils_fw_rpc_call(self, sw.len); 46298c4c201SDavid VomLehn if (err < 0) 46398c4c201SDavid VomLehn goto err_exit; 46498c4c201SDavid VomLehn } 46598c4c201SDavid VomLehn } while (sw.tid != fw.tid || 0xFFFFU == fw.len); 46698c4c201SDavid VomLehn if (err < 0) 46798c4c201SDavid VomLehn goto err_exit; 46898c4c201SDavid VomLehn 46998c4c201SDavid VomLehn if (rpc) { 47098c4c201SDavid VomLehn if (fw.len) { 47198c4c201SDavid VomLehn err = 47298c4c201SDavid VomLehn hw_atl_utils_fw_downld_dwords(self, 4731a713f87SIgor Russkikh self->rpc_addr, 47498c4c201SDavid VomLehn (u32 *)(void *) 4751a713f87SIgor Russkikh &self->rpc, 47698c4c201SDavid VomLehn (fw.len + sizeof(u32) - 47798c4c201SDavid VomLehn sizeof(u8)) / 47898c4c201SDavid VomLehn sizeof(u32)); 47998c4c201SDavid VomLehn if (err < 0) 48098c4c201SDavid VomLehn goto err_exit; 48198c4c201SDavid VomLehn } 48298c4c201SDavid VomLehn 4831a713f87SIgor Russkikh *rpc = &self->rpc; 48498c4c201SDavid VomLehn } 48598c4c201SDavid VomLehn 48698c4c201SDavid VomLehn err_exit: 48798c4c201SDavid VomLehn return err; 48898c4c201SDavid VomLehn } 48998c4c201SDavid VomLehn 4901a713f87SIgor Russkikh static int hw_atl_utils_mpi_create(struct aq_hw_s *self) 49198c4c201SDavid VomLehn { 49298c4c201SDavid VomLehn int err = 0; 49398c4c201SDavid VomLehn 4941a713f87SIgor Russkikh err = hw_atl_utils_init_ucp(self, self->aq_nic_cfg->aq_hw_caps); 49598c4c201SDavid VomLehn if (err < 0) 49698c4c201SDavid VomLehn goto err_exit; 49798c4c201SDavid VomLehn 49898c4c201SDavid VomLehn err = hw_atl_utils_fw_rpc_init(self); 49998c4c201SDavid VomLehn if (err < 0) 50098c4c201SDavid VomLehn goto err_exit; 50198c4c201SDavid VomLehn 50298c4c201SDavid VomLehn err_exit: 50398c4c201SDavid VomLehn return err; 50498c4c201SDavid VomLehn } 50598c4c201SDavid VomLehn 50665e665e6SIgor Russkikh int hw_atl_utils_mpi_read_mbox(struct aq_hw_s *self, 50765e665e6SIgor Russkikh struct hw_aq_atl_utils_mbox_header *pmbox) 50865e665e6SIgor Russkikh { 50965e665e6SIgor Russkikh return hw_atl_utils_fw_downld_dwords(self, 5101a713f87SIgor Russkikh self->mbox_addr, 51165e665e6SIgor Russkikh (u32 *)(void *)pmbox, 51265e665e6SIgor Russkikh sizeof(*pmbox) / sizeof(u32)); 51365e665e6SIgor Russkikh } 51465e665e6SIgor Russkikh 51598c4c201SDavid VomLehn void hw_atl_utils_mpi_read_stats(struct aq_hw_s *self, 51698c4c201SDavid VomLehn struct hw_aq_atl_utils_mbox *pmbox) 51798c4c201SDavid VomLehn { 51898c4c201SDavid VomLehn int err = 0; 51998c4c201SDavid VomLehn 52098c4c201SDavid VomLehn err = hw_atl_utils_fw_downld_dwords(self, 5211a713f87SIgor Russkikh self->mbox_addr, 52298c4c201SDavid VomLehn (u32 *)(void *)pmbox, 52398c4c201SDavid VomLehn sizeof(*pmbox) / sizeof(u32)); 52498c4c201SDavid VomLehn if (err < 0) 52598c4c201SDavid VomLehn goto err_exit; 52698c4c201SDavid VomLehn 52798c4c201SDavid VomLehn if (IS_CHIP_FEATURE(REVISION_A0)) { 52898c4c201SDavid VomLehn unsigned int mtu = self->aq_nic_cfg ? 52998c4c201SDavid VomLehn self->aq_nic_cfg->mtu : 1514U; 53098c4c201SDavid VomLehn pmbox->stats.ubrc = pmbox->stats.uprc * mtu; 53198c4c201SDavid VomLehn pmbox->stats.ubtc = pmbox->stats.uptc * mtu; 5321a713f87SIgor Russkikh pmbox->stats.dpc = atomic_read(&self->dpc); 53398c4c201SDavid VomLehn } else { 5348e1c072fSIgor Russkikh pmbox->stats.dpc = hw_atl_reg_rx_dma_stat_counter7get(self); 53598c4c201SDavid VomLehn } 53698c4c201SDavid VomLehn 53798c4c201SDavid VomLehn err_exit:; 53898c4c201SDavid VomLehn } 53998c4c201SDavid VomLehn 540dfbd0749SWei Yongjun static int hw_atl_utils_mpi_set_speed(struct aq_hw_s *self, u32 speed) 54198c4c201SDavid VomLehn { 5420c58c35fSIgor Russkikh u32 val = aq_hw_read_reg(self, HW_ATL_MPI_CONTROL_ADR); 54398c4c201SDavid VomLehn 54444e00dd8SIgor Russkikh val = val & ~HW_ATL_MPI_SPEED_MSK; 54544e00dd8SIgor Russkikh val |= speed << HW_ATL_MPI_SPEED_SHIFT; 5460c58c35fSIgor Russkikh aq_hw_write_reg(self, HW_ATL_MPI_CONTROL_ADR, val); 54798c4c201SDavid VomLehn 54898c4c201SDavid VomLehn return 0; 54998c4c201SDavid VomLehn } 55098c4c201SDavid VomLehn 551dfbd0749SWei Yongjun static int hw_atl_utils_mpi_set_state(struct aq_hw_s *self, 55244e00dd8SIgor Russkikh enum hal_atl_utils_fw_state_e state) 55398c4c201SDavid VomLehn { 55498c4c201SDavid VomLehn int err = 0; 55598c4c201SDavid VomLehn u32 transaction_id = 0; 55665e665e6SIgor Russkikh struct hw_aq_atl_utils_mbox_header mbox; 55744e00dd8SIgor Russkikh u32 val = aq_hw_read_reg(self, HW_ATL_MPI_CONTROL_ADR); 55898c4c201SDavid VomLehn 55998c4c201SDavid VomLehn if (state == MPI_RESET) { 56065e665e6SIgor Russkikh hw_atl_utils_mpi_read_mbox(self, &mbox); 56198c4c201SDavid VomLehn 56265e665e6SIgor Russkikh transaction_id = mbox.transaction_id; 56398c4c201SDavid VomLehn 56498c4c201SDavid VomLehn AQ_HW_WAIT_FOR(transaction_id != 56565e665e6SIgor Russkikh (hw_atl_utils_mpi_read_mbox(self, &mbox), 56665e665e6SIgor Russkikh mbox.transaction_id), 56798c4c201SDavid VomLehn 1000U, 100U); 56898c4c201SDavid VomLehn if (err < 0) 56998c4c201SDavid VomLehn goto err_exit; 57098c4c201SDavid VomLehn } 57144e00dd8SIgor Russkikh /* On interface DEINIT we disable DW (raise bit) 57244e00dd8SIgor Russkikh * Otherwise enable DW (clear bit) 57344e00dd8SIgor Russkikh */ 57444e00dd8SIgor Russkikh if (state == MPI_DEINIT || state == MPI_POWER) 57544e00dd8SIgor Russkikh val |= HW_ATL_MPI_DIRTY_WAKE_MSK; 57644e00dd8SIgor Russkikh else 57744e00dd8SIgor Russkikh val &= ~HW_ATL_MPI_DIRTY_WAKE_MSK; 57898c4c201SDavid VomLehn 57944e00dd8SIgor Russkikh /* Set new state bits */ 58044e00dd8SIgor Russkikh val = val & ~HW_ATL_MPI_STATE_MSK; 58144e00dd8SIgor Russkikh val |= state & HW_ATL_MPI_STATE_MSK; 58298c4c201SDavid VomLehn 5830c58c35fSIgor Russkikh aq_hw_write_reg(self, HW_ATL_MPI_CONTROL_ADR, val); 58444e00dd8SIgor Russkikh err_exit: 58544e00dd8SIgor Russkikh return err; 5860c58c35fSIgor Russkikh } 5870c58c35fSIgor Russkikh 588bd8ed441SPavel Belous int hw_atl_utils_mpi_get_link_status(struct aq_hw_s *self) 58998c4c201SDavid VomLehn { 59098c4c201SDavid VomLehn u32 cp0x036C = aq_hw_read_reg(self, HW_ATL_MPI_STATE_ADR); 59198c4c201SDavid VomLehn u32 link_speed_mask = cp0x036C >> HW_ATL_MPI_SPEED_SHIFT; 592bd8ed441SPavel Belous struct aq_hw_link_status_s *link_status = &self->aq_link_status; 59398c4c201SDavid VomLehn 59498c4c201SDavid VomLehn if (!link_speed_mask) { 59598c4c201SDavid VomLehn link_status->mbps = 0U; 59698c4c201SDavid VomLehn } else { 59798c4c201SDavid VomLehn switch (link_speed_mask) { 59898c4c201SDavid VomLehn case HAL_ATLANTIC_RATE_10G: 59998c4c201SDavid VomLehn link_status->mbps = 10000U; 60098c4c201SDavid VomLehn break; 60198c4c201SDavid VomLehn 60298c4c201SDavid VomLehn case HAL_ATLANTIC_RATE_5G: 60398c4c201SDavid VomLehn case HAL_ATLANTIC_RATE_5GSR: 60498c4c201SDavid VomLehn link_status->mbps = 5000U; 60598c4c201SDavid VomLehn break; 60698c4c201SDavid VomLehn 60798c4c201SDavid VomLehn case HAL_ATLANTIC_RATE_2GS: 60898c4c201SDavid VomLehn link_status->mbps = 2500U; 60998c4c201SDavid VomLehn break; 61098c4c201SDavid VomLehn 61198c4c201SDavid VomLehn case HAL_ATLANTIC_RATE_1G: 61298c4c201SDavid VomLehn link_status->mbps = 1000U; 61398c4c201SDavid VomLehn break; 61498c4c201SDavid VomLehn 61598c4c201SDavid VomLehn case HAL_ATLANTIC_RATE_100M: 61698c4c201SDavid VomLehn link_status->mbps = 100U; 61798c4c201SDavid VomLehn break; 61898c4c201SDavid VomLehn 61998c4c201SDavid VomLehn default: 620a7bb1beaSIgor Russkikh return -EBUSY; 62198c4c201SDavid VomLehn } 62298c4c201SDavid VomLehn } 62398c4c201SDavid VomLehn 62498c4c201SDavid VomLehn return 0; 62598c4c201SDavid VomLehn } 62698c4c201SDavid VomLehn 62798c4c201SDavid VomLehn int hw_atl_utils_get_mac_permanent(struct aq_hw_s *self, 62898c4c201SDavid VomLehn u8 *mac) 62998c4c201SDavid VomLehn { 63098c4c201SDavid VomLehn int err = 0; 63198c4c201SDavid VomLehn u32 h = 0U; 63298c4c201SDavid VomLehn u32 l = 0U; 63398c4c201SDavid VomLehn u32 mac_addr[2]; 63498c4c201SDavid VomLehn 63598c4c201SDavid VomLehn if (!aq_hw_read_reg(self, HW_ATL_UCP_0X370_REG)) { 63698c4c201SDavid VomLehn unsigned int rnd = 0; 63798c4c201SDavid VomLehn unsigned int ucp_0x370 = 0; 63898c4c201SDavid VomLehn 63998c4c201SDavid VomLehn get_random_bytes(&rnd, sizeof(unsigned int)); 64098c4c201SDavid VomLehn 64198c4c201SDavid VomLehn ucp_0x370 = 0x02020202 | (0xFEFEFEFE & rnd); 64298c4c201SDavid VomLehn aq_hw_write_reg(self, HW_ATL_UCP_0X370_REG, ucp_0x370); 64398c4c201SDavid VomLehn } 64498c4c201SDavid VomLehn 64598c4c201SDavid VomLehn err = hw_atl_utils_fw_downld_dwords(self, 64698c4c201SDavid VomLehn aq_hw_read_reg(self, 0x00000374U) + 64798c4c201SDavid VomLehn (40U * 4U), 64898c4c201SDavid VomLehn mac_addr, 64908b5cf08SIgor Russkikh ARRAY_SIZE(mac_addr)); 65098c4c201SDavid VomLehn if (err < 0) { 65198c4c201SDavid VomLehn mac_addr[0] = 0U; 65298c4c201SDavid VomLehn mac_addr[1] = 0U; 65398c4c201SDavid VomLehn err = 0; 65498c4c201SDavid VomLehn } else { 65598c4c201SDavid VomLehn mac_addr[0] = __swab32(mac_addr[0]); 65698c4c201SDavid VomLehn mac_addr[1] = __swab32(mac_addr[1]); 65798c4c201SDavid VomLehn } 65898c4c201SDavid VomLehn 65998c4c201SDavid VomLehn ether_addr_copy(mac, (u8 *)mac_addr); 66098c4c201SDavid VomLehn 66198c4c201SDavid VomLehn if ((mac[0] & 0x01U) || ((mac[0] | mac[1] | mac[2]) == 0x00U)) { 66298c4c201SDavid VomLehn /* chip revision */ 663e9157848SNikita Danilov l = 0xE3000000U | 664e9157848SNikita Danilov (0xFFFFU & aq_hw_read_reg(self, HW_ATL_UCP_0X370_REG)) | 665e9157848SNikita Danilov (0x00 << 16); 66698c4c201SDavid VomLehn h = 0x8001300EU; 66798c4c201SDavid VomLehn 66898c4c201SDavid VomLehn mac[5] = (u8)(0xFFU & l); 66998c4c201SDavid VomLehn l >>= 8; 67098c4c201SDavid VomLehn mac[4] = (u8)(0xFFU & l); 67198c4c201SDavid VomLehn l >>= 8; 67298c4c201SDavid VomLehn mac[3] = (u8)(0xFFU & l); 67398c4c201SDavid VomLehn l >>= 8; 67498c4c201SDavid VomLehn mac[2] = (u8)(0xFFU & l); 67598c4c201SDavid VomLehn mac[1] = (u8)(0xFFU & h); 67698c4c201SDavid VomLehn h >>= 8; 67798c4c201SDavid VomLehn mac[0] = (u8)(0xFFU & h); 67898c4c201SDavid VomLehn } 67998c4c201SDavid VomLehn 68098c4c201SDavid VomLehn return err; 68198c4c201SDavid VomLehn } 68298c4c201SDavid VomLehn 68398c4c201SDavid VomLehn unsigned int hw_atl_utils_mbps_2_speed_index(unsigned int mbps) 68498c4c201SDavid VomLehn { 68598c4c201SDavid VomLehn unsigned int ret = 0U; 68698c4c201SDavid VomLehn 68798c4c201SDavid VomLehn switch (mbps) { 68898c4c201SDavid VomLehn case 100U: 68998c4c201SDavid VomLehn ret = 5U; 69098c4c201SDavid VomLehn break; 69198c4c201SDavid VomLehn 69298c4c201SDavid VomLehn case 1000U: 69398c4c201SDavid VomLehn ret = 4U; 69498c4c201SDavid VomLehn break; 69598c4c201SDavid VomLehn 69698c4c201SDavid VomLehn case 2500U: 69798c4c201SDavid VomLehn ret = 3U; 69898c4c201SDavid VomLehn break; 69998c4c201SDavid VomLehn 70098c4c201SDavid VomLehn case 5000U: 70198c4c201SDavid VomLehn ret = 1U; 70298c4c201SDavid VomLehn break; 70398c4c201SDavid VomLehn 70498c4c201SDavid VomLehn case 10000U: 70598c4c201SDavid VomLehn ret = 0U; 70698c4c201SDavid VomLehn break; 70798c4c201SDavid VomLehn 70898c4c201SDavid VomLehn default: 70998c4c201SDavid VomLehn break; 71098c4c201SDavid VomLehn } 71198c4c201SDavid VomLehn return ret; 71298c4c201SDavid VomLehn } 71398c4c201SDavid VomLehn 71498c4c201SDavid VomLehn void hw_atl_utils_hw_chip_features_init(struct aq_hw_s *self, u32 *p) 71598c4c201SDavid VomLehn { 71698c4c201SDavid VomLehn u32 chip_features = 0U; 7178e1c072fSIgor Russkikh u32 val = hw_atl_reg_glb_mif_id_get(self); 71898c4c201SDavid VomLehn u32 mif_rev = val & 0xFFU; 71998c4c201SDavid VomLehn 72047203b34SIgor Russkikh if ((0xFU & mif_rev) == 1U) { 72147203b34SIgor Russkikh chip_features |= HAL_ATLANTIC_UTILS_CHIP_REVISION_A0 | 72298c4c201SDavid VomLehn HAL_ATLANTIC_UTILS_CHIP_MPI_AQ | 72398c4c201SDavid VomLehn HAL_ATLANTIC_UTILS_CHIP_MIPS; 72447203b34SIgor Russkikh } else if ((0xFU & mif_rev) == 2U) { 72547203b34SIgor Russkikh chip_features |= HAL_ATLANTIC_UTILS_CHIP_REVISION_B0 | 72647203b34SIgor Russkikh HAL_ATLANTIC_UTILS_CHIP_MPI_AQ | 72747203b34SIgor Russkikh HAL_ATLANTIC_UTILS_CHIP_MIPS | 72847203b34SIgor Russkikh HAL_ATLANTIC_UTILS_CHIP_TPO2 | 72947203b34SIgor Russkikh HAL_ATLANTIC_UTILS_CHIP_RPF2; 73047203b34SIgor Russkikh } else if ((0xFU & mif_rev) == 0xAU) { 73147203b34SIgor Russkikh chip_features |= HAL_ATLANTIC_UTILS_CHIP_REVISION_B1 | 73298c4c201SDavid VomLehn HAL_ATLANTIC_UTILS_CHIP_MPI_AQ | 73398c4c201SDavid VomLehn HAL_ATLANTIC_UTILS_CHIP_MIPS | 73498c4c201SDavid VomLehn HAL_ATLANTIC_UTILS_CHIP_TPO2 | 73598c4c201SDavid VomLehn HAL_ATLANTIC_UTILS_CHIP_RPF2; 73698c4c201SDavid VomLehn } 73798c4c201SDavid VomLehn 73898c4c201SDavid VomLehn *p = chip_features; 73998c4c201SDavid VomLehn } 74098c4c201SDavid VomLehn 74144e00dd8SIgor Russkikh static int hw_atl_fw1x_deinit(struct aq_hw_s *self) 74298c4c201SDavid VomLehn { 74344e00dd8SIgor Russkikh hw_atl_utils_mpi_set_speed(self, 0); 74444e00dd8SIgor Russkikh hw_atl_utils_mpi_set_state(self, MPI_DEINIT); 74598c4c201SDavid VomLehn return 0; 74698c4c201SDavid VomLehn } 74798c4c201SDavid VomLehn 74865e665e6SIgor Russkikh int hw_atl_utils_update_stats(struct aq_hw_s *self) 74965e665e6SIgor Russkikh { 75065e665e6SIgor Russkikh struct hw_aq_atl_utils_mbox mbox; 75165e665e6SIgor Russkikh 75265e665e6SIgor Russkikh hw_atl_utils_mpi_read_stats(self, &mbox); 75365e665e6SIgor Russkikh 7541a713f87SIgor Russkikh #define AQ_SDELTA(_N_) (self->curr_stats._N_ += \ 7551a713f87SIgor Russkikh mbox.stats._N_ - self->last_stats._N_) 7561a713f87SIgor Russkikh 757be08d839SIgor Russkikh if (self->aq_link_status.mbps) { 75865e665e6SIgor Russkikh AQ_SDELTA(uprc); 75965e665e6SIgor Russkikh AQ_SDELTA(mprc); 76065e665e6SIgor Russkikh AQ_SDELTA(bprc); 76165e665e6SIgor Russkikh AQ_SDELTA(erpt); 76265e665e6SIgor Russkikh 76365e665e6SIgor Russkikh AQ_SDELTA(uptc); 76465e665e6SIgor Russkikh AQ_SDELTA(mptc); 76565e665e6SIgor Russkikh AQ_SDELTA(bptc); 76665e665e6SIgor Russkikh AQ_SDELTA(erpr); 76765e665e6SIgor Russkikh 76865e665e6SIgor Russkikh AQ_SDELTA(ubrc); 76965e665e6SIgor Russkikh AQ_SDELTA(ubtc); 77065e665e6SIgor Russkikh AQ_SDELTA(mbrc); 77165e665e6SIgor Russkikh AQ_SDELTA(mbtc); 77265e665e6SIgor Russkikh AQ_SDELTA(bbrc); 77365e665e6SIgor Russkikh AQ_SDELTA(bbtc); 77465e665e6SIgor Russkikh AQ_SDELTA(dpc); 775be08d839SIgor Russkikh } 77665e665e6SIgor Russkikh #undef AQ_SDELTA 7778e1c072fSIgor Russkikh self->curr_stats.dma_pkt_rc = hw_atl_stats_rx_dma_good_pkt_counterlsw_get(self); 7788e1c072fSIgor Russkikh self->curr_stats.dma_pkt_tc = hw_atl_stats_tx_dma_good_pkt_counterlsw_get(self); 7798e1c072fSIgor Russkikh self->curr_stats.dma_oct_rc = hw_atl_stats_rx_dma_good_octet_counterlsw_get(self); 7808e1c072fSIgor Russkikh self->curr_stats.dma_oct_tc = hw_atl_stats_tx_dma_good_octet_counterlsw_get(self); 78165e665e6SIgor Russkikh 7821a713f87SIgor Russkikh memcpy(&self->last_stats, &mbox.stats, sizeof(mbox.stats)); 78365e665e6SIgor Russkikh 78465e665e6SIgor Russkikh return 0; 78565e665e6SIgor Russkikh } 78665e665e6SIgor Russkikh 787be08d839SIgor Russkikh struct aq_stats_s *hw_atl_utils_get_hw_stats(struct aq_hw_s *self) 78898c4c201SDavid VomLehn { 7891a713f87SIgor Russkikh return &self->curr_stats; 79098c4c201SDavid VomLehn } 79198c4c201SDavid VomLehn 79298c4c201SDavid VomLehn static const u32 hw_atl_utils_hw_mac_regs[] = { 79398c4c201SDavid VomLehn 0x00005580U, 0x00005590U, 0x000055B0U, 0x000055B4U, 79498c4c201SDavid VomLehn 0x000055C0U, 0x00005B00U, 0x00005B04U, 0x00005B08U, 79598c4c201SDavid VomLehn 0x00005B0CU, 0x00005B10U, 0x00005B14U, 0x00005B18U, 79698c4c201SDavid VomLehn 0x00005B1CU, 0x00005B20U, 0x00005B24U, 0x00005B28U, 79798c4c201SDavid VomLehn 0x00005B2CU, 0x00005B30U, 0x00005B34U, 0x00005B38U, 79898c4c201SDavid VomLehn 0x00005B3CU, 0x00005B40U, 0x00005B44U, 0x00005B48U, 79998c4c201SDavid VomLehn 0x00005B4CU, 0x00005B50U, 0x00005B54U, 0x00005B58U, 80098c4c201SDavid VomLehn 0x00005B5CU, 0x00005B60U, 0x00005B64U, 0x00005B68U, 80198c4c201SDavid VomLehn 0x00005B6CU, 0x00005B70U, 0x00005B74U, 0x00005B78U, 80298c4c201SDavid VomLehn 0x00005B7CU, 0x00007C00U, 0x00007C04U, 0x00007C08U, 80398c4c201SDavid VomLehn 0x00007C0CU, 0x00007C10U, 0x00007C14U, 0x00007C18U, 80498c4c201SDavid VomLehn 0x00007C1CU, 0x00007C20U, 0x00007C40U, 0x00007C44U, 80598c4c201SDavid VomLehn 0x00007C48U, 0x00007C4CU, 0x00007C50U, 0x00007C54U, 80698c4c201SDavid VomLehn 0x00007C58U, 0x00007C5CU, 0x00007C60U, 0x00007C80U, 80798c4c201SDavid VomLehn 0x00007C84U, 0x00007C88U, 0x00007C8CU, 0x00007C90U, 80898c4c201SDavid VomLehn 0x00007C94U, 0x00007C98U, 0x00007C9CU, 0x00007CA0U, 80998c4c201SDavid VomLehn 0x00007CC0U, 0x00007CC4U, 0x00007CC8U, 0x00007CCCU, 81098c4c201SDavid VomLehn 0x00007CD0U, 0x00007CD4U, 0x00007CD8U, 0x00007CDCU, 81198c4c201SDavid VomLehn 0x00007CE0U, 0x00000300U, 0x00000304U, 0x00000308U, 81298c4c201SDavid VomLehn 0x0000030cU, 0x00000310U, 0x00000314U, 0x00000318U, 81398c4c201SDavid VomLehn 0x0000031cU, 0x00000360U, 0x00000364U, 0x00000368U, 81498c4c201SDavid VomLehn 0x0000036cU, 0x00000370U, 0x00000374U, 0x00006900U, 81598c4c201SDavid VomLehn }; 81698c4c201SDavid VomLehn 81798c4c201SDavid VomLehn int hw_atl_utils_hw_get_regs(struct aq_hw_s *self, 8184cbc9f92SIgor Russkikh const struct aq_hw_caps_s *aq_hw_caps, 81998c4c201SDavid VomLehn u32 *regs_buff) 82098c4c201SDavid VomLehn { 82198c4c201SDavid VomLehn unsigned int i = 0U; 82298c4c201SDavid VomLehn 82398c4c201SDavid VomLehn for (i = 0; i < aq_hw_caps->mac_regs_count; i++) 82498c4c201SDavid VomLehn regs_buff[i] = aq_hw_read_reg(self, 82598c4c201SDavid VomLehn hw_atl_utils_hw_mac_regs[i]); 82698c4c201SDavid VomLehn return 0; 82798c4c201SDavid VomLehn } 82898c4c201SDavid VomLehn 82998c4c201SDavid VomLehn int hw_atl_utils_get_fw_version(struct aq_hw_s *self, u32 *fw_version) 83098c4c201SDavid VomLehn { 83198c4c201SDavid VomLehn *fw_version = aq_hw_read_reg(self, 0x18U); 83298c4c201SDavid VomLehn return 0; 83398c4c201SDavid VomLehn } 8340c58c35fSIgor Russkikh 835a0da96c0SYana Esina static int aq_fw1x_set_wol(struct aq_hw_s *self, bool wol_enabled, u8 *mac) 836a0da96c0SYana Esina { 837a0da96c0SYana Esina struct hw_aq_atl_utils_fw_rpc *prpc = NULL; 838a0da96c0SYana Esina unsigned int rpc_size = 0U; 839a0da96c0SYana Esina int err = 0; 840a0da96c0SYana Esina 841a0da96c0SYana Esina err = hw_atl_utils_fw_rpc_wait(self, &prpc); 842a0da96c0SYana Esina if (err < 0) 843a0da96c0SYana Esina goto err_exit; 844a0da96c0SYana Esina 845a0da96c0SYana Esina memset(prpc, 0, sizeof(*prpc)); 846a0da96c0SYana Esina 847a0da96c0SYana Esina if (wol_enabled) { 848a0da96c0SYana Esina rpc_size = sizeof(prpc->msg_id) + sizeof(prpc->msg_wol); 849a0da96c0SYana Esina 850a0da96c0SYana Esina prpc->msg_id = HAL_ATLANTIC_UTILS_FW_MSG_WOL_ADD; 851a0da96c0SYana Esina prpc->msg_wol.priority = 852a0da96c0SYana Esina HAL_ATLANTIC_UTILS_FW_MSG_WOL_PRIOR; 853a0da96c0SYana Esina prpc->msg_wol.pattern_id = 854a0da96c0SYana Esina HAL_ATLANTIC_UTILS_FW_MSG_WOL_PATTERN; 855a0da96c0SYana Esina prpc->msg_wol.wol_packet_type = 856a0da96c0SYana Esina HAL_ATLANTIC_UTILS_FW_MSG_WOL_MAG_PKT; 857a0da96c0SYana Esina 858a0da96c0SYana Esina ether_addr_copy((u8 *)&prpc->msg_wol.wol_pattern, mac); 859a0da96c0SYana Esina } else { 860a0da96c0SYana Esina rpc_size = sizeof(prpc->msg_id) + sizeof(prpc->msg_del_id); 861a0da96c0SYana Esina 862a0da96c0SYana Esina prpc->msg_id = HAL_ATLANTIC_UTILS_FW_MSG_WOL_DEL; 863a0da96c0SYana Esina prpc->msg_wol.pattern_id = 864a0da96c0SYana Esina HAL_ATLANTIC_UTILS_FW_MSG_WOL_PATTERN; 865a0da96c0SYana Esina } 866a0da96c0SYana Esina 867a0da96c0SYana Esina err = hw_atl_utils_fw_rpc_call(self, rpc_size); 868a0da96c0SYana Esina 869a0da96c0SYana Esina err_exit: 870a0da96c0SYana Esina return err; 871a0da96c0SYana Esina } 872a0da96c0SYana Esina 873a0da96c0SYana Esina int aq_fw1x_set_power(struct aq_hw_s *self, unsigned int power_state, 874a0da96c0SYana Esina u8 *mac) 875a0da96c0SYana Esina { 876a0da96c0SYana Esina struct hw_aq_atl_utils_fw_rpc *prpc = NULL; 877a0da96c0SYana Esina unsigned int rpc_size = 0U; 878a0da96c0SYana Esina int err = 0; 879a0da96c0SYana Esina 880a0da96c0SYana Esina if (self->aq_nic_cfg->wol & AQ_NIC_WOL_ENABLED) { 881a0da96c0SYana Esina err = aq_fw1x_set_wol(self, 1, mac); 882a0da96c0SYana Esina 883a0da96c0SYana Esina if (err < 0) 884a0da96c0SYana Esina goto err_exit; 885a0da96c0SYana Esina 886a0da96c0SYana Esina rpc_size = sizeof(prpc->msg_id) + 887a0da96c0SYana Esina sizeof(prpc->msg_enable_wakeup); 888a0da96c0SYana Esina 889a0da96c0SYana Esina err = hw_atl_utils_fw_rpc_wait(self, &prpc); 890a0da96c0SYana Esina 891a0da96c0SYana Esina if (err < 0) 892a0da96c0SYana Esina goto err_exit; 893a0da96c0SYana Esina 894a0da96c0SYana Esina memset(prpc, 0, rpc_size); 895a0da96c0SYana Esina 896a0da96c0SYana Esina prpc->msg_id = HAL_ATLANTIC_UTILS_FW_MSG_ENABLE_WAKEUP; 897a0da96c0SYana Esina prpc->msg_enable_wakeup.pattern_mask = 0x00000002; 898a0da96c0SYana Esina 899a0da96c0SYana Esina err = hw_atl_utils_fw_rpc_call(self, rpc_size); 900a0da96c0SYana Esina if (err < 0) 901a0da96c0SYana Esina goto err_exit; 902a0da96c0SYana Esina } 903a0da96c0SYana Esina hw_atl_utils_mpi_set_speed(self, 0); 904a0da96c0SYana Esina hw_atl_utils_mpi_set_state(self, MPI_POWER); 905a0da96c0SYana Esina 906a0da96c0SYana Esina err_exit: 907a0da96c0SYana Esina return err; 908a0da96c0SYana Esina } 909a0da96c0SYana Esina 9100c58c35fSIgor Russkikh const struct aq_fw_ops aq_fw_1x_ops = { 9110c58c35fSIgor Russkikh .init = hw_atl_utils_mpi_create, 91244e00dd8SIgor Russkikh .deinit = hw_atl_fw1x_deinit, 9130c58c35fSIgor Russkikh .reset = NULL, 9140c58c35fSIgor Russkikh .get_mac_permanent = hw_atl_utils_get_mac_permanent, 9150c58c35fSIgor Russkikh .set_link_speed = hw_atl_utils_mpi_set_speed, 9160c58c35fSIgor Russkikh .set_state = hw_atl_utils_mpi_set_state, 9170c58c35fSIgor Russkikh .update_link_status = hw_atl_utils_mpi_get_link_status, 9180c58c35fSIgor Russkikh .update_stats = hw_atl_utils_update_stats, 919a0da96c0SYana Esina .set_power = aq_fw1x_set_power, 92092ab6407SYana Esina .set_eee_rate = NULL, 92192ab6407SYana Esina .get_eee_rate = NULL, 922288551deSIgor Russkikh .set_flow_control = NULL, 9230c58c35fSIgor Russkikh }; 924