| /** @file |
| * |
| * Marvell AQtion family network card driver, hardware-specific functions. |
| * |
| * Copyright(C) 2017-2024 Marvell |
| * |
| * SPDX-License-Identifier: BSD-2-Clause |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are met: |
| |
| * 1. Redistributions of source code must retain the above copyright notice, |
| * this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright notice, |
| * this list of conditions and the following disclaimer in the documentation |
| * and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
| * TO,THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR HOLDER OR |
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; |
| * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
| * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR |
| * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, |
| * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| * |
| */ |
| |
| FILE_LICENCE ( BSD2 ); |
| |
| #include <errno.h> |
| #include <stdio.h> |
| #include <unistd.h> |
| #include <byteswap.h> |
| #include <ipxe/pci.h> |
| #include "aqc1xx.h" |
| #include "atl_hw.h" |
| #include <compiler.h> |
| |
| |
| int atl_hw_reset_flb_ ( struct atl_nic *nic ) { |
| uint32_t val; |
| int k = 0; |
| |
| ATL_WRITE_REG ( ATL_GLB_CTRL2_MBOX_ERR_UP_RUN_STALL, ATL_GLB_CTRL2 ); |
| mdelay ( ATL_DELAY_50_MNS ); |
| |
| /* Cleanup SPI */ |
| val = ATL_READ_REG ( ATL_GLB_NVR_PROV4 ); |
| ATL_WRITE_REG ( val | ATL_GBL_NVR_PROV4_RESET, ATL_GLB_NVR_PROV4 ); |
| |
| ATL_WRITE_REG( ( ATL_READ_REG ( ATL_GLB_STD_CTRL ) & |
| ~ATL_GLB_CTRL_RST_DIS ) | ATL_GLB_STD_CTRL_RESET, |
| ATL_GLB_STD_CTRL ); |
| |
| /* Kickstart MAC */ |
| ATL_WRITE_REG ( ATL_GLB_CTRL2_FW_RESET, ATL_GLB_CTRL2 ); |
| ATL_WRITE_REG ( ATL_MIF_PWR_GATING_EN_CTRL_RESET, |
| ATL_MIF_PWR_GATING_EN_CTRL ); |
| |
| ATL_WRITE_REG ( ATL_GEN_PROV9_ENABLE, ATL_GEN_PROV9 ); |
| |
| /* Reset SPI again because of possible interrupted SPI burst */ |
| val = ATL_READ_REG ( ATL_GLB_NVR_PROV4 ); |
| ATL_WRITE_REG ( val | ATL_GBL_NVR_PROV4_RESET, ATL_GLB_NVR_PROV4 ); |
| mdelay ( ATL_DELAY_10_MNS ); |
| /* Clear SPI reset state */ |
| ATL_WRITE_REG ( val & ~ATL_GBL_NVR_PROV4_RESET, ATL_GLB_NVR_PROV4 ); |
| |
| /* MAC Kickstart */ |
| ATL_WRITE_REG ( ATL_GLB_CTRL2_MAC_KICK_START, ATL_GLB_CTRL2 ); |
| |
| for (k = 0; k < 1000; k++) { |
| uint32_t flb_status = ATL_READ_REG ( ATL_MPI_DAISY_CHAIN_STS ); |
| |
| flb_status = flb_status & FLB_LOAD_STS; |
| if ( flb_status ) |
| break; |
| mdelay ( ATL_DELAY_10_MNS ); |
| } |
| if ( k == 1000 ) { |
| DBGC (nic, "MAC kickstart failed\n" ); |
| return -EIO; |
| } |
| |
| /* FW reset */ |
| ATL_WRITE_REG ( ATL_GLB_CTRL2_FW_RESET, ATL_GLB_CTRL2 ); |
| mdelay ( ATL_DELAY_50_MNS ); |
| |
| ATL_WRITE_REG ( ATL_GBL_MCP_SEM1_RELEASE, ATL_GLB_MCP_SEM1 ); |
| |
| /* Global software reset*/ |
| ATL_WRITE_REG ( ATL_READ_REG ( ATL_RX_CTRL ) & |
| ~ATL_RX_CTRL_RST_DIS, ATL_RX_CTRL ); |
| ATL_WRITE_REG ( ATL_READ_REG ( ATL_TX_CTRL ) & |
| ~ATL_TX_CTRL_RST_DIS, ATL_TX_CTRL ); |
| |
| ATL_WRITE_REG ( ATL_READ_REG ( ATL_MAC_PHY_CTRL ) & |
| ~ATL_MAC_PHY_CTRL_RST_DIS, ATL_MAC_PHY_CTRL ); |
| |
| ATL_WRITE_REG ( ( ATL_READ_REG ( ATL_GLB_STD_CTRL ) & |
| ~ATL_GLB_CTRL_RST_DIS ) | ATL_GLB_STD_CTRL_RESET, |
| ATL_GLB_STD_CTRL ); |
| |
| for (k = 0; k < 1000; k++) { |
| u32 fw_state = ATL_READ_REG ( ATL_FW_VER ); |
| |
| if ( fw_state ) |
| break; |
| mdelay ( ATL_DELAY_10_MNS ); |
| } |
| if ( k == 1000 ) { |
| DBGC ( nic, "FW kickstart failed\n" ); |
| return -EIO; |
| } |
| /* Old FW requires fixed delay after init */ |
| mdelay ( ATL_DELAY_15_MNS ); |
| |
| return 0; |
| } |
| |
| int atl_hw_reset_rbl_ ( struct atl_nic *nic ) { |
| uint32_t val, rbl_status; |
| int k; |
| |
| ATL_WRITE_REG ( ATL_GLB_CTRL2_MBOX_ERR_UP_RUN_STALL, ATL_GLB_CTRL2 ); |
| ATL_WRITE_REG ( ATL_GBL_MCP_SEM1_RELEASE, ATL_GLB_MCP_SEM1 ); |
| ATL_WRITE_REG ( ATL_MIF_PWR_GATING_EN_CTRL_RESET, |
| ATL_MIF_PWR_GATING_EN_CTRL ); |
| |
| /* Alter RBL status */ |
| ATL_WRITE_REG ( POISON_SIGN, ATL_MPI_BOOT_EXIT_CODE ); |
| |
| /* Cleanup SPI */ |
| val = ATL_READ_REG ( ATL_GLB_NVR_PROV4 ); |
| ATL_WRITE_REG ( val | ATL_GBL_NVR_PROV4_RESET, ATL_GLB_NVR_PROV4 ); |
| |
| /* Global software reset*/ |
| ATL_WRITE_REG ( ATL_READ_REG ( ATL_RX_CTRL ) & ~ATL_RX_CTRL_RST_DIS, |
| ATL_RX_CTRL ); |
| ATL_WRITE_REG ( ATL_READ_REG ( ATL_TX_CTRL ) & ~ATL_TX_CTRL_RST_DIS, |
| ATL_TX_CTRL ); |
| ATL_WRITE_REG ( ATL_READ_REG ( ATL_MAC_PHY_CTRL ) & |
| ~ATL_MAC_PHY_CTRL_RST_DIS, ATL_MAC_PHY_CTRL ); |
| |
| ATL_WRITE_REG ( ( ATL_READ_REG ( ATL_GLB_STD_CTRL ) & |
| ~ATL_GLB_CTRL_RST_DIS ) | ATL_GLB_STD_CTRL_RESET, |
| ATL_GLB_STD_CTRL ); |
| |
| ATL_WRITE_REG ( ATL_GLB_CTRL2_MBOX_ERR_UP_RUN_NORMAL, ATL_GLB_CTRL2 ); |
| |
| /* Wait for RBL boot */ |
| for ( k = 0; k < 1000; k++ ) { |
| rbl_status = ATL_READ_REG ( ATL_MPI_BOOT_EXIT_CODE ) & 0xFFFF; |
| if ( rbl_status && rbl_status != POISON_SIGN ) |
| break; |
| mdelay ( ATL_DELAY_10_MNS ); |
| } |
| if ( !rbl_status || rbl_status == POISON_SIGN ) { |
| DBGC ( nic, "RBL Restart failed\n" ); |
| return -EIO; |
| } |
| |
| if ( rbl_status == FW_NOT_SUPPORT ) |
| return -ENOTSUP; |
| |
| for ( k = 0; k < 1000; k++ ) { |
| u32 fw_state = ATL_READ_REG ( ATL_FW_VER ); |
| |
| if ( fw_state ) |
| break; |
| mdelay ( ATL_DELAY_10_MNS ); |
| } |
| if ( k == 1000 ) { |
| DBGC ( nic, "FW kickstart failed\n" ); |
| return -EIO; |
| } |
| /* Old FW requires fixed delay after init */ |
| mdelay ( ATL_DELAY_15_MNS ); |
| |
| return 0; |
| } |
| |
| int atl_hw_reset ( struct atl_nic *nic ) { |
| uint32_t boot_exit_code = 0; |
| uint32_t k; |
| int rbl_enabled; |
| uint32_t fw_ver; |
| uint32_t sem_timeout; |
| |
| for ( k = 0; k < 1000; ++k ) { |
| uint32_t flb_status = ATL_READ_REG ( ATL_MPI_DAISY_CHAIN_STS ); |
| boot_exit_code = ATL_READ_REG ( ATL_MPI_BOOT_EXIT_CODE ); |
| if ( flb_status != ATL_MPI_DAISY_CHAIN_STS_ERROR_STATUS || |
| boot_exit_code != 0 ) |
| break; |
| } |
| |
| if ( k == 1000 ) { |
| DBGC ( nic, "Neither RBL nor FLB firmware started\n" ); |
| return -ENOTSUP; |
| } |
| |
| rbl_enabled = ( boot_exit_code != 0 ); |
| |
| fw_ver = ATL_READ_REG ( ATL_FW_VER ); |
| if ( ( ( fw_ver >> 24 ) & 0xFF ) >= 4 ) { |
| sem_timeout = ATL_READ_REG ( ATL_SEM_TIMEOUT ); |
| if ( sem_timeout > ATL_SEM_MAX_TIMEOUT ) |
| sem_timeout = ATL_SEM_MAX_TIMEOUT; |
| |
| for ( k = 0; k < sem_timeout; ++k ) { |
| if ( ATL_READ_REG ( ATL_GLB_MCP_SEM4 ) ) |
| break; |
| |
| mdelay ( ATL_DELAY_1_MNS ); |
| } |
| for ( k = 0; k < sem_timeout; ++k ) { |
| if ( ATL_READ_REG ( ATL_GLB_MCP_SEM5 ) ) |
| break; |
| |
| mdelay ( ATL_DELAY_1_MNS ); |
| } |
| } |
| |
| |
| if ( rbl_enabled ) |
| return atl_hw_reset_rbl_ ( nic ); |
| else |
| return atl_hw_reset_flb_ ( nic ); |
| } |
| |
| int atl_hw_start ( struct atl_nic *nic ) { |
| ATL_WRITE_REG ( ATL_LINK_ADV_AUTONEG, ATL_LINK_ADV ); |
| return 0; |
| } |
| |
| int atl_hw_stop ( struct atl_nic *nic ) { |
| ATL_WRITE_REG ( ATL_SHUT_LINK, ATL_LINK_ADV ); |
| return 0; |
| } |
| |
| int atl_hw_get_link ( struct atl_nic *nic ) { |
| return ( ATL_READ_REG ( ATL_LINK_ST) & ATL_LINK_ADV_AUTONEG ) != 0; |
| } |
| |
| int atl_hw_read_mem ( struct atl_nic *nic, uint32_t addr, uint32_t *buffer, |
| uint32_t size ) { |
| uint32_t i; |
| |
| for ( i = 0; i < 100; ++i ) { |
| if ( ATL_READ_REG( ATL_SEM_RAM ) ) |
| break; |
| mdelay ( ATL_DELAY_1_MNS ); |
| } |
| if ( i == 100 ) { |
| DBGC ( nic, "Semaphore Register not set\n" ); |
| return -EIO; |
| } |
| |
| ATL_WRITE_REG ( addr, ATL_MBOX_CTRL3 ); |
| |
| for ( i = 0; i < size; ++i, addr += 4 ) { |
| uint32_t j; |
| |
| ATL_WRITE_REG ( ATL_MBOX_CTRL1_START_MBOX_OPT, ATL_MBOX_CTRL1 ); |
| for ( j = 0; j < 10000; ++j ) { |
| if ( ATL_READ_REG (ATL_MBOX_CTRL3 ) != addr ) |
| break; |
| udelay ( ATL_DELAY_10_MNS ); |
| } |
| if ( j == 10000 ) { |
| DBGC ( nic, "Reading from CTRL3 Register Failed\n" ); |
| return -EIO; |
| } |
| |
| buffer[i] = ATL_READ_REG ( ATL_MBOX_CTRL5 ); |
| } |
| |
| ATL_WRITE_REG( ATL_SEM_RAM_RESET, ATL_SEM_RAM ); |
| |
| return 0; |
| } |
| |
| int atl_hw_get_mac ( struct atl_nic *nic, uint8_t *mac ) { |
| uint32_t mac_addr[2] = {0}; |
| int err = 0; |
| uint32_t efuse_addr = ATL_READ_REG ( ATL_GLB_MCP_SP26 ); |
| |
| if ( efuse_addr != 0) { |
| uint32_t mac_efuse_addr = efuse_addr + 40 * sizeof ( uint32_t ); |
| err = atl_hw_read_mem ( nic, mac_efuse_addr, mac_addr, 2 ); |
| if ( err != 0 ) |
| return err; |
| |
| mac_addr[0] = cpu_to_be32 ( mac_addr[0] ); |
| mac_addr[1] = cpu_to_be32 ( mac_addr[1] ); |
| |
| memcpy ( mac, ( uint8_t * )mac_addr, ATL_MAC_ADDRESS_SIZE ); |
| } |
| return 0; |
| } |
| |
| struct atl_hw_ops atl_hw = { |
| .reset = atl_hw_reset, |
| .start = atl_hw_start, |
| .stop = atl_hw_stop, |
| .get_link = atl_hw_get_link, |
| .get_mac = atl_hw_get_mac, |
| }; |