From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from NAM01-SN1-obe.outbound.protection.outlook.com (mail-sn1nam01on0075.outbound.protection.outlook.com [104.47.32.75]) by dpdk.org (Postfix) with ESMTP id 3FC4B1B2AE for ; Tue, 9 Oct 2018 11:31:40 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=AQUANTIA1COM.onmicrosoft.com; s=selector1-aquantia-com; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=pWnxsDFQAEFeKU248XJ80N5sRPzfnKTEq9DeKFzC0Kk=; b=jIuND+ob58ENHK0nMdrSHJEyrIbtbbNpPqi7k6DsKRHSDKKmfpec23GovhxkXRLLo/kdxp47pZzVhybRNiUzmrphxeztrteW5Iepna3U7BVFwavTGrxvuqfS/MvaZGzE1VdUeOX9bbdAMY/bOGMMGEThOZHFRiNSkKP3MbF2vCU= Received: from BLUPR0701MB1651.namprd07.prod.outlook.com (10.163.84.21) by BLUPR0701MB1650.namprd07.prod.outlook.com (10.163.84.20) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.1207.23; Tue, 9 Oct 2018 09:31:37 +0000 Received: from BLUPR0701MB1651.namprd07.prod.outlook.com ([fe80::7c97:1527:4c0:8b46]) by BLUPR0701MB1651.namprd07.prod.outlook.com ([fe80::7c97:1527:4c0:8b46%3]) with mapi id 15.20.1207.024; Tue, 9 Oct 2018 09:31:37 +0000 From: Igor Russkikh To: "dev@dpdk.org" CC: Pavel Belous , Igor Russkikh , "ferruh.yigit@intel.com" , Pavel Belous Thread-Topic: [PATCH v4 05/22] net/atlantic: firmware operations layer Thread-Index: AQHUX7Lfb99sWVCFgku/CihESXYyFQ== Date: Tue, 9 Oct 2018 09:31:36 +0000 Message-ID: References: In-Reply-To: Accept-Language: en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: x-clientproxiedby: AM0PR01CA0028.eurprd01.prod.exchangelabs.com (2603:10a6:208:69::41) To BLUPR0701MB1651.namprd07.prod.outlook.com (2a01:111:e400:58c6::21) authentication-results: spf=none (sender IP is ) smtp.mailfrom=Igor.Russkikh@aquantia.com; x-ms-exchange-messagesentrepresentingtype: 1 x-originating-ip: [95.79.108.179] x-ms-publictraffictype: Email x-microsoft-exchange-diagnostics: 1; BLUPR0701MB1650; 6:2mlHd8fJ+aTvLQUCoWGRFNKD4mFm2DlNjIWHtl+2glbaHydzzXPJcavRksST/LOyIj8vycpTRMdgITfvS1Bl+MJRHz6o/YF5y/jjp+T0xCVFmIjOEeFGPFjrNcnnqAFfXtv7yiHOb6BbWtgSNQ7pgg8ooLmJ1eWca6aKmY5YLdDoBimlrtESQsnTpYjVpg8PEYbmSOBuwoShSN4pVTsnxv3RKJHCoD2kclylvIRY+6wXP+ObRXpwSsdaCg6yUI12pelN93gtrA9WFFj+HvUdvTLnr+WZ4cwkTNQ17EpVdVUHe+GRGYAG7Fa84KuliAz1W5DWLXtNlG5QU03xiacNE+fvyVDMljWQTJw9t5h6hK0VOwTsosLMKqNlgGhhYQ9KE6kSrINEUqbUpcvppgX1OzDGudLRha8dwqvBY8ZFxPGhGBvY0SLLXLBiVNFYcJUOMEys5B0HsuqzdYr8FeL7xw==; 5:Zp9RzvUgHqgqSVx2omIFNMr8HkGnPweieRqVzUGCqRTSYozgZCR7W0nInId17B67ZKNVg/K/jflrK6T752yqFPtVj2Qb8QtbKJ3QuAlxWgsDaEAq+Y3u94ZGE8GwwpShNU3tSzwuXF5oTVrDtLQ5Mh/6Q/hSv7PkrR+q2AG0XfA=; 7:HbUdALix8w9kjQj2DBXHfbq5PurwAnZWqwxCmDnuO55etIj2HPUCnI64UPD+GwFx6C5fkrz+A8TFVFwimpBtmn4P3NN0g49uypqh3HBa9dx6w+WpcocKEBr1FYNOFeUZIkF8rfnnLkFKcbnfS3fv6uhVOxDnsYHvUdWWfBTYhzgsB3alHmCA3c+dQzc5OQGs+YufbjNRT5BVCnnQSrUx+U9gJWzCGJyUrTdhDisXqIgRrO3p/tUlH7DBlrmbFY3k x-ms-office365-filtering-correlation-id: 9e091ef2-5e9d-461c-76b3-08d62dca0226 x-microsoft-antispam: BCL:0; PCL:0; RULEID:(7020095)(4652040)(8989299)(4534185)(4627221)(201703031133081)(201702281549075)(8990200)(5600074)(711020)(2017052603328)(7153060)(7193020); SRVR:BLUPR0701MB1650; x-ms-traffictypediagnostic: BLUPR0701MB1650: x-microsoft-antispam-prvs: x-exchange-antispam-report-test: UriScan:; x-ms-exchange-senderadcheck: 1 x-exchange-antispam-report-cfa-test: BCL:0; PCL:0; RULEID:(6040522)(2401047)(8121501046)(5005006)(10201501046)(3231355)(944501410)(52105095)(93006095)(93001095)(3002001)(149066)(150057)(6041310)(20161123564045)(20161123562045)(201703131423095)(201702281528075)(20161123555045)(201703061421075)(201703061406153)(20161123560045)(20161123558120)(201708071742011)(7699051); SRVR:BLUPR0701MB1650; BCL:0; PCL:0; RULEID:; SRVR:BLUPR0701MB1650; x-forefront-prvs: 08200063E9 x-forefront-antispam-report: SFV:NSPM; SFS:(10009020)(366004)(39850400004)(346002)(376002)(396003)(136003)(189003)(199004)(81156014)(5640700003)(6116002)(71200400001)(105586002)(68736007)(316002)(3846002)(107886003)(71190400001)(26005)(118296001)(2616005)(551934003)(5250100002)(25786009)(4326008)(102836004)(44832011)(446003)(476003)(11346002)(186003)(2906002)(106356001)(97736004)(2501003)(2351001)(486006)(478600001)(99286004)(53936002)(386003)(1730700003)(53946003)(5660300001)(16200700003)(6506007)(36756003)(66066001)(54906003)(305945005)(256004)(14444005)(4744004)(86362001)(575784001)(7736002)(2900100001)(76176011)(8936002)(52116002)(8676002)(6486002)(72206003)(6436002)(14454004)(81166006)(6512007)(6916009)(559001)(569006); DIR:OUT; SFP:1101; SCL:1; SRVR:BLUPR0701MB1650; H:BLUPR0701MB1651.namprd07.prod.outlook.com; FPR:; SPF:None; LANG:en; PTR:InfoNoRecords; MX:1; A:1; received-spf: None (protection.outlook.com: aquantia.com does not designate permitted sender hosts) x-microsoft-antispam-message-info: iVqS39vBH+N7bDmVmLtt4LCyAQLxH+INGPx5yObtv1fYLDsrBgSjEGNQz6VdkAYQVn23rjlL4A7kFYd4j1S00+RBofa4QeJziCFjY7OlAE0zzv1ET5wVn8K0Sb/S3UE5BjKdnC0fRCx8ZH/1HH9WOB9VRRaDh8OI0ARdSCO9lnmEiRw8hktTnZzbWGdKz6PHcx0ul0X8dr3FWNyhlKvSb3nuWU29ILBwWnbevLmVmcQV1yI8ogMVRyDKCULUcoNmiqhfnH8ss3sMCZyYVbyRo75mLa64nfYYXGzMEjDHklFzz6yJsCNuHTl325jA/g4r8z8WFvPu8HfVaBqqSUDXqqDuqm5Z/L8V4xOwRYrUEio= spamdiagnosticoutput: 1:99 spamdiagnosticmetadata: NSPM Content-Type: text/plain; charset="iso-8859-1" Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 X-OriginatorOrg: aquantia.com X-MS-Exchange-CrossTenant-Network-Message-Id: 9e091ef2-5e9d-461c-76b3-08d62dca0226 X-MS-Exchange-CrossTenant-originalarrivaltime: 09 Oct 2018 09:31:36.8647 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: 83e2e134-991c-4ede-8ced-34d47e38e6b1 X-MS-Exchange-Transport-CrossTenantHeadersStamped: BLUPR0701MB1650 Subject: [dpdk-dev] [PATCH v4 05/22] net/atlantic: firmware operations layer X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 09 Oct 2018 09:31:41 -0000 AQC NICs comes in fields with two major FW generations: 1x and 3x. This is part of linux atlantic driver shared code, responsible for internal NIC firmware interactions, including link management ops, FW initialization, various lifecycle features. Signed-off-by: Igor Russkikh Signed-off-by: Pavel Belous --- drivers/net/atlantic/Makefile | 2 + drivers/net/atlantic/atl_types.h | 95 +++ drivers/net/atlantic/hw_atl/hw_atl_utils.c | 942 ++++++++++++++++++++= ++++ drivers/net/atlantic/hw_atl/hw_atl_utils.h | 510 +++++++++++++ drivers/net/atlantic/hw_atl/hw_atl_utils_fw2x.c | 618 ++++++++++++++++ drivers/net/atlantic/meson.build | 2 + 6 files changed, 2169 insertions(+) create mode 100644 drivers/net/atlantic/hw_atl/hw_atl_utils.c create mode 100644 drivers/net/atlantic/hw_atl/hw_atl_utils.h create mode 100644 drivers/net/atlantic/hw_atl/hw_atl_utils_fw2x.c diff --git a/drivers/net/atlantic/Makefile b/drivers/net/atlantic/Makefile index cae317badc0c..a291053b5ab7 100644 --- a/drivers/net/atlantic/Makefile +++ b/drivers/net/atlantic/Makefile @@ -26,6 +26,8 @@ VPATH +=3D $(SRCDIR)/hw_atl # SRCS-$(CONFIG_RTE_LIBRTE_ATLANTIC_PMD) +=3D atl_ethdev.c SRCS-$(CONFIG_RTE_LIBRTE_ATLANTIC_PMD) +=3D atl_hw_regs.c +SRCS-$(CONFIG_RTE_LIBRTE_ATLANTIC_PMD) +=3D hw_atl_utils.c SRCS-$(CONFIG_RTE_LIBRTE_ATLANTIC_PMD) +=3D hw_atl_llh.c +SRCS-$(CONFIG_RTE_LIBRTE_ATLANTIC_PMD) +=3D hw_atl_utils_fw2x.c =20 include $(RTE_SDK)/mk/rte.lib.mk diff --git a/drivers/net/atlantic/atl_types.h b/drivers/net/atlantic/atl_ty= pes.h index 725b0c4dc670..af24da3a5a54 100644 --- a/drivers/net/atlantic/atl_types.h +++ b/drivers/net/atlantic/atl_types.h @@ -22,8 +22,103 @@ typedef uint64_t u64; #define min(a, b) RTE_MIN(a, b) #define max(a, b) RTE_MAX(a, b) =20 +#include "hw_atl/hw_atl_utils.h" + +struct aq_hw_link_status_s { + unsigned int mbps; +}; + +struct aq_stats_s { + u64 uprc; + u64 mprc; + u64 bprc; + u64 erpt; + u64 uptc; + u64 mptc; + u64 bptc; + u64 erpr; + u64 mbtc; + u64 bbtc; + u64 mbrc; + u64 bbrc; + u64 ubrc; + u64 ubtc; + u64 dpc; + u64 dma_pkt_rc; + u64 dma_pkt_tc; + u64 dma_oct_rc; + u64 dma_oct_tc; +}; + +struct aq_hw_cfg_s { + bool is_lro; + int wol; + + int link_speed_msk; + int irq_type; + int irq_mask; + unsigned int vecs; + + uint32_t flow_control; +}; + struct aq_hw_s { + u8 rbl_enabled:1; + struct aq_hw_cfg_s *aq_nic_cfg; + const struct aq_fw_ops *aq_fw_ops; void *mmio; + + struct aq_hw_link_status_s aq_link_status; + + struct hw_aq_atl_utils_mbox mbox; + struct hw_atl_stats_s last_stats; + struct aq_stats_s curr_stats; + + unsigned int chip_features; + u32 fw_ver_actual; + u32 mbox_addr; + u32 rpc_addr; + u32 rpc_tid; + struct hw_aq_atl_utils_fw_rpc rpc; +}; + +struct aq_fw_ops { + int (*init)(struct aq_hw_s *self); + + int (*deinit)(struct aq_hw_s *self); + + int (*reset)(struct aq_hw_s *self); + + int (*get_mac_permanent)(struct aq_hw_s *self, u8 *mac); + + int (*set_link_speed)(struct aq_hw_s *self, u32 speed); + + int (*set_state)(struct aq_hw_s *self, + enum hal_atl_utils_fw_state_e state); + + int (*update_link_status)(struct aq_hw_s *self); + + int (*update_stats)(struct aq_hw_s *self); + + int (*set_power)(struct aq_hw_s *self, unsigned int power_state, + u8 *mac); + + int (*get_temp)(struct aq_hw_s *self, int *temp); + + int (*get_cable_len)(struct aq_hw_s *self, int *cable_len); + + int (*set_eee_rate)(struct aq_hw_s *self, u32 speed); + + int (*get_eee_rate)(struct aq_hw_s *self, u32 *rate, + u32 *supported_rates); + + int (*set_flow_control)(struct aq_hw_s *self); + + int (*led_control)(struct aq_hw_s *self, u32 mode); + + int (*get_eeprom)(struct aq_hw_s *self, u32 *data, u32 len); + + int (*set_eeprom)(struct aq_hw_s *self, u32 *data, u32 len); }; =20 #endif diff --git a/drivers/net/atlantic/hw_atl/hw_atl_utils.c b/drivers/net/atlan= tic/hw_atl/hw_atl_utils.c new file mode 100644 index 000000000000..f18ce54762dc --- /dev/null +++ b/drivers/net/atlantic/hw_atl/hw_atl_utils.c @@ -0,0 +1,942 @@ +// SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0) +/* Copyright (C) 2014-2017 aQuantia Corporation. */ + +/* File hw_atl_utils.c: Definition of common functions for Atlantic hardwa= re + * abstraction layer. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "../atl_hw_regs.h" + +#include "hw_atl_llh.h" +#include "hw_atl_llh_internal.h" +#include "../atl_logs.h" + +#define HW_ATL_UCP_0X370_REG 0x0370U + +#define HW_ATL_MIF_CMD 0x0200U +#define HW_ATL_MIF_ADDR 0x0208U +#define HW_ATL_MIF_VAL 0x020CU + +#define HW_ATL_FW_SM_RAM 0x2U +#define HW_ATL_MPI_FW_VERSION 0x18 +#define HW_ATL_MPI_CONTROL_ADR 0x0368U +#define HW_ATL_MPI_STATE_ADR 0x036CU + +#define HW_ATL_MPI_STATE_MSK 0x00FFU +#define HW_ATL_MPI_STATE_SHIFT 0U +#define HW_ATL_MPI_SPEED_MSK 0x00FF0000U +#define HW_ATL_MPI_SPEED_SHIFT 16U +#define HW_ATL_MPI_DIRTY_WAKE_MSK 0x02000000U + +#define HW_ATL_MPI_DAISY_CHAIN_STATUS 0x704 +#define HW_ATL_MPI_BOOT_EXIT_CODE 0x388 + +#define HW_ATL_MAC_PHY_CONTROL 0x4000 +#define HW_ATL_MAC_PHY_MPI_RESET_BIT 0x1D + +#define HW_ATL_FW_VER_1X 0x01050006U +#define HW_ATL_FW_VER_2X 0x02000000U +#define HW_ATL_FW_VER_3X 0x03000000U + +#define FORCE_FLASHLESS 0 + +static int hw_atl_utils_ver_match(u32 ver_expected, u32 ver_actual); +static int hw_atl_utils_mpi_set_state(struct aq_hw_s *self, + enum hal_atl_utils_fw_state_e state); + + +int hw_atl_utils_initfw(struct aq_hw_s *self, const struct aq_fw_ops **fw_= ops) +{ + int err =3D 0; + + err =3D hw_atl_utils_soft_reset(self); + if (err) + return err; + + hw_atl_utils_hw_chip_features_init(self, + &self->chip_features); + + hw_atl_utils_get_fw_version(self, &self->fw_ver_actual); + + if (hw_atl_utils_ver_match(HW_ATL_FW_VER_1X, + self->fw_ver_actual) =3D=3D 0) { + *fw_ops =3D &aq_fw_1x_ops; + } else if (hw_atl_utils_ver_match(HW_ATL_FW_VER_2X, + self->fw_ver_actual) =3D=3D 0) { + *fw_ops =3D &aq_fw_2x_ops; + } else if (hw_atl_utils_ver_match(HW_ATL_FW_VER_3X, + self->fw_ver_actual) =3D=3D 0) { + *fw_ops =3D &aq_fw_2x_ops; + } else { + PMD_DRV_LOG(ERR, "Bad FW version detected: %x\n", + self->fw_ver_actual); + return -EOPNOTSUPP; + } + self->aq_fw_ops =3D *fw_ops; + err =3D self->aq_fw_ops->init(self); + return err; +} + +static int hw_atl_utils_soft_reset_flb(struct aq_hw_s *self) +{ + u32 gsr, val; + int k =3D 0; + + aq_hw_write_reg(self, 0x404, 0x40e1); + AQ_HW_SLEEP(50); + + /* Cleanup SPI */ + val =3D aq_hw_read_reg(self, 0x53C); + aq_hw_write_reg(self, 0x53C, val | 0x10); + + gsr =3D aq_hw_read_reg(self, HW_ATL_GLB_SOFT_RES_ADR); + aq_hw_write_reg(self, HW_ATL_GLB_SOFT_RES_ADR, (gsr & 0xBFFF) | 0x8000); + + /* Kickstart MAC */ + aq_hw_write_reg(self, 0x404, 0x80e0); + aq_hw_write_reg(self, 0x32a8, 0x0); + aq_hw_write_reg(self, 0x520, 0x1); + + /* Reset SPI again because of possible interrupted SPI burst */ + val =3D aq_hw_read_reg(self, 0x53C); + aq_hw_write_reg(self, 0x53C, val | 0x10); + AQ_HW_SLEEP(10); + /* Clear SPI reset state */ + aq_hw_write_reg(self, 0x53C, val & ~0x10); + + aq_hw_write_reg(self, 0x404, 0x180e0); + + for (k =3D 0; k < 1000; k++) { + u32 flb_status =3D aq_hw_read_reg(self, + HW_ATL_MPI_DAISY_CHAIN_STATUS); + + flb_status =3D flb_status & 0x10; + if (flb_status) + break; + AQ_HW_SLEEP(10); + } + if (k =3D=3D 1000) { + PMD_DRV_LOG(ERR, "MAC kickstart failed\n"); + return -EIO; + } + + /* FW reset */ + aq_hw_write_reg(self, 0x404, 0x80e0); + AQ_HW_SLEEP(50); + aq_hw_write_reg(self, 0x3a0, 0x1); + + /* Kickstart PHY - skipped */ + + /* Global software reset*/ + hw_atl_rx_rx_reg_res_dis_set(self, 0U); + hw_atl_tx_tx_reg_res_dis_set(self, 0U); + aq_hw_write_reg_bit(self, HW_ATL_MAC_PHY_CONTROL, + BIT(HW_ATL_MAC_PHY_MPI_RESET_BIT), + HW_ATL_MAC_PHY_MPI_RESET_BIT, 0x0); + gsr =3D aq_hw_read_reg(self, HW_ATL_GLB_SOFT_RES_ADR); + aq_hw_write_reg(self, HW_ATL_GLB_SOFT_RES_ADR, (gsr & 0xBFFF) | 0x8000); + + for (k =3D 0; k < 1000; k++) { + u32 fw_state =3D aq_hw_read_reg(self, HW_ATL_MPI_FW_VERSION); + + if (fw_state) + break; + AQ_HW_SLEEP(10); + } + if (k =3D=3D 1000) { + PMD_DRV_LOG(ERR, "FW kickstart failed\n"); + return -EIO; + } + /* Old FW requires fixed delay after init */ + AQ_HW_SLEEP(15); + + return 0; +} + +static int hw_atl_utils_soft_reset_rbl(struct aq_hw_s *self) +{ + u32 gsr, val, rbl_status; + int k; + + aq_hw_write_reg(self, 0x404, 0x40e1); + aq_hw_write_reg(self, 0x3a0, 0x1); + aq_hw_write_reg(self, 0x32a8, 0x0); + + /* Alter RBL status */ + aq_hw_write_reg(self, 0x388, 0xDEAD); + + /* Cleanup SPI */ + val =3D aq_hw_read_reg(self, 0x53C); + aq_hw_write_reg(self, 0x53C, val | 0x10); + + /* Global software reset*/ + hw_atl_rx_rx_reg_res_dis_set(self, 0U); + hw_atl_tx_tx_reg_res_dis_set(self, 0U); + aq_hw_write_reg_bit(self, HW_ATL_MAC_PHY_CONTROL, + BIT(HW_ATL_MAC_PHY_MPI_RESET_BIT), + HW_ATL_MAC_PHY_MPI_RESET_BIT, 0x0); + gsr =3D aq_hw_read_reg(self, HW_ATL_GLB_SOFT_RES_ADR); + aq_hw_write_reg(self, HW_ATL_GLB_SOFT_RES_ADR, + (gsr & 0xFFFFBFFF) | 0x8000); + + if (FORCE_FLASHLESS) + aq_hw_write_reg(self, 0x534, 0x0); + + aq_hw_write_reg(self, 0x404, 0x40e0); + + /* Wait for RBL boot */ + for (k =3D 0; k < 1000; k++) { + rbl_status =3D aq_hw_read_reg(self, 0x388) & 0xFFFF; + if (rbl_status && rbl_status !=3D 0xDEAD) + break; + AQ_HW_SLEEP(10); + } + if (!rbl_status || rbl_status =3D=3D 0xDEAD) { + PMD_DRV_LOG(ERR, "RBL Restart failed"); + return -EIO; + } + + /* Restore NVR */ + if (FORCE_FLASHLESS) + aq_hw_write_reg(self, 0x534, 0xA0); + + if (rbl_status =3D=3D 0xF1A7) { + PMD_DRV_LOG(ERR, "No FW detected. Dynamic FW load not implemented\n"); + return -EOPNOTSUPP; + } + + for (k =3D 0; k < 1000; k++) { + u32 fw_state =3D aq_hw_read_reg(self, HW_ATL_MPI_FW_VERSION); + + if (fw_state) + break; + AQ_HW_SLEEP(10); + } + if (k =3D=3D 1000) { + PMD_DRV_LOG(ERR, "FW kickstart failed\n"); + return -EIO; + } + /* Old FW requires fixed delay after init */ + AQ_HW_SLEEP(15); + + return 0; +} + +int hw_atl_utils_soft_reset(struct aq_hw_s *self) +{ + int err =3D 0; + int k; + u32 boot_exit_code =3D 0; + + for (k =3D 0; k < 1000; ++k) { + u32 flb_status =3D aq_hw_read_reg(self, + HW_ATL_MPI_DAISY_CHAIN_STATUS); + boot_exit_code =3D aq_hw_read_reg(self, + HW_ATL_MPI_BOOT_EXIT_CODE); + if (flb_status !=3D 0x06000000 || boot_exit_code !=3D 0) + break; + } + + if (k =3D=3D 1000) { + PMD_DRV_LOG(ERR, "Neither RBL nor FLB firmware started\n"); + return -EOPNOTSUPP; + } + + self->rbl_enabled =3D (boot_exit_code !=3D 0); + + /* FW 1.x may bootup in an invalid POWER state (WOL feature). + * We should work around this by forcing its state back to DEINIT + */ + if (!hw_atl_utils_ver_match(HW_ATL_FW_VER_1X, + aq_hw_read_reg(self, + HW_ATL_MPI_FW_VERSION))) { + hw_atl_utils_mpi_set_state(self, MPI_DEINIT); + AQ_HW_WAIT_FOR((aq_hw_read_reg(self, HW_ATL_MPI_STATE_ADR) & + HW_ATL_MPI_STATE_MSK) =3D=3D MPI_DEINIT, + 10, 1000U); + } + + if (self->rbl_enabled) + err =3D hw_atl_utils_soft_reset_rbl(self); + else + err =3D hw_atl_utils_soft_reset_flb(self); + + return err; +} + +int hw_atl_utils_fw_downld_dwords(struct aq_hw_s *self, u32 a, + u32 *p, u32 cnt) +{ + int err =3D 0; + + AQ_HW_WAIT_FOR(hw_atl_reg_glb_cpu_sem_get(self, + HW_ATL_FW_SM_RAM) =3D=3D 1U, + 1U, 10000U); + + if (err < 0) { + bool is_locked; + + hw_atl_reg_glb_cpu_sem_set(self, 1U, HW_ATL_FW_SM_RAM); + is_locked =3D hw_atl_reg_glb_cpu_sem_get(self, HW_ATL_FW_SM_RAM); + if (!is_locked) { + err =3D -ETIMEDOUT; + goto err_exit; + } + } + + aq_hw_write_reg(self, HW_ATL_MIF_ADDR, a); + + for (++cnt; --cnt && !err;) { + aq_hw_write_reg(self, HW_ATL_MIF_CMD, 0x00008000U); + + if (IS_CHIP_FEATURE(REVISION_B1)) + AQ_HW_WAIT_FOR(a !=3D aq_hw_read_reg(self, + HW_ATL_MIF_ADDR), + 1, 1000U); + else + AQ_HW_WAIT_FOR(!(0x100 & aq_hw_read_reg(self, + HW_ATL_MIF_CMD)), + 1, 1000U); + + *(p++) =3D aq_hw_read_reg(self, HW_ATL_MIF_VAL); + a +=3D 4; + } + + hw_atl_reg_glb_cpu_sem_set(self, 1U, HW_ATL_FW_SM_RAM); + +err_exit: + return err; +} + +int hw_atl_utils_fw_upload_dwords(struct aq_hw_s *self, u32 a, u32 *p, + u32 cnt) +{ + int err =3D 0; + bool is_locked; + + is_locked =3D hw_atl_reg_glb_cpu_sem_get(self, HW_ATL_FW_SM_RAM); + if (!is_locked) { + err =3D -ETIMEDOUT; + goto err_exit; + } + if (IS_CHIP_FEATURE(REVISION_B1)) { + u32 offset =3D 0; + + for (; offset < cnt; ++offset) { + aq_hw_write_reg(self, 0x328, p[offset]); + aq_hw_write_reg(self, 0x32C, + (0x80000000 | (0xFFFF & (offset * 4)))); + mcp_up_force_intr_set(self, 1); + /* 1000 times by 10us =3D 10ms */ + AQ_HW_WAIT_FOR((aq_hw_read_reg(self, + 0x32C) & 0xF0000000) !=3D 0x80000000, + 10, 1000); + } + } else { + u32 offset =3D 0; + + aq_hw_write_reg(self, 0x208, a); + + for (; offset < cnt; ++offset) { + aq_hw_write_reg(self, 0x20C, p[offset]); + aq_hw_write_reg(self, 0x200, 0xC000); + + AQ_HW_WAIT_FOR((aq_hw_read_reg(self, 0x200U) + & 0x100) =3D=3D 0, 10, 1000); + } + } + + hw_atl_reg_glb_cpu_sem_set(self, 1U, HW_ATL_FW_SM_RAM); + +err_exit: + return err; +} + +static int hw_atl_utils_ver_match(u32 ver_expected, u32 ver_actual) +{ + int err =3D 0; + const u32 dw_major_mask =3D 0xff000000U; + const u32 dw_minor_mask =3D 0x00ffffffU; + + err =3D (dw_major_mask & (ver_expected ^ ver_actual)) ? -EOPNOTSUPP : 0; + if (err < 0) + goto err_exit; + err =3D ((dw_minor_mask & ver_expected) > (dw_minor_mask & ver_actual)) ? + -EOPNOTSUPP : 0; +err_exit: + return err; +} + +static int hw_atl_utils_init_ucp(struct aq_hw_s *self) +{ + int err =3D 0; + + if (!aq_hw_read_reg(self, 0x370U)) { + unsigned int rnd =3D (uint32_t)rte_rand(); + unsigned int ucp_0x370 =3D 0U; + + ucp_0x370 =3D 0x02020202U | (0xFEFEFEFEU & rnd); + aq_hw_write_reg(self, HW_ATL_UCP_0X370_REG, ucp_0x370); + } + + hw_atl_reg_glb_cpu_scratch_scp_set(self, 0x00000000U, 25U); + + /* check 10 times by 1ms */ + AQ_HW_WAIT_FOR(0U !=3D (self->mbox_addr =3D + aq_hw_read_reg(self, 0x360U)), 1000U, 10U); + AQ_HW_WAIT_FOR(0U !=3D (self->rpc_addr =3D + aq_hw_read_reg(self, 0x334U)), 1000U, 100U); + + return err; +} + +#define HW_ATL_RPC_CONTROL_ADR 0x0338U +#define HW_ATL_RPC_STATE_ADR 0x033CU + +struct aq_hw_atl_utils_fw_rpc_tid_s { + union { + u32 val; + struct { + u16 tid; + u16 len; + }; + }; +}; + +#define hw_atl_utils_fw_rpc_init(_H_) hw_atl_utils_fw_rpc_wait(_H_, NULL) + +int hw_atl_utils_fw_rpc_call(struct aq_hw_s *self, unsigned int rpc_size) +{ + int err =3D 0; + struct aq_hw_atl_utils_fw_rpc_tid_s sw; + + if (!IS_CHIP_FEATURE(MIPS)) { + err =3D -1; + goto err_exit; + } + err =3D hw_atl_utils_fw_upload_dwords(self, self->rpc_addr, + (u32 *)(void *)&self->rpc, + (rpc_size + sizeof(u32) - + sizeof(u8)) / sizeof(u32)); + if (err < 0) + goto err_exit; + + sw.tid =3D 0xFFFFU & (++self->rpc_tid); + sw.len =3D (u16)rpc_size; + aq_hw_write_reg(self, HW_ATL_RPC_CONTROL_ADR, sw.val); + +err_exit: + return err; +} + +int hw_atl_utils_fw_rpc_wait(struct aq_hw_s *self, + struct hw_aq_atl_utils_fw_rpc **rpc) +{ + int err =3D 0; + struct aq_hw_atl_utils_fw_rpc_tid_s sw; + struct aq_hw_atl_utils_fw_rpc_tid_s fw; + + do { + sw.val =3D aq_hw_read_reg(self, HW_ATL_RPC_CONTROL_ADR); + + self->rpc_tid =3D sw.tid; + + AQ_HW_WAIT_FOR(sw.tid =3D=3D + (fw.val =3D + aq_hw_read_reg(self, HW_ATL_RPC_STATE_ADR), + fw.tid), 1000U, 100U); + if (err < 0) + goto err_exit; + + if (fw.len =3D=3D 0xFFFFU) { + err =3D hw_atl_utils_fw_rpc_call(self, sw.len); + if (err < 0) + goto err_exit; + } + } while (sw.tid !=3D fw.tid || 0xFFFFU =3D=3D fw.len); + if (err < 0) + goto err_exit; + + if (rpc) { + if (fw.len) { + err =3D + hw_atl_utils_fw_downld_dwords(self, + self->rpc_addr, + (u32 *)(void *) + &self->rpc, + (fw.len + sizeof(u32) - + sizeof(u8)) / + sizeof(u32)); + if (err < 0) + goto err_exit; + } + + *rpc =3D &self->rpc; + } + +err_exit: + return err; +} + +static int hw_atl_utils_mpi_create(struct aq_hw_s *self) +{ + int err =3D 0; + + err =3D hw_atl_utils_init_ucp(self); + if (err < 0) + goto err_exit; + + err =3D hw_atl_utils_fw_rpc_init(self); + if (err < 0) + goto err_exit; + +err_exit: + return err; +} + +int hw_atl_utils_mpi_read_mbox(struct aq_hw_s *self, + struct hw_aq_atl_utils_mbox_header *pmbox) +{ + return hw_atl_utils_fw_downld_dwords(self, + self->mbox_addr, + (u32 *)(void *)pmbox, + sizeof(*pmbox) / sizeof(u32)); +} + +void hw_atl_utils_mpi_read_stats(struct aq_hw_s *self, + struct hw_aq_atl_utils_mbox *pmbox) +{ + int err =3D 0; + + err =3D hw_atl_utils_fw_downld_dwords(self, + self->mbox_addr, + (u32 *)(void *)pmbox, + sizeof(*pmbox) / sizeof(u32)); + if (err < 0) + goto err_exit; + + if (IS_CHIP_FEATURE(REVISION_A0)) { + unsigned int mtu =3D 1514; + pmbox->stats.ubrc =3D pmbox->stats.uprc * mtu; + pmbox->stats.ubtc =3D pmbox->stats.uptc * mtu; + } else { + pmbox->stats.dpc =3D hw_atl_rpb_rx_dma_drop_pkt_cnt_get(self); + } + +err_exit:; +} + +static +int hw_atl_utils_mpi_set_speed(struct aq_hw_s *self, u32 speed) +{ + u32 val =3D aq_hw_read_reg(self, HW_ATL_MPI_CONTROL_ADR); + + val =3D val & ~HW_ATL_MPI_SPEED_MSK; + val |=3D speed << HW_ATL_MPI_SPEED_SHIFT; + aq_hw_write_reg(self, HW_ATL_MPI_CONTROL_ADR, val); + + return 0; +} + +int hw_atl_utils_mpi_set_state(struct aq_hw_s *self, + enum hal_atl_utils_fw_state_e state) +{ + int err =3D 0; + u32 transaction_id =3D 0; + struct hw_aq_atl_utils_mbox_header mbox; + u32 val =3D aq_hw_read_reg(self, HW_ATL_MPI_CONTROL_ADR); + + if (state =3D=3D MPI_RESET) { + hw_atl_utils_mpi_read_mbox(self, &mbox); + + transaction_id =3D mbox.transaction_id; + + AQ_HW_WAIT_FOR(transaction_id !=3D + (hw_atl_utils_mpi_read_mbox(self, &mbox), + mbox.transaction_id), + 1000U, 100U); + if (err < 0) + goto err_exit; + } + /* On interface DEINIT we disable DW (raise bit) + * Otherwise enable DW (clear bit) + */ + if (state =3D=3D MPI_DEINIT || state =3D=3D MPI_POWER) + val |=3D HW_ATL_MPI_DIRTY_WAKE_MSK; + else + val &=3D ~HW_ATL_MPI_DIRTY_WAKE_MSK; + + /* Set new state bits */ + val =3D val & ~HW_ATL_MPI_STATE_MSK; + val |=3D state & HW_ATL_MPI_STATE_MSK; + + aq_hw_write_reg(self, HW_ATL_MPI_CONTROL_ADR, val); +err_exit: + return err; +} + +int hw_atl_utils_mpi_get_link_status(struct aq_hw_s *self) +{ + u32 cp0x036C =3D aq_hw_read_reg(self, HW_ATL_MPI_STATE_ADR); + u32 link_speed_mask =3D cp0x036C >> HW_ATL_MPI_SPEED_SHIFT; + struct aq_hw_link_status_s *link_status =3D &self->aq_link_status; + + if (!link_speed_mask) { + link_status->mbps =3D 0U; + } else { + switch (link_speed_mask) { + case HAL_ATLANTIC_RATE_10G: + link_status->mbps =3D 10000U; + break; + + case HAL_ATLANTIC_RATE_5G: + case HAL_ATLANTIC_RATE_5GSR: + link_status->mbps =3D 5000U; + break; + + case HAL_ATLANTIC_RATE_2GS: + link_status->mbps =3D 2500U; + break; + + case HAL_ATLANTIC_RATE_1G: + link_status->mbps =3D 1000U; + break; + + case HAL_ATLANTIC_RATE_100M: + link_status->mbps =3D 100U; + break; + + default: + return -EBUSY; + } + } + + return 0; +} + +static int hw_atl_utils_get_mac_permanent(struct aq_hw_s *self, + u8 *mac) +{ + int err =3D 0; + u32 h =3D 0U; + u32 l =3D 0U; + u32 mac_addr[2]; + + if (!aq_hw_read_reg(self, HW_ATL_UCP_0X370_REG)) { + unsigned int rnd =3D (uint32_t)rte_rand(); + unsigned int ucp_0x370 =3D 0; + + //get_random_bytes(&rnd, sizeof(unsigned int)); + + ucp_0x370 =3D 0x02020202 | (0xFEFEFEFE & rnd); + aq_hw_write_reg(self, HW_ATL_UCP_0X370_REG, ucp_0x370); + } + + err =3D hw_atl_utils_fw_downld_dwords(self, + aq_hw_read_reg(self, 0x00000374U) + + (40U * 4U), + mac_addr, + ARRAY_SIZE(mac_addr)); + if (err < 0) { + mac_addr[0] =3D 0U; + mac_addr[1] =3D 0U; + err =3D 0; + } else { + mac_addr[0] =3D rte_constant_bswap32(mac_addr[0]); + mac_addr[1] =3D rte_constant_bswap32(mac_addr[1]); + } + + ether_addr_copy((struct ether_addr *)mac_addr, + (struct ether_addr *)mac); + + if ((mac[0] & 0x01U) || ((mac[0] | mac[1] | mac[2]) =3D=3D 0x00U)) { + /* chip revision */ + l =3D 0xE3000000U + | (0xFFFFU & aq_hw_read_reg(self, HW_ATL_UCP_0X370_REG)) + | (0x00 << 16); + h =3D 0x8001300EU; + + mac[5] =3D (u8)(0xFFU & l); + l >>=3D 8; + mac[4] =3D (u8)(0xFFU & l); + l >>=3D 8; + mac[3] =3D (u8)(0xFFU & l); + l >>=3D 8; + mac[2] =3D (u8)(0xFFU & l); + mac[1] =3D (u8)(0xFFU & h); + h >>=3D 8; + mac[0] =3D (u8)(0xFFU & h); + } + + return err; +} + +unsigned int hw_atl_utils_mbps_2_speed_index(unsigned int mbps) +{ + unsigned int ret =3D 0U; + + switch (mbps) { + case 100U: + ret =3D 5U; + break; + + case 1000U: + ret =3D 4U; + break; + + case 2500U: + ret =3D 3U; + break; + + case 5000U: + ret =3D 1U; + break; + + case 10000U: + ret =3D 0U; + break; + + default: + break; + } + return ret; +} + +void hw_atl_utils_hw_chip_features_init(struct aq_hw_s *self, u32 *p) +{ + u32 chip_features =3D 0U; + u32 val =3D hw_atl_reg_glb_mif_id_get(self); + u32 mif_rev =3D val & 0xFFU; + + if ((0xFU & mif_rev) =3D=3D 1U) { + chip_features |=3D HAL_ATLANTIC_UTILS_CHIP_REVISION_A0 | + HAL_ATLANTIC_UTILS_CHIP_MPI_AQ | + HAL_ATLANTIC_UTILS_CHIP_MIPS; + } else if ((0xFU & mif_rev) =3D=3D 2U) { + chip_features |=3D HAL_ATLANTIC_UTILS_CHIP_REVISION_B0 | + HAL_ATLANTIC_UTILS_CHIP_MPI_AQ | + HAL_ATLANTIC_UTILS_CHIP_MIPS | + HAL_ATLANTIC_UTILS_CHIP_TPO2 | + HAL_ATLANTIC_UTILS_CHIP_RPF2; + } else if ((0xFU & mif_rev) =3D=3D 0xAU) { + chip_features |=3D HAL_ATLANTIC_UTILS_CHIP_REVISION_B1 | + HAL_ATLANTIC_UTILS_CHIP_MPI_AQ | + HAL_ATLANTIC_UTILS_CHIP_MIPS | + HAL_ATLANTIC_UTILS_CHIP_TPO2 | + HAL_ATLANTIC_UTILS_CHIP_RPF2; + } + + *p =3D chip_features; +} + +static int hw_atl_fw1x_deinit(struct aq_hw_s *self) +{ + hw_atl_utils_mpi_set_speed(self, 0); + hw_atl_utils_mpi_set_state(self, MPI_DEINIT); + return 0; +} + +int hw_atl_utils_update_stats(struct aq_hw_s *self) +{ + struct hw_aq_atl_utils_mbox mbox; + + hw_atl_utils_mpi_read_stats(self, &mbox); + +#define AQ_SDELTA(_N_) (self->curr_stats._N_ +=3D \ + mbox.stats._N_ - self->last_stats._N_) + + if (1) {//self->aq_link_status.mbps) { + AQ_SDELTA(uprc); + AQ_SDELTA(mprc); + AQ_SDELTA(bprc); + AQ_SDELTA(erpt); + + AQ_SDELTA(uptc); + AQ_SDELTA(mptc); + AQ_SDELTA(bptc); + AQ_SDELTA(erpr); + AQ_SDELTA(ubrc); + AQ_SDELTA(ubtc); + AQ_SDELTA(mbrc); + AQ_SDELTA(mbtc); + AQ_SDELTA(bbrc); + AQ_SDELTA(bbtc); + AQ_SDELTA(dpc); + } +#undef AQ_SDELTA + self->curr_stats.dma_pkt_rc =3D + hw_atl_stats_rx_dma_good_pkt_counterlsw_get(self) + + ((u64)hw_atl_stats_rx_dma_good_pkt_countermsw_get(self) << 32); + self->curr_stats.dma_pkt_tc =3D + hw_atl_stats_tx_dma_good_pkt_counterlsw_get(self) + + ((u64)hw_atl_stats_tx_dma_good_pkt_countermsw_get(self) << 32); + self->curr_stats.dma_oct_rc =3D + hw_atl_stats_rx_dma_good_octet_counterlsw_get(self) + + ((u64)hw_atl_stats_rx_dma_good_octet_countermsw_get(self) << 32); + self->curr_stats.dma_oct_tc =3D + hw_atl_stats_tx_dma_good_octet_counterlsw_get(self) + + ((u64)hw_atl_stats_tx_dma_good_octet_countermsw_get(self) << 32); + + self->curr_stats.dpc =3D hw_atl_rpb_rx_dma_drop_pkt_cnt_get(self); + + memcpy(&self->last_stats, &mbox.stats, sizeof(mbox.stats)); + + return 0; +} + +struct aq_stats_s *hw_atl_utils_get_hw_stats(struct aq_hw_s *self) +{ + return &self->curr_stats; +} + +static const u32 hw_atl_utils_hw_mac_regs[] =3D { + 0x00005580U, 0x00005590U, 0x000055B0U, 0x000055B4U, + 0x000055C0U, 0x00005B00U, 0x00005B04U, 0x00005B08U, + 0x00005B0CU, 0x00005B10U, 0x00005B14U, 0x00005B18U, + 0x00005B1CU, 0x00005B20U, 0x00005B24U, 0x00005B28U, + 0x00005B2CU, 0x00005B30U, 0x00005B34U, 0x00005B38U, + 0x00005B3CU, 0x00005B40U, 0x00005B44U, 0x00005B48U, + 0x00005B4CU, 0x00005B50U, 0x00005B54U, 0x00005B58U, + 0x00005B5CU, 0x00005B60U, 0x00005B64U, 0x00005B68U, + 0x00005B6CU, 0x00005B70U, 0x00005B74U, 0x00005B78U, + 0x00005B7CU, 0x00007C00U, 0x00007C04U, 0x00007C08U, + 0x00007C0CU, 0x00007C10U, 0x00007C14U, 0x00007C18U, + 0x00007C1CU, 0x00007C20U, 0x00007C40U, 0x00007C44U, + 0x00007C48U, 0x00007C4CU, 0x00007C50U, 0x00007C54U, + 0x00007C58U, 0x00007C5CU, 0x00007C60U, 0x00007C80U, + 0x00007C84U, 0x00007C88U, 0x00007C8CU, 0x00007C90U, + 0x00007C94U, 0x00007C98U, 0x00007C9CU, 0x00007CA0U, + 0x00007CC0U, 0x00007CC4U, 0x00007CC8U, 0x00007CCCU, + 0x00007CD0U, 0x00007CD4U, 0x00007CD8U, 0x00007CDCU, + 0x00007CE0U, 0x00000300U, 0x00000304U, 0x00000308U, + 0x0000030cU, 0x00000310U, 0x00000314U, 0x00000318U, + 0x0000031cU, 0x00000360U, 0x00000364U, 0x00000368U, + 0x0000036cU, 0x00000370U, 0x00000374U, 0x00006900U, +}; + +unsigned int hw_atl_utils_hw_get_reg_length(void) +{ + return ARRAY_SIZE(hw_atl_utils_hw_mac_regs); +} + +int hw_atl_utils_hw_get_regs(struct aq_hw_s *self, + u32 *regs_buff) +{ + unsigned int i =3D 0U; + unsigned int mac_regs_count =3D hw_atl_utils_hw_get_reg_length(); + + for (i =3D 0; i < mac_regs_count; i++) + regs_buff[i] =3D aq_hw_read_reg(self, + hw_atl_utils_hw_mac_regs[i]); + return 0; +} + +int hw_atl_utils_get_fw_version(struct aq_hw_s *self, u32 *fw_version) +{ + *fw_version =3D aq_hw_read_reg(self, 0x18U); + return 0; +} + +static int aq_fw1x_set_wol(struct aq_hw_s *self, bool wol_enabled, u8 *mac= ) +{ + struct hw_aq_atl_utils_fw_rpc *prpc =3D NULL; + unsigned int rpc_size =3D 0U; + int err =3D 0; + + err =3D hw_atl_utils_fw_rpc_wait(self, &prpc); + if (err < 0) + goto err_exit; + + memset(prpc, 0, sizeof(*prpc)); + + if (wol_enabled) { + rpc_size =3D sizeof(prpc->msg_id) + sizeof(prpc->msg_wol); + + prpc->msg_id =3D HAL_ATLANTIC_UTILS_FW_MSG_WOL_ADD; + prpc->msg_wol.priority =3D 0x10000000; /* normal priority */ + prpc->msg_wol.pattern_id =3D 1U; + prpc->msg_wol.wol_packet_type =3D 2U; /* Magic Packet */ + + ether_addr_copy((struct ether_addr *)mac, + (struct ether_addr *)&prpc->msg_wol.wol_pattern); + } else { + rpc_size =3D sizeof(prpc->msg_id) + sizeof(prpc->msg_del_id); + + prpc->msg_id =3D HAL_ATLANTIC_UTILS_FW_MSG_WOL_DEL; + prpc->msg_wol.pattern_id =3D 1U; + } + + err =3D hw_atl_utils_fw_rpc_call(self, rpc_size); + if (err < 0) + goto err_exit; +err_exit: + return err; +} + +static +int aq_fw1x_set_power(struct aq_hw_s *self, + unsigned int power_state __rte_unused, + u8 *mac) +{ + struct hw_aq_atl_utils_fw_rpc *prpc =3D NULL; + unsigned int rpc_size =3D 0U; + int err =3D 0; + if (self->aq_nic_cfg->wol & AQ_NIC_WOL_ENABLED) { + err =3D aq_fw1x_set_wol(self, 1, mac); + + if (err < 0) + goto err_exit; + + rpc_size =3D sizeof(prpc->msg_id) + + sizeof(prpc->msg_enable_wakeup); + + err =3D hw_atl_utils_fw_rpc_wait(self, &prpc); + + if (err < 0) + goto err_exit; + + memset(prpc, 0, rpc_size); + + prpc->msg_id =3D HAL_ATLANTIC_UTILS_FW_MSG_ENABLE_WAKEUP; + prpc->msg_enable_wakeup.pattern_mask =3D 0x00000002; + + err =3D hw_atl_utils_fw_rpc_call(self, rpc_size); + if (err < 0) + goto err_exit; + } + + hw_atl_utils_mpi_set_speed(self, 0); + hw_atl_utils_mpi_set_state(self, MPI_POWER); +err_exit: + return err; +} + + + +const struct aq_fw_ops aq_fw_1x_ops =3D { + .init =3D hw_atl_utils_mpi_create, + .deinit =3D hw_atl_fw1x_deinit, + .reset =3D NULL, + .get_mac_permanent =3D hw_atl_utils_get_mac_permanent, + .set_link_speed =3D hw_atl_utils_mpi_set_speed, + .set_state =3D hw_atl_utils_mpi_set_state, + .update_link_status =3D hw_atl_utils_mpi_get_link_status, + .update_stats =3D hw_atl_utils_update_stats, + .set_power =3D aq_fw1x_set_power, + .get_temp =3D NULL, + .get_cable_len =3D NULL, + .set_eee_rate =3D NULL, + .get_eee_rate =3D NULL, + .set_flow_control =3D NULL, + .led_control =3D NULL, + .get_eeprom =3D NULL, + .set_eeprom =3D NULL, +}; diff --git a/drivers/net/atlantic/hw_atl/hw_atl_utils.h b/drivers/net/atlan= tic/hw_atl/hw_atl_utils.h new file mode 100644 index 000000000000..5f3f70847310 --- /dev/null +++ b/drivers/net/atlantic/hw_atl/hw_atl_utils.h @@ -0,0 +1,510 @@ +/* SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0) */ +/* Copyright (C) 2014-2017 aQuantia Corporation. */ + +/* File hw_atl_utils.h: Declaration of common functions for Atlantic hardw= are + * abstraction layer. + */ + +#ifndef HW_ATL_UTILS_H +#define HW_ATL_UTILS_H + +#define HW_ATL_FLUSH() { (void)aq_hw_read_reg(self, 0x10); } + +/* Hardware tx descriptor */ +struct hw_atl_txd_s { + u64 buf_addr; + + union { + struct { + u32 type:3; + u32:1; + u32 len:16; + u32 dd:1; + u32 eop:1; + u32 cmd:8; + u32:14; + u32 ct_idx:1; + u32 ct_en:1; + u32 pay_len:18; + } __attribute__((__packed__)); + u64 flags; + }; +} __attribute__((__packed__)); + +/* Hardware tx context descriptor */ +union hw_atl_txc_s { + struct { + u64 flags1; + u64 flags2; + }; + + struct { + u64:40; + u32 tun_len:8; + u32 out_len:16; + u32 type:3; + u32 idx:1; + u32 vlan_tag:16; + u32 cmd:4; + u32 l2_len:7; + u32 l3_len:9; + u32 l4_len:8; + u32 mss_len:16; + } __attribute__((__packed__)); +} __attribute__((__packed__)); + +enum aq_tx_desc_type { + tx_desc_type_desc =3D 1, + tx_desc_type_ctx =3D 2, +}; + +enum aq_tx_desc_cmd { + tx_desc_cmd_vlan =3D 1, + tx_desc_cmd_fcs =3D 2, + tx_desc_cmd_ipv4 =3D 4, + tx_desc_cmd_l4cs =3D 8, + tx_desc_cmd_lso =3D 0x10, + tx_desc_cmd_wb =3D 0x20, +}; + + +/* Hardware rx descriptor */ +struct hw_atl_rxd_s { + u64 buf_addr; + u64 hdr_addr; +} __attribute__((__packed__)); + +/* Hardware rx descriptor writeback */ +struct hw_atl_rxd_wb_s { + u32 rss_type:4; + u32 pkt_type:8; + u32 type:20; + u32 rss_hash; + u16 dd:1; + u16 eop:1; + u16 rx_stat:4; + u16 rx_estat:6; + u16 rsc_cnt:4; + u16 pkt_len; + u16 next_desc_ptr; + u16 vlan; +} __attribute__((__packed__)); + +struct hw_atl_stats_s { + u32 uprc; + u32 mprc; + u32 bprc; + u32 erpt; + u32 uptc; + u32 mptc; + u32 bptc; + u32 erpr; + u32 mbtc; + u32 bbtc; + u32 mbrc; + u32 bbrc; + u32 ubrc; + u32 ubtc; + u32 dpc; +} __attribute__((__packed__)); + +union ip_addr { + struct { + u8 addr[16]; + } v6; + struct { + u8 padding[12]; + u8 addr[4]; + } v4; +} __attribute__((__packed__)); + +struct hw_aq_atl_utils_fw_rpc { + u32 msg_id; + + union { + struct { + u32 pong; + } msg_ping; + + struct { + u8 mac_addr[6]; + u32 ip_addr_cnt; + + struct { + union ip_addr addr; + union ip_addr mask; + } ip[1]; + } msg_arp; + + struct { + u32 len; + u8 packet[1514U]; + } msg_inject; + + struct { + u32 priority; + u32 wol_packet_type; + u32 pattern_id; + u32 next_wol_pattern_offset; + union { + struct { + u32 flags; + u8 ipv4_source_address[4]; + u8 ipv4_dest_address[4]; + u16 tcp_source_port_number; + u16 tcp_dest_port_number; + } ipv4_tcp_syn_parameters; + + struct { + u32 flags; + u8 ipv6_source_address[16]; + u8 ipv6_dest_address[16]; + u16 tcp_source_port_number; + u16 tcp_dest_port_number; + } ipv6_tcp_syn_parameters; + + struct { + u32 flags; + } eapol_request_id_message_parameters; + + struct { + u32 flags; + u32 mask_offset; + u32 mask_size; + u32 pattern_offset; + u32 pattern_size; + } wol_bit_map_pattern; + struct { + u8 mac_addr[6]; + } wol_magic_packet_pattern; + + } wol_pattern; + } msg_wol; + + struct { + u16 tc_quanta[8]; + u16 tc_threshold[8]; + } msg_msm_pfc_quantas; + + struct { + union { + u32 pattern_mask; + struct { + u32 aq_pm_wol_reason_arp_v4_pkt : 1; + u32 aq_pm_wol_reason_ipv4_ping_pkt : 1; + u32 aq_pm_wol_reason_ipv6_ns_pkt : 1; + u32 aq_pm_wol_reason_ipv6_ping_pkt : 1; + u32 aq_pm_wol_reason_link_up : 1; + u32 aq_pm_wol_reason_link_down : 1; + u32 aq_pm_wol_reason_maximum : 1; + }; + }; + union { + u32 offload_mask; + }; + } msg_enable_wakeup; + + struct { + u32 priority; + u32 protocol_offload_type; + u32 protocol_offload_id; + u32 next_protocol_offload_offset; + + union { + struct { + u32 flags; + u8 remote_ipv4_addr[4]; + u8 host_ipv4_addr[4]; + u8 mac_addr[6]; + } ipv4_arp_params; + }; + } msg_offload; + + struct { + u32 id; + } msg_del_id; + + }; +} __attribute__((__packed__)); + +struct hw_aq_atl_utils_mbox_header { + u32 version; + u32 transaction_id; + u32 error; +} __attribute__((__packed__)); + +struct hw_aq_info { + u8 reserved[6]; + u16 phy_fault_code; + u16 phy_temperature; + u8 cable_len; + u8 reserved1; + u32 cable_diag_data[4]; + u8 reserved2[32]; + u32 caps_lo; + u32 caps_hi; +} __attribute__((__packed__)); + +struct hw_aq_atl_utils_mbox { + struct hw_aq_atl_utils_mbox_header header; + struct hw_atl_stats_s stats; + struct hw_aq_info info; +} __attribute__((__packed__)); + +/* fw2x */ +typedef u16 in_port_t; +typedef u32 ip4_addr_t; +typedef int int32_t; +typedef short int16_t; +typedef u32 fw_offset_t; + +struct ip6_addr { + u32 addr[4]; +} __attribute__((__packed__)); + +struct offload_ka_v4 { + u32 timeout; + in_port_t local_port; + in_port_t remote_port; + u8 remote_mac_addr[6]; + u16 win_size; + u32 seq_num; + u32 ack_num; + ip4_addr_t local_ip; + ip4_addr_t remote_ip; +} __attribute__((__packed__)); + +struct offload_ka_v6 { + u32 timeout; + in_port_t local_port; + in_port_t remote_port; + u8 remote_mac_addr[6]; + u16 win_size; + u32 seq_num; + u32 ack_num; + struct ip6_addr local_ip; + struct ip6_addr remote_ip; +} __attribute__((__packed__)); + +struct offload_ip_info { + u8 v4_local_addr_count; + u8 v4_addr_count; + u8 v6_local_addr_count; + u8 v6_addr_count; + fw_offset_t v4_addr; + fw_offset_t v4_prefix; + fw_offset_t v6_addr; + fw_offset_t v6_prefix; +} __attribute__((__packed__)); + +struct offload_port_info { + u16 udp_port_count; + u16 tcp_port_count; + fw_offset_t udp_port; + fw_offset_t tcp_port; +} __attribute__((__packed__)); + +struct offload_ka_info { + u16 v4_ka_count; + u16 v6_ka_count; + u32 retry_count; + u32 retry_interval; + fw_offset_t v4_ka; + fw_offset_t v6_ka; +} __attribute__((__packed__)); + +struct offload_rr_info { + u32 rr_count; + u32 rr_buf_len; + fw_offset_t rr_id_x; + fw_offset_t rr_buf; +} __attribute__((__packed__)); + +struct offload_info { + u32 version; // current version is 0x00000000 + u32 len; // The whole structure length + // including the variable-size buf + u8 mac_addr[6]; // 8 bytes to keep alignment. Only + // first 6 meaningful. + + u8 reserved[2]; + + struct offload_ip_info ips; + struct offload_port_info ports; + struct offload_ka_info kas; + struct offload_rr_info rrs; + u8 buf[0]; +} __attribute__((__packed__)); + +struct smbus_read_request { + u32 offset; /* not used */ + u32 device_id; + u32 address; + u32 length; +} __attribute__((__packed__)); + +struct smbus_write_request { + u32 offset; /* not used */ + u32 device_id; + u32 address; + u32 length; +} __attribute__((__packed__)); + +#define HAL_ATLANTIC_UTILS_CHIP_MIPS 0x00000001U +#define HAL_ATLANTIC_UTILS_CHIP_TPO2 0x00000002U +#define HAL_ATLANTIC_UTILS_CHIP_RPF2 0x00000004U +#define HAL_ATLANTIC_UTILS_CHIP_MPI_AQ 0x00000010U +#define HAL_ATLANTIC_UTILS_CHIP_REVISION_A0 0x01000000U +#define HAL_ATLANTIC_UTILS_CHIP_REVISION_B0 0x02000000U +#define HAL_ATLANTIC_UTILS_CHIP_REVISION_B1 0x04000000U + + +#define IS_CHIP_FEATURE(_F_) (HAL_ATLANTIC_UTILS_CHIP_##_F_ & \ + self->chip_features) + +enum hal_atl_utils_fw_state_e { + MPI_DEINIT =3D 0, + MPI_RESET =3D 1, + MPI_INIT =3D 2, + MPI_POWER =3D 4, +}; + +#define HAL_ATLANTIC_RATE_10G BIT(0) +#define HAL_ATLANTIC_RATE_5G BIT(1) +#define HAL_ATLANTIC_RATE_5GSR BIT(2) +#define HAL_ATLANTIC_RATE_2GS BIT(3) +#define HAL_ATLANTIC_RATE_1G BIT(4) +#define HAL_ATLANTIC_RATE_100M BIT(5) +#define HAL_ATLANTIC_RATE_INVALID BIT(6) + +#define HAL_ATLANTIC_UTILS_FW_MSG_PING 1U +#define HAL_ATLANTIC_UTILS_FW_MSG_ARP 2U +#define HAL_ATLANTIC_UTILS_FW_MSG_INJECT 3U +#define HAL_ATLANTIC_UTILS_FW_MSG_WOL_ADD 4U +#define HAL_ATLANTIC_UTILS_FW_MSG_WOL_DEL 5U +#define HAL_ATLANTIC_UTILS_FW_MSG_ENABLE_WAKEUP 6U +#define HAL_ATLANTIC_UTILS_FW_MSG_MSM_PFC 7U +#define HAL_ATLANTIC_UTILS_FW_MSG_PROVISIONING 8U +#define HAL_ATLANTIC_UTILS_FW_MSG_OFFLOAD_ADD 9U +#define HAL_ATLANTIC_UTILS_FW_MSG_OFFLOAD_DEL 10U +#define HAL_ATLANTIC_UTILS_FW_MSG_CABLE_DIAG 13U // 0xd + +#define SMBUS_READ_REQUEST BIT(13) +#define SMBUS_WRITE_REQUEST BIT(14) +#define SMBUS_DEVICE_ID 0x50 + +enum hw_atl_fw2x_rate { + FW2X_RATE_100M =3D 0x20, + FW2X_RATE_1G =3D 0x100, + FW2X_RATE_2G5 =3D 0x200, + FW2X_RATE_5G =3D 0x400, + FW2X_RATE_10G =3D 0x800, +}; + +enum hw_atl_fw2x_caps_lo { + CAPS_LO_10BASET_HD =3D 0x00, + CAPS_LO_10BASET_FD, + CAPS_LO_100BASETX_HD, + CAPS_LO_100BASET4_HD, + CAPS_LO_100BASET2_HD, + CAPS_LO_100BASETX_FD, + CAPS_LO_100BASET2_FD, + CAPS_LO_1000BASET_HD, + CAPS_LO_1000BASET_FD, + CAPS_LO_2P5GBASET_FD, + CAPS_LO_5GBASET_FD, + CAPS_LO_10GBASET_FD, +}; + +enum hw_atl_fw2x_caps_hi { + CAPS_HI_RESERVED1 =3D 0x00, + CAPS_HI_10BASET_EEE, + CAPS_HI_RESERVED2, + CAPS_HI_PAUSE, + CAPS_HI_ASYMMETRIC_PAUSE, + CAPS_HI_100BASETX_EEE, + CAPS_HI_RESERVED3, + CAPS_HI_RESERVED4, + CAPS_HI_1000BASET_FD_EEE, + CAPS_HI_2P5GBASET_FD_EEE, + CAPS_HI_5GBASET_FD_EEE, + CAPS_HI_10GBASET_FD_EEE, + CAPS_HI_RESERVED5, + CAPS_HI_RESERVED6, + CAPS_HI_RESERVED7, + CAPS_HI_RESERVED8, + CAPS_HI_RESERVED9, + CAPS_HI_CABLE_DIAG, + CAPS_HI_TEMPERATURE, + CAPS_HI_DOWNSHIFT, + CAPS_HI_PTP_AVB_EN, + CAPS_HI_MEDIA_DETECT, + CAPS_HI_LINK_DROP, + CAPS_HI_SLEEP_PROXY, + CAPS_HI_WOL, + CAPS_HI_MAC_STOP, + CAPS_HI_EXT_LOOPBACK, + CAPS_HI_INT_LOOPBACK, + CAPS_HI_EFUSE_AGENT, + CAPS_HI_WOL_TIMER, + CAPS_HI_STATISTICS, + CAPS_HI_TRANSACTION_ID, +}; + +struct aq_hw_s; +struct aq_fw_ops; +struct aq_hw_link_status_s; + +int hw_atl_utils_initfw(struct aq_hw_s *self, const struct aq_fw_ops **fw_= ops); + +int hw_atl_utils_soft_reset(struct aq_hw_s *self); + +void hw_atl_utils_hw_chip_features_init(struct aq_hw_s *self, u32 *p); + +int hw_atl_utils_mpi_read_mbox(struct aq_hw_s *self, + struct hw_aq_atl_utils_mbox_header *pmbox); + +void hw_atl_utils_mpi_read_stats(struct aq_hw_s *self, + struct hw_aq_atl_utils_mbox *pmbox); + +void hw_atl_utils_mpi_set(struct aq_hw_s *self, + enum hal_atl_utils_fw_state_e state, + u32 speed); + +int hw_atl_utils_mpi_get_link_status(struct aq_hw_s *self); + +unsigned int hw_atl_utils_mbps_2_speed_index(unsigned int mbps); + +unsigned int hw_atl_utils_hw_get_reg_length(void); + +int hw_atl_utils_hw_get_regs(struct aq_hw_s *self, + u32 *regs_buff); + +int hw_atl_utils_hw_set_power(struct aq_hw_s *self, + unsigned int power_state); + +int hw_atl_utils_hw_deinit(struct aq_hw_s *self); + +int hw_atl_utils_get_fw_version(struct aq_hw_s *self, u32 *fw_version); + +int hw_atl_utils_update_stats(struct aq_hw_s *self); + +struct aq_stats_s *hw_atl_utils_get_hw_stats(struct aq_hw_s *self); + +int hw_atl_utils_fw_downld_dwords(struct aq_hw_s *self, u32 a, + u32 *p, u32 cnt); + +int hw_atl_utils_fw_upload_dwords(struct aq_hw_s *self, u32 a, u32 *p, + u32 cnt); + +int hw_atl_utils_fw_set_wol(struct aq_hw_s *self, bool wol_enabled, u8 *ma= c); + +int hw_atl_utils_fw_rpc_call(struct aq_hw_s *self, unsigned int rpc_size); + +int hw_atl_utils_fw_rpc_wait(struct aq_hw_s *self, + struct hw_aq_atl_utils_fw_rpc **rpc); + +extern const struct aq_fw_ops aq_fw_1x_ops; +extern const struct aq_fw_ops aq_fw_2x_ops; + +#endif /* HW_ATL_UTILS_H */ diff --git a/drivers/net/atlantic/hw_atl/hw_atl_utils_fw2x.c b/drivers/net/= atlantic/hw_atl/hw_atl_utils_fw2x.c new file mode 100644 index 000000000000..6841d9bce39c --- /dev/null +++ b/drivers/net/atlantic/hw_atl/hw_atl_utils_fw2x.c @@ -0,0 +1,618 @@ +// SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0) +/* Copyright (C) 2014-2017 aQuantia Corporation. */ + +/* File hw_atl_utils_fw2x.c: Definition of firmware 2.x functions for + * Atlantic hardware abstraction layer. + */ + +#include +#include "../atl_hw_regs.h" + +#include "../atl_types.h" +#include "hw_atl_utils.h" +#include "hw_atl_llh.h" + +#define HW_ATL_FW2X_MPI_EFUSE_ADDR 0x364 +#define HW_ATL_FW2X_MPI_MBOX_ADDR 0x360 +#define HW_ATL_FW2X_MPI_RPC_ADDR 0x334 + +#define HW_ATL_FW2X_MPI_CONTROL_ADDR 0x368 +#define HW_ATL_FW2X_MPI_CONTROL2_ADDR 0x36C +#define HW_ATL_FW2X_MPI_LED_ADDR 0x31c + +#define HW_ATL_FW2X_MPI_STATE_ADDR 0x370 +#define HW_ATL_FW2X_MPI_STATE2_ADDR 0x374 + +#define HW_ATL_FW2X_CAP_SLEEP_PROXY BIT(CAPS_HI_SLEEP_PROXY) +#define HW_ATL_FW2X_CAP_WOL BIT(CAPS_HI_WOL) + +#define HW_ATL_FW2X_CAP_EEE_1G_MASK BIT(CAPS_HI_1000BASET_FD_EEE) +#define HW_ATL_FW2X_CAP_EEE_2G5_MASK BIT(CAPS_HI_2P5GBASET_FD_EEE) +#define HW_ATL_FW2X_CAP_EEE_5G_MASK BIT(CAPS_HI_5GBASET_FD_EEE) +#define HW_ATL_FW2X_CAP_EEE_10G_MASK BIT(CAPS_HI_10GBASET_FD_EEE) + +#define HAL_ATLANTIC_WOL_FILTERS_COUNT 8 +#define HAL_ATLANTIC_UTILS_FW2X_MSG_WOL 0x0E + +#define HW_ATL_FW_FEATURE_EEPROM 0x03010025 +#define HW_ATL_FW_FEATURE_LED 0x03010026 + +struct fw2x_msg_wol_pattern { + u8 mask[16]; + u32 crc; +} __attribute__((__packed__)); + +struct fw2x_msg_wol { + u32 msg_id; + u8 hw_addr[6]; + u8 magic_packet_enabled; + u8 filter_count; + struct fw2x_msg_wol_pattern filter[HAL_ATLANTIC_WOL_FILTERS_COUNT]; + u8 link_up_enabled; + u8 link_down_enabled; + u16 reserved; + u32 link_up_timeout; + u32 link_down_timeout; +} __attribute__((__packed__)); + +static int aq_fw2x_set_link_speed(struct aq_hw_s *self, u32 speed); +static int aq_fw2x_set_state(struct aq_hw_s *self, + enum hal_atl_utils_fw_state_e state); + +static int aq_fw2x_init(struct aq_hw_s *self) +{ + int err =3D 0; + + /* check 10 times by 1ms */ + AQ_HW_WAIT_FOR(0U !=3D (self->mbox_addr =3D + aq_hw_read_reg(self, HW_ATL_FW2X_MPI_MBOX_ADDR)), + 1000U, 10U); + AQ_HW_WAIT_FOR(0U !=3D (self->rpc_addr =3D + aq_hw_read_reg(self, HW_ATL_FW2X_MPI_RPC_ADDR)), + 1000U, 100U); + return err; +} + +static int aq_fw2x_deinit(struct aq_hw_s *self) +{ + int err =3D aq_fw2x_set_link_speed(self, 0); + + if (!err) + err =3D aq_fw2x_set_state(self, MPI_DEINIT); + + return err; +} + +static enum hw_atl_fw2x_rate link_speed_mask_2fw2x_ratemask(u32 speed) +{ + enum hw_atl_fw2x_rate rate =3D 0; + + if (speed & AQ_NIC_RATE_10G) + rate |=3D FW2X_RATE_10G; + + if (speed & AQ_NIC_RATE_5G) + rate |=3D FW2X_RATE_5G; + + if (speed & AQ_NIC_RATE_5G5R) + rate |=3D FW2X_RATE_5G; + + if (speed & AQ_NIC_RATE_2G5) + rate |=3D FW2X_RATE_2G5; + + if (speed & AQ_NIC_RATE_1G) + rate |=3D FW2X_RATE_1G; + + if (speed & AQ_NIC_RATE_100M) + rate |=3D FW2X_RATE_100M; + + return rate; +} + +static u32 fw2x_to_eee_mask(u32 speed) +{ + u32 rate =3D 0; + + if (speed & HW_ATL_FW2X_CAP_EEE_10G_MASK) + rate |=3D AQ_NIC_RATE_EEE_10G; + + if (speed & HW_ATL_FW2X_CAP_EEE_5G_MASK) + rate |=3D AQ_NIC_RATE_EEE_5G; + + if (speed & HW_ATL_FW2X_CAP_EEE_2G5_MASK) + rate |=3D AQ_NIC_RATE_EEE_2G5; + + if (speed & HW_ATL_FW2X_CAP_EEE_1G_MASK) + rate |=3D AQ_NIC_RATE_EEE_1G; + + return rate; +} + +static int aq_fw2x_set_link_speed(struct aq_hw_s *self, u32 speed) +{ + u32 val =3D link_speed_mask_2fw2x_ratemask(speed); + + aq_hw_write_reg(self, HW_ATL_FW2X_MPI_CONTROL_ADDR, val); + + return 0; +} + +static void aq_fw2x_set_mpi_flow_control(struct aq_hw_s *self, u32 *mpi_st= ate) +{ + if (self->aq_nic_cfg->flow_control & AQ_NIC_FC_RX) + *mpi_state |=3D BIT(CAPS_HI_PAUSE); + else + *mpi_state &=3D ~BIT(CAPS_HI_PAUSE); + + if (self->aq_nic_cfg->flow_control & AQ_NIC_FC_TX) + *mpi_state |=3D BIT(CAPS_HI_ASYMMETRIC_PAUSE); + else + *mpi_state &=3D ~BIT(CAPS_HI_ASYMMETRIC_PAUSE); +} + +static int aq_fw2x_set_state(struct aq_hw_s *self, + enum hal_atl_utils_fw_state_e state) +{ + u32 mpi_state =3D aq_hw_read_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR); + + switch (state) { + case MPI_INIT: + mpi_state &=3D ~BIT(CAPS_HI_LINK_DROP); + aq_fw2x_set_mpi_flow_control(self, &mpi_state); + break; + case MPI_DEINIT: + mpi_state |=3D BIT(CAPS_HI_LINK_DROP); + break; + case MPI_RESET: + case MPI_POWER: + /* No actions */ + break; + } + aq_hw_write_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR, mpi_state); + return 0; +} + +static int aq_fw2x_update_link_status(struct aq_hw_s *self) +{ + u32 mpi_state =3D aq_hw_read_reg(self, HW_ATL_FW2X_MPI_STATE_ADDR); + u32 speed =3D mpi_state & (FW2X_RATE_100M | FW2X_RATE_1G | + FW2X_RATE_2G5 | FW2X_RATE_5G | FW2X_RATE_10G); + struct aq_hw_link_status_s *link_status =3D &self->aq_link_status; + + if (speed) { + if (speed & FW2X_RATE_10G) + link_status->mbps =3D 10000; + else if (speed & FW2X_RATE_5G) + link_status->mbps =3D 5000; + else if (speed & FW2X_RATE_2G5) + link_status->mbps =3D 2500; + else if (speed & FW2X_RATE_1G) + link_status->mbps =3D 1000; + else if (speed & FW2X_RATE_100M) + link_status->mbps =3D 100; + else + link_status->mbps =3D 10000; + } else { + link_status->mbps =3D 0; + } + + return 0; +} + +static +int aq_fw2x_get_mac_permanent(struct aq_hw_s *self, u8 *mac) +{ + int err =3D 0; + u32 h =3D 0U; + u32 l =3D 0U; + u32 mac_addr[2] =3D { 0 }; + u32 efuse_addr =3D aq_hw_read_reg(self, HW_ATL_FW2X_MPI_EFUSE_ADDR); + + if (efuse_addr !=3D 0) { + err =3D hw_atl_utils_fw_downld_dwords(self, + efuse_addr + (40U * 4U), + mac_addr, + ARRAY_SIZE(mac_addr)); + if (err) + return err; + mac_addr[0] =3D rte_constant_bswap32(mac_addr[0]); + mac_addr[1] =3D rte_constant_bswap32(mac_addr[1]); + } + + ether_addr_copy((struct ether_addr *)mac_addr, + (struct ether_addr *)mac); + + if ((mac[0] & 0x01U) || ((mac[0] | mac[1] | mac[2]) =3D=3D 0x00U)) { + unsigned int rnd =3D (uint32_t)rte_rand(); + + //get_random_bytes(&rnd, sizeof(unsigned int)); + + l =3D 0xE3000000U + | (0xFFFFU & rnd) + | (0x00 << 16); + h =3D 0x8001300EU; + + mac[5] =3D (u8)(0xFFU & l); + l >>=3D 8; + mac[4] =3D (u8)(0xFFU & l); + l >>=3D 8; + mac[3] =3D (u8)(0xFFU & l); + l >>=3D 8; + mac[2] =3D (u8)(0xFFU & l); + mac[1] =3D (u8)(0xFFU & h); + h >>=3D 8; + mac[0] =3D (u8)(0xFFU & h); + } + return err; +} + +static int aq_fw2x_update_stats(struct aq_hw_s *self) +{ + int err =3D 0; + u32 mpi_opts =3D aq_hw_read_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR); + u32 orig_stats_val =3D mpi_opts & BIT(CAPS_HI_STATISTICS); + + /* Toggle statistics bit for FW to update */ + mpi_opts =3D mpi_opts ^ BIT(CAPS_HI_STATISTICS); + aq_hw_write_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR, mpi_opts); + + /* Wait FW to report back */ + AQ_HW_WAIT_FOR(orig_stats_val !=3D + (aq_hw_read_reg(self, HW_ATL_FW2X_MPI_STATE2_ADDR) & + BIT(CAPS_HI_STATISTICS)), + 1U, 10000U); + if (err) + return err; + + return hw_atl_utils_update_stats(self); +} + +static int aq_fw2x_get_temp(struct aq_hw_s *self, int *temp) +{ + int err =3D 0; + u32 mpi_opts =3D aq_hw_read_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR); + u32 temp_val =3D mpi_opts & BIT(CAPS_HI_TEMPERATURE); + u32 temp_res; + + /* Toggle statistics bit for FW to 0x36C.18 (CAPS_HI_TEMPERATURE) */ + mpi_opts =3D mpi_opts ^ BIT(CAPS_HI_TEMPERATURE); + aq_hw_write_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR, mpi_opts); + + /* Wait FW to report back */ + AQ_HW_WAIT_FOR(temp_val !=3D + (aq_hw_read_reg(self, HW_ATL_FW2X_MPI_STATE2_ADDR) & + BIT(CAPS_HI_TEMPERATURE)), 1U, 10000U); + err =3D hw_atl_utils_fw_downld_dwords(self, + self->mbox_addr + + offsetof(struct hw_aq_atl_utils_mbox, info) + + offsetof(struct hw_aq_info, phy_temperature), + &temp_res, + sizeof(temp_res) / sizeof(u32)); + + if (err) + return err; + + *temp =3D temp_res * 100 / 256; + return 0; +} + +static int aq_fw2x_get_cable_len(struct aq_hw_s *self, int *cable_len) +{ + int err =3D 0; + u32 cable_len_res; + + err =3D hw_atl_utils_fw_downld_dwords(self, + self->mbox_addr + + offsetof(struct hw_aq_atl_utils_mbox, info) + + offsetof(struct hw_aq_info, phy_temperature), + &cable_len_res, + sizeof(cable_len_res) / sizeof(u32)); + + if (err) + return err; + + *cable_len =3D (cable_len_res >> 16) & 0xFF; + return 0; +} + +#ifndef ETH_ALEN +#define ETH_ALEN 6 +#endif + +static int aq_fw2x_set_sleep_proxy(struct aq_hw_s *self, u8 *mac) +{ + int err =3D 0; + struct hw_aq_atl_utils_fw_rpc *rpc =3D NULL; + struct offload_info *cfg =3D NULL; + unsigned int rpc_size =3D 0U; + u32 mpi_opts; + + rpc_size =3D sizeof(rpc->msg_id) + sizeof(*cfg); + + err =3D hw_atl_utils_fw_rpc_wait(self, &rpc); + if (err < 0) + goto err_exit; + + memset(rpc, 0, rpc_size); + cfg =3D (struct offload_info *)(&rpc->msg_id + 1); + + memcpy(cfg->mac_addr, mac, ETH_ALEN); + cfg->len =3D sizeof(*cfg); + + /* Clear bit 0x36C.23 */ + mpi_opts =3D aq_hw_read_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR); + mpi_opts &=3D ~HW_ATL_FW2X_CAP_SLEEP_PROXY; + + aq_hw_write_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR, mpi_opts); + + err =3D hw_atl_utils_fw_rpc_call(self, rpc_size); + if (err < 0) + goto err_exit; + + /* Set bit 0x36C.23 */ + mpi_opts |=3D HW_ATL_FW2X_CAP_SLEEP_PROXY; + aq_hw_write_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR, mpi_opts); + + AQ_HW_WAIT_FOR((aq_hw_read_reg(self, HW_ATL_FW2X_MPI_STATE2_ADDR) & + HW_ATL_FW2X_CAP_SLEEP_PROXY), 1U, 10000U); +err_exit: + return err; +} + +static int aq_fw2x_set_wol_params(struct aq_hw_s *self, u8 *mac) +{ + int err =3D 0; + struct fw2x_msg_wol *msg =3D NULL; + u32 mpi_opts; + + struct hw_aq_atl_utils_fw_rpc *rpc =3D NULL; + + err =3D hw_atl_utils_fw_rpc_wait(self, &rpc); + if (err < 0) + goto err_exit; + + msg =3D (struct fw2x_msg_wol *)rpc; + + msg->msg_id =3D HAL_ATLANTIC_UTILS_FW2X_MSG_WOL; + msg->magic_packet_enabled =3D true; + memcpy(msg->hw_addr, mac, ETH_ALEN); + + mpi_opts =3D aq_hw_read_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR); + mpi_opts &=3D ~(HW_ATL_FW2X_CAP_SLEEP_PROXY | HW_ATL_FW2X_CAP_WOL); + + aq_hw_write_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR, mpi_opts); + + err =3D hw_atl_utils_fw_rpc_call(self, sizeof(*msg)); + if (err < 0) + goto err_exit; + + /* Set bit 0x36C.24 */ + mpi_opts |=3D HW_ATL_FW2X_CAP_WOL; + aq_hw_write_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR, mpi_opts); + + AQ_HW_WAIT_FOR((aq_hw_read_reg(self, HW_ATL_FW2X_MPI_STATE2_ADDR) & + HW_ATL_FW2X_CAP_WOL), 1U, 10000U); +err_exit: + return err; +} + +static int aq_fw2x_set_power(struct aq_hw_s *self, + unsigned int power_state __rte_unused, + u8 *mac) +{ + int err =3D 0; + + if (self->aq_nic_cfg->wol & AQ_NIC_WOL_ENABLED) { + err =3D aq_fw2x_set_sleep_proxy(self, mac); + if (err < 0) + goto err_exit; + err =3D aq_fw2x_set_wol_params(self, mac); + if (err < 0) + goto err_exit; + } +err_exit: + return err; +} + +static int aq_fw2x_set_eee_rate(struct aq_hw_s *self, u32 speed) +{ + u32 mpi_opts =3D aq_hw_read_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR); + mpi_opts &=3D ~(HW_ATL_FW2X_CAP_EEE_1G_MASK | + HW_ATL_FW2X_CAP_EEE_2G5_MASK | HW_ATL_FW2X_CAP_EEE_5G_MASK | + HW_ATL_FW2X_CAP_EEE_10G_MASK); + + if (speed & AQ_NIC_RATE_EEE_10G) + mpi_opts |=3D HW_ATL_FW2X_CAP_EEE_10G_MASK; + + if (speed & AQ_NIC_RATE_EEE_5G) + mpi_opts |=3D HW_ATL_FW2X_CAP_EEE_5G_MASK; + + if (speed & AQ_NIC_RATE_EEE_2G5) + mpi_opts |=3D HW_ATL_FW2X_CAP_EEE_2G5_MASK; + + if (speed & AQ_NIC_RATE_EEE_1G) + mpi_opts |=3D HW_ATL_FW2X_CAP_EEE_1G_MASK; + + aq_hw_write_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR, mpi_opts); + + return 0; +} + +static int aq_fw2x_get_eee_rate(struct aq_hw_s *self, u32 *rate, + u32 *supported_rates) +{ + int err =3D 0; + u32 caps_hi; + u32 mpi_state; + + err =3D hw_atl_utils_fw_downld_dwords(self, + self->mbox_addr + + offsetof(struct hw_aq_atl_utils_mbox, info) + + offsetof(struct hw_aq_info, caps_hi), + &caps_hi, + sizeof(caps_hi) / sizeof(u32)); + + if (err) + return err; + + *supported_rates =3D fw2x_to_eee_mask(caps_hi); + + mpi_state =3D aq_hw_read_reg(self, HW_ATL_FW2X_MPI_STATE2_ADDR); + *rate =3D fw2x_to_eee_mask(mpi_state); + + return err; +} + + + +static int aq_fw2x_set_flow_control(struct aq_hw_s *self) +{ + u32 mpi_state =3D aq_hw_read_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR); + + aq_fw2x_set_mpi_flow_control(self, &mpi_state); + + aq_hw_write_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR, mpi_state); + + return 0; +} + +static int aq_fw2x_led_control(struct aq_hw_s *self, u32 mode) +{ + if (self->fw_ver_actual < HW_ATL_FW_FEATURE_LED) + return -EOPNOTSUPP; + + aq_hw_write_reg(self, HW_ATL_FW2X_MPI_LED_ADDR, mode); + return 0; +} + +static int aq_fw2x_get_eeprom(struct aq_hw_s *self, u32 *data, u32 len) +{ + int err =3D 0; + struct smbus_read_request request; + u32 mpi_opts; + u32 result =3D 0; + + if (self->fw_ver_actual < HW_ATL_FW_FEATURE_EEPROM) + return -EOPNOTSUPP; + + request.device_id =3D SMBUS_DEVICE_ID; + request.address =3D 0; + request.length =3D len; + + /* Write SMBUS request to cfg memory */ + err =3D hw_atl_utils_fw_upload_dwords(self, self->rpc_addr, + (u32 *)(void *)&request, + RTE_ALIGN(sizeof(request), sizeof(u32))); + + if (err < 0) + return err; + + /* Toggle 0x368.SMBUS_READ_REQUEST bit */ + mpi_opts =3D aq_hw_read_reg(self, HW_ATL_FW2X_MPI_CONTROL_ADDR); + mpi_opts ^=3D SMBUS_READ_REQUEST; + + aq_hw_write_reg(self, HW_ATL_FW2X_MPI_CONTROL_ADDR, mpi_opts); + + /* Wait until REQUEST_BIT matched in 0x370 */ + + AQ_HW_WAIT_FOR((aq_hw_read_reg(self, HW_ATL_FW2X_MPI_STATE_ADDR) & + SMBUS_READ_REQUEST) =3D=3D (mpi_opts & SMBUS_READ_REQUEST), + 10U, 10000U); + + if (err < 0) + return err; + + err =3D hw_atl_utils_fw_downld_dwords(self, self->rpc_addr + sizeof(u32), + &result, + RTE_ALIGN(sizeof(result), sizeof(u32))); + + if (err < 0) + return err; + + if (result =3D=3D 0) { + err =3D hw_atl_utils_fw_downld_dwords(self, + self->rpc_addr + sizeof(u32) * 2, + data, + RTE_ALIGN(len, sizeof(u32))); + + if (err < 0) + return err; + } + + return 0; +} + + +static int aq_fw2x_set_eeprom(struct aq_hw_s *self, u32 *data, u32 len) +{ + struct smbus_write_request request; + u32 mpi_opts, result =3D 0; + int err =3D 0; + + if (self->fw_ver_actual < HW_ATL_FW_FEATURE_EEPROM) + return -EOPNOTSUPP; + + request.device_id =3D SMBUS_DEVICE_ID; + request.address =3D 0; + request.length =3D len; + + /* Write SMBUS request to cfg memory */ + err =3D hw_atl_utils_fw_upload_dwords(self, self->rpc_addr, + (u32 *)(void *)&request, + RTE_ALIGN(sizeof(request), sizeof(u32))); + + if (err < 0) + return err; + + /* Write SMBUS data to cfg memory */ + err =3D hw_atl_utils_fw_upload_dwords(self, + self->rpc_addr + sizeof(request), + (u32 *)(void *)data, + RTE_ALIGN(len, sizeof(u32))); + + if (err < 0) + return err; + + /* Toggle 0x368.SMBUS_WRITE_REQUEST bit */ + mpi_opts =3D aq_hw_read_reg(self, HW_ATL_FW2X_MPI_CONTROL_ADDR); + mpi_opts ^=3D SMBUS_WRITE_REQUEST; + + aq_hw_write_reg(self, HW_ATL_FW2X_MPI_CONTROL_ADDR, mpi_opts); + + /* Wait until REQUEST_BIT matched in 0x370 */ + AQ_HW_WAIT_FOR((aq_hw_read_reg(self, HW_ATL_FW2X_MPI_STATE_ADDR) & + SMBUS_WRITE_REQUEST) =3D=3D (mpi_opts & SMBUS_WRITE_REQUEST), + 10U, 10000U); + + if (err < 0) + return err; + + /* Read status of write operation */ + err =3D hw_atl_utils_fw_downld_dwords(self, self->rpc_addr + sizeof(u32), + &result, + RTE_ALIGN(sizeof(result), sizeof(u32))); + + if (err < 0) + return err; + + return 0; +} + +const struct aq_fw_ops aq_fw_2x_ops =3D { + .init =3D aq_fw2x_init, + .deinit =3D aq_fw2x_deinit, + .reset =3D NULL, + .get_mac_permanent =3D aq_fw2x_get_mac_permanent, + .set_link_speed =3D aq_fw2x_set_link_speed, + .set_state =3D aq_fw2x_set_state, + .update_link_status =3D aq_fw2x_update_link_status, + .update_stats =3D aq_fw2x_update_stats, + .set_power =3D aq_fw2x_set_power, + .get_temp =3D aq_fw2x_get_temp, + .get_cable_len =3D aq_fw2x_get_cable_len, + .set_eee_rate =3D aq_fw2x_set_eee_rate, + .get_eee_rate =3D aq_fw2x_get_eee_rate, + .set_flow_control =3D aq_fw2x_set_flow_control, + .led_control =3D aq_fw2x_led_control, + .get_eeprom =3D aq_fw2x_get_eeprom, + .set_eeprom =3D aq_fw2x_set_eeprom, +}; diff --git a/drivers/net/atlantic/meson.build b/drivers/net/atlantic/meson.= build index 055342ec9db7..b05c97773541 100644 --- a/drivers/net/atlantic/meson.build +++ b/drivers/net/atlantic/meson.build @@ -5,4 +5,6 @@ sources =3D files( 'atl_ethdev.c', 'atl_hw_regs.c', 'hw_atl/hw_atl_llh.c', + 'hw_atl/hw_atl_utils_fw2x.c', + 'hw_atl/hw_atl_utils.c', ) --=20 2.7.4