175a6faf6SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 298c4c201SDavid VomLehn /* 398c4c201SDavid VomLehn * aQuantia Corporation Network Driver 4593f7b43SDmitry Bezrukov * Copyright (C) 2014-2019 aQuantia Corporation. All rights reserved 598c4c201SDavid VomLehn */ 698c4c201SDavid VomLehn 798c4c201SDavid VomLehn /* File hw_atl_utils.c: Definition of common functions for Atlantic hardware 898c4c201SDavid VomLehn * abstraction layer. 998c4c201SDavid VomLehn */ 1098c4c201SDavid VomLehn 111a713f87SIgor Russkikh #include "../aq_nic.h" 1298c4c201SDavid VomLehn #include "../aq_hw_utils.h" 1398c4c201SDavid VomLehn #include "hw_atl_utils.h" 1498c4c201SDavid VomLehn #include "hw_atl_llh.h" 150c58c35fSIgor Russkikh #include "hw_atl_llh_internal.h" 1698c4c201SDavid VomLehn 1798c4c201SDavid VomLehn #include <linux/random.h> 1898c4c201SDavid VomLehn 1998c4c201SDavid VomLehn #define HW_ATL_UCP_0X370_REG 0x0370U 2098c4c201SDavid VomLehn 2147203b34SIgor Russkikh #define HW_ATL_MIF_CMD 0x0200U 2247203b34SIgor Russkikh #define HW_ATL_MIF_ADDR 0x0208U 2347203b34SIgor Russkikh #define HW_ATL_MIF_VAL 0x020CU 2447203b34SIgor Russkikh 256a7f2277SNikita Danilov #define HW_ATL_RPC_CONTROL_ADR 0x0338U 266a7f2277SNikita Danilov #define HW_ATL_RPC_STATE_ADR 0x033CU 276a7f2277SNikita Danilov 280c58c35fSIgor Russkikh #define HW_ATL_MPI_FW_VERSION 0x18 2998c4c201SDavid VomLehn #define HW_ATL_MPI_CONTROL_ADR 0x0368U 3098c4c201SDavid VomLehn #define HW_ATL_MPI_STATE_ADR 0x036CU 3198c4c201SDavid VomLehn 3298c4c201SDavid VomLehn #define HW_ATL_MPI_STATE_MSK 0x00FFU 3398c4c201SDavid VomLehn #define HW_ATL_MPI_STATE_SHIFT 0U 3444e00dd8SIgor Russkikh #define HW_ATL_MPI_SPEED_MSK 0x00FF0000U 3598c4c201SDavid VomLehn #define HW_ATL_MPI_SPEED_SHIFT 16U 3644e00dd8SIgor Russkikh #define HW_ATL_MPI_DIRTY_WAKE_MSK 0x02000000U 3798c4c201SDavid VomLehn 38c8c82eb3SIgor Russkikh #define HW_ATL_MPI_DAISY_CHAIN_STATUS 0x704 39c8c82eb3SIgor Russkikh #define HW_ATL_MPI_BOOT_EXIT_CODE 0x388 40c8c82eb3SIgor Russkikh 41c8c82eb3SIgor Russkikh #define HW_ATL_MAC_PHY_CONTROL 0x4000 42c8c82eb3SIgor Russkikh #define HW_ATL_MAC_PHY_MPI_RESET_BIT 0x1D 43c8c82eb3SIgor Russkikh 440c58c35fSIgor Russkikh #define HW_ATL_FW_VER_1X 0x01050006U 45a57d3929SIgor Russkikh #define HW_ATL_FW_VER_2X 0x02000000U 46a57d3929SIgor Russkikh #define HW_ATL_FW_VER_3X 0x03000000U 470c58c35fSIgor Russkikh 48c8c82eb3SIgor Russkikh #define FORCE_FLASHLESS 0 49c8c82eb3SIgor Russkikh 50dc12f75aSNikita Danilov enum mcp_area { 51dc12f75aSNikita Danilov MCP_AREA_CONFIG = 0x80000000, 52dc12f75aSNikita Danilov MCP_AREA_SETTINGS = 0x20000000, 53dc12f75aSNikita Danilov }; 54dc12f75aSNikita Danilov 550c58c35fSIgor Russkikh static int hw_atl_utils_ver_match(u32 ver_expected, u32 ver_actual); 56e9157848SNikita Danilov 57cce96d18SIgor Russkikh static int hw_atl_utils_mpi_set_state(struct aq_hw_s *self, 58cce96d18SIgor Russkikh enum hal_atl_utils_fw_state_e state); 590c58c35fSIgor Russkikh 606a7f2277SNikita Danilov static u32 hw_atl_utils_get_mpi_mbox_tid(struct aq_hw_s *self); 616a7f2277SNikita Danilov static u32 hw_atl_utils_mpi_get_state(struct aq_hw_s *self); 626a7f2277SNikita Danilov static u32 hw_atl_utils_mif_cmd_get(struct aq_hw_s *self); 636a7f2277SNikita Danilov static u32 hw_atl_utils_mif_addr_get(struct aq_hw_s *self); 646a7f2277SNikita Danilov static u32 hw_atl_utils_rpc_state_get(struct aq_hw_s *self); 656a7f2277SNikita Danilov 660c58c35fSIgor Russkikh int hw_atl_utils_initfw(struct aq_hw_s *self, const struct aq_fw_ops **fw_ops) 670c58c35fSIgor Russkikh { 680c58c35fSIgor Russkikh int err = 0; 690c58c35fSIgor Russkikh 70c8c82eb3SIgor Russkikh err = hw_atl_utils_soft_reset(self); 71c8c82eb3SIgor Russkikh if (err) 72c8c82eb3SIgor Russkikh return err; 73c8c82eb3SIgor Russkikh 740c58c35fSIgor Russkikh hw_atl_utils_hw_chip_features_init(self, 750c58c35fSIgor Russkikh &self->chip_features); 760c58c35fSIgor Russkikh 770c58c35fSIgor Russkikh hw_atl_utils_get_fw_version(self, &self->fw_ver_actual); 780c58c35fSIgor Russkikh 79c8c82eb3SIgor Russkikh if (hw_atl_utils_ver_match(HW_ATL_FW_VER_1X, 80c8c82eb3SIgor Russkikh self->fw_ver_actual) == 0) { 810c58c35fSIgor Russkikh *fw_ops = &aq_fw_1x_ops; 82c8c82eb3SIgor Russkikh } else if (hw_atl_utils_ver_match(HW_ATL_FW_VER_2X, 83c8c82eb3SIgor Russkikh self->fw_ver_actual) == 0) { 84a57d3929SIgor Russkikh *fw_ops = &aq_fw_2x_ops; 85c8c82eb3SIgor Russkikh } else if (hw_atl_utils_ver_match(HW_ATL_FW_VER_3X, 86c8c82eb3SIgor Russkikh self->fw_ver_actual) == 0) { 87a57d3929SIgor Russkikh *fw_ops = &aq_fw_2x_ops; 88c8c82eb3SIgor Russkikh } else { 890c58c35fSIgor Russkikh aq_pr_err("Bad FW version detected: %x\n", 900c58c35fSIgor Russkikh self->fw_ver_actual); 910c58c35fSIgor Russkikh return -EOPNOTSUPP; 920c58c35fSIgor Russkikh } 930c58c35fSIgor Russkikh self->aq_fw_ops = *fw_ops; 940c58c35fSIgor Russkikh err = self->aq_fw_ops->init(self); 957b0c342fSNikita Danilov 960c58c35fSIgor Russkikh return err; 970c58c35fSIgor Russkikh } 980c58c35fSIgor Russkikh 99c8c82eb3SIgor Russkikh static int hw_atl_utils_soft_reset_flb(struct aq_hw_s *self) 100c8c82eb3SIgor Russkikh { 1011bf9a752SIgor Russkikh u32 gsr, val; 102c8c82eb3SIgor Russkikh int k = 0; 103c8c82eb3SIgor Russkikh 104c8c82eb3SIgor Russkikh aq_hw_write_reg(self, 0x404, 0x40e1); 105c8c82eb3SIgor Russkikh AQ_HW_SLEEP(50); 106c8c82eb3SIgor Russkikh 107c8c82eb3SIgor Russkikh /* Cleanup SPI */ 1081bf9a752SIgor Russkikh val = aq_hw_read_reg(self, 0x53C); 1091bf9a752SIgor Russkikh aq_hw_write_reg(self, 0x53C, val | 0x10); 110c8c82eb3SIgor Russkikh 111c8c82eb3SIgor Russkikh gsr = aq_hw_read_reg(self, HW_ATL_GLB_SOFT_RES_ADR); 112c8c82eb3SIgor Russkikh aq_hw_write_reg(self, HW_ATL_GLB_SOFT_RES_ADR, (gsr & 0xBFFF) | 0x8000); 113c8c82eb3SIgor Russkikh 114c8c82eb3SIgor Russkikh /* Kickstart MAC */ 115c8c82eb3SIgor Russkikh aq_hw_write_reg(self, 0x404, 0x80e0); 116c8c82eb3SIgor Russkikh aq_hw_write_reg(self, 0x32a8, 0x0); 117c8c82eb3SIgor Russkikh aq_hw_write_reg(self, 0x520, 0x1); 1181bf9a752SIgor Russkikh 1191bf9a752SIgor Russkikh /* Reset SPI again because of possible interrupted SPI burst */ 1201bf9a752SIgor Russkikh val = aq_hw_read_reg(self, 0x53C); 1211bf9a752SIgor Russkikh aq_hw_write_reg(self, 0x53C, val | 0x10); 122c8c82eb3SIgor Russkikh AQ_HW_SLEEP(10); 1231bf9a752SIgor Russkikh /* Clear SPI reset state */ 1241bf9a752SIgor Russkikh aq_hw_write_reg(self, 0x53C, val & ~0x10); 1251bf9a752SIgor Russkikh 126c8c82eb3SIgor Russkikh aq_hw_write_reg(self, 0x404, 0x180e0); 127c8c82eb3SIgor Russkikh 128c8c82eb3SIgor Russkikh for (k = 0; k < 1000; k++) { 129c8c82eb3SIgor Russkikh u32 flb_status = aq_hw_read_reg(self, 130c8c82eb3SIgor Russkikh HW_ATL_MPI_DAISY_CHAIN_STATUS); 131c8c82eb3SIgor Russkikh 132c8c82eb3SIgor Russkikh flb_status = flb_status & 0x10; 133c8c82eb3SIgor Russkikh if (flb_status) 134c8c82eb3SIgor Russkikh break; 135c8c82eb3SIgor Russkikh AQ_HW_SLEEP(10); 136c8c82eb3SIgor Russkikh } 137c8c82eb3SIgor Russkikh if (k == 1000) { 138c8c82eb3SIgor Russkikh aq_pr_err("MAC kickstart failed\n"); 139c8c82eb3SIgor Russkikh return -EIO; 140c8c82eb3SIgor Russkikh } 141c8c82eb3SIgor Russkikh 142c8c82eb3SIgor Russkikh /* FW reset */ 143c8c82eb3SIgor Russkikh aq_hw_write_reg(self, 0x404, 0x80e0); 144c8c82eb3SIgor Russkikh AQ_HW_SLEEP(50); 145c8c82eb3SIgor Russkikh aq_hw_write_reg(self, 0x3a0, 0x1); 146c8c82eb3SIgor Russkikh 147c8c82eb3SIgor Russkikh /* Kickstart PHY - skipped */ 148c8c82eb3SIgor Russkikh 149c8c82eb3SIgor Russkikh /* Global software reset*/ 150c8c82eb3SIgor Russkikh hw_atl_rx_rx_reg_res_dis_set(self, 0U); 151c8c82eb3SIgor Russkikh hw_atl_tx_tx_reg_res_dis_set(self, 0U); 152c8c82eb3SIgor Russkikh aq_hw_write_reg_bit(self, HW_ATL_MAC_PHY_CONTROL, 153c8c82eb3SIgor Russkikh BIT(HW_ATL_MAC_PHY_MPI_RESET_BIT), 154c8c82eb3SIgor Russkikh HW_ATL_MAC_PHY_MPI_RESET_BIT, 0x0); 155c8c82eb3SIgor Russkikh gsr = aq_hw_read_reg(self, HW_ATL_GLB_SOFT_RES_ADR); 156c8c82eb3SIgor Russkikh aq_hw_write_reg(self, HW_ATL_GLB_SOFT_RES_ADR, (gsr & 0xBFFF) | 0x8000); 157c8c82eb3SIgor Russkikh 158c8c82eb3SIgor Russkikh for (k = 0; k < 1000; k++) { 159c8c82eb3SIgor Russkikh u32 fw_state = aq_hw_read_reg(self, HW_ATL_MPI_FW_VERSION); 160c8c82eb3SIgor Russkikh 161c8c82eb3SIgor Russkikh if (fw_state) 162c8c82eb3SIgor Russkikh break; 163c8c82eb3SIgor Russkikh AQ_HW_SLEEP(10); 164c8c82eb3SIgor Russkikh } 165c8c82eb3SIgor Russkikh if (k == 1000) { 166c8c82eb3SIgor Russkikh aq_pr_err("FW kickstart failed\n"); 167c8c82eb3SIgor Russkikh return -EIO; 168c8c82eb3SIgor Russkikh } 169d0f0fb25SIgor Russkikh /* Old FW requires fixed delay after init */ 170d0f0fb25SIgor Russkikh AQ_HW_SLEEP(15); 171c8c82eb3SIgor Russkikh 172c8c82eb3SIgor Russkikh return 0; 173c8c82eb3SIgor Russkikh } 174c8c82eb3SIgor Russkikh 175c8c82eb3SIgor Russkikh static int hw_atl_utils_soft_reset_rbl(struct aq_hw_s *self) 176c8c82eb3SIgor Russkikh { 1771bf9a752SIgor Russkikh u32 gsr, val, rbl_status; 178c8c82eb3SIgor Russkikh int k; 179c8c82eb3SIgor Russkikh 180c8c82eb3SIgor Russkikh aq_hw_write_reg(self, 0x404, 0x40e1); 181c8c82eb3SIgor Russkikh aq_hw_write_reg(self, 0x3a0, 0x1); 182c8c82eb3SIgor Russkikh aq_hw_write_reg(self, 0x32a8, 0x0); 183c8c82eb3SIgor Russkikh 184c8c82eb3SIgor Russkikh /* Alter RBL status */ 185c8c82eb3SIgor Russkikh aq_hw_write_reg(self, 0x388, 0xDEAD); 186c8c82eb3SIgor Russkikh 1871bf9a752SIgor Russkikh /* Cleanup SPI */ 1881bf9a752SIgor Russkikh val = aq_hw_read_reg(self, 0x53C); 1891bf9a752SIgor Russkikh aq_hw_write_reg(self, 0x53C, val | 0x10); 1901bf9a752SIgor Russkikh 191c8c82eb3SIgor Russkikh /* Global software reset*/ 192c8c82eb3SIgor Russkikh hw_atl_rx_rx_reg_res_dis_set(self, 0U); 193c8c82eb3SIgor Russkikh hw_atl_tx_tx_reg_res_dis_set(self, 0U); 194c8c82eb3SIgor Russkikh aq_hw_write_reg_bit(self, HW_ATL_MAC_PHY_CONTROL, 195c8c82eb3SIgor Russkikh BIT(HW_ATL_MAC_PHY_MPI_RESET_BIT), 196c8c82eb3SIgor Russkikh HW_ATL_MAC_PHY_MPI_RESET_BIT, 0x0); 197c8c82eb3SIgor Russkikh gsr = aq_hw_read_reg(self, HW_ATL_GLB_SOFT_RES_ADR); 198c8c82eb3SIgor Russkikh aq_hw_write_reg(self, HW_ATL_GLB_SOFT_RES_ADR, 199c8c82eb3SIgor Russkikh (gsr & 0xFFFFBFFF) | 0x8000); 200c8c82eb3SIgor Russkikh 201c8c82eb3SIgor Russkikh if (FORCE_FLASHLESS) 202c8c82eb3SIgor Russkikh aq_hw_write_reg(self, 0x534, 0x0); 203c8c82eb3SIgor Russkikh 204c8c82eb3SIgor Russkikh aq_hw_write_reg(self, 0x404, 0x40e0); 205c8c82eb3SIgor Russkikh 206c8c82eb3SIgor Russkikh /* Wait for RBL boot */ 207c8c82eb3SIgor Russkikh for (k = 0; k < 1000; k++) { 208c8c82eb3SIgor Russkikh rbl_status = aq_hw_read_reg(self, 0x388) & 0xFFFF; 209c8c82eb3SIgor Russkikh if (rbl_status && rbl_status != 0xDEAD) 210c8c82eb3SIgor Russkikh break; 211c8c82eb3SIgor Russkikh AQ_HW_SLEEP(10); 212c8c82eb3SIgor Russkikh } 213c8c82eb3SIgor Russkikh if (!rbl_status || rbl_status == 0xDEAD) { 214c8c82eb3SIgor Russkikh aq_pr_err("RBL Restart failed"); 215c8c82eb3SIgor Russkikh return -EIO; 216c8c82eb3SIgor Russkikh } 217c8c82eb3SIgor Russkikh 218c8c82eb3SIgor Russkikh /* Restore NVR */ 219c8c82eb3SIgor Russkikh if (FORCE_FLASHLESS) 220c8c82eb3SIgor Russkikh aq_hw_write_reg(self, 0x534, 0xA0); 221c8c82eb3SIgor Russkikh 222c8c82eb3SIgor Russkikh if (rbl_status == 0xF1A7) { 223c8c82eb3SIgor Russkikh aq_pr_err("No FW detected. Dynamic FW load not implemented\n"); 224c8c82eb3SIgor Russkikh return -ENOTSUPP; 225c8c82eb3SIgor Russkikh } 226c8c82eb3SIgor Russkikh 227c8c82eb3SIgor Russkikh for (k = 0; k < 1000; k++) { 228c8c82eb3SIgor Russkikh u32 fw_state = aq_hw_read_reg(self, HW_ATL_MPI_FW_VERSION); 229c8c82eb3SIgor Russkikh 230c8c82eb3SIgor Russkikh if (fw_state) 231c8c82eb3SIgor Russkikh break; 232c8c82eb3SIgor Russkikh AQ_HW_SLEEP(10); 233c8c82eb3SIgor Russkikh } 234c8c82eb3SIgor Russkikh if (k == 1000) { 235c8c82eb3SIgor Russkikh aq_pr_err("FW kickstart failed\n"); 236c8c82eb3SIgor Russkikh return -EIO; 237c8c82eb3SIgor Russkikh } 238d0f0fb25SIgor Russkikh /* Old FW requires fixed delay after init */ 239d0f0fb25SIgor Russkikh AQ_HW_SLEEP(15); 240c8c82eb3SIgor Russkikh 241c8c82eb3SIgor Russkikh return 0; 242c8c82eb3SIgor Russkikh } 243c8c82eb3SIgor Russkikh 244c8c82eb3SIgor Russkikh int hw_atl_utils_soft_reset(struct aq_hw_s *self) 245c8c82eb3SIgor Russkikh { 246c8c82eb3SIgor Russkikh u32 boot_exit_code = 0; 2476a7f2277SNikita Danilov u32 val; 2487b0c342fSNikita Danilov int k; 249c8c82eb3SIgor Russkikh 250c8c82eb3SIgor Russkikh for (k = 0; k < 1000; ++k) { 251c8c82eb3SIgor Russkikh u32 flb_status = aq_hw_read_reg(self, 252c8c82eb3SIgor Russkikh HW_ATL_MPI_DAISY_CHAIN_STATUS); 253c8c82eb3SIgor Russkikh boot_exit_code = aq_hw_read_reg(self, 254c8c82eb3SIgor Russkikh HW_ATL_MPI_BOOT_EXIT_CODE); 255c8c82eb3SIgor Russkikh if (flb_status != 0x06000000 || boot_exit_code != 0) 256c8c82eb3SIgor Russkikh break; 257c8c82eb3SIgor Russkikh } 258c8c82eb3SIgor Russkikh 259c8c82eb3SIgor Russkikh if (k == 1000) { 260c8c82eb3SIgor Russkikh aq_pr_err("Neither RBL nor FLB firmware started\n"); 261c8c82eb3SIgor Russkikh return -EOPNOTSUPP; 262c8c82eb3SIgor Russkikh } 263c8c82eb3SIgor Russkikh 264c8c82eb3SIgor Russkikh self->rbl_enabled = (boot_exit_code != 0); 265c8c82eb3SIgor Russkikh 266cce96d18SIgor Russkikh /* FW 1.x may bootup in an invalid POWER state (WOL feature). 267cce96d18SIgor Russkikh * We should work around this by forcing its state back to DEINIT 268cce96d18SIgor Russkikh */ 269cce96d18SIgor Russkikh if (!hw_atl_utils_ver_match(HW_ATL_FW_VER_1X, 270cce96d18SIgor Russkikh aq_hw_read_reg(self, 271cce96d18SIgor Russkikh HW_ATL_MPI_FW_VERSION))) { 272cce96d18SIgor Russkikh int err = 0; 273cce96d18SIgor Russkikh 274cce96d18SIgor Russkikh hw_atl_utils_mpi_set_state(self, MPI_DEINIT); 2756a7f2277SNikita Danilov err = readx_poll_timeout_atomic(hw_atl_utils_mpi_get_state, 2766a7f2277SNikita Danilov self, val, 2776a7f2277SNikita Danilov (val & HW_ATL_MPI_STATE_MSK) == 2786a7f2277SNikita Danilov MPI_DEINIT, 2796a7f2277SNikita Danilov 10, 10000U); 2804e3c7c00SYueHaibing if (err) 2814e3c7c00SYueHaibing return err; 282cce96d18SIgor Russkikh } 283cce96d18SIgor Russkikh 284c8c82eb3SIgor Russkikh if (self->rbl_enabled) 285c8c82eb3SIgor Russkikh return hw_atl_utils_soft_reset_rbl(self); 286c8c82eb3SIgor Russkikh else 287c8c82eb3SIgor Russkikh return hw_atl_utils_soft_reset_flb(self); 288c8c82eb3SIgor Russkikh } 289c8c82eb3SIgor Russkikh 290a57d3929SIgor Russkikh int hw_atl_utils_fw_downld_dwords(struct aq_hw_s *self, u32 a, 29198c4c201SDavid VomLehn u32 *p, u32 cnt) 29298c4c201SDavid VomLehn { 29398c4c201SDavid VomLehn int err = 0; 2946a7f2277SNikita Danilov u32 val; 29598c4c201SDavid VomLehn 2966a7f2277SNikita Danilov err = readx_poll_timeout_atomic(hw_atl_sem_ram_get, 2976a7f2277SNikita Danilov self, val, val == 1U, 29898c4c201SDavid VomLehn 1U, 10000U); 29998c4c201SDavid VomLehn 30098c4c201SDavid VomLehn if (err < 0) { 30198c4c201SDavid VomLehn bool is_locked; 30298c4c201SDavid VomLehn 3038e1c072fSIgor Russkikh hw_atl_reg_glb_cpu_sem_set(self, 1U, HW_ATL_FW_SM_RAM); 3040b926d46SNikita Danilov is_locked = hw_atl_sem_ram_get(self); 30598c4c201SDavid VomLehn if (!is_locked) { 30698c4c201SDavid VomLehn err = -ETIME; 30798c4c201SDavid VomLehn goto err_exit; 30898c4c201SDavid VomLehn } 30998c4c201SDavid VomLehn } 31098c4c201SDavid VomLehn 31147203b34SIgor Russkikh aq_hw_write_reg(self, HW_ATL_MIF_ADDR, a); 31298c4c201SDavid VomLehn 31347203b34SIgor Russkikh for (++cnt; --cnt && !err;) { 31447203b34SIgor Russkikh aq_hw_write_reg(self, HW_ATL_MIF_CMD, 0x00008000U); 31598c4c201SDavid VomLehn 31647203b34SIgor Russkikh if (IS_CHIP_FEATURE(REVISION_B1)) 3176a7f2277SNikita Danilov err = readx_poll_timeout_atomic(hw_atl_utils_mif_addr_get, 3186a7f2277SNikita Danilov self, val, val != a, 3196a7f2277SNikita Danilov 1U, 1000U); 32047203b34SIgor Russkikh else 3216a7f2277SNikita Danilov err = readx_poll_timeout_atomic(hw_atl_utils_mif_cmd_get, 3226a7f2277SNikita Danilov self, val, 3236a7f2277SNikita Danilov !(val & 0x100), 3246a7f2277SNikita Danilov 1U, 1000U); 32598c4c201SDavid VomLehn 32647203b34SIgor Russkikh *(p++) = aq_hw_read_reg(self, HW_ATL_MIF_VAL); 32747203b34SIgor Russkikh a += 4; 32898c4c201SDavid VomLehn } 32998c4c201SDavid VomLehn 3308e1c072fSIgor Russkikh hw_atl_reg_glb_cpu_sem_set(self, 1U, HW_ATL_FW_SM_RAM); 33198c4c201SDavid VomLehn 33298c4c201SDavid VomLehn err_exit: 33398c4c201SDavid VomLehn return err; 33498c4c201SDavid VomLehn } 33598c4c201SDavid VomLehn 336dc12f75aSNikita Danilov static int hw_atl_utils_write_b1_mbox(struct aq_hw_s *self, u32 addr, 337dc12f75aSNikita Danilov u32 *p, u32 cnt, enum mcp_area area) 33898c4c201SDavid VomLehn { 339dc12f75aSNikita Danilov u32 data_offset = 0; 340dc12f75aSNikita Danilov u32 offset = addr; 34198c4c201SDavid VomLehn int err = 0; 342dc12f75aSNikita Danilov u32 val; 34398c4c201SDavid VomLehn 344dc12f75aSNikita Danilov switch (area) { 345dc12f75aSNikita Danilov case MCP_AREA_CONFIG: 346dc12f75aSNikita Danilov offset -= self->rpc_addr; 347dc12f75aSNikita Danilov break; 348930b9a05SNikita Danilov 349dc12f75aSNikita Danilov case MCP_AREA_SETTINGS: 350dc12f75aSNikita Danilov offset -= self->settings_addr; 351dc12f75aSNikita Danilov break; 352dc12f75aSNikita Danilov } 35398c4c201SDavid VomLehn 354dc12f75aSNikita Danilov offset = offset / sizeof(u32); 355dc12f75aSNikita Danilov 356dc12f75aSNikita Danilov for (; data_offset < cnt; ++data_offset, ++offset) { 357dc12f75aSNikita Danilov aq_hw_write_reg(self, 0x328, p[data_offset]); 3583ee5c887SYana Esina aq_hw_write_reg(self, 0x32C, 359dc12f75aSNikita Danilov (area | (0xFFFF & (offset * 4)))); 3603ee5c887SYana Esina hw_atl_mcp_up_force_intr_set(self, 1); 3613ee5c887SYana Esina /* 1000 times by 10us = 10ms */ 3626a7f2277SNikita Danilov err = readx_poll_timeout_atomic(hw_atl_scrpad12_get, 3636a7f2277SNikita Danilov self, val, 364930b9a05SNikita Danilov (val & 0xF0000000) != 365dc12f75aSNikita Danilov area, 3666a7f2277SNikita Danilov 10U, 10000U); 36798c4c201SDavid VomLehn 368dc12f75aSNikita Danilov if (err < 0) 369dc12f75aSNikita Danilov break; 370dc12f75aSNikita Danilov } 371dc12f75aSNikita Danilov 372dc12f75aSNikita Danilov return err; 373dc12f75aSNikita Danilov } 374dc12f75aSNikita Danilov 375dc12f75aSNikita Danilov static int hw_atl_utils_write_b0_mbox(struct aq_hw_s *self, u32 addr, 376dc12f75aSNikita Danilov u32 *p, u32 cnt) 377dc12f75aSNikita Danilov { 378dc12f75aSNikita Danilov u32 offset = 0; 379dc12f75aSNikita Danilov int err = 0; 380dc12f75aSNikita Danilov u32 val; 381dc12f75aSNikita Danilov 382dc12f75aSNikita Danilov aq_hw_write_reg(self, 0x208, addr); 38398c4c201SDavid VomLehn 3843ee5c887SYana Esina for (; offset < cnt; ++offset) { 3853ee5c887SYana Esina aq_hw_write_reg(self, 0x20C, p[offset]); 3863ee5c887SYana Esina aq_hw_write_reg(self, 0x200, 0xC000); 38798c4c201SDavid VomLehn 3886a7f2277SNikita Danilov err = readx_poll_timeout_atomic(hw_atl_utils_mif_cmd_get, 3896a7f2277SNikita Danilov self, val, 390dc12f75aSNikita Danilov (val & 0x100) == 0U, 391dc12f75aSNikita Danilov 10U, 10000U); 392dc12f75aSNikita Danilov 393dc12f75aSNikita Danilov if (err < 0) 394dc12f75aSNikita Danilov break; 39598c4c201SDavid VomLehn } 396dc12f75aSNikita Danilov 397dc12f75aSNikita Danilov return err; 39898c4c201SDavid VomLehn } 39998c4c201SDavid VomLehn 400dc12f75aSNikita Danilov static int hw_atl_utils_fw_upload_dwords(struct aq_hw_s *self, u32 addr, u32 *p, 401dc12f75aSNikita Danilov u32 cnt, enum mcp_area area) 402dc12f75aSNikita Danilov { 403dc12f75aSNikita Danilov int err = 0; 404dc12f75aSNikita Danilov u32 val; 405dc12f75aSNikita Danilov 406dc12f75aSNikita Danilov err = readx_poll_timeout_atomic(hw_atl_sem_ram_get, self, 407dc12f75aSNikita Danilov val, val == 1U, 408dc12f75aSNikita Danilov 10U, 100000U); 409dc12f75aSNikita Danilov if (err < 0) 410dc12f75aSNikita Danilov goto err_exit; 411dc12f75aSNikita Danilov 412dc12f75aSNikita Danilov if (IS_CHIP_FEATURE(REVISION_B1)) 413dc12f75aSNikita Danilov err = hw_atl_utils_write_b1_mbox(self, addr, p, cnt, area); 414dc12f75aSNikita Danilov else 415dc12f75aSNikita Danilov err = hw_atl_utils_write_b0_mbox(self, addr, p, cnt); 416dc12f75aSNikita Danilov 4178e1c072fSIgor Russkikh hw_atl_reg_glb_cpu_sem_set(self, 1U, HW_ATL_FW_SM_RAM); 41898c4c201SDavid VomLehn 419dc12f75aSNikita Danilov if (err < 0) 420dc12f75aSNikita Danilov goto err_exit; 421dc12f75aSNikita Danilov 422dc12f75aSNikita Danilov err = aq_hw_err_from_flags(self); 423dc12f75aSNikita Danilov 42498c4c201SDavid VomLehn err_exit: 42598c4c201SDavid VomLehn return err; 42698c4c201SDavid VomLehn } 42798c4c201SDavid VomLehn 428dc12f75aSNikita Danilov int hw_atl_write_fwcfg_dwords(struct aq_hw_s *self, u32 *p, u32 cnt) 429dc12f75aSNikita Danilov { 430dc12f75aSNikita Danilov return hw_atl_utils_fw_upload_dwords(self, self->rpc_addr, p, 431dc12f75aSNikita Danilov cnt, MCP_AREA_CONFIG); 432dc12f75aSNikita Danilov } 433dc12f75aSNikita Danilov 434dc12f75aSNikita Danilov int hw_atl_write_fwsettings_dwords(struct aq_hw_s *self, u32 offset, u32 *p, 435dc12f75aSNikita Danilov u32 cnt) 436dc12f75aSNikita Danilov { 437dc12f75aSNikita Danilov return hw_atl_utils_fw_upload_dwords(self, self->settings_addr + offset, 438dc12f75aSNikita Danilov p, cnt, MCP_AREA_SETTINGS); 439dc12f75aSNikita Danilov } 440dc12f75aSNikita Danilov 44198c4c201SDavid VomLehn static int hw_atl_utils_ver_match(u32 ver_expected, u32 ver_actual) 44298c4c201SDavid VomLehn { 44398c4c201SDavid VomLehn const u32 dw_major_mask = 0xff000000U; 44498c4c201SDavid VomLehn const u32 dw_minor_mask = 0x00ffffffU; 4457b0c342fSNikita Danilov int err = 0; 44698c4c201SDavid VomLehn 44798c4c201SDavid VomLehn err = (dw_major_mask & (ver_expected ^ ver_actual)) ? -EOPNOTSUPP : 0; 44898c4c201SDavid VomLehn if (err < 0) 44998c4c201SDavid VomLehn goto err_exit; 45098c4c201SDavid VomLehn err = ((dw_minor_mask & ver_expected) > (dw_minor_mask & ver_actual)) ? 45198c4c201SDavid VomLehn -EOPNOTSUPP : 0; 4527b0c342fSNikita Danilov 45398c4c201SDavid VomLehn err_exit: 45498c4c201SDavid VomLehn return err; 45598c4c201SDavid VomLehn } 45698c4c201SDavid VomLehn 45798c4c201SDavid VomLehn static int hw_atl_utils_init_ucp(struct aq_hw_s *self, 4584cbc9f92SIgor Russkikh const struct aq_hw_caps_s *aq_hw_caps) 45998c4c201SDavid VomLehn { 46098c4c201SDavid VomLehn int err = 0; 46198c4c201SDavid VomLehn 46298c4c201SDavid VomLehn if (!aq_hw_read_reg(self, 0x370U)) { 46398c4c201SDavid VomLehn unsigned int rnd = 0U; 46498c4c201SDavid VomLehn unsigned int ucp_0x370 = 0U; 46598c4c201SDavid VomLehn 46698c4c201SDavid VomLehn get_random_bytes(&rnd, sizeof(unsigned int)); 46798c4c201SDavid VomLehn 46898c4c201SDavid VomLehn ucp_0x370 = 0x02020202U | (0xFEFEFEFEU & rnd); 46998c4c201SDavid VomLehn aq_hw_write_reg(self, HW_ATL_UCP_0X370_REG, ucp_0x370); 47098c4c201SDavid VomLehn } 47198c4c201SDavid VomLehn 4728e1c072fSIgor Russkikh hw_atl_reg_glb_cpu_scratch_scp_set(self, 0x00000000U, 25U); 47398c4c201SDavid VomLehn 47498c4c201SDavid VomLehn /* check 10 times by 1ms */ 4756a7f2277SNikita Danilov err = readx_poll_timeout_atomic(hw_atl_scrpad25_get, 4766a7f2277SNikita Danilov self, self->mbox_addr, 4776a7f2277SNikita Danilov self->mbox_addr != 0U, 4786a7f2277SNikita Danilov 1000U, 10000U); 47998c4c201SDavid VomLehn 48098c4c201SDavid VomLehn return err; 48198c4c201SDavid VomLehn } 48298c4c201SDavid VomLehn 48398c4c201SDavid VomLehn struct aq_hw_atl_utils_fw_rpc_tid_s { 48498c4c201SDavid VomLehn union { 48598c4c201SDavid VomLehn u32 val; 48698c4c201SDavid VomLehn struct { 48798c4c201SDavid VomLehn u16 tid; 48898c4c201SDavid VomLehn u16 len; 48998c4c201SDavid VomLehn }; 49098c4c201SDavid VomLehn }; 49198c4c201SDavid VomLehn }; 49298c4c201SDavid VomLehn 49398c4c201SDavid VomLehn #define hw_atl_utils_fw_rpc_init(_H_) hw_atl_utils_fw_rpc_wait(_H_, NULL) 49498c4c201SDavid VomLehn 4953ee5c887SYana Esina int hw_atl_utils_fw_rpc_call(struct aq_hw_s *self, unsigned int rpc_size) 49698c4c201SDavid VomLehn { 49798c4c201SDavid VomLehn struct aq_hw_atl_utils_fw_rpc_tid_s sw; 4987b0c342fSNikita Danilov int err = 0; 49998c4c201SDavid VomLehn 50098c4c201SDavid VomLehn if (!IS_CHIP_FEATURE(MIPS)) { 50198c4c201SDavid VomLehn err = -1; 50298c4c201SDavid VomLehn goto err_exit; 50398c4c201SDavid VomLehn } 504dc12f75aSNikita Danilov err = hw_atl_write_fwcfg_dwords(self, (u32 *)(void *)&self->rpc, 50598c4c201SDavid VomLehn (rpc_size + sizeof(u32) - 50698c4c201SDavid VomLehn sizeof(u8)) / sizeof(u32)); 50798c4c201SDavid VomLehn if (err < 0) 50898c4c201SDavid VomLehn goto err_exit; 50998c4c201SDavid VomLehn 5101a713f87SIgor Russkikh sw.tid = 0xFFFFU & (++self->rpc_tid); 51198c4c201SDavid VomLehn sw.len = (u16)rpc_size; 51298c4c201SDavid VomLehn aq_hw_write_reg(self, HW_ATL_RPC_CONTROL_ADR, sw.val); 51398c4c201SDavid VomLehn 51498c4c201SDavid VomLehn err_exit: 51598c4c201SDavid VomLehn return err; 51698c4c201SDavid VomLehn } 51798c4c201SDavid VomLehn 5183ee5c887SYana Esina int hw_atl_utils_fw_rpc_wait(struct aq_hw_s *self, 5198f60f762SNikita Danilov struct hw_atl_utils_fw_rpc **rpc) 52098c4c201SDavid VomLehn { 52198c4c201SDavid VomLehn struct aq_hw_atl_utils_fw_rpc_tid_s sw; 52298c4c201SDavid VomLehn struct aq_hw_atl_utils_fw_rpc_tid_s fw; 5237b0c342fSNikita Danilov int err = 0; 52498c4c201SDavid VomLehn 52598c4c201SDavid VomLehn do { 52698c4c201SDavid VomLehn sw.val = aq_hw_read_reg(self, HW_ATL_RPC_CONTROL_ADR); 52798c4c201SDavid VomLehn 5281a713f87SIgor Russkikh self->rpc_tid = sw.tid; 52998c4c201SDavid VomLehn 5306a7f2277SNikita Danilov err = readx_poll_timeout_atomic(hw_atl_utils_rpc_state_get, 5316a7f2277SNikita Danilov self, fw.val, 5326a7f2277SNikita Danilov sw.tid == fw.tid, 5336a7f2277SNikita Danilov 1000U, 100000U); 53498c4c201SDavid VomLehn 53598c4c201SDavid VomLehn if (fw.len == 0xFFFFU) { 53698c4c201SDavid VomLehn err = hw_atl_utils_fw_rpc_call(self, sw.len); 53798c4c201SDavid VomLehn if (err < 0) 53898c4c201SDavid VomLehn goto err_exit; 53998c4c201SDavid VomLehn } 54098c4c201SDavid VomLehn } while (sw.tid != fw.tid || 0xFFFFU == fw.len); 54198c4c201SDavid VomLehn 54298c4c201SDavid VomLehn if (rpc) { 54398c4c201SDavid VomLehn if (fw.len) { 54498c4c201SDavid VomLehn err = 54598c4c201SDavid VomLehn hw_atl_utils_fw_downld_dwords(self, 5461a713f87SIgor Russkikh self->rpc_addr, 54798c4c201SDavid VomLehn (u32 *)(void *) 5481a713f87SIgor Russkikh &self->rpc, 54998c4c201SDavid VomLehn (fw.len + sizeof(u32) - 55098c4c201SDavid VomLehn sizeof(u8)) / 55198c4c201SDavid VomLehn sizeof(u32)); 55298c4c201SDavid VomLehn if (err < 0) 55398c4c201SDavid VomLehn goto err_exit; 55498c4c201SDavid VomLehn } 55598c4c201SDavid VomLehn 5561a713f87SIgor Russkikh *rpc = &self->rpc; 55798c4c201SDavid VomLehn } 55898c4c201SDavid VomLehn 55998c4c201SDavid VomLehn err_exit: 56098c4c201SDavid VomLehn return err; 56198c4c201SDavid VomLehn } 56298c4c201SDavid VomLehn 5631a713f87SIgor Russkikh static int hw_atl_utils_mpi_create(struct aq_hw_s *self) 56498c4c201SDavid VomLehn { 56598c4c201SDavid VomLehn int err = 0; 56698c4c201SDavid VomLehn 5671a713f87SIgor Russkikh err = hw_atl_utils_init_ucp(self, self->aq_nic_cfg->aq_hw_caps); 56898c4c201SDavid VomLehn if (err < 0) 56998c4c201SDavid VomLehn goto err_exit; 57098c4c201SDavid VomLehn 57198c4c201SDavid VomLehn err = hw_atl_utils_fw_rpc_init(self); 57298c4c201SDavid VomLehn if (err < 0) 57398c4c201SDavid VomLehn goto err_exit; 57498c4c201SDavid VomLehn 57598c4c201SDavid VomLehn err_exit: 57698c4c201SDavid VomLehn return err; 57798c4c201SDavid VomLehn } 57898c4c201SDavid VomLehn 57965e665e6SIgor Russkikh int hw_atl_utils_mpi_read_mbox(struct aq_hw_s *self, 5808f60f762SNikita Danilov struct hw_atl_utils_mbox_header *pmbox) 58165e665e6SIgor Russkikh { 58265e665e6SIgor Russkikh return hw_atl_utils_fw_downld_dwords(self, 5831a713f87SIgor Russkikh self->mbox_addr, 58465e665e6SIgor Russkikh (u32 *)(void *)pmbox, 58565e665e6SIgor Russkikh sizeof(*pmbox) / sizeof(u32)); 58665e665e6SIgor Russkikh } 58765e665e6SIgor Russkikh 58898c4c201SDavid VomLehn void hw_atl_utils_mpi_read_stats(struct aq_hw_s *self, 5898f60f762SNikita Danilov struct hw_atl_utils_mbox *pmbox) 59098c4c201SDavid VomLehn { 59198c4c201SDavid VomLehn int err = 0; 59298c4c201SDavid VomLehn 59398c4c201SDavid VomLehn err = hw_atl_utils_fw_downld_dwords(self, 5941a713f87SIgor Russkikh self->mbox_addr, 59598c4c201SDavid VomLehn (u32 *)(void *)pmbox, 59698c4c201SDavid VomLehn sizeof(*pmbox) / sizeof(u32)); 59798c4c201SDavid VomLehn if (err < 0) 59898c4c201SDavid VomLehn goto err_exit; 59998c4c201SDavid VomLehn 60098c4c201SDavid VomLehn if (IS_CHIP_FEATURE(REVISION_A0)) { 60198c4c201SDavid VomLehn unsigned int mtu = self->aq_nic_cfg ? 60298c4c201SDavid VomLehn self->aq_nic_cfg->mtu : 1514U; 60398c4c201SDavid VomLehn pmbox->stats.ubrc = pmbox->stats.uprc * mtu; 60498c4c201SDavid VomLehn pmbox->stats.ubtc = pmbox->stats.uptc * mtu; 6051a713f87SIgor Russkikh pmbox->stats.dpc = atomic_read(&self->dpc); 60698c4c201SDavid VomLehn } else { 607ce4cdbe4SDmitry Bogdanov pmbox->stats.dpc = hw_atl_rpb_rx_dma_drop_pkt_cnt_get(self); 60898c4c201SDavid VomLehn } 60998c4c201SDavid VomLehn 61098c4c201SDavid VomLehn err_exit:; 61198c4c201SDavid VomLehn } 61298c4c201SDavid VomLehn 613dfbd0749SWei Yongjun static int hw_atl_utils_mpi_set_speed(struct aq_hw_s *self, u32 speed) 61498c4c201SDavid VomLehn { 6150c58c35fSIgor Russkikh u32 val = aq_hw_read_reg(self, HW_ATL_MPI_CONTROL_ADR); 61698c4c201SDavid VomLehn 61744e00dd8SIgor Russkikh val = val & ~HW_ATL_MPI_SPEED_MSK; 61844e00dd8SIgor Russkikh val |= speed << HW_ATL_MPI_SPEED_SHIFT; 6190c58c35fSIgor Russkikh aq_hw_write_reg(self, HW_ATL_MPI_CONTROL_ADR, val); 62098c4c201SDavid VomLehn 62198c4c201SDavid VomLehn return 0; 62298c4c201SDavid VomLehn } 62398c4c201SDavid VomLehn 624dfbd0749SWei Yongjun static int hw_atl_utils_mpi_set_state(struct aq_hw_s *self, 62544e00dd8SIgor Russkikh enum hal_atl_utils_fw_state_e state) 62698c4c201SDavid VomLehn { 62744e00dd8SIgor Russkikh u32 val = aq_hw_read_reg(self, HW_ATL_MPI_CONTROL_ADR); 6287b0c342fSNikita Danilov struct hw_atl_utils_mbox_header mbox; 6297b0c342fSNikita Danilov u32 transaction_id = 0; 6307b0c342fSNikita Danilov int err = 0; 63198c4c201SDavid VomLehn 63298c4c201SDavid VomLehn if (state == MPI_RESET) { 63365e665e6SIgor Russkikh hw_atl_utils_mpi_read_mbox(self, &mbox); 63498c4c201SDavid VomLehn 63565e665e6SIgor Russkikh transaction_id = mbox.transaction_id; 63698c4c201SDavid VomLehn 6376a7f2277SNikita Danilov err = readx_poll_timeout_atomic(hw_atl_utils_get_mpi_mbox_tid, 6386a7f2277SNikita Danilov self, mbox.transaction_id, 6396a7f2277SNikita Danilov transaction_id != 6406a7f2277SNikita Danilov mbox.transaction_id, 6416a7f2277SNikita Danilov 1000U, 100000U); 64298c4c201SDavid VomLehn if (err < 0) 64398c4c201SDavid VomLehn goto err_exit; 64498c4c201SDavid VomLehn } 64544e00dd8SIgor Russkikh /* On interface DEINIT we disable DW (raise bit) 64644e00dd8SIgor Russkikh * Otherwise enable DW (clear bit) 64744e00dd8SIgor Russkikh */ 64844e00dd8SIgor Russkikh if (state == MPI_DEINIT || state == MPI_POWER) 64944e00dd8SIgor Russkikh val |= HW_ATL_MPI_DIRTY_WAKE_MSK; 65044e00dd8SIgor Russkikh else 65144e00dd8SIgor Russkikh val &= ~HW_ATL_MPI_DIRTY_WAKE_MSK; 65298c4c201SDavid VomLehn 65344e00dd8SIgor Russkikh /* Set new state bits */ 65444e00dd8SIgor Russkikh val = val & ~HW_ATL_MPI_STATE_MSK; 65544e00dd8SIgor Russkikh val |= state & HW_ATL_MPI_STATE_MSK; 65698c4c201SDavid VomLehn 6570c58c35fSIgor Russkikh aq_hw_write_reg(self, HW_ATL_MPI_CONTROL_ADR, val); 6587b0c342fSNikita Danilov 65944e00dd8SIgor Russkikh err_exit: 66044e00dd8SIgor Russkikh return err; 6610c58c35fSIgor Russkikh } 6620c58c35fSIgor Russkikh 663bd8ed441SPavel Belous int hw_atl_utils_mpi_get_link_status(struct aq_hw_s *self) 66498c4c201SDavid VomLehn { 665bd8ed441SPavel Belous struct aq_hw_link_status_s *link_status = &self->aq_link_status; 6667b0c342fSNikita Danilov u32 mpi_state; 6677b0c342fSNikita Danilov u32 speed; 66898c4c201SDavid VomLehn 6697b0c342fSNikita Danilov mpi_state = hw_atl_utils_mpi_get_state(self); 670ac70957eSIgor Russkikh speed = mpi_state >> HW_ATL_MPI_SPEED_SHIFT; 6717b0c342fSNikita Danilov 6727b0c342fSNikita Danilov if (!speed) { 67398c4c201SDavid VomLehn link_status->mbps = 0U; 67498c4c201SDavid VomLehn } else { 6757b0c342fSNikita Danilov switch (speed) { 67698c4c201SDavid VomLehn case HAL_ATLANTIC_RATE_10G: 67798c4c201SDavid VomLehn link_status->mbps = 10000U; 67898c4c201SDavid VomLehn break; 67998c4c201SDavid VomLehn 68098c4c201SDavid VomLehn case HAL_ATLANTIC_RATE_5G: 68198c4c201SDavid VomLehn case HAL_ATLANTIC_RATE_5GSR: 68298c4c201SDavid VomLehn link_status->mbps = 5000U; 68398c4c201SDavid VomLehn break; 68498c4c201SDavid VomLehn 68598c4c201SDavid VomLehn case HAL_ATLANTIC_RATE_2GS: 68698c4c201SDavid VomLehn link_status->mbps = 2500U; 68798c4c201SDavid VomLehn break; 68898c4c201SDavid VomLehn 68998c4c201SDavid VomLehn case HAL_ATLANTIC_RATE_1G: 69098c4c201SDavid VomLehn link_status->mbps = 1000U; 69198c4c201SDavid VomLehn break; 69298c4c201SDavid VomLehn 69398c4c201SDavid VomLehn case HAL_ATLANTIC_RATE_100M: 69498c4c201SDavid VomLehn link_status->mbps = 100U; 69598c4c201SDavid VomLehn break; 69698c4c201SDavid VomLehn 69798c4c201SDavid VomLehn default: 698a7bb1beaSIgor Russkikh return -EBUSY; 69998c4c201SDavid VomLehn } 70098c4c201SDavid VomLehn } 70198c4c201SDavid VomLehn 70298c4c201SDavid VomLehn return 0; 70398c4c201SDavid VomLehn } 70498c4c201SDavid VomLehn 70598c4c201SDavid VomLehn int hw_atl_utils_get_mac_permanent(struct aq_hw_s *self, 70698c4c201SDavid VomLehn u8 *mac) 70798c4c201SDavid VomLehn { 7087b0c342fSNikita Danilov u32 mac_addr[2]; 7097b0c342fSNikita Danilov u32 efuse_addr; 71098c4c201SDavid VomLehn int err = 0; 71198c4c201SDavid VomLehn u32 h = 0U; 71298c4c201SDavid VomLehn u32 l = 0U; 71398c4c201SDavid VomLehn 71498c4c201SDavid VomLehn if (!aq_hw_read_reg(self, HW_ATL_UCP_0X370_REG)) { 71598c4c201SDavid VomLehn unsigned int ucp_0x370 = 0; 7167b0c342fSNikita Danilov unsigned int rnd = 0; 71798c4c201SDavid VomLehn 71898c4c201SDavid VomLehn get_random_bytes(&rnd, sizeof(unsigned int)); 71998c4c201SDavid VomLehn 72098c4c201SDavid VomLehn ucp_0x370 = 0x02020202 | (0xFEFEFEFE & rnd); 72198c4c201SDavid VomLehn aq_hw_write_reg(self, HW_ATL_UCP_0X370_REG, ucp_0x370); 72298c4c201SDavid VomLehn } 72398c4c201SDavid VomLehn 7247b0c342fSNikita Danilov efuse_addr = aq_hw_read_reg(self, 0x00000374U); 7257b0c342fSNikita Danilov 7267b0c342fSNikita Danilov err = hw_atl_utils_fw_downld_dwords(self, efuse_addr + (40U * 4U), 7277b0c342fSNikita Danilov mac_addr, ARRAY_SIZE(mac_addr)); 72898c4c201SDavid VomLehn if (err < 0) { 72998c4c201SDavid VomLehn mac_addr[0] = 0U; 73098c4c201SDavid VomLehn mac_addr[1] = 0U; 73198c4c201SDavid VomLehn err = 0; 73298c4c201SDavid VomLehn } else { 73398c4c201SDavid VomLehn mac_addr[0] = __swab32(mac_addr[0]); 73498c4c201SDavid VomLehn mac_addr[1] = __swab32(mac_addr[1]); 73598c4c201SDavid VomLehn } 73698c4c201SDavid VomLehn 73798c4c201SDavid VomLehn ether_addr_copy(mac, (u8 *)mac_addr); 73898c4c201SDavid VomLehn 73998c4c201SDavid VomLehn if ((mac[0] & 0x01U) || ((mac[0] | mac[1] | mac[2]) == 0x00U)) { 74098c4c201SDavid VomLehn /* chip revision */ 741e9157848SNikita Danilov l = 0xE3000000U | 742e9157848SNikita Danilov (0xFFFFU & aq_hw_read_reg(self, HW_ATL_UCP_0X370_REG)) | 743e9157848SNikita Danilov (0x00 << 16); 74498c4c201SDavid VomLehn h = 0x8001300EU; 74598c4c201SDavid VomLehn 74698c4c201SDavid VomLehn mac[5] = (u8)(0xFFU & l); 74798c4c201SDavid VomLehn l >>= 8; 74898c4c201SDavid VomLehn mac[4] = (u8)(0xFFU & l); 74998c4c201SDavid VomLehn l >>= 8; 75098c4c201SDavid VomLehn mac[3] = (u8)(0xFFU & l); 75198c4c201SDavid VomLehn l >>= 8; 75298c4c201SDavid VomLehn mac[2] = (u8)(0xFFU & l); 75398c4c201SDavid VomLehn mac[1] = (u8)(0xFFU & h); 75498c4c201SDavid VomLehn h >>= 8; 75598c4c201SDavid VomLehn mac[0] = (u8)(0xFFU & h); 75698c4c201SDavid VomLehn } 75798c4c201SDavid VomLehn 75898c4c201SDavid VomLehn return err; 75998c4c201SDavid VomLehn } 76098c4c201SDavid VomLehn 76198c4c201SDavid VomLehn unsigned int hw_atl_utils_mbps_2_speed_index(unsigned int mbps) 76298c4c201SDavid VomLehn { 76398c4c201SDavid VomLehn unsigned int ret = 0U; 76498c4c201SDavid VomLehn 76598c4c201SDavid VomLehn switch (mbps) { 76698c4c201SDavid VomLehn case 100U: 76798c4c201SDavid VomLehn ret = 5U; 76898c4c201SDavid VomLehn break; 76998c4c201SDavid VomLehn 77098c4c201SDavid VomLehn case 1000U: 77198c4c201SDavid VomLehn ret = 4U; 77298c4c201SDavid VomLehn break; 77398c4c201SDavid VomLehn 77498c4c201SDavid VomLehn case 2500U: 77598c4c201SDavid VomLehn ret = 3U; 77698c4c201SDavid VomLehn break; 77798c4c201SDavid VomLehn 77898c4c201SDavid VomLehn case 5000U: 77998c4c201SDavid VomLehn ret = 1U; 78098c4c201SDavid VomLehn break; 78198c4c201SDavid VomLehn 78298c4c201SDavid VomLehn case 10000U: 78398c4c201SDavid VomLehn ret = 0U; 78498c4c201SDavid VomLehn break; 78598c4c201SDavid VomLehn 78698c4c201SDavid VomLehn default: 78798c4c201SDavid VomLehn break; 78898c4c201SDavid VomLehn } 7897b0c342fSNikita Danilov 79098c4c201SDavid VomLehn return ret; 79198c4c201SDavid VomLehn } 79298c4c201SDavid VomLehn 79398c4c201SDavid VomLehn void hw_atl_utils_hw_chip_features_init(struct aq_hw_s *self, u32 *p) 79498c4c201SDavid VomLehn { 7958e1c072fSIgor Russkikh u32 val = hw_atl_reg_glb_mif_id_get(self); 79698c4c201SDavid VomLehn u32 mif_rev = val & 0xFFU; 7977b0c342fSNikita Danilov u32 chip_features = 0U; 79898c4c201SDavid VomLehn 79947203b34SIgor Russkikh if ((0xFU & mif_rev) == 1U) { 80047203b34SIgor Russkikh chip_features |= HAL_ATLANTIC_UTILS_CHIP_REVISION_A0 | 80198c4c201SDavid VomLehn HAL_ATLANTIC_UTILS_CHIP_MPI_AQ | 80298c4c201SDavid VomLehn HAL_ATLANTIC_UTILS_CHIP_MIPS; 80347203b34SIgor Russkikh } else if ((0xFU & mif_rev) == 2U) { 80447203b34SIgor Russkikh chip_features |= HAL_ATLANTIC_UTILS_CHIP_REVISION_B0 | 80547203b34SIgor Russkikh HAL_ATLANTIC_UTILS_CHIP_MPI_AQ | 80647203b34SIgor Russkikh HAL_ATLANTIC_UTILS_CHIP_MIPS | 80747203b34SIgor Russkikh HAL_ATLANTIC_UTILS_CHIP_TPO2 | 80847203b34SIgor Russkikh HAL_ATLANTIC_UTILS_CHIP_RPF2; 80947203b34SIgor Russkikh } else if ((0xFU & mif_rev) == 0xAU) { 81047203b34SIgor Russkikh chip_features |= HAL_ATLANTIC_UTILS_CHIP_REVISION_B1 | 81198c4c201SDavid VomLehn HAL_ATLANTIC_UTILS_CHIP_MPI_AQ | 81298c4c201SDavid VomLehn HAL_ATLANTIC_UTILS_CHIP_MIPS | 81398c4c201SDavid VomLehn HAL_ATLANTIC_UTILS_CHIP_TPO2 | 81498c4c201SDavid VomLehn HAL_ATLANTIC_UTILS_CHIP_RPF2; 81598c4c201SDavid VomLehn } 81698c4c201SDavid VomLehn 81798c4c201SDavid VomLehn *p = chip_features; 81898c4c201SDavid VomLehn } 81998c4c201SDavid VomLehn 82044e00dd8SIgor Russkikh static int hw_atl_fw1x_deinit(struct aq_hw_s *self) 82198c4c201SDavid VomLehn { 82244e00dd8SIgor Russkikh hw_atl_utils_mpi_set_speed(self, 0); 82344e00dd8SIgor Russkikh hw_atl_utils_mpi_set_state(self, MPI_DEINIT); 8247b0c342fSNikita Danilov 82598c4c201SDavid VomLehn return 0; 82698c4c201SDavid VomLehn } 82798c4c201SDavid VomLehn 82865e665e6SIgor Russkikh int hw_atl_utils_update_stats(struct aq_hw_s *self) 82965e665e6SIgor Russkikh { 830ce4cdbe4SDmitry Bogdanov struct aq_stats_s *cs = &self->curr_stats; 8317b0c342fSNikita Danilov struct hw_atl_utils_mbox mbox; 83265e665e6SIgor Russkikh 83365e665e6SIgor Russkikh hw_atl_utils_mpi_read_stats(self, &mbox); 83465e665e6SIgor Russkikh 8351a713f87SIgor Russkikh #define AQ_SDELTA(_N_) (self->curr_stats._N_ += \ 8361a713f87SIgor Russkikh mbox.stats._N_ - self->last_stats._N_) 8371a713f87SIgor Russkikh 838be08d839SIgor Russkikh if (self->aq_link_status.mbps) { 83965e665e6SIgor Russkikh AQ_SDELTA(uprc); 84065e665e6SIgor Russkikh AQ_SDELTA(mprc); 84165e665e6SIgor Russkikh AQ_SDELTA(bprc); 84265e665e6SIgor Russkikh AQ_SDELTA(erpt); 84365e665e6SIgor Russkikh 84465e665e6SIgor Russkikh AQ_SDELTA(uptc); 84565e665e6SIgor Russkikh AQ_SDELTA(mptc); 84665e665e6SIgor Russkikh AQ_SDELTA(bptc); 84765e665e6SIgor Russkikh AQ_SDELTA(erpr); 84865e665e6SIgor Russkikh 84965e665e6SIgor Russkikh AQ_SDELTA(ubrc); 85065e665e6SIgor Russkikh AQ_SDELTA(ubtc); 85165e665e6SIgor Russkikh AQ_SDELTA(mbrc); 85265e665e6SIgor Russkikh AQ_SDELTA(mbtc); 85365e665e6SIgor Russkikh AQ_SDELTA(bbrc); 85465e665e6SIgor Russkikh AQ_SDELTA(bbtc); 85565e665e6SIgor Russkikh AQ_SDELTA(dpc); 856be08d839SIgor Russkikh } 85765e665e6SIgor Russkikh #undef AQ_SDELTA 858ce4cdbe4SDmitry Bogdanov 859ce4cdbe4SDmitry Bogdanov cs->dma_pkt_rc = hw_atl_stats_rx_dma_good_pkt_counter_get(self); 860ce4cdbe4SDmitry Bogdanov cs->dma_pkt_tc = hw_atl_stats_tx_dma_good_pkt_counter_get(self); 861ce4cdbe4SDmitry Bogdanov cs->dma_oct_rc = hw_atl_stats_rx_dma_good_octet_counter_get(self); 862ce4cdbe4SDmitry Bogdanov cs->dma_oct_tc = hw_atl_stats_tx_dma_good_octet_counter_get(self); 86365e665e6SIgor Russkikh 8641a713f87SIgor Russkikh memcpy(&self->last_stats, &mbox.stats, sizeof(mbox.stats)); 86565e665e6SIgor Russkikh 86665e665e6SIgor Russkikh return 0; 86765e665e6SIgor Russkikh } 86865e665e6SIgor Russkikh 869be08d839SIgor Russkikh struct aq_stats_s *hw_atl_utils_get_hw_stats(struct aq_hw_s *self) 87098c4c201SDavid VomLehn { 8711a713f87SIgor Russkikh return &self->curr_stats; 87298c4c201SDavid VomLehn } 87398c4c201SDavid VomLehn 87498c4c201SDavid VomLehn static const u32 hw_atl_utils_hw_mac_regs[] = { 87598c4c201SDavid VomLehn 0x00005580U, 0x00005590U, 0x000055B0U, 0x000055B4U, 87698c4c201SDavid VomLehn 0x000055C0U, 0x00005B00U, 0x00005B04U, 0x00005B08U, 87798c4c201SDavid VomLehn 0x00005B0CU, 0x00005B10U, 0x00005B14U, 0x00005B18U, 87898c4c201SDavid VomLehn 0x00005B1CU, 0x00005B20U, 0x00005B24U, 0x00005B28U, 87998c4c201SDavid VomLehn 0x00005B2CU, 0x00005B30U, 0x00005B34U, 0x00005B38U, 88098c4c201SDavid VomLehn 0x00005B3CU, 0x00005B40U, 0x00005B44U, 0x00005B48U, 88198c4c201SDavid VomLehn 0x00005B4CU, 0x00005B50U, 0x00005B54U, 0x00005B58U, 88298c4c201SDavid VomLehn 0x00005B5CU, 0x00005B60U, 0x00005B64U, 0x00005B68U, 88398c4c201SDavid VomLehn 0x00005B6CU, 0x00005B70U, 0x00005B74U, 0x00005B78U, 88498c4c201SDavid VomLehn 0x00005B7CU, 0x00007C00U, 0x00007C04U, 0x00007C08U, 88598c4c201SDavid VomLehn 0x00007C0CU, 0x00007C10U, 0x00007C14U, 0x00007C18U, 88698c4c201SDavid VomLehn 0x00007C1CU, 0x00007C20U, 0x00007C40U, 0x00007C44U, 88798c4c201SDavid VomLehn 0x00007C48U, 0x00007C4CU, 0x00007C50U, 0x00007C54U, 88898c4c201SDavid VomLehn 0x00007C58U, 0x00007C5CU, 0x00007C60U, 0x00007C80U, 88998c4c201SDavid VomLehn 0x00007C84U, 0x00007C88U, 0x00007C8CU, 0x00007C90U, 89098c4c201SDavid VomLehn 0x00007C94U, 0x00007C98U, 0x00007C9CU, 0x00007CA0U, 89198c4c201SDavid VomLehn 0x00007CC0U, 0x00007CC4U, 0x00007CC8U, 0x00007CCCU, 89298c4c201SDavid VomLehn 0x00007CD0U, 0x00007CD4U, 0x00007CD8U, 0x00007CDCU, 89398c4c201SDavid VomLehn 0x00007CE0U, 0x00000300U, 0x00000304U, 0x00000308U, 89498c4c201SDavid VomLehn 0x0000030cU, 0x00000310U, 0x00000314U, 0x00000318U, 89598c4c201SDavid VomLehn 0x0000031cU, 0x00000360U, 0x00000364U, 0x00000368U, 89698c4c201SDavid VomLehn 0x0000036cU, 0x00000370U, 0x00000374U, 0x00006900U, 89798c4c201SDavid VomLehn }; 89898c4c201SDavid VomLehn 89998c4c201SDavid VomLehn int hw_atl_utils_hw_get_regs(struct aq_hw_s *self, 9004cbc9f92SIgor Russkikh const struct aq_hw_caps_s *aq_hw_caps, 90198c4c201SDavid VomLehn u32 *regs_buff) 90298c4c201SDavid VomLehn { 90398c4c201SDavid VomLehn unsigned int i = 0U; 90498c4c201SDavid VomLehn 90598c4c201SDavid VomLehn for (i = 0; i < aq_hw_caps->mac_regs_count; i++) 90698c4c201SDavid VomLehn regs_buff[i] = aq_hw_read_reg(self, 90798c4c201SDavid VomLehn hw_atl_utils_hw_mac_regs[i]); 9087b0c342fSNikita Danilov 90998c4c201SDavid VomLehn return 0; 91098c4c201SDavid VomLehn } 91198c4c201SDavid VomLehn 91298c4c201SDavid VomLehn int hw_atl_utils_get_fw_version(struct aq_hw_s *self, u32 *fw_version) 91398c4c201SDavid VomLehn { 91498c4c201SDavid VomLehn *fw_version = aq_hw_read_reg(self, 0x18U); 9157b0c342fSNikita Danilov 91698c4c201SDavid VomLehn return 0; 91798c4c201SDavid VomLehn } 9180c58c35fSIgor Russkikh 919837c6378SNikita Danilov static int aq_fw1x_set_wake_magic(struct aq_hw_s *self, bool wol_enabled, 920837c6378SNikita Danilov u8 *mac) 921a0da96c0SYana Esina { 9228f60f762SNikita Danilov struct hw_atl_utils_fw_rpc *prpc = NULL; 923a0da96c0SYana Esina unsigned int rpc_size = 0U; 924a0da96c0SYana Esina int err = 0; 925a0da96c0SYana Esina 926a0da96c0SYana Esina err = hw_atl_utils_fw_rpc_wait(self, &prpc); 927a0da96c0SYana Esina if (err < 0) 928a0da96c0SYana Esina goto err_exit; 929a0da96c0SYana Esina 930a0da96c0SYana Esina memset(prpc, 0, sizeof(*prpc)); 931a0da96c0SYana Esina 932a0da96c0SYana Esina if (wol_enabled) { 933d993e14bSNikita Danilov rpc_size = offsetof(struct hw_atl_utils_fw_rpc, msg_wol_add) + 934d993e14bSNikita Danilov sizeof(prpc->msg_wol_add); 935d993e14bSNikita Danilov 936a0da96c0SYana Esina 937a0da96c0SYana Esina prpc->msg_id = HAL_ATLANTIC_UTILS_FW_MSG_WOL_ADD; 938d993e14bSNikita Danilov prpc->msg_wol_add.priority = 939a0da96c0SYana Esina HAL_ATLANTIC_UTILS_FW_MSG_WOL_PRIOR; 940d993e14bSNikita Danilov prpc->msg_wol_add.pattern_id = 941a0da96c0SYana Esina HAL_ATLANTIC_UTILS_FW_MSG_WOL_PATTERN; 942d993e14bSNikita Danilov prpc->msg_wol_add.packet_type = 943a0da96c0SYana Esina HAL_ATLANTIC_UTILS_FW_MSG_WOL_MAG_PKT; 944a0da96c0SYana Esina 945d993e14bSNikita Danilov ether_addr_copy((u8 *)&prpc->msg_wol_add.magic_packet_pattern, 946d993e14bSNikita Danilov mac); 947a0da96c0SYana Esina } else { 948d993e14bSNikita Danilov rpc_size = sizeof(prpc->msg_wol_remove) + 949d993e14bSNikita Danilov offsetof(struct hw_atl_utils_fw_rpc, msg_wol_remove); 950a0da96c0SYana Esina 951a0da96c0SYana Esina prpc->msg_id = HAL_ATLANTIC_UTILS_FW_MSG_WOL_DEL; 952d993e14bSNikita Danilov prpc->msg_wol_add.pattern_id = 953a0da96c0SYana Esina HAL_ATLANTIC_UTILS_FW_MSG_WOL_PATTERN; 954a0da96c0SYana Esina } 955a0da96c0SYana Esina 956a0da96c0SYana Esina err = hw_atl_utils_fw_rpc_call(self, rpc_size); 957a0da96c0SYana Esina 958a0da96c0SYana Esina err_exit: 959a0da96c0SYana Esina return err; 960a0da96c0SYana Esina } 961a0da96c0SYana Esina 9623d5537f9SWei Yongjun static int aq_fw1x_set_power(struct aq_hw_s *self, unsigned int power_state, 963a0da96c0SYana Esina u8 *mac) 964a0da96c0SYana Esina { 9658f60f762SNikita Danilov struct hw_atl_utils_fw_rpc *prpc = NULL; 966a0da96c0SYana Esina unsigned int rpc_size = 0U; 967a0da96c0SYana Esina int err = 0; 968a0da96c0SYana Esina 969837c6378SNikita Danilov if (self->aq_nic_cfg->wol & WAKE_MAGIC) { 970837c6378SNikita Danilov err = aq_fw1x_set_wake_magic(self, 1, mac); 971a0da96c0SYana Esina 972a0da96c0SYana Esina if (err < 0) 973a0da96c0SYana Esina goto err_exit; 974a0da96c0SYana Esina 975a0da96c0SYana Esina rpc_size = sizeof(prpc->msg_id) + 976a0da96c0SYana Esina sizeof(prpc->msg_enable_wakeup); 977a0da96c0SYana Esina 978a0da96c0SYana Esina err = hw_atl_utils_fw_rpc_wait(self, &prpc); 979a0da96c0SYana Esina 980a0da96c0SYana Esina if (err < 0) 981a0da96c0SYana Esina goto err_exit; 982a0da96c0SYana Esina 983a0da96c0SYana Esina memset(prpc, 0, rpc_size); 984a0da96c0SYana Esina 985a0da96c0SYana Esina prpc->msg_id = HAL_ATLANTIC_UTILS_FW_MSG_ENABLE_WAKEUP; 986a0da96c0SYana Esina prpc->msg_enable_wakeup.pattern_mask = 0x00000002; 987a0da96c0SYana Esina 988a0da96c0SYana Esina err = hw_atl_utils_fw_rpc_call(self, rpc_size); 989a0da96c0SYana Esina if (err < 0) 990a0da96c0SYana Esina goto err_exit; 991a0da96c0SYana Esina } 992a0da96c0SYana Esina hw_atl_utils_mpi_set_speed(self, 0); 993a0da96c0SYana Esina hw_atl_utils_mpi_set_state(self, MPI_POWER); 994a0da96c0SYana Esina 995a0da96c0SYana Esina err_exit: 996a0da96c0SYana Esina return err; 997a0da96c0SYana Esina } 998a0da96c0SYana Esina 9996a7f2277SNikita Danilov static u32 hw_atl_utils_get_mpi_mbox_tid(struct aq_hw_s *self) 10006a7f2277SNikita Danilov { 10016a7f2277SNikita Danilov struct hw_atl_utils_mbox_header mbox; 10026a7f2277SNikita Danilov 10036a7f2277SNikita Danilov hw_atl_utils_mpi_read_mbox(self, &mbox); 10046a7f2277SNikita Danilov 10056a7f2277SNikita Danilov return mbox.transaction_id; 10066a7f2277SNikita Danilov } 10076a7f2277SNikita Danilov 10086a7f2277SNikita Danilov static u32 hw_atl_utils_mpi_get_state(struct aq_hw_s *self) 10096a7f2277SNikita Danilov { 10106a7f2277SNikita Danilov return aq_hw_read_reg(self, HW_ATL_MPI_STATE_ADR); 10116a7f2277SNikita Danilov } 10126a7f2277SNikita Danilov 10136a7f2277SNikita Danilov static u32 hw_atl_utils_mif_cmd_get(struct aq_hw_s *self) 10146a7f2277SNikita Danilov { 10156a7f2277SNikita Danilov return aq_hw_read_reg(self, HW_ATL_MIF_CMD); 10166a7f2277SNikita Danilov } 10176a7f2277SNikita Danilov 10186a7f2277SNikita Danilov static u32 hw_atl_utils_mif_addr_get(struct aq_hw_s *self) 10196a7f2277SNikita Danilov { 10206a7f2277SNikita Danilov return aq_hw_read_reg(self, HW_ATL_MIF_ADDR); 10216a7f2277SNikita Danilov } 10226a7f2277SNikita Danilov 10236a7f2277SNikita Danilov static u32 hw_atl_utils_rpc_state_get(struct aq_hw_s *self) 10246a7f2277SNikita Danilov { 10256a7f2277SNikita Danilov return aq_hw_read_reg(self, HW_ATL_RPC_STATE_ADR); 10266a7f2277SNikita Danilov } 10276a7f2277SNikita Danilov 10280c58c35fSIgor Russkikh const struct aq_fw_ops aq_fw_1x_ops = { 10290c58c35fSIgor Russkikh .init = hw_atl_utils_mpi_create, 103044e00dd8SIgor Russkikh .deinit = hw_atl_fw1x_deinit, 10310c58c35fSIgor Russkikh .reset = NULL, 10320c58c35fSIgor Russkikh .get_mac_permanent = hw_atl_utils_get_mac_permanent, 10330c58c35fSIgor Russkikh .set_link_speed = hw_atl_utils_mpi_set_speed, 10340c58c35fSIgor Russkikh .set_state = hw_atl_utils_mpi_set_state, 10350c58c35fSIgor Russkikh .update_link_status = hw_atl_utils_mpi_get_link_status, 10360c58c35fSIgor Russkikh .update_stats = hw_atl_utils_update_stats, 10378f894011SYana Esina .get_phy_temp = NULL, 1038a0da96c0SYana Esina .set_power = aq_fw1x_set_power, 103992ab6407SYana Esina .set_eee_rate = NULL, 104092ab6407SYana Esina .get_eee_rate = NULL, 1041288551deSIgor Russkikh .set_flow_control = NULL, 1042910479a9SEgor Pomozov .send_fw_request = NULL, 1043910479a9SEgor Pomozov .enable_ptp = NULL, 1044d1287ce4SNikita Danilov .led_control = NULL, 10450c58c35fSIgor Russkikh }; 1046