| /** @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 "atl2_hw.h" |
| |
| static int atl2_hw_boot_completed_ ( struct atl_nic *nic ) { |
| uint32_t reset_status = ATL_READ_REG ( ATL2_GLB_RST_CTRL2 ); |
| |
| return ( reset_status & ATL2_RESET_STATUS_BOOT_COMPLETED_MASK ) || |
| ( ATL_READ_REG ( ATL2_HOST_ITR_REQ ) |
| & ATL2_FW_HOST_INTERRUPT_REQUEST_READY ); |
| } |
| |
| void atl2_hw_read_shared_in_ ( struct atl_nic *nic, uint32_t offset, |
| uint32_t *data, uint32_t len ) { |
| uint32_t i; |
| |
| for (i = 0; i < len; ++i ) |
| { |
| data[i] = ATL_READ_REG ( ATL2_MIF_SHARED_BUF_IN + offset + i * 4 ); |
| } |
| } |
| |
| void atl2_hw_write_shared_in_ ( struct atl_nic *nic, uint32_t offset, |
| uint32_t *data, uint32_t len ) { |
| uint32_t i; |
| |
| for ( i = 0; i < len; ++i ) |
| { |
| ATL_WRITE_REG ( data[i], ATL2_MIF_SHARED_BUF_IN + offset + i * 4 ); |
| } |
| } |
| |
| int atl2_hw_finish_ack_ ( struct atl_nic *nic, uint32_t ms ) { |
| uint32_t i; |
| int err = 0; |
| |
| ATL_WRITE_REG ( ATL_READ_REG ( ATL2_HOST_FINISHED_WRITE ) |
| | 1, ATL2_HOST_FINISHED_WRITE ); |
| |
| for ( i = 0; i < ( ms / 100 ); ++i ) |
| { |
| if ( ( ATL_READ_REG ( ATL2_MCP_BUSY_WRITE ) & 1 ) == 0 ) |
| { |
| break; |
| } |
| udelay ( ATL2_DELAY_100 ); |
| } |
| if (i == ( ms / 100 ) ) |
| err = -ETIME; |
| |
| return err; |
| } |
| |
| int atl2_hw_fw_init_ ( struct atl_nic *nic ) { |
| uint32_t val; |
| int err = 0; |
| |
| atl2_hw_read_shared_in_ ( nic, ATL2_LINK_CTRL_IN_OFF, &val, 1 ); |
| val |= ( ATL2_HOST_MODE_ACTIVE | ( 1U << 13 ) ); |
| atl2_hw_write_shared_in_ ( nic, ATL2_LINK_CTRL_IN_OFF, &val, 1 ); |
| |
| atl2_hw_read_shared_in_ ( nic, ATL2_MTU_IN_OFF, &val, 1 ); |
| val = 16352; |
| atl2_hw_write_shared_in_ ( nic, ATL2_MTU_IN_OFF, &val, 1 ); |
| |
| atl2_hw_read_shared_in_ ( nic, ATL2_LINK_OPTS_IN_OFF, &val, 1 ); |
| val = 0; |
| atl2_hw_write_shared_in_( nic, ATL2_LINK_OPTS_IN_OFF, &val, 1 ); |
| err = atl2_hw_finish_ack_ ( nic, 50000000 ); |
| |
| return err; |
| } |
| |
| int atl2_hw_reset ( struct atl_nic *nic ) { |
| int completed = 0; |
| uint32_t status = 0; |
| uint32_t request; |
| int err = 0; |
| int i; |
| |
| request = ATL2_RESET_STATUS_REQ_GSR; |
| |
| ATL_WRITE_REG ( request, ATL2_GLB_RST_CTRL2 ); |
| |
| /* Wait for boot code started every 10us, 200 ms */ |
| for ( i = 0; i < 20000; ++i ) |
| { |
| status = ATL_READ_REG ( ATL2_GLB_RST_CTRL2 ); |
| |
| if ( ( ( status & ATL2_RESET_STATUS_BC_STARTED ) && |
| ( status != 0xFFFFFFFFu ) ) ) |
| break; |
| |
| udelay ( ATL2_DELAY_10 ); |
| } |
| if ( i == 20000 ) |
| { |
| DBGC ( nic, "Boot code hanged" ); |
| err = -EIO; |
| goto err_exit; |
| } |
| |
| /* Wait for boot succeed, failed or host request every 10us, 480ms */ |
| for ( i = 0; i < 48000; ++i ) |
| { |
| completed = atl2_hw_boot_completed_ ( nic ); |
| if ( completed ) |
| break; |
| |
| udelay ( ATL2_DELAY_10 ); |
| } |
| |
| if ( !completed ) |
| { |
| DBGC ( nic, "FW Restart timed out" ); |
| err = -ETIME; |
| goto err_exit; |
| } |
| |
| status = ATL_READ_REG ( ATL2_GLB_RST_CTRL2 ); |
| |
| if ( status & ATL2_RESET_STATUS_BOOT_FAILED_MASK ) |
| { |
| err = -EIO; |
| DBGC ( nic, "FW Restart failed" ); |
| DBGC ( nic, "status = 0x%x", status ); |
| goto err_exit; |
| } |
| |
| if ( ATL_READ_REG ( ATL2_HOST_ITR_REQ ) |
| & ATL2_FW_HOST_INTERRUPT_REQUEST_READY ) |
| { |
| err = -ENOTSUP; |
| DBGC ( nic, "Dynamic FW load not implemented" ); |
| goto err_exit; |
| } |
| |
| err = atl2_hw_fw_init_ ( nic ); |
| |
| err_exit: |
| return err; |
| } |
| |
| int atl2_hw_start ( struct atl_nic *nic ) { |
| uint32_t val; |
| |
| atl2_hw_read_shared_in_ ( nic, ATL2_LINK_OPTS_IN_OFF, &val, 1 ); |
| val = 0x4B00FFE1; |
| atl2_hw_write_shared_in_ ( nic, ATL2_LINK_OPTS_IN_OFF, &val, 1 ); |
| |
| return atl2_hw_finish_ack_ ( nic, 100000 ); |
| } |
| |
| int atl2_hw_stop ( struct atl_nic *nic ) { |
| uint32_t val; |
| |
| atl2_hw_read_shared_in_ ( nic, ATL2_LINK_OPTS_IN_OFF, &val, 1 ); |
| val = 0; |
| atl2_hw_write_shared_in_ ( nic, ATL2_LINK_OPTS_IN_OFF, &val, 1 ); |
| |
| return atl2_hw_finish_ack_ ( nic, 100000 ); |
| } |
| |
| int atl2_hw_get_link ( struct atl_nic *nic ) { |
| uint32_t val; |
| |
| val = ATL_READ_REG ( ATL2_MIF_SHARED_BUF_OUT + ATL2_LINK_STS_OUT_OFF ); |
| |
| return ( ( val & 0xf ) != 0 ) && ( ( val & 0xF0 ) != 0 ); |
| } |
| |
| int atl2_hw_get_mac ( struct atl_nic *nic, uint8_t *mac ) { |
| uint32_t mac_addr[2] = {0}; |
| |
| atl2_hw_read_shared_in_ ( nic, ATL2_MAC_ADDR_IN_OFF, mac_addr, 2 ); |
| |
| memcpy ( mac, ( uint8_t * )mac_addr, 6 ); |
| |
| return 0; |
| } |
| |
| struct atl_hw_ops atl2_hw = { |
| .reset = atl2_hw_reset, |
| .start = atl2_hw_start, |
| .stop = atl2_hw_stop, |
| .get_link = atl2_hw_get_link, |
| .get_mac = atl2_hw_get_mac, |
| }; |