From patchwork Mon Jun 22 13:35:18 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Florinel Iordache X-Patchwork-Id: 1314384 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming-netdev@ozlabs.org Delivered-To: patchwork-incoming-netdev@ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=vger.kernel.org (client-ip=23.128.96.18; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=pass (p=none dis=none) header.from=nxp.com Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=nxp.com header.i=@nxp.com header.a=rsa-sha256 header.s=selector2 header.b=MXI2+V+j; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by ozlabs.org (Postfix) with ESMTP id 49r9R72bxyz9sRW for ; Mon, 22 Jun 2020 23:36:03 +1000 (AEST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1729053AbgFVNgB (ORCPT ); Mon, 22 Jun 2020 09:36:01 -0400 Received: from mail-db8eur05on2051.outbound.protection.outlook.com ([40.107.20.51]:36110 "EHLO EUR05-DB8-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1728431AbgFVNf6 (ORCPT ); Mon, 22 Jun 2020 09:35:58 -0400 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=gB5BUJQgFiUCB4g/VgWAlzV9lsqjhDT7PDOZOnjAI1+8jOfo6QLpAUrY6bf6SlWFrDszwyDnxte03sekH6rEC5RnFJIMBKnOJrI8w4ZBTUB2tAobByonroChk7/4GgJBksAeq9U9PDsK/mOFdNZ6YNEcj32qeK6zSy+EDslIKs6/0PufmpySHRPxjyq5sSUcuLJarJr7EVD3zuROE/zDNeSPgBknFWM1h7ftc2knLDmIxwlHdKiYypjWejIrJsgArswhaKjY3n96y57HG6B7rAtwNASysokhAkMNqenB4MnnzsyZI32dm6OljYLznlw5X26oBV8hrO2wwIe81zPrZg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=2vfHvB8BU1+CY48gtaYTZuh4XrxpoUkm9d/B4/qFB6I=; b=hX3E3H0ByFP0jDu9phy1+fQTMNBV6PUF+H1EA1n0Pgvom7YshKLX94o1IZuGMPGmvV8F3PjGdwEcmsTjWvsseJ6x7vxwW92YmpUjPHXjKxgZPhadBhqV/bNaO0fIxM3vzT6jXO9ylSr0bmxUWPx1mVR9mM0Fo4LvMNBkg+F0I7Jvr2+m63hbynDpfVOgcS/UyekLzG79m2VbXlECbKLPtKByU6RuJrK8X8d8NxcA3Wvy2u/9iGkw6Kuo8oiwNM3EcOZ0be7sQcRRV4jOqLBrJ67q5aCqeif7qnnZOxtCAh3+MZ3xrIyLvey7P/rHNw7w1JItgjAfwXDXSiE+5u6kiw== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=nxp.com; dmarc=pass action=none header.from=nxp.com; dkim=pass header.d=nxp.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=nxp.com; s=selector2; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=2vfHvB8BU1+CY48gtaYTZuh4XrxpoUkm9d/B4/qFB6I=; b=MXI2+V+j32lYtP9R2s49FSHdT58HdeUH1ikAIVDbolQdlkatuCZ/S4NX69+8/2Z+5uD584ucQytBB2eMjhSkp4Gh16ddyrmiYdAekFms8IEnti5NQPySZGKvdeCwfCwD7TOUrNyEBU4o8FoymzgqNGoEYvOPSnv/gdgpxtUh+4A= Authentication-Results: davemloft.net; dkim=none (message not signed) header.d=none;davemloft.net; dmarc=none action=none header.from=nxp.com; Received: from AM0PR04MB5443.eurprd04.prod.outlook.com (2603:10a6:208:119::33) by AM0PR04MB5075.eurprd04.prod.outlook.com (2603:10a6:208:bf::24) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.3109.22; Mon, 22 Jun 2020 13:35:52 +0000 Received: from AM0PR04MB5443.eurprd04.prod.outlook.com ([fe80::f0b7:8439:3b5a:61bd]) by AM0PR04MB5443.eurprd04.prod.outlook.com ([fe80::f0b7:8439:3b5a:61bd%7]) with mapi id 15.20.3109.027; Mon, 22 Jun 2020 13:35:52 +0000 From: Florinel Iordache To: davem@davemloft.net, netdev@vger.kernel.org, andrew@lunn.ch, f.fainelli@gmail.com, hkallweit1@gmail.com, linux@armlinux.org.uk Cc: devicetree@vger.kernel.org, linux-doc@vger.kernel.org, robh+dt@kernel.org, mark.rutland@arm.com, kuba@kernel.org, corbet@lwn.net, shawnguo@kernel.org, leoyang.li@nxp.com, madalin.bucur@oss.nxp.com, ioana.ciornei@nxp.com, linux-kernel@vger.kernel.org, Florinel Iordache Subject: [PATCH net-next v3 1/7] doc: net: add backplane documentation Date: Mon, 22 Jun 2020 16:35:18 +0300 Message-Id: <1592832924-31733-2-git-send-email-florinel.iordache@nxp.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1592832924-31733-1-git-send-email-florinel.iordache@nxp.com> References: <1592832924-31733-1-git-send-email-florinel.iordache@nxp.com> Reply-to: florinel.iordache@nxp.com X-ClientProxiedBy: AM3PR07CA0086.eurprd07.prod.outlook.com (2603:10a6:207:6::20) To AM0PR04MB5443.eurprd04.prod.outlook.com (2603:10a6:208:119::33) MIME-Version: 1.0 X-MS-Exchange-MessageSentRepresentingType: 1 Received: from fsr-ub1464-128.ea.freescale.net (83.217.231.2) by AM3PR07CA0086.eurprd07.prod.outlook.com (2603:10a6:207:6::20) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256) id 15.20.3131.12 via Frontend Transport; Mon, 22 Jun 2020 13:35:51 +0000 X-Mailer: git-send-email 1.9.1 X-Originating-IP: [83.217.231.2] X-MS-PublicTrafficType: Email X-MS-Office365-Filtering-HT: Tenant X-MS-Office365-Filtering-Correlation-Id: db003716-9494-4648-8981-08d816b12ef4 X-MS-TrafficTypeDiagnostic: AM0PR04MB5075: X-MS-Exchange-Transport-Forked: True X-Microsoft-Antispam-PRVS: X-MS-Oob-TLC-OOBClassifiers: OLM:9508; X-Forefront-PRVS: 0442E569BC X-MS-Exchange-SenderADCheck: 1 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: UZHOaTPuFBgwpttIBfjtHcb7Qp6vZW5rmM6Qv4EcODujh/wXLmLurfyLSZAEf/48oeOGXg/DzVl08vNgzFIjvBnxZV9jWeMIjSc8B0xlzVdf1n/C6ST3PZxWdeppNFfK68Q4ot0rUr/n9k6QY64e1RwkurnrMHq9ZOWNFFrGTvQ5/t8CpNhtsFRFpuurtKrSE3QJF3OVKlLGXCrg3Qj8ZGC/tRIYdrqggy82rDe/pS5VnHVib1imEzlxxet/az6Dkl55CWc8EnQd+LI8xH4X3g0NJsy/OhO8oKSPyKEJytIgcrJdTqaOQ6GgeWfcMsgtcnmcQ9jhKbjYU+TexqMx5w== X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:AM0PR04MB5443.eurprd04.prod.outlook.com;PTR:;CAT:NONE;SFTY:;SFS:(4636009)(136003)(376002)(346002)(39860400002)(366004)(396003)(66476007)(66556008)(66946007)(83380400001)(5660300002)(2616005)(956004)(44832011)(86362001)(6666004)(2906002)(8676002)(6512007)(52116002)(16526019)(186003)(26005)(3450700001)(6506007)(4326008)(6486002)(316002)(478600001)(8936002)(7416002)(36756003);DIR:OUT;SFP:1101; X-MS-Exchange-AntiSpam-MessageData: Y6/DNVXO++JZvBoU7TBdGT+s7mU4KvElQxZMsRTPdWX0BwqXka9VIjfL5bb7dSHrM2ousBm3yIo/VEHDynO6r6+vZRnvAn79wF5L9hlGgNK1/xjwpJ/cNt8E6QwDJ9rippF1Rgmwxw5rOA2Cuf+K5sIr82F29Y6W12FEe1L2StO+xT0B9bjHsM9ynO8nH8QBdKTIPa7Ha/fccu3HTJxFG2HAULzhCBhHXGMfo3vUyFKPoowvH+q91wCItrGBZjCtYN5vMlCkGzT5tk7k4I2IRsGgUggmRJ2IqxXYjEThCCVG/s/oLBodXOKoYaE32wYqKtCCl6LTl3h3pA5iV1XOBGor1ur1+WMG+PcDFL0eZzFEGjNbOnAO5VGi09fPb6ilR9I5CoiTksemz2XL4fshqkhBslDNqbOcKAXkI9xfuyHBVuR00pa7CLgfe0qVv0VFDsTb3ZBLtjpry+ODoIfaEdxJBwtd6YjkeFPYp5aea6g= X-OriginatorOrg: nxp.com X-MS-Exchange-CrossTenant-Network-Message-Id: db003716-9494-4648-8981-08d816b12ef4 X-MS-Exchange-CrossTenant-OriginalArrivalTime: 22 Jun 2020 13:35:52.7234 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 686ea1d3-bc2b-4c6f-a92c-d99c5c301635 X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: 0B4jUNiV2BZQzipMnlmP1hhPuLBQ93A7zG0bUmflSApAygwlWBS+mWnA9L7aDpYm0wzhyDbMmLCUKowRUlkWxuPKxaVBd1a36VqqSMq1z8Y= X-MS-Exchange-Transport-CrossTenantHeadersStamped: AM0PR04MB5075 Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Add ethernet backplane documentation Signed-off-by: Florinel Iordache --- Documentation/networking/backplane.rst | 159 +++++++++++++++++++++++++++++++++ Documentation/networking/phy.rst | 9 +- 2 files changed, 165 insertions(+), 3 deletions(-) create mode 100644 Documentation/networking/backplane.rst diff --git a/Documentation/networking/backplane.rst b/Documentation/networking/backplane.rst new file mode 100644 index 0000000..fb91ba8 --- /dev/null +++ b/Documentation/networking/backplane.rst @@ -0,0 +1,159 @@ +.. SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) + +========================= +Ethernet Backplane Driver +========================= + +Author: +Florinel Iordache + +Contents +======== + + - Ethernet Backplane Overview + - Equalization + - Auto-negotiation + - Link training + - Enable backplane support in Linux kernel + - Ethernet Backplane support architecture + - Supported equalization algorithms + - Supported backplane protocols + - Supported platforms + +Ethernet Backplane Overview +=========================== + +Ethernet operation over electrical backplanes, also referred to as Ethernet +Backplane, combines the IEEE 802.3 Media Access Control (MAC) and MAC +Control sublayers with a family of Physical Layers defined to support +operation over a modular chassis backplane. +The main standard specification for Ethernet Backplane is: IEEE802.3ap-2007 +Amendment 4: Ethernet Operation over Electrical Backplanes +which includes the new Clause 69 through Clause 74. +Additional specifications define support for various speeds and 4-lanes: +IEEE802.3ba-2010. +Signal equalization is required based on the link quality. The standard +specifies that a start-up algorithm should be in place in order to get the +link up. + +Equalization +============ + +Equalization represents the procedure required to minimize the effects of signal +distortion, noise, interference occurred in high-speed communication channels. +The equalizer purpose is to improve signal integrity in terms of bit error rate +(BER) in order to allow accurate recovery of the transmitted symbols. + +A simplified view of channel equalization: + + LD <======== channel =========> LP + Local Device Link Partner + + |-----| ___ + | | <======== channel =========> / | + | | witout signal Equalization / \ + | | / | + ____| |____ ___/ \___ + + |\ _ |-----| + | \_/ | <======== channel =========> | | + | | with signal Equalization | | + | | | | + ____| |____ ____| |____ + + LD Tx waveform LP Rx waveform + +Auto-negotiation +================ + +Auto-negotiation allows the devices at both ends of a link segment to advertise +abilities, acknowledge receipt, and discover the common modes of operation that +both devices share. It also rejects the use of operational modes not shared by +both devices. Auto-negotiation does not test link segment characteristics. + +Link training +============= + +Link training occurs after auto-negotiation has determined the link to be a +Base-KR, but before auto-negotiation is done. It continuously exchanges messages +(training frames) between the local and the remote device as part of the +start-up phase. Link training tunes the equalization parameters of the remote and +local transmitter to improve the link quality in terms of bit error rate. +Both LP (link partner/remote device) and LD (local device) perform link training +in parallel. Link training is finished when both sides decide that the channel is +equalized and then the link is considered up. + +Enable backplane support in Linux kernel +======================================== + +To enable the Ethernet Backplane, the following Kconfig options are available: + +# enable generic Ethernet Backplane support: +CONFIG_ETH_BACKPLANE=y +# enable Fixed (No Equalization) algorithm: +CONFIG_ETH_BACKPLANE_FIXED=y +# enable 3-Taps Bit Edge Equalization (BEE) algorithm: +CONFIG_ETH_BACKPLANE_BEE=y +# enable QorIQ Ethernet Backplane driver: +CONFIG_ETH_BACKPLANE_QORIQ=y + +Ethernet Backplane support architecture +======================================= + +Ethernet Backplane support in Linux kernel complies with the following standard +design concepts: +* Modularity: + # internal components are separated in well defined functional modules +* Reusability: + # lower layer components provide basic functionalities which are reused by + the upper layer modules +* Extensibility: + # architecture can be easily extended with support for new: + - backplane protocols + - equalization algorithms + - supported devices +It is designed as a loosely coupled architecture in order to allow the +possibility to easily create desired backplane system configurations according +to user needs by specifying different components and initialization parameters +without recompiling the kernel. + + ------------------ ------------------------------------ + | EQ Algorithms | | Specific device drivers | + | ------------ | | Backplane support | + | | Fixed | | | ------------------ ----------- | + | ------------ | | | QorIQ | | | | + | | BEE | | | | devices | | | | + | ------------ | | | -------------- | | other | | + | | others | | | | | Serdes 10G | | | devices | | + ---------------------------- | | -------------- | | support | | + | Link Training | | | | Serdes 28G | | | | | + | and Auto-negotiation | | | -------------- | | | | + | (IEEE 802.3-ap/ba) | | |----------------| |---------| | + --------------------------------------------------------------------------- + | Ethernet Backplane Generic Driver | + --------------------------------------------------------------------------- + | PHY Abstraction Layer | + --------------------------------------------------------------------------- + +Supported equalization algorithms +================================= + +Ethernet Backplane supports the following equalization algorithms: + +- Fixed setup (No Equalization algorithm) +- 3-Taps Bit Edge Equalization (BEE) algorithm + +Supported backplane protocols +============================= + +Ethernet Backplane supports the following protocols: + +- Single-lane: +10GBase-KR + +Supported platforms +=================== + +Ethernet Backplane is enabled on the following platforms: + +LS1046A diff --git a/Documentation/networking/phy.rst b/Documentation/networking/phy.rst index 2561060..ec17c31 100644 --- a/Documentation/networking/phy.rst +++ b/Documentation/networking/phy.rst @@ -279,9 +279,12 @@ Some of the interface modes are described below: XFI and SFI are not PHY interface types in their own right. ``PHY_INTERFACE_MODE_10GKR`` - This is the IEEE 802.3 Clause 49 defined 10GBASE-R with Clause 73 - autonegotiation. Please refer to the IEEE standard for further - information. + This is 10G Ethernet Backplane over single lane specified in + IEEE802.3ap-2007 Amendment 4: Ethernet Operation over Electrical + Backplanes which includes the new Clause 69 through Clause 74 + including autonegotiation. 10GKR uses the same physical layer + encoding as 10GBASE-R defined in IEEE802.3 Clause 49. Please refer + to the IEEE standard for further information. Note: due to legacy usage, some 10GBASE-R usage incorrectly makes use of this definition. From patchwork Mon Jun 22 13:35:19 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Florinel Iordache X-Patchwork-Id: 1314385 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming-netdev@ozlabs.org Delivered-To: patchwork-incoming-netdev@ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=vger.kernel.org (client-ip=23.128.96.18; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=pass (p=none dis=none) header.from=nxp.com Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=nxp.com header.i=@nxp.com header.a=rsa-sha256 header.s=selector2 header.b=KxqZvsqd; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by ozlabs.org (Postfix) with ESMTP id 49r9RH2NMTz9sQx for ; Mon, 22 Jun 2020 23:36:11 +1000 (AEST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1729096AbgFVNgJ (ORCPT ); Mon, 22 Jun 2020 09:36:09 -0400 Received: from mail-db8eur05on2051.outbound.protection.outlook.com ([40.107.20.51]:36110 "EHLO EUR05-DB8-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1728259AbgFVNgE (ORCPT ); Mon, 22 Jun 2020 09:36:04 -0400 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=PDZWT/syNaNtKKo/4+uD2ESS3WRpKuxiHlBg4ojctjrj8VFkN4w9oxvEkgccaa5YmWgBSEHPIUzoGqRddeQlUnBLxuZFeT2J4KJpAotd5wEaX5MGyRczC48jplppKCf7qG/5AKM8B5kXo4irCY1n7AnbhjRD+OylzAkrwuokpsdgaNWhmt0Oj9ACGePT5sifev+tBhK41YHjh8GgrxPHd5wNGs8K2OivCVN0Z1TNAtTiFPaivw0vQTfIQXEiUsDfBOHqV733cETyAcxK9Rs3SRqHbMSTJvD4vb1DTAfU3elU6P7GVUh2CxEEmlLZDxJpG7MkRVWVz+vBZNbmm+n6dQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=9jPICUlJxMub93j+6Chh4K8Kuj2oMgzO4E6FeYty6SA=; b=cVzZRtBXexai/WsPzEZuXKZup8wyywAr+TXOXUC2R72RLWyZiQRWRkdHK03Bl5wrhY4kuslpstVh8TN2G2/ywvq34inesiHDv0pUXA15HCHILx5dY52YbrDDxTYoak/wgu6i+ljERBv7QkmMUWo8jjo9OdpHUQYfQ+IgtaO01KgPTJuxBKDQjJ8VbVbLUOdvYOg82hC0oDfa6e2ls+nKzJtmzcfpD4RMmKVIZnNEvfrck/2KxIy6nhDn5TggHWHtMRwodBC6fmJKPo73vivQdrcUe1+rdoyfnnx8s3V0E2dOGvd4WYbvrjhPphDKTa3dC1UEIGhqM9e13KdwUsKfQg== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=nxp.com; dmarc=pass action=none header.from=nxp.com; dkim=pass header.d=nxp.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=nxp.com; s=selector2; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=9jPICUlJxMub93j+6Chh4K8Kuj2oMgzO4E6FeYty6SA=; b=KxqZvsqdNl5pwPLhOd1pnUawqh526dYGL1UJcewbPxrBA69B4HYSn/v4yxQY8uS6CJtXVl7juxASGHXp0XlceJrZB88AwloOsGKCQxvbtxjm6ya+aM5J+CgAJshPgv6p1eei64rHQeT6IA9W+UcijaoCmK8vufgxKVJnbrdaLTc= Authentication-Results: davemloft.net; dkim=none (message not signed) header.d=none;davemloft.net; dmarc=none action=none header.from=nxp.com; Received: from AM0PR04MB5443.eurprd04.prod.outlook.com (2603:10a6:208:119::33) by AM0PR04MB5075.eurprd04.prod.outlook.com (2603:10a6:208:bf::24) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.3109.22; Mon, 22 Jun 2020 13:35:53 +0000 Received: from AM0PR04MB5443.eurprd04.prod.outlook.com ([fe80::f0b7:8439:3b5a:61bd]) by AM0PR04MB5443.eurprd04.prod.outlook.com ([fe80::f0b7:8439:3b5a:61bd%7]) with mapi id 15.20.3109.027; Mon, 22 Jun 2020 13:35:53 +0000 From: Florinel Iordache To: davem@davemloft.net, netdev@vger.kernel.org, andrew@lunn.ch, f.fainelli@gmail.com, hkallweit1@gmail.com, linux@armlinux.org.uk Cc: devicetree@vger.kernel.org, linux-doc@vger.kernel.org, robh+dt@kernel.org, mark.rutland@arm.com, kuba@kernel.org, corbet@lwn.net, shawnguo@kernel.org, leoyang.li@nxp.com, madalin.bucur@oss.nxp.com, ioana.ciornei@nxp.com, linux-kernel@vger.kernel.org, Florinel Iordache Subject: [PATCH net-next v3 2/7] dt-bindings: net: add backplane dt bindings Date: Mon, 22 Jun 2020 16:35:19 +0300 Message-Id: <1592832924-31733-3-git-send-email-florinel.iordache@nxp.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1592832924-31733-1-git-send-email-florinel.iordache@nxp.com> References: <1592832924-31733-1-git-send-email-florinel.iordache@nxp.com> Reply-to: florinel.iordache@nxp.com X-ClientProxiedBy: AM3PR07CA0086.eurprd07.prod.outlook.com (2603:10a6:207:6::20) To AM0PR04MB5443.eurprd04.prod.outlook.com (2603:10a6:208:119::33) MIME-Version: 1.0 X-MS-Exchange-MessageSentRepresentingType: 1 Received: from fsr-ub1464-128.ea.freescale.net (83.217.231.2) by AM3PR07CA0086.eurprd07.prod.outlook.com (2603:10a6:207:6::20) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256) id 15.20.3131.12 via Frontend Transport; Mon, 22 Jun 2020 13:35:52 +0000 X-Mailer: git-send-email 1.9.1 X-Originating-IP: [83.217.231.2] X-MS-PublicTrafficType: Email X-MS-Office365-Filtering-HT: Tenant X-MS-Office365-Filtering-Correlation-Id: 765a6a81-38b9-4111-7d59-08d816b12f9f X-MS-TrafficTypeDiagnostic: AM0PR04MB5075: X-MS-Exchange-Transport-Forked: True X-Microsoft-Antispam-PRVS: X-MS-Oob-TLC-OOBClassifiers: OLM:6108; X-Forefront-PRVS: 0442E569BC X-MS-Exchange-SenderADCheck: 1 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: 8zQEWGWd4NUxCgIVEPSHJH82JxyR9TOs2XxKb1SWAuSGVgw/KBPhcHm5K4VTWXaKcb5BsXBF4xuFX1mJy/P4nZn8IYKgwEKr0GYvw6wAx6+6oS8z0l4hvLp8QCGyVG32P9Rj9+B75bBhrNOGbEpoUrEyRtg4FLvE7/6wMVYRDmF5uGsvTk4ldiEqoJWeuAqLTLWSRmhdaS+BhwV4GKYNe2IY+aLZ6vM0Gcf0AiAXNUZy+I1RQgEfBAA46EEuZ9jpaCkbnzIDPIEyDvCDUDZTEtOAuc7Pxkw7LJnNDWuxYipgV3xALCeY/sQeuHVwu6piZOKEWl/qfxoD9wzOLgCcGT6bGE2Jwx3v9d3K9kXKCkA7EuUxlA4lqMDa3MdFRdF92BPiSDPaGCe6n1vkMgYGiA== X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:AM0PR04MB5443.eurprd04.prod.outlook.com;PTR:;CAT:NONE;SFTY:;SFS:(4636009)(136003)(376002)(346002)(39860400002)(366004)(396003)(66476007)(66556008)(66946007)(83380400001)(5660300002)(2616005)(956004)(44832011)(86362001)(6666004)(2906002)(8676002)(6512007)(52116002)(16526019)(186003)(26005)(3450700001)(6506007)(4326008)(6486002)(966005)(316002)(478600001)(8936002)(7416002)(36756003);DIR:OUT;SFP:1101; X-MS-Exchange-AntiSpam-MessageData: /Py8OFL2uNPETKk1FbAAYXfFM92aDU/qK/Xq+UV8ZE4gEUku/HfsF62nCxEPTGkBp3zMtjqeqmnILYu96zRw7+cb965xVRfCd53i5dSK0U6zKm7nKV/rYeaLvEo19bznoxH3oRTUGZBntLGy+LxZbxhq0aeC1xc0HFXtIz+Ma7nYgFj8UKHcxgGJsqDD8E5B3aX7uObUyqTuK4K/kqScnoqmY2LcEROQkKuoZAOnhDkD32C7YrRWFOTYvFrFm9F9mGwOhxMW3x6DC4P5L8+FxI/nALuKDIwaTGXAWkZyuQRSAmcX3wJyNbfSY5WQb1DXPYbRPJj8OADFP0ssgxSKadZFneLjRhIo6xW3nI9bZTaVdkbTmT1oTs1JwlLNF5WfXvFdDbGZUHVk11Zc8WVSnhRNbKidFhn+W6Y0C7pyh+cScgdNfowzI2SLP2uHSRlAQD4Rz3j97nUalYgsO2wKuYFNkaUk6Bzt9hl/kEyuDq0= X-OriginatorOrg: nxp.com X-MS-Exchange-CrossTenant-Network-Message-Id: 765a6a81-38b9-4111-7d59-08d816b12f9f X-MS-Exchange-CrossTenant-OriginalArrivalTime: 22 Jun 2020 13:35:53.8228 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 686ea1d3-bc2b-4c6f-a92c-d99c5c301635 X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: ffNJP54skKIFVxSIfSjDOqFOqomNM+r278FaY7CE+U9DhyURi72jnpajlaV/tonUf0ncX1zcrB+Zqpk1Dl0nZxgh43dwsZp6x4DuGoefoHU= X-MS-Exchange-Transport-CrossTenantHeadersStamped: AM0PR04MB5075 Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Add ethernet backplane device tree bindings Signed-off-by: Florinel Iordache --- .../bindings/net/ethernet-controller.yaml | 7 ++- .../devicetree/bindings/net/ethernet-phy.yaml | 50 ++++++++++++++++++++++ .../devicetree/bindings/net/serdes-lane.yaml | 49 +++++++++++++++++++++ Documentation/devicetree/bindings/net/serdes.yaml | 42 ++++++++++++++++++ 4 files changed, 146 insertions(+), 2 deletions(-) create mode 100644 Documentation/devicetree/bindings/net/serdes-lane.yaml create mode 100644 Documentation/devicetree/bindings/net/serdes.yaml diff --git a/Documentation/devicetree/bindings/net/ethernet-controller.yaml b/Documentation/devicetree/bindings/net/ethernet-controller.yaml index 1c44740..6c4c7d8 100644 --- a/Documentation/devicetree/bindings/net/ethernet-controller.yaml +++ b/Documentation/devicetree/bindings/net/ethernet-controller.yaml @@ -91,10 +91,13 @@ properties: - rxaui - xaui - # 10GBASE-KR, XFI, SFI - - 10gbase-kr + # 10GBASE-R, XFI, SFI + - 10gbase-r - usxgmii + # 10GBASE-KR (10G Ethernet Backplane with autonegotiation) + - 10gbase-kr + phy-mode: $ref: "#/properties/phy-connection-type" diff --git a/Documentation/devicetree/bindings/net/ethernet-phy.yaml b/Documentation/devicetree/bindings/net/ethernet-phy.yaml index 9b1f114..a23a7d6 100644 --- a/Documentation/devicetree/bindings/net/ethernet-phy.yaml +++ b/Documentation/devicetree/bindings/net/ethernet-phy.yaml @@ -162,6 +162,42 @@ properties: description: Specifies a reference to a node representing a SFP cage. + eq-algorithm: + description: + Specifies the desired equalization algorithm to be used + by the KR link training + oneOf: + - const: fixed + description: + Backplane KR using fixed coefficients meaning no + equalization algorithm + - const: bee + description: + Backplane KR using 3-Taps Bit Edge Equalization (BEE) + algorithm + + eq-init: + $ref: /schemas/types.yaml#/definitions/uint32-array + minItems: 3 + maxItems: 3 + description: + Triplet of KR coefficients. Specifies the initialization + values for standard KR equalization coefficients used by + the link training (pre-cursor, main-cursor, post-cursor) + + eq-params: + $ref: /schemas/types.yaml#/definitions/uint32-array + description: + Variable size array of KR parameters. Specifies the HW + specific parameters used by the link training. + + lane-handle: + $ref: /schemas/types.yaml#definitions/phandle + description: + Specifies a reference (or array of references) to a node + representing the desired SERDES lane (or lanes) used in + backplane mode. + required: - reg @@ -184,3 +220,17 @@ examples: reset-deassert-us = <2000>; }; }; + - | + ethernet { + #address-cells = <1>; + #size-cells = <0>; + + ethernet-phy@0 { + compatible = "ethernet-phy-ieee802.3-c45"; + reg = <0x0>; + lane-handle = <&lane_d>; + eq-algorithm = "fixed"; + eq-init = <0x2 0x29 0x5>; + eq-params = <0>; + }; + }; diff --git a/Documentation/devicetree/bindings/net/serdes-lane.yaml b/Documentation/devicetree/bindings/net/serdes-lane.yaml new file mode 100644 index 0000000..d83a6a9 --- /dev/null +++ b/Documentation/devicetree/bindings/net/serdes-lane.yaml @@ -0,0 +1,49 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/serdes-lane.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Serdes Lane Binding + +maintainers: + - Florinel Iordache + +properties: + $nodename: + pattern: "^lane(@[a-f0-9]+)?$" + + compatible: + oneOf: + - const: lane-10g + description: Lane part of a 10G SerDes module + + reg: + description: + Registers memory map offset and size for this lane + + reg-names: + description: + Names of the register map given in "reg" node. + +examples: + - | + serdes1: serdes@1ea0000 { + compatible = "serdes-10g"; + reg = <0x0 0x1ea0000 0 0x00002000>; + reg-names = "serdes", "serdes-10g"; + little-endian; + + #address-cells = <1>; + #size-cells = <1>; + lane_a: lane@800 { + compatible = "lane-10g"; + reg = <0x800 0x40>; + reg-names = "lane", "serdes-lane"; + }; + lane_b: lane@840 { + compatible = "lane-10g"; + reg = <0x840 0x40>; + reg-names = "lane", "serdes-lane"; + }; + }; diff --git a/Documentation/devicetree/bindings/net/serdes.yaml b/Documentation/devicetree/bindings/net/serdes.yaml new file mode 100644 index 0000000..ed77689c --- /dev/null +++ b/Documentation/devicetree/bindings/net/serdes.yaml @@ -0,0 +1,42 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/serdes.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Serdes Module Binding + +maintainers: + - Florinel Iordache + +properties: + $nodename: + pattern: "^serdes(@[a-f0-9]+)?$" + + compatible: + oneOf: + - const: serdes-10g + description: SerDes module type of 10G + + reg: + description: + Registers memory map offset and size for this serdes module + + reg-names: + description: + Names of the register map given in "reg" node. + + little-endian: + description: + Specifies the endianness of serdes module + For complete definition see + Documentation/devicetree/bindings/common-properties.txt + +examples: + - | + serdes1: serdes@1ea0000 { + compatible = "serdes-10g"; + reg = <0x0 0x1ea0000 0 0x00002000>; + reg-names = "serdes", "serdes-10g"; + little-endian; + }; From patchwork Mon Jun 22 13:35:20 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Florinel Iordache X-Patchwork-Id: 1314387 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming-netdev@ozlabs.org Delivered-To: patchwork-incoming-netdev@ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=vger.kernel.org (client-ip=23.128.96.18; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=pass (p=none dis=none) header.from=nxp.com Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=nxp.com header.i=@nxp.com header.a=rsa-sha256 header.s=selector2 header.b=U5KWavIT; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by ozlabs.org (Postfix) with ESMTP id 49r9RP5Pcyz9sSF for ; Mon, 22 Jun 2020 23:36:17 +1000 (AEST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1729126AbgFVNgQ (ORCPT ); Mon, 22 Jun 2020 09:36:16 -0400 Received: from mail-db8eur05on2055.outbound.protection.outlook.com ([40.107.20.55]:24081 "EHLO EUR05-DB8-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1728431AbgFVNgH (ORCPT ); Mon, 22 Jun 2020 09:36:07 -0400 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=FKQZjv3PXhtwXB4phH2SnBkYrPcuokOJjJiuSL0VvnSrlBmaHvFGbIS0ixW+rx7kzSFKzz198hvdhsPtlOGDVJBEGgesuqU/hNn6h+vASzR9WXj+h7hUvpwiXINf8AYdmKPRllbwufH7m6xqePjz7ULAyYLngVAUqlZG3o73Fd68GRnQo6pd9RpcMIb7w9URfPSm8aQVPcKJtBYO62l9jES834zN3y4QbjPilyz4W6grz29HXdHl2zBMMrNEt7ZnxtAWy5HCl1Jc8slbOSrZoM26nt2EQQXEK0Rvo7hDa8hZi3QhQngdBEOA/dV7kQfiaXE5P43Vii4UNj9V9ia6FA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=O9u4rjTqPj5dpCXxPPN+Lop/Yuq6Yzfwlq5jqg0+/Q8=; b=K/H9pzo3wnIXwaBscfFRciovvzW7y7XG8p7RxoHn0hEMScg3sppgrOv1vj7CH8zqDEpnWMrp7J1qycO5G4/kPByTJ+wQAdCpr8bxh95ENmeTHXtoteK4IiOVQaBJ6we00TpaiOYvnHHuHjq3mVP89iya7d8VJLkBSviXX2QcuQocLYxOMs6/oUlN44Ln8GneWyijpnsApC1/Mo+lQ4Kfm3/ca9wzPjHjPPGfuyxsUqML0GaoxW+hZ1ATbZd1LXaYagijTabYbPgm52Mwu5OGSfS4QIBBQZ24OfBUIcHhWNh2gQnJEqTC8hwE9n5JxP2mrYLbSrSxzG16qxO4dKwqKA== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=nxp.com; dmarc=pass action=none header.from=nxp.com; dkim=pass header.d=nxp.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=nxp.com; s=selector2; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=O9u4rjTqPj5dpCXxPPN+Lop/Yuq6Yzfwlq5jqg0+/Q8=; b=U5KWavIT/7vCM1LLw2rOQx3E2NYsGAD5EHOAZb5wuj6en4RXGIAGKGbknREHAUjdTrmPRHY2R+5xqUuiyBqCAbhJXZ2yAttOtsUCZ/dIQrtV3rN3f6yCYY8o8Xzy1x94PCS5DSnkyBcFbW7zFQiDx9lpz1uyAftiYQ7HyJhwY2k= Authentication-Results: davemloft.net; dkim=none (message not signed) header.d=none;davemloft.net; dmarc=none action=none header.from=nxp.com; Received: from AM0PR04MB5443.eurprd04.prod.outlook.com (2603:10a6:208:119::33) by AM0PR04MB5075.eurprd04.prod.outlook.com (2603:10a6:208:bf::24) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.3109.22; Mon, 22 Jun 2020 13:35:55 +0000 Received: from AM0PR04MB5443.eurprd04.prod.outlook.com ([fe80::f0b7:8439:3b5a:61bd]) by AM0PR04MB5443.eurprd04.prod.outlook.com ([fe80::f0b7:8439:3b5a:61bd%7]) with mapi id 15.20.3109.027; Mon, 22 Jun 2020 13:35:55 +0000 From: Florinel Iordache To: davem@davemloft.net, netdev@vger.kernel.org, andrew@lunn.ch, f.fainelli@gmail.com, hkallweit1@gmail.com, linux@armlinux.org.uk Cc: devicetree@vger.kernel.org, linux-doc@vger.kernel.org, robh+dt@kernel.org, mark.rutland@arm.com, kuba@kernel.org, corbet@lwn.net, shawnguo@kernel.org, leoyang.li@nxp.com, madalin.bucur@oss.nxp.com, ioana.ciornei@nxp.com, linux-kernel@vger.kernel.org, Florinel Iordache Subject: [PATCH net-next v3 3/7] net: fman: add kr support for dpaa1 mac Date: Mon, 22 Jun 2020 16:35:20 +0300 Message-Id: <1592832924-31733-4-git-send-email-florinel.iordache@nxp.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1592832924-31733-1-git-send-email-florinel.iordache@nxp.com> References: <1592832924-31733-1-git-send-email-florinel.iordache@nxp.com> Reply-to: florinel.iordache@nxp.com X-ClientProxiedBy: AM3PR07CA0086.eurprd07.prod.outlook.com (2603:10a6:207:6::20) To AM0PR04MB5443.eurprd04.prod.outlook.com (2603:10a6:208:119::33) MIME-Version: 1.0 X-MS-Exchange-MessageSentRepresentingType: 1 Received: from fsr-ub1464-128.ea.freescale.net (83.217.231.2) by AM3PR07CA0086.eurprd07.prod.outlook.com (2603:10a6:207:6::20) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256) id 15.20.3131.12 via Frontend Transport; Mon, 22 Jun 2020 13:35:53 +0000 X-Mailer: git-send-email 1.9.1 X-Originating-IP: [83.217.231.2] X-MS-PublicTrafficType: Email X-MS-Office365-Filtering-HT: Tenant X-MS-Office365-Filtering-Correlation-Id: 0f5b9d7c-ffb4-4959-11c5-08d816b13049 X-MS-TrafficTypeDiagnostic: AM0PR04MB5075: X-MS-Exchange-Transport-Forked: True X-Microsoft-Antispam-PRVS: X-MS-Oob-TLC-OOBClassifiers: OLM:2150; X-Forefront-PRVS: 0442E569BC X-MS-Exchange-SenderADCheck: 1 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: 0yxe9SXwJB4dsthxN8MiEl7C4UuOl9TQfvuu0LHKeLJkfHkB35WwqEdFNnYqA3NQJJFmF+p9qVDlbQVWFYZwjTbibQ6qBg9QLVYd6/Hw6ywxj9JqaEj9SxKO98Vsei6KkYEJqIVEROeBMU+Gp9MeO2IXyp+pkkryl3KFOCyIE/WA/G4miVR1AJiOMt2+sFLvof2N8mixHNNKziXqzEWrDKiiDy/zPAIux2SivqYv61rLZHMoo+z0wTBPULpDUPB5MUz+EdPpyR7njPcE9ByFJAtVhmZMOLE4DKoZfl4ksaHZ6KIdptrBD/7OafmQzV034tHpFaGQptG/O3jRVcjZKw== X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:AM0PR04MB5443.eurprd04.prod.outlook.com;PTR:;CAT:NONE;SFTY:;SFS:(4636009)(136003)(376002)(346002)(39860400002)(366004)(396003)(66476007)(66556008)(66946007)(83380400001)(5660300002)(2616005)(956004)(44832011)(86362001)(6666004)(2906002)(8676002)(6512007)(52116002)(16526019)(186003)(26005)(3450700001)(6506007)(4326008)(6486002)(316002)(478600001)(8936002)(7416002)(36756003);DIR:OUT;SFP:1101; X-MS-Exchange-AntiSpam-MessageData: GuJcJvxxWQz1ozPg7xQxDec0ADsgHNDCmhfHZDIwf9seomii72ipLJyK2wN15uYkQxkYPgPxUe0twkXZ/IU0X1YFIBvrtkqs1KPOuOO7MJ0dSYAUABUs3goUsmkDJoxdQ5pVoCB1GiIpZqlVNha205qvfub3ofNaYAlQkVfHCtIwkvBWJy0NtenWDQQ0jn7l5nsThj76eCvT2O5By9de8C+GbpMFDEUa98tR0ZLHLM7P1Z7eJUdJ2drP2nmrVqLfYsgCYHGjpvs9g7+Z3BOtnJK3qpNDCF5VBTdv0KcZc10+DASIW5w4rYDyOlo1XVWSSngnKsy0OXjNAoxK6pss70aze9qdz/hma/Q/2wYlr5RSuLHFwer7I5FRJfkciN2Wf+jgiAShMGNk2kr8LYTjjlMXgO+3iwoqgiHHJMy9yQCP6TISl9e9cP4xULDngIH6EF6te/GmFBR/LxOS6zIGMi2LN9XZhIGroAqy8612WlI= X-OriginatorOrg: nxp.com X-MS-Exchange-CrossTenant-Network-Message-Id: 0f5b9d7c-ffb4-4959-11c5-08d816b13049 X-MS-Exchange-CrossTenant-OriginalArrivalTime: 22 Jun 2020 13:35:54.9531 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 686ea1d3-bc2b-4c6f-a92c-d99c5c301635 X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: 4//vHN2et8FpriLa7TDdPaIFyF1AgmVu/jylKvQH7/N+aKVWPagXH0Ef72AAQum8Hf821LAG23l5YhWHzgxYrVhjUQOzTUmtJv+57MbQORo= X-MS-Exchange-Transport-CrossTenantHeadersStamped: AM0PR04MB5075 Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Add kr support in mac driver for dpaa1 Signed-off-by: Florinel Iordache --- drivers/net/ethernet/freescale/fman/mac.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/freescale/fman/mac.c b/drivers/net/ethernet/freescale/fman/mac.c index 43427c5..90fe594 100644 --- a/drivers/net/ethernet/freescale/fman/mac.c +++ b/drivers/net/ethernet/freescale/fman/mac.c @@ -1,4 +1,5 @@ /* Copyright 2008-2015 Freescale Semiconductor, Inc. + * Copyright 2020 NXP * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -220,6 +221,7 @@ static int memac_initialization(struct mac_device *mac_dev) set_fman_mac_params(mac_dev, ¶ms); + /* use XGMII mode for all 10G interfaces to setup memac */ if (priv->max_speed == SPEED_10000) params.phy_if = PHY_INTERFACE_MODE_XGMII; @@ -540,7 +542,8 @@ static void setup_memac(struct mac_device *mac_dev) [PHY_INTERFACE_MODE_RGMII_TXID] = SPEED_1000, [PHY_INTERFACE_MODE_RTBI] = SPEED_1000, [PHY_INTERFACE_MODE_QSGMII] = SPEED_1000, - [PHY_INTERFACE_MODE_XGMII] = SPEED_10000 + [PHY_INTERFACE_MODE_XGMII] = SPEED_10000, + [PHY_INTERFACE_MODE_10GKR] = SPEED_10000 }; static struct platform_device *dpaa_eth_add_device(int fman_id, @@ -795,9 +798,12 @@ static int mac_probe(struct platform_device *_of_dev) if (priv->max_speed == 1000) mac_dev->if_support |= SUPPORTED_1000baseT_Full; - /* The 10G interface only supports one mode */ + /* Supported 10G interfaces */ if (mac_dev->phy_if == PHY_INTERFACE_MODE_XGMII) mac_dev->if_support = SUPPORTED_10000baseT_Full; + /* Supported KR interfaces */ + if (mac_dev->phy_if == PHY_INTERFACE_MODE_10GKR) + mac_dev->if_support = SUPPORTED_10000baseKR_Full; /* Get the rest of the PHY information */ mac_dev->phy_node = of_parse_phandle(mac_node, "phy-handle", 0); From patchwork Mon Jun 22 13:35:21 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Florinel Iordache X-Patchwork-Id: 1314389 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming-netdev@ozlabs.org Delivered-To: patchwork-incoming-netdev@ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=vger.kernel.org (client-ip=23.128.96.18; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=pass (p=none dis=none) header.from=nxp.com Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=nxp.com header.i=@nxp.com header.a=rsa-sha256 header.s=selector2 header.b=Unfc6Fuz; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by ozlabs.org (Postfix) with ESMTP id 49r9Rq3Zggz9sQt for ; Mon, 22 Jun 2020 23:36:39 +1000 (AEST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1729210AbgFVNgi (ORCPT ); Mon, 22 Jun 2020 09:36:38 -0400 Received: from mail-db8eur05on2051.outbound.protection.outlook.com ([40.107.20.51]:36110 "EHLO EUR05-DB8-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1729076AbgFVNg3 (ORCPT ); Mon, 22 Jun 2020 09:36:29 -0400 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=B4oNLyzendWmGESLDLAlhAPiFZUHJj573LYFqmmmu1M2vTaKJ5WqfyXeB+01AH41Ocu6Xml/UUnTDJMEKsUpg9ODVGvbOwOPIZnCActiM2Wj1wV94GbMMvk53dL8MWBpzCjdchWjIeO38lzu7jjzrCDyuE6bRBhyGOHO8I4TKCcQHilc2XJ/+YT9lDXLjQmBvB2lveEj+vU+yy7SaT00VzLEHl20WmvTITy8haJz3c8r21MYXTPhk4nFvFMYzmctd+XpwtF0xDugt8awXrvecdpnnerKy7yco30004Lj2w/vo7J02cVYQ+k3zInNpGUMB0zcV5gI98fvWQwFGKafQw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=E6LZb2KO5kJ/ueOyBBnA4T52KylABg5FxSlVtxLg0BU=; b=Ej0MSiUGr/AKyBIJCXWJyeYgxy/zwlfLzVSp5A2hQeNxWQyXxB1M2i11vQS7Q0a4vNbedIwsicZh4H7gdkb0QADlO5PgzUiNOpjlbYp38NEE4GFODwWbsvmHWUGXB+zs0aurU8g2xrP5KpBKyocuYysJmojY1XtNgBDRx6TADE5pehyC6PJAlIKInOR1cCrHz+Fu8O+6JD++2lrHHdVlt1O/sgirqLnmiCKfGO05FC5LIDTHFMP8gwkmasYAAlPtwwWzyPSGsAr1yM7hKTyrn9cTpWu/2iQbpO/pMIA+YcjST50f+VXTWVJTwpmRJDnyvOX3kYGrRNk8HgSIrVyvQA== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=nxp.com; dmarc=pass action=none header.from=nxp.com; dkim=pass header.d=nxp.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=nxp.com; s=selector2; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=E6LZb2KO5kJ/ueOyBBnA4T52KylABg5FxSlVtxLg0BU=; b=Unfc6FuzTz2OYiCD/03U/vnUpoOKGsYHGqYRLZIxFLuL/2XxSElFBbHFXBsHBmAwzFmsaGxbPh5BQz8ULDc4pUf/9ug1rvymdxUP/q39aKsL1mlZuwRV2DpPG8TrHmU120mz9DjV35XMi6qUHw4QuxpAnegiscOILcfyCBqgcsI= Authentication-Results: davemloft.net; dkim=none (message not signed) header.d=none;davemloft.net; dmarc=none action=none header.from=nxp.com; Received: from AM0PR04MB5443.eurprd04.prod.outlook.com (2603:10a6:208:119::33) by AM0PR04MB5075.eurprd04.prod.outlook.com (2603:10a6:208:bf::24) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.3109.22; Mon, 22 Jun 2020 13:35:56 +0000 Received: from AM0PR04MB5443.eurprd04.prod.outlook.com ([fe80::f0b7:8439:3b5a:61bd]) by AM0PR04MB5443.eurprd04.prod.outlook.com ([fe80::f0b7:8439:3b5a:61bd%7]) with mapi id 15.20.3109.027; Mon, 22 Jun 2020 13:35:56 +0000 From: Florinel Iordache To: davem@davemloft.net, netdev@vger.kernel.org, andrew@lunn.ch, f.fainelli@gmail.com, hkallweit1@gmail.com, linux@armlinux.org.uk Cc: devicetree@vger.kernel.org, linux-doc@vger.kernel.org, robh+dt@kernel.org, mark.rutland@arm.com, kuba@kernel.org, corbet@lwn.net, shawnguo@kernel.org, leoyang.li@nxp.com, madalin.bucur@oss.nxp.com, ioana.ciornei@nxp.com, linux-kernel@vger.kernel.org, Florinel Iordache Subject: [PATCH net-next v3 4/7] net: phy: add backplane kr driver support Date: Mon, 22 Jun 2020 16:35:21 +0300 Message-Id: <1592832924-31733-5-git-send-email-florinel.iordache@nxp.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1592832924-31733-1-git-send-email-florinel.iordache@nxp.com> References: <1592832924-31733-1-git-send-email-florinel.iordache@nxp.com> Reply-to: florinel.iordache@nxp.com X-ClientProxiedBy: AM3PR07CA0086.eurprd07.prod.outlook.com (2603:10a6:207:6::20) To AM0PR04MB5443.eurprd04.prod.outlook.com (2603:10a6:208:119::33) MIME-Version: 1.0 X-MS-Exchange-MessageSentRepresentingType: 1 Received: from fsr-ub1464-128.ea.freescale.net (83.217.231.2) by AM3PR07CA0086.eurprd07.prod.outlook.com (2603:10a6:207:6::20) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256) id 15.20.3131.12 via Frontend Transport; Mon, 22 Jun 2020 13:35:55 +0000 X-Mailer: git-send-email 1.9.1 X-Originating-IP: [83.217.231.2] X-MS-PublicTrafficType: Email X-MS-Office365-Filtering-HT: Tenant X-MS-Office365-Filtering-Correlation-Id: 0a2785e9-2cf2-41f2-38cd-08d816b13100 X-MS-TrafficTypeDiagnostic: AM0PR04MB5075: X-MS-Exchange-Transport-Forked: True X-Microsoft-Antispam-PRVS: X-MS-Oob-TLC-OOBClassifiers: OLM:2399; X-Forefront-PRVS: 0442E569BC X-MS-Exchange-SenderADCheck: 1 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: xioB7JQcjGeiS3T8dZ+YNNUyEL8aLEFYuD7g3kBjQhMT0VTNxpm5m3jyY1KzIBzE2CsHRAWcvlhOYonIJ/w3GvoiQgXLTGFZkFceqvxT3BkVugm4L2xk3mCZsbvhDv19L+pCmnTQWZhciAJd9u+2wtVoUtn1neKVI0bO+//Wu9VhVM3XorHKpDbDJu8qpYz/bXr1K6wk7bE1x7GSn1T6xtN9rk2rSyAA/F0BX3V2M73QUVMjkyAVDaaLn1el10lf5aIdgEWzOj5N8dh0zN6rcGBV9mtgXRmPsh+dAM3BPzfQuB5YRgD10QbwpB4a/DpHi4MW1St1+X5LFIckSvNrYw== X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:AM0PR04MB5443.eurprd04.prod.outlook.com;PTR:;CAT:NONE;SFTY:;SFS:(4636009)(136003)(376002)(346002)(39860400002)(366004)(396003)(66476007)(66556008)(66946007)(83380400001)(5660300002)(2616005)(30864003)(956004)(44832011)(86362001)(6666004)(2906002)(8676002)(6512007)(52116002)(16526019)(186003)(26005)(3450700001)(6506007)(4326008)(6486002)(316002)(478600001)(8936002)(7416002)(36756003)(579004)(559001);DIR:OUT;SFP:1101; X-MS-Exchange-AntiSpam-MessageData: m/W6qPtj8xal3btln//Dhc5OeLIHjm/+9fKI3V3mkZ5VIdnM9Yd8ED2VQax029mi/dmdL+NWMKwTeSXtKM5aBx1dtOciSucwSTaiuK63PrxbPhu/KG4mhgvrOyWHChPk671YWAj+s8Cy8hS+Q2GfIHV4L2aNA3rCNFmJNnLqQzUU3Xye8EmjC4zcuBkrGnH4Ily/BVZ97sodD5OjZti4Z4+3+6aeXNmFZ3HObAwi8MFtnbdATVPlDOQbpJl6q637fI0Qb6nDZH9WVTGjBHnzQ9P7KqUj4Lph77IUydJKpXL7YQBcy6vnqaUTFq9YzgvbmnNIUkwo2pl1xdd0q7c25E675khCfth6lB4lRq7BuyTysLSUzOlr78KifYN6+xbUKuX5baqJQQVW8+mQOkawgLBLsU4qGGkmKzl+U8qbdOxrX6NHAK2WpQ2wdNwjlGXKHAOwhGWVkZS4xH/4vHSSPADO9BoV5jYi40VJ0RjsJmQ= X-OriginatorOrg: nxp.com X-MS-Exchange-CrossTenant-Network-Message-Id: 0a2785e9-2cf2-41f2-38cd-08d816b13100 X-MS-Exchange-CrossTenant-OriginalArrivalTime: 22 Jun 2020 13:35:56.2444 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 686ea1d3-bc2b-4c6f-a92c-d99c5c301635 X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: a2onu3eBzo37Jz7ZoVvcaUlq2KpOx5q5Y4tdWbms1CuA2bqEJCJBl3f458+eJDjQjz/rnSaR1giuR8xGKPvfph3ad0FyybP3boJ9hegOCfk= X-MS-Exchange-Transport-CrossTenantHeadersStamped: AM0PR04MB5075 Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Add support for backplane kr generic driver including link training (ieee802.3ap/ba) and fixed equalization algorithm Signed-off-by: Florinel Iordache Reported-by: kernel test robot --- drivers/net/phy/Kconfig | 2 + drivers/net/phy/Makefile | 1 + drivers/net/phy/backplane/Kconfig | 20 + drivers/net/phy/backplane/Makefile | 9 + drivers/net/phy/backplane/backplane.c | 1557 +++++++++++++++++++++++++++++ drivers/net/phy/backplane/backplane.h | 293 ++++++ drivers/net/phy/backplane/eq_fixed.c | 83 ++ drivers/net/phy/backplane/equalization.h | 275 +++++ drivers/net/phy/backplane/link_training.c | 1529 ++++++++++++++++++++++++++++ drivers/net/phy/backplane/link_training.h | 32 + 10 files changed, 3801 insertions(+) create mode 100644 drivers/net/phy/backplane/Kconfig create mode 100644 drivers/net/phy/backplane/Makefile create mode 100644 drivers/net/phy/backplane/backplane.c create mode 100644 drivers/net/phy/backplane/backplane.h create mode 100644 drivers/net/phy/backplane/eq_fixed.c create mode 100644 drivers/net/phy/backplane/equalization.h create mode 100644 drivers/net/phy/backplane/link_training.c create mode 100644 drivers/net/phy/backplane/link_training.h diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index f257023..fa48b8e 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -549,6 +549,8 @@ config XILINX_GMII2RGMII the Reduced Gigabit Media Independent Interface(RGMII) between Ethernet physical media devices and the Gigabit Ethernet controller. +source "drivers/net/phy/backplane/Kconfig" + endif # PHYLIB config MICREL_KS8995MA diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index dc9e53b..4849c16 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -104,3 +104,4 @@ obj-$(CONFIG_STE10XP) += ste10Xp.o obj-$(CONFIG_TERANETICS_PHY) += teranetics.o obj-$(CONFIG_VITESSE_PHY) += vitesse.o obj-$(CONFIG_XILINX_GMII2RGMII) += xilinx_gmii2rgmii.o +obj-$(CONFIG_ETH_BACKPLANE) += backplane/ diff --git a/drivers/net/phy/backplane/Kconfig b/drivers/net/phy/backplane/Kconfig new file mode 100644 index 0000000..9ec54b5 --- /dev/null +++ b/drivers/net/phy/backplane/Kconfig @@ -0,0 +1,20 @@ +# SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +config ETH_BACKPLANE + tristate "Ethernet Backplane support" + depends on OF_MDIO + help + This module provides driver support for Ethernet Operation over + Electrical Backplanes. It includes Backplane generic + driver including support for Link Training (IEEE802.3ap/ba). + Based on the link quality, a signal equalization is required. + The standard specifies that a start-up algorithm should be in place + in order to get the link up. + +config ETH_BACKPLANE_FIXED + tristate "Fixed: No Equalization algorithm" + depends on ETH_BACKPLANE + help + This module provides a driver to setup fixed user configurable + coefficient values for backplanes equalization. This means + No Equalization algorithm is used to adapt the initial coefficients + initially set by the user. \ No newline at end of file diff --git a/drivers/net/phy/backplane/Makefile b/drivers/net/phy/backplane/Makefile new file mode 100644 index 0000000..ded6f2d --- /dev/null +++ b/drivers/net/phy/backplane/Makefile @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +# +# Makefile for Ethernet Backplane driver +# + +obj-$(CONFIG_ETH_BACKPLANE) += eth_backplane.o +obj-$(CONFIG_ETH_BACKPLANE_FIXED) += eq_fixed.o + +eth_backplane-objs := backplane.o link_training.o diff --git a/drivers/net/phy/backplane/backplane.c b/drivers/net/phy/backplane/backplane.c new file mode 100644 index 0000000..d9a4770 --- /dev/null +++ b/drivers/net/phy/backplane/backplane.c @@ -0,0 +1,1557 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* Backplane driver + * + * Copyright 2015 Freescale Semiconductor, Inc. + * Copyright 2018-2020 NXP + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "backplane.h" +#include "link_training.h" + +/* KR timeouts in milliseconds */ +#define KR_TIMEOUT_1 100 +#define KR_TIMEOUT_2 1000 +#define KR_AN_TIMEOUT 3000 +#define KR_LT_TIMEOUT 500 + +/* KR timings in interations */ +#define KR_AN_WAIT_ITERATIONS 5 +#define KR_TRAIN_STEP_ITERATIONS 2 +#define CDR_LOCK_RETRY_COUNT 3 + +/* AN register initialization */ +#define AN_CTRL_INIT 0x1200 + +/* AN status register (Clause 45) (MMD 7): MDIO_STAT1 */ +#define AN_LINK_UP_MASK 0x04 + +/* Backplane Ethernet status (Register 7.48) */ +#define AN_BP_ETH_STATUS 48 + +/* AN masks: Backplane Ethernet status: register 7.48 */ +#define AN_MASK_10GBASE_KR 0x08 + +/* AN advertisement 1 (Register 7.17) */ +#define AN_ADVERTISEMENT_1 17 + +/* AN advertisement initialization for register 7.17 */ +#define AN_ADV1_KR_INIT_10G 0x85 + +/* Backplane features */ +__ETHTOOL_DECLARE_LINK_MODE_MASK(backplane_features) __ro_after_init; +EXPORT_SYMBOL(backplane_features); + +const int backplane_common_features_array[] = { + ETHTOOL_LINK_MODE_Backplane_BIT, + ETHTOOL_LINK_MODE_Autoneg_BIT, + ETHTOOL_LINK_MODE_MII_BIT, +}; + +const int backplane_protocol_features_array[] = { + ETHTOOL_LINK_MODE_10000baseKR_Full_BIT, +}; + +/* map string key to pointer data */ +struct spmap_node { + struct list_head entry; + const char *key; + void *pdata; +}; + +/* registered equalization algorithms info */ +static LIST_HEAD(eqalg_list); + +/* lanes attached to an equalization algorithm */ +static LIST_HEAD(lnalg_list); + +/* Backplane mutex between all KR PHY threads */ +static struct mutex backplane_lock; + +static int get_backplane_speed(phy_interface_t interface) +{ + switch (interface) { + case PHY_INTERFACE_MODE_10GKR: + return SPEED_10000; + default: + pr_err("%s: Unsupported backplane interface\n", + BACKPLANE_DRIVER_NAME); + return SPEED_UNKNOWN; + } + return SPEED_UNKNOWN; +} + +static enum ethtool_link_mode_bit_indices + get_backplane_supported_mode(phy_interface_t interface) +{ + switch (interface) { + case PHY_INTERFACE_MODE_10GKR: + return ETHTOOL_LINK_MODE_10000baseKR_Full_BIT; + default: + pr_err("%s: Unsupported backplane interface\n", + BACKPLANE_DRIVER_NAME); + return ETHTOOL_LINK_MODE_Backplane_BIT; + } + return ETHTOOL_LINK_MODE_Backplane_BIT; +} + +static int spmap_add(struct list_head *list, const char *key, void *pdata) +{ + struct spmap_node *node; + + /* create a new entry with desired key */ + node = kzalloc(sizeof(*node), GFP_KERNEL); + if (!node) + return -ENOMEM; + + node->key = key; + node->pdata = pdata; + + list_add(&node->entry, list); + + return 0; +} + +static const struct equalization_algorithm *eq_find(const char *key) +{ + struct spmap_node *eqalg, *eqalg_tmp; + + if (!key) + return NULL; + + /* search desired single key */ + list_for_each_entry_safe(eqalg, eqalg_tmp, &eqalg_list, entry) { + if (strcmp(eqalg->key, key) == 0) + return (struct equalization_algorithm *)eqalg->pdata; + } + return NULL; +} + +static void backplane_features_init(void) +{ + linkmode_set_bit_array(backplane_common_features_array, + ARRAY_SIZE(backplane_common_features_array), + backplane_features); + + linkmode_set_bit_array(backplane_protocol_features_array, + ARRAY_SIZE(backplane_protocol_features_array), + backplane_features); +} + +static u32 le_ioread32(void __iomem *reg) +{ + return ioread32(reg); +} + +static void le_iowrite32(u32 value, void __iomem *reg) +{ + iowrite32(value, reg); +} + +static u32 be_ioread32(void __iomem *reg) +{ + return ioread32be(reg); +} + +static void be_iowrite32(u32 value, void __iomem *reg) +{ + iowrite32be(value, reg); +} + +static void training_status_init(struct training_status *trst) +{ + trst->done_training = false; + trst->remote_tx_complete = false; + trst->remote_tx_running = false; + trst->sent_init = false; + trst->lp_rx_ready = 0; + trst->local_tx_running = false; +} + +static void init_kr_lane(struct lane_device *lane, bool revert_default) +{ + if (revert_default) + backplane_default_kr_lane(lane); + + training_status_init(&lane->krln.trst); + lane->krln.state = DETECTING_LP; + lane->krln.an_kr_detected = false; + + lane->krln.ld_update = 0; + lane->krln.prev_ld_update = 0; + lane->krln.ld_last_nonhold_update = 0; + lane->krln.lp_status = 0; + lane->krln.lp_last_change_status = 0; + lane->krln.last_lp_update_status[C_M1] = 0; + lane->krln.last_lp_update_status[C_Z0] = 0; + lane->krln.last_lp_update_status[C_P1] = 0; + lane->krln.ld_status = 0; + lane->krln.move_back_prev = false; + lane->krln.move_back_cnt = 0; + lane->krln.move_back_lp_status = 0; + + lt_init_ld(lane); +} + +static void setup_supported_linkmode(struct phy_device *phydev) +{ + int i; + + /* Clear all supported backplane protocols features + * and setup only the currently configured protocol + */ + for (i = 0; i < ARRAY_SIZE(backplane_protocol_features_array); i++) + linkmode_clear_bit(backplane_protocol_features_array[i], + phydev->supported); + + linkmode_set_bit(get_backplane_supported_mode(phydev->interface), + phydev->supported); +} + +/* Read AN Link Status */ +static int is_an_link_up(struct phy_device *phydev) +{ + struct backplane_device *bpdev = phydev->priv; + int ret, val = 0; + + mutex_lock(&bpdev->bpphy_lock); + + /* Read twice because Link_Status is LL (Latched Low) bit */ + val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_STAT1); + val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_STAT1); + + mutex_unlock(&bpdev->bpphy_lock); + + ret = (val & AN_LINK_UP_MASK) ? 1 : 0; + + return ret; +} + +static void start_kr_state_machine(struct lane_device *lane, u32 timeout) +{ + /* Check if equalization algorithm is installed */ + if (!lane->krln.eq_alg) + return; + + /* Check if link training is used */ + if (!lane->krln.eq_alg->use_local_tx_training && + !lane->krln.eq_alg->use_remote_tx_training) + return; + + queue_delayed_work(system_power_efficient_wq, &lane->krwk, + msecs_to_jiffies(timeout)); +} + +static void stop_kr_state_machine(struct lane_device *lane) +{ + /* Check if equalization algorithm is installed */ + if (!lane->krln.eq_alg) + return; + + /* Check if link training is used */ + if (!lane->krln.eq_alg->use_local_tx_training && + !lane->krln.eq_alg->use_remote_tx_training) + return; + + cancel_delayed_work_sync(&lane->krwk); +} + +static void setup_default_settings(struct lane_device *lane) +{ + if (lane->bpdev->bpkr.valid_eq_init) + lane->krln.def_kr = lane->bpdev->bpkr.def_kr; + else + lane->bpdev->drv.lane_ops->read_lane_kr(lane->reg_base, + &lane->krln.def_kr); + + /* HW specific default settings */ + if (lane->bpdev->drv.bp_ops.setup_default_settings) + lane->bpdev->drv.bp_ops.setup_default_settings(lane); +} + +static void kr_reset_master_lane(struct lane_device *lane) +{ + const struct lane_ops *lane_ops = lane->bpdev->drv.lane_ops; + struct backplane_device *bpdev = lane->bpdev; + + if (backplane_is_multi_lane(bpdev)) { + /* Reset only the Master Lane */ + if (lane->idx == MASTER_LANE) + lane_ops->reset_lane(lane->reg_base, LANE_RX_TX); + } else { + lane_ops->reset_lane(lane->reg_base, LANE_RX_TX); + } +} + +static void print_single_lane_trained(struct lane_device *lane) +{ + struct phy_device *phydev = lane->phydev; + + phydev_info(phydev, + "%s link trained, Tx equalization: C(-1)=0x%x, C(0)=0x%x, C(+1)=0x%x\n", + phy_modes(phydev->interface), + lane->krln.tuned_kr.preq, lane->krln.tuned_kr.mainq, + lane->krln.tuned_kr.postq); +} + +static void print_multi_lane_trained(struct lane_device *lane) +{ + struct backplane_device *bpdev = lane->bpdev; + struct phy_device *phydev = lane->phydev; + int i; + + phydev_info(phydev, + "%s link trained, Tx equalization:\n", + phy_modes(phydev->interface)); + + for (i = 0; i < bpdev->num_lanes; i++) + phydev_info(phydev, + "\t|- Lane %d: C(-1)=0x%x, C(0)=0x%x, C(+1)=0x%x\n", + i + 1, bpdev->lane[i].krln.tuned_kr.preq, + bpdev->lane[i].krln.tuned_kr.mainq, + bpdev->lane[i].krln.tuned_kr.postq); +} + +static void kr_link_trained(struct lane_device *lane) +{ + struct backplane_device *bpdev = lane->bpdev; + + mutex_lock(&bpdev->trained_lock); + /* Setup lane state as TRAINED inside the phy trained lock + * to avoid duplicated message printed on multi-lane PHYs + */ + lane->krln.state = TRAINED; + + mutex_lock(&backplane_lock); + + if (backplane_is_single_lane(bpdev)) + print_single_lane_trained(lane); + else + if (backplane_are_all_lanes_trained(bpdev)) + print_multi_lane_trained(lane); + + mutex_unlock(&backplane_lock); + mutex_unlock(&bpdev->trained_lock); +} + +static void kr_train_step(struct lane_device *lane) +{ + struct training_status *trst = &lane->krln.trst; + u32 lt_timeout = KR_LT_TIMEOUT; + u64 dead_line; + int i = 0; + + /* Check if equalization algorithm is installed */ + if (!lane->krln.eq_alg) + return; + + /* Check if link training is used */ + if (!lane->krln.eq_alg->use_local_tx_training && + !lane->krln.eq_alg->use_remote_tx_training) + return; + + lt_start(lane); + + while (i < KR_TRAIN_STEP_ITERATIONS) { + dead_line = jiffies + msecs_to_jiffies(lt_timeout); + while (time_before(jiffies, (unsigned long)dead_line)) { + /* check if the LT is already failed */ + if (lt_is_training_failure(lane)) { + /* LT failed already, reset lane to avoid + * it run into hanging, then start LT again. + */ + kr_reset_master_lane(lane); + lt_start(lane); + } else if (lt_is_frame_lock(lane)) { + break; + } + /* wait frame lock (without training_failure) */ + usleep_range(100, 500); + } + + if (!lt_is_frame_lock(lane)) { + i++; + continue; + } + + /* the LT should be finished in 500ms, failed or OK. */ + dead_line = jiffies + msecs_to_jiffies(lt_timeout); + while (time_before(jiffies, (unsigned long)dead_line)) { + /* check if the LT is already failed */ + if (lt_is_training_failure(lane)) { + kr_reset_master_lane(lane); + break; + } + + if (lane->krln.eq_alg->use_local_tx_training) + lt_train_local_tx(lane); + + if (lane->krln.eq_alg->use_remote_tx_training) + lt_train_remote_tx(lane); + + if (lane->krln.lt_error) + break; + + if (trst->lp_rx_ready && trst->remote_tx_complete) + break; + + usleep_range(100, 500); + } + + i++; + /* check if LT Error occurred */ + if (lane->krln.lt_error) { + init_kr_lane(lane, false); + continue; + } else { + break; + } + } + + lt_stop(lane); + + /* check if Link is successfully TRAINED */ + if (lt_is_rx_trained(lane)) + kr_link_trained(lane); + else + kr_reset_master_lane(lane); +} + +static void an_init(struct lane_device *masterln) +{ + struct backplane_device *bpdev = masterln->bpdev; + struct phy_device *phydev = masterln->phydev; + struct lane_device *lane; + u32 init_an_adv1; + int i, err; + + if (!backplane_is_mode_kr(phydev->interface)) + return; + + if (masterln->idx != MASTER_LANE) + return; + + init_an_adv1 = backplane_get_an_adv1_init(phydev->interface); + + /* setup AN init on each lane */ + for (i = 0; i < bpdev->num_lanes; i++) { + lane = &bpdev->lane[i]; + if (bpdev->drv.bp_ops.an_advertisement_init) { + bpdev->drv.bp_ops.an_advertisement_init(lane); + } else { + err = backplane_write_mmd(lane, MDIO_MMD_AN, + AN_ADVERTISEMENT_1, + init_an_adv1); + if (err) + phydev_err(phydev, + "Setting AN register 0x%02x on lane %d failed with error code: 0x%08x\n", + AN_ADVERTISEMENT_1, lane->idx, err); + } + } + + udelay(1); + + err = backplane_write_mmd(masterln, MDIO_MMD_AN, MDIO_CTRL1, + AN_CTRL_INIT); + if (err) + phydev_err(phydev, + "Setting AN register 0x%02x on Master Lane failed with error code: 0x%08x\n", + MDIO_CTRL1, err); +} + +static void an_request_restart(struct lane_device *lane) +{ + const struct lane_ops *lane_ops = lane->bpdev->drv.lane_ops; + struct backplane_device *bpdev = lane->bpdev; + struct phy_device *phydev = lane->phydev; + int i; + + if (time_before(jiffies, (unsigned long)lane->krln.an_kr_timeout)) + return; + if (!backplane_is_mode_kr(phydev->interface)) + return; + + for (i = 0; i < bpdev->num_lanes; i++) { + init_kr_lane(&bpdev->lane[i], true); + /* Reset the lane to recover from link down */ + lane_ops->reset_lane(bpdev->lane[i].reg_base, LANE_RX_TX); + lt_reset(&bpdev->lane[i]); + } + /* AN init only for Master Lane */ + an_init(&bpdev->lane[MASTER_LANE]); + + lane->krln.an_kr_timeout = jiffies + msecs_to_jiffies(KR_AN_TIMEOUT); +} + +static bool detect_lp(struct lane_device *lane) +{ + struct backplane_device *bpdev = lane->bpdev; + struct phy_device *phydev = lane->phydev; + struct lane_device *masterln; + bool start_train = false; + bool an_kr_link; + int an_state; + u32 an_mask; + + if (bpdev->drv.bp_ops.is_an_link_detected) { + an_kr_link = bpdev->drv.bp_ops.is_an_link_detected(lane); + } else { + /* Check AN state only on Master Lane */ + masterln = &bpdev->lane[MASTER_LANE]; + an_mask = backplane_get_an_bp_eth_status_bit(phydev->interface); + an_state = backplane_read_mmd(masterln, MDIO_MMD_AN, + AN_BP_ETH_STATUS); + an_kr_link = an_state & an_mask; + } + + /* The link training occurs after auto-negotiation + * has determined the link to be a Base-KR link. + * This is indicated by asserting the corresponding bit. + * This occurs before auto-negotiation can declare auto-negotiation + * complete, as this requires the PCS to report a valid link. + */ + if (an_kr_link) { + /* AN acquired: + * Train all lanes in order starting with Master Lane + */ + lane->krln.an_kr_detected = true; + lane->krln.an_kr_wait_count = 0; + start_train = true; + } else { + /* AN lost or not yet acquired */ + if (lane->krln.an_kr_detected) { + /* AN acquired first time but now was lost */ + if (!backplane_is_link_up(phydev)) { + /* Link is down: restart training */ + lane->krln.an_kr_wait_count = 0; + an_request_restart(lane); + } else { + /* Link is up: + * wait few iterations for AN to be acquired + */ + if (lane->krln.an_kr_wait_count >= + KR_AN_WAIT_ITERATIONS) { + lane->krln.an_kr_wait_count = 0; + an_request_restart(lane); + } else { + lane->krln.an_kr_wait_count++; + } + } + } + /* else: AN was not yet acquired first time + * DO nothing, just wait AN to be acquired first time + */ + } + + return start_train; +} + +static void detect_hotplug(struct lane_device *lane) +{ + struct backplane_device *bpdev = lane->bpdev; + struct phy_device *phydev = lane->phydev; + int i; + + if (lane->idx == MASTER_LANE) { + /* check if all lanes are trained + * only if current lane is Master Lane + */ + if (backplane_are_all_lanes_trained(bpdev)) { + phydev_info(phydev, "Detect hotplug, restart training\n"); + for (i = 0; i < bpdev->num_lanes; i++) { + /* initializations on Detect hotplug / restart: + * they must not be part of init_kr_lane + */ + bpdev->lane[i].krln.first_recv_init = false; + } + an_request_restart(lane); + } + } +} + +static void bp_kr_state_machine(struct work_struct *work) +{ + struct delayed_work *dwork = to_delayed_work(work); + struct backplane_device *bpdev; + u32 kr_timeout = KR_TIMEOUT_1; + struct phy_device *phydev; + struct lane_device *lane; + bool start_train = false; + + lane = container_of(dwork, struct lane_device, krwk); + if (!lane) + return; + + bpdev = lane->bpdev; + phydev = lane->phydev; + + if (!backplane_is_mode_kr(phydev->interface)) + return; + + /* Check if equalization algorithm is installed */ + if (!lane->krln.eq_alg) + return; + + /* Check if link training is used */ + if (!lane->krln.eq_alg->use_local_tx_training && + !lane->krln.eq_alg->use_remote_tx_training) + return; + + mutex_lock(&lane->lane_lock); + switch (lane->krln.state) { + case DETECTING_LP: + start_train = detect_lp(lane); + break; + case TRAINED: + kr_timeout = KR_TIMEOUT_2; + if (!backplane_is_link_up(phydev)) { + kr_timeout = KR_TIMEOUT_1; + detect_hotplug(lane); + } + break; + } + + if (start_train) + kr_train_step(lane); + + mutex_unlock(&lane->lane_lock); + start_kr_state_machine(lane, kr_timeout); +} + +static void init_kr_state_machine(struct lane_device *lane) +{ + /* Check if equalization algorithm is installed */ + if (!lane->krln.eq_alg) + return; + + /* Check if link training is used */ + if (!lane->krln.eq_alg->use_local_tx_training && + !lane->krln.eq_alg->use_remote_tx_training) + return; + + INIT_DELAYED_WORK(&lane->krwk, bp_kr_state_machine); +} + +/* backplane_get_current_taps + * convert coefficient taps from internal backplane driver to link training + */ +void backplane_get_current_taps(struct lane_device *lane, u32 *coef) +{ + coef[C_M1] = lane->krln.crt_kr.preq; + coef[C_Z0] = lane->krln.crt_kr.mainq; + coef[C_P1] = lane->krln.crt_kr.postq; +} + +/* backplane_set_current_taps + * convert coefficient taps from link training to internal backplane driver + */ +void backplane_set_current_taps(struct lane_device *lane, u32 *coef) +{ + lane->krln.crt_kr.preq = coef[C_M1]; + lane->krln.crt_kr.mainq = coef[C_Z0]; + lane->krln.crt_kr.postq = coef[C_P1]; +} + +/* backplane_set_all_taps_to_max + * setup all coefficients to MAX values from IEEE802.3ap perspective + */ +void backplane_set_all_taps_to_max(struct lane_device *lane) +{ + lane->krln.crt_kr = lane->bpdev->bpkr.max_kr; +} + +void backplane_tune_kr_lane(struct lane_device *lane, bool reset_lane) +{ + struct backplane_device *bpdev = lane->bpdev; + bool reset = false; + + if (backplane_is_multi_lane(bpdev)) { + /* Reset only the Master Lane */ + reset = (lane->idx == MASTER_LANE); + } else { + reset = true; + } + + /* Do not reset the lane if this is how it was asked */ + if (!reset_lane) + reset = false; + + lane->bpdev->drv.lane_ops->tune_lane_kr(lane->reg_base, + &lane->krln.crt_kr, reset); + lane->krln.tuned_kr = lane->krln.crt_kr; +} + +void backplane_default_kr_lane(struct lane_device *lane) +{ + lane->krln.crt_kr = lane->krln.def_kr; + + backplane_tune_kr_lane(lane, true); +} + +/* backplane_write_mmd - Wrapper function for phy_write_mmd + * for writing a register on an MMD on a given PHY. + * + * Same rules as for phy_write_mmd(); + */ +int backplane_write_mmd(struct lane_device *lane, int devad, u32 regnum, + u16 val) +{ + struct backplane_device *bpdev = lane->bpdev; + struct phy_device *phydev = lane->phydev; + int err; + + mutex_lock(&bpdev->bpphy_lock); + + err = phy_write_mmd(phydev, devad, regnum, val); + if (err) + phydev_err(phydev, + "Writing PHY (%p) MMD = 0x%02x register = 0x%02x failed with error code: 0x%08x\n", + phydev, devad, regnum, err); + + mutex_unlock(&bpdev->bpphy_lock); + + return err; +} +EXPORT_SYMBOL(backplane_write_mmd); + +/* backplane_read_mmd - Wrapper function for phy_read_mmd + * for reading a register from an MMD on a given PHY. + * + * Same rules as for phy_read_mmd(); + */ +int backplane_read_mmd(struct lane_device *lane, int devad, u32 regnum) +{ + struct backplane_device *bpdev = lane->bpdev; + struct phy_device *phydev = lane->phydev; + int ret; + + mutex_lock(&bpdev->bpphy_lock); + + ret = phy_read_mmd(phydev, devad, regnum); + + mutex_unlock(&bpdev->bpphy_lock); + + return ret; +} +EXPORT_SYMBOL(backplane_read_mmd); + +/* backplane_eq_register + * + * Registers an equalization algorithm with the specified key + * + * key: desired key on which eq algorithm must be registered + * eq_info: eq algorithm information to be registered + * + * Returns: Zero for success or error code in case of failure + */ +int backplane_eq_register(const char *key, + const struct equalization_algorithm *eq_info) +{ + struct spmap_node *eqalg, *eqalg_tmp; + + /* check if desired key already exists */ + list_for_each_entry_safe(eqalg, eqalg_tmp, &eqalg_list, entry) { + if (strcmp(eqalg->key, key) == 0) { + pr_err("%s: Equalization algorithm registration failed: key '%s' already exists\n", + BACKPLANE_DRIVER_NAME, key); + return -EEXIST; + } + } + + spmap_add(&eqalg_list, key, (void *)eq_info); + + return 0; +} +EXPORT_SYMBOL(backplane_eq_register); + +/* backplane_eq_unregister + * + * Unregisters all equalization algorithm for the specified key + * + * key: desired key for which all registered eq algorithms must be removed + * + * Returns: None + */ +void backplane_eq_unregister(const char *key) +{ + const struct equalization_algorithm *eq_alg; + struct spmap_node *node, *node_tmp; + struct lane_device *lane; + + if (!key) + return; + + /* search all keys in lanes list */ + list_for_each_entry_safe(node, node_tmp, &lnalg_list, entry) { + if (strcmp(node->key, key) == 0) { + lane = (struct lane_device *)node->pdata; + eq_alg = lane->krln.eq_alg; + if (eq_alg->ops.destroy) + eq_alg->ops.destroy(lane->krln.eq_priv); + lane->krln.eq_alg = NULL; + lane->krln.eq_priv = NULL; + list_del_init(&node->entry); + kfree(node); + } + } + + /* search single key in eq algorithms list */ + list_for_each_entry_safe(node, node_tmp, &eqalg_list, entry) { + if (strcmp(node->key, key) == 0) { + list_del_init(&node->entry); + kfree(node); + break; + } + } +} +EXPORT_SYMBOL(backplane_eq_unregister); + +bool backplane_is_mode_kr(phy_interface_t interface) +{ + return (interface == PHY_INTERFACE_MODE_10GKR); +} +EXPORT_SYMBOL(backplane_is_mode_kr); + +bool backplane_is_valid_mode(phy_interface_t interface) +{ + return backplane_is_mode_kr(interface); +} +EXPORT_SYMBOL(backplane_is_valid_mode); + +u8 backplane_num_lanes(phy_interface_t interface) +{ + const char *bp_name; + char num_lanes; + int len; + + if (!backplane_is_valid_mode(interface)) + return 0; + + bp_name = phy_modes(interface); + if (!bp_name) + return 0; + if (strcmp(bp_name, "unknown") == 0) + return 0; + + len = strlen(bp_name); + if (len == 0) + return 0; + num_lanes = bp_name[len - 1]; + if (num_lanes >= '0' && num_lanes <= '9') + return num_lanes - '0'; + + return 1; +} +EXPORT_SYMBOL(backplane_num_lanes); + +/* Backplane Ethernet Status Register Register 7.48 (an_bp_eth_status) + * - AN_MASK_10GBASE_KR for 10GBase-KR + */ +u32 backplane_get_an_bp_eth_status_bit(phy_interface_t interface) +{ + switch (interface) { + case PHY_INTERFACE_MODE_10GKR: + return AN_MASK_10GBASE_KR; + /* add AN support for other backplane modes here */ + default: + pr_err("%s: Unsupported backplane interface\n", + BACKPLANE_DRIVER_NAME); + return 0; + } + return 0; +} +EXPORT_SYMBOL(backplane_get_an_bp_eth_status_bit); + +u32 backplane_get_an_adv1_init(phy_interface_t interface) +{ + switch (interface) { + case PHY_INTERFACE_MODE_10GKR: + return AN_ADV1_KR_INIT_10G; + /* add AN support for other backplane modes here */ + default: + pr_err("%s: Unsupported backplane interface\n", + BACKPLANE_DRIVER_NAME); + return 0; + } + return 0; +} +EXPORT_SYMBOL(backplane_get_an_adv1_init); + +void backplane_kr_lt_mmd_c45(struct backplane_kr *bpkr) +{ + lt_mmd_c45(bpkr); +} +EXPORT_SYMBOL(backplane_kr_lt_mmd_c45); + +void backplane_kr_lt_mmd_setup(struct backplane_kr *bpkr, int devad, u32 base) +{ + lt_mmd_setup(bpkr, devad, base); +} +EXPORT_SYMBOL(backplane_kr_lt_mmd_setup); + +bool backplane_is_single_lane(struct backplane_device *bpdev) +{ + return (bpdev->num_lanes == 1); +} +EXPORT_SYMBOL(backplane_is_single_lane); + +bool backplane_is_multi_lane(struct backplane_device *bpdev) +{ + return (bpdev->num_lanes > 1); +} +EXPORT_SYMBOL(backplane_is_multi_lane); + +/* backplane_is_cdr_lock + * + * Checks clock and data recovery bit: CDR Lock + * + * lane: desired lane to be verified + * retry: boolean value that specifies if to retry the check + * + * Returns: true if CDR_Lock bit is asserted or false otherwise + */ +bool backplane_is_cdr_lock(struct lane_device *lane, bool retry) +{ + const struct lane_ops *lane_ops = lane->bpdev->drv.lane_ops; + int i; + + if (lane_ops->is_cdr_lock(lane->reg_base)) + return true; + + if (!retry) + return false; + + /* Try RX_RESET: Allow for few retries */ + for (i = 0; i < CDR_LOCK_RETRY_COUNT; i++) { + lane_ops->reset_lane(lane->reg_base, LANE_RX); + usleep_range(10, 50); + + if (lane_ops->is_cdr_lock(lane->reg_base)) + return true; + } + return false; +} +EXPORT_SYMBOL(backplane_is_cdr_lock); + +/* backplane_is_link_up + * Generic Link-up Status: use AN link-up + */ +int backplane_is_link_up(struct phy_device *phydev) +{ + return is_an_link_up(phydev); +} +EXPORT_SYMBOL(backplane_is_link_up); + +int backplane_get_lanes_trained_count(struct backplane_device *bpdev) +{ + int i, lanes_trained = 0; + + for (i = 0; i < bpdev->num_lanes; i++) { + if (bpdev->lane[i].krln.state == TRAINED) + lanes_trained++; + } + return lanes_trained; +} +EXPORT_SYMBOL(backplane_get_lanes_trained_count); + +int backplane_are_all_lanes_trained(struct backplane_device *bpdev) +{ + int i; + + for (i = 0; i < bpdev->num_lanes; i++) { + if (bpdev->lane[i].krln.state != TRAINED) + return 0; + } + return 1; +} +EXPORT_SYMBOL(backplane_are_all_lanes_trained); + +int backplane_create(struct phy_device *phydev) +{ + struct backplane_device *bpdev; + struct device_node *dev_node; + + dev_node = phydev->mdio.dev.of_node; + if (!dev_node) { + phydev_err(phydev, "No associated device tree node\n"); + return -EINVAL; + } + + /* allocate memory for backplane info structure */ + bpdev = devm_kzalloc(&phydev->mdio.dev, sizeof(*bpdev), GFP_KERNEL); + if (!bpdev) + return -ENOMEM; + + bpdev->phydev = phydev; + + /* save bpdev as phy private data pointer */ + phydev->priv = bpdev; + + return 0; +} +EXPORT_SYMBOL(backplane_create); + +/* backplane_parse_dt + * parses the device tree and saves backplane relevant data + * in backplane phy info structure + */ +int backplane_parse_dt(struct phy_device *phydev) +{ + struct backplane_device *bpdev = phydev->priv; + struct device_node *dev_node; + u32 eqinit[C_NO]; + const char *eqa; + int proplen; + int ret; + + if (!bpdev) { + phydev_err(phydev, "Backplane phy info is not allocated\n"); + return -EINVAL; + } + + dev_node = phydev->mdio.dev.of_node; + if (!dev_node) { + phydev_err(phydev, "No associated device tree node\n"); + return -EINVAL; + } + + if (!backplane_is_valid_mode(phydev->interface)) + return -EINVAL; + + ret = of_property_read_string(dev_node, "eq-algorithm", &eqa); + /* if eq-algorithm node is not found then use the default algorithm */ + if (ret == 0) + bpdev->bpkr.eqa_name = eqa; + else + bpdev->bpkr.eqa_name = DEFAULT_EQ_ALGORITHM; + + /* if eq-init node exists then use the DTS specified values + * if eq-init node doesn't exist then use values already found in HW + */ + proplen = of_property_count_u32_elems(dev_node, "eq-init"); + if (proplen > 0) { + /* There are 3 standard equalization coefficient taps */ + if (proplen > C_NO) + proplen = C_NO; + ret = of_property_read_u32_array(dev_node, "eq-init", + (u32 *)eqinit, proplen); + if (ret == 0) { + bpdev->bpkr.valid_eq_init = true; + bpdev->bpkr.def_kr.preq = eqinit[C_M1]; + bpdev->bpkr.def_kr.mainq = eqinit[C_Z0]; + bpdev->bpkr.def_kr.postq = eqinit[C_P1]; + } + } + + return 0; +} +EXPORT_SYMBOL(backplane_parse_dt); + +/* backplane_setup_memio + * Setup memory I/O access + */ +int backplane_setup_memio(struct phy_device *phydev) +{ + struct backplane_device *bpdev = phydev->priv; + + if (!bpdev) { + phydev_err(phydev, "Backplane phy info is not allocated\n"); + return -EINVAL; + } + + /* setup ioread/iowrite according to endianness */ + if (bpdev->drv.is_little_endian) { + bpdev->drv.io.read32 = le_ioread32; + bpdev->drv.io.write32 = le_iowrite32; + } else { + bpdev->drv.io.read32 = be_ioread32; + bpdev->drv.io.write32 = be_iowrite32; + } + + return 0; +} +EXPORT_SYMBOL(backplane_setup_memio); + +/* backplane_setup_mmd + * Setup default MMD (MDIO Managed Device) + */ +int backplane_setup_mmd(struct phy_device *phydev) +{ + struct backplane_device *bpdev = phydev->priv; + + if (!bpdev) { + phydev_err(phydev, "Backplane phy info is not allocated\n"); + return -EINVAL; + } + + /* By default setup LT MMD Clause 45 */ + backplane_kr_lt_mmd_c45(&bpdev->bpkr); + + return 0; +} +EXPORT_SYMBOL(backplane_setup_mmd); + +/* backplane_setup_lanes + * Allocates lanes memory map and setup lanes relevant data + * Requires: + * - backplane_driver#lane_ops + * for lane access operations + * - backplane_driver#lane_ops#memmap_size + * for lane memory map allocation + * - backplane_kr#equalizer + * for specific Equalizer access + * - backplane_kr#cx_def + * for default coefficient setup + */ +int backplane_setup_lanes(struct phy_device *phydev) +{ + struct backplane_device *bpdev = phydev->priv; + const struct equalization_algorithm *eq_alg; + struct equalizer_driver eqdrv; + struct lane_device *lane; + int i; + + if (!bpdev) { + phydev_err(phydev, "Backplane phy info is not allocated\n"); + return -EINVAL; + } + if (!bpdev->drv.lane_ops) { + phydev_err(phydev, "Backplane lane ops is not set\n"); + return -EINVAL; + } + if (!bpdev->bpkr.equalizer) { + phydev_err(phydev, "Backplane equalizer info is not set\n"); + return -EINVAL; + } + if (bpdev->drv.lane_ops->memmap_size == 0) { + phydev_err(phydev, "Lane memory map size is zero\n"); + return -EINVAL; + } + + bpdev->num_lanes = backplane_num_lanes(phydev->interface); + if (bpdev->num_lanes > MAX_KR_LANES_PER_PHY) { + phydev_err(phydev, "Unsupported number of lanes per phy: %d\n", + bpdev->num_lanes); + return -EINVAL; + } + + if (backplane_is_mode_kr(phydev->interface)) { + if (bpdev->bpkr.valid_eq_init && + bpdev->bpkr.def_kr.preq == 0 && + bpdev->bpkr.def_kr.mainq == 0 && + bpdev->bpkr.def_kr.postq == 0) + phydev_warn(phydev, + "All KR default values from DT are zero\n"); + } + + for (i = 0; i < bpdev->num_lanes; i++) { + lane = &bpdev->lane[i]; + + /* setup lane memory map size */ + lane->memmap_size = bpdev->drv.lane_ops->memmap_size; + + lane->reg_base = devm_ioremap(&phydev->mdio.dev, + lane->lane_addr, + lane->memmap_size); + if (!lane->reg_base) { + phydev_err(phydev, "Lane memory map allocation failed\n"); + return -ENOMEM; + } + + lane->idx = i; + lane->phydev = phydev; + lane->bpdev = bpdev; + lane->krln.an_kr_timeout = + jiffies + msecs_to_jiffies(KR_AN_TIMEOUT); + + if (backplane_is_mode_kr(phydev->interface)) { + setup_default_settings(lane); + + /* Find EQ Algorithm info */ + eq_alg = eq_find(bpdev->bpkr.eqa_name); + if (!eq_alg) { + /* key for desired algorithm was not found */ + phydev_err(phydev, + "Equalization algorithm '%s' is not registered\n", + bpdev->bpkr.eqa_name); + return -EINVAL; + } + if (!eq_alg->ops.create) { + phydev_err(phydev, + "Equalization algorithm creation failed: create operation is not available\n"); + return -EINVAL; + } + lane->krln.eq_alg = eq_alg; + + /* Setup EQ Algorithm */ + eqdrv.lane = lane; + eqdrv.phydev = lane->phydev; + eqdrv.reg_base = lane->reg_base; + eqdrv.equalizer = lane->bpdev->bpkr.equalizer; + + /* Create EQ Algorithm */ + lane->krln.eq_priv = eq_alg->ops.create(eqdrv); + + /* register lane attached to an algorithm */ + spmap_add(&lnalg_list, bpdev->bpkr.eqa_name, lane); + + if (eq_alg->use_remote_tx_training) { + if (!eq_alg->ops.is_rx_ok) + phydev_warn(phydev, + "Required operation for remote Tx training is missing: is_rx_ok\n"); + if (!eq_alg->ops.is_eq_done) + phydev_warn(phydev, + "Required operation for remote Tx training is missing: is_eq_done\n"); + if (!eq_alg->ops.collect_statistics) + phydev_warn(phydev, + "Required operation for remote Tx training is missing: collect_statistics\n"); + if (!eq_alg->ops.generate_request) + phydev_warn(phydev, + "Required operation for remote Tx training is missing: generate_request\n"); + } + } + } + + return 0; +} +EXPORT_SYMBOL(backplane_setup_lanes); + +/* backplane_initialize + * Initializes all PHY and lane mutexes and + * starts lane timers for running the algorithm + */ +int backplane_initialize(struct phy_device *phydev) +{ + struct backplane_device *bpdev = phydev->priv; + int i; + + if (!bpdev) { + phydev_err(phydev, "Backplane phy info is not allocated\n"); + return -EINVAL; + } + + mutex_init(&bpdev->bpphy_lock); + mutex_init(&bpdev->trained_lock); + + for (i = 0; i < bpdev->num_lanes; i++) + mutex_init(&bpdev->lane[i].lane_lock); + + phydev->speed = get_backplane_speed(phydev->interface); + if (phydev->speed < 0) { + phydev_err(phydev, "Unsupported backplane mode\n"); + return -EINVAL; + } + + if (backplane_is_mode_kr(phydev->interface)) { + for (i = 0; i < bpdev->num_lanes; i++) + init_kr_state_machine(&bpdev->lane[i]); + } + + return 0; +} +EXPORT_SYMBOL(backplane_initialize); + +/* backplane_probe + * + * Probe function for backplane driver to provide generic device behavior + * + * phydev: backplane phy device + * this is an internal phy block controlled by the software + * which contains other component blocks like: PMA/PMD, PCS, AN + * + * Return: Zero for success or error code in case of failure + */ +int backplane_probe(struct phy_device *phydev) +{ + return backplane_create(phydev); +} +EXPORT_SYMBOL(backplane_probe); + +void backplane_remove(struct phy_device *phydev) +{ + struct backplane_device *bpdev = phydev->priv; + + if (!bpdev) { + phydev_err(phydev, "Backplane phy info is not allocated\n"); + return; + } + + kfree(bpdev); + phydev->priv = NULL; +} +EXPORT_SYMBOL(backplane_remove); + +/* backplane_config_init + * + * Config_Init function for backplane driver to provide generic device behavior + * + * phydev: backplane phy device + * + * Return: Zero for success or error code in case of failure + */ +int backplane_config_init(struct phy_device *phydev) +{ + int ret; + + ret = backplane_parse_dt(phydev); + if (ret) + return ret; + + ret = backplane_setup_memio(phydev); + if (ret) + return ret; + + ret = backplane_setup_mmd(phydev); + if (ret) + return ret; + + ret = backplane_setup_lanes(phydev); + if (ret) + return ret; + + ret = backplane_initialize(phydev); + if (ret) + return ret; + + return 0; +} +EXPORT_SYMBOL(backplane_config_init); + +int backplane_aneg_done(struct phy_device *phydev) +{ + struct backplane_device *bpdev = phydev->priv; + + if (!phydev->mdio.dev.of_node) { + phydev_err(phydev, "No associated device tree node\n"); + return -EINVAL; + } + if (!bpdev) { + phydev_err(phydev, "Backplane phy info is not allocated\n"); + return -EINVAL; + } + + bpdev->aneg_done = true; + phydev->state = PHY_RUNNING; + + return 1; +} +EXPORT_SYMBOL(backplane_aneg_done); + +int backplane_config_aneg(struct phy_device *phydev) +{ + struct backplane_device *bpdev = phydev->priv; + struct equalization_ops ops; + struct lane_device *lane; + int i; + + if (!phydev->mdio.dev.of_node) { + phydev_err(phydev, "No associated device tree node\n"); + return -EINVAL; + } + if (!bpdev) { + phydev_err(phydev, "Backplane phy info is not allocated\n"); + return -EINVAL; + } + if (backplane_get_lanes_trained_count(bpdev) > 0) { + phydev_err(phydev, "Incorrectly trained lanes detected\n"); + return -EINVAL; + } + + for (i = 0; i < bpdev->num_lanes; i++) { + lane = &bpdev->lane[i]; + if (lane->krln.eq_alg) { + ops = lane->krln.eq_alg->ops; + if (ops.dump_algorithm_context) + ops.dump_algorithm_context(lane->krln.eq_priv); + } + } + + if (backplane_is_mode_kr(phydev->interface)) { + /* Warning: + * Order of the operations below is important + * otherwise the training may be failing + * with error: 'link_training_failed' + */ + + /* setup all lanes to default */ + for (i = 0; i < bpdev->num_lanes; i++) + setup_default_settings(&bpdev->lane[i]); + + /* Initialize all lanes and reset LT */ + for (i = 0; i < bpdev->num_lanes; i++) { + init_kr_lane(&bpdev->lane[i], true); + lt_reset(&bpdev->lane[i]); + } + } + + /* Warning: + * speed and protocol setup operation + * must be done just before AN and state machine start + * otherwise if it is done earlier, + * the error: 'REQ Timeout' will occur + */ + /* setup supported speed and protocol */ + phydev->speed = get_backplane_speed(phydev->interface); + if (phydev->speed < 0) { + phydev_err(phydev, "Unsupported backplane mode\n"); + return -EINVAL; + } + + setup_supported_linkmode(phydev); + linkmode_copy(phydev->advertising, phydev->supported); + phydev->duplex = DUPLEX_FULL; + + if (backplane_is_mode_kr(phydev->interface)) { + /* AN init only for Master Lane */ + an_init(&bpdev->lane[MASTER_LANE]); + /* start state machine on all lanes */ + for (i = 0; i < bpdev->num_lanes; i++) + start_kr_state_machine(&bpdev->lane[i], KR_TIMEOUT_1); + } + + bpdev->aneg_config = true; + + return 0; +} +EXPORT_SYMBOL(backplane_config_aneg); + +int backplane_suspend(struct phy_device *phydev) +{ + struct backplane_device *bpdev = phydev->priv; + int i; + + if (!phydev->mdio.dev.of_node) { + phydev_err(phydev, "No associated device tree node\n"); + return -EINVAL; + } + if (!bpdev) { + phydev_err(phydev, "Backplane phy info is not allocated\n"); + return -EINVAL; + } + + if (bpdev->aneg_config && !bpdev->phy_suspended) { + if (backplane_is_mode_kr(phydev->interface)) { + for (i = 0; i < bpdev->num_lanes; i++) + stop_kr_state_machine(&bpdev->lane[i]); + } + bpdev->phy_suspended = true; + } + + return 0; +} +EXPORT_SYMBOL(backplane_suspend); + +int backplane_resume(struct phy_device *phydev) +{ + struct backplane_device *bpdev = phydev->priv; + int i; + + if (!phydev->mdio.dev.of_node) { + phydev_err(phydev, "No associated device tree node\n"); + return -EINVAL; + } + if (!bpdev) { + phydev_err(phydev, "Backplane phy info is not allocated\n"); + return -EINVAL; + } + + if (bpdev->aneg_config && bpdev->phy_suspended) { + if (backplane_is_mode_kr(phydev->interface)) { + for (i = 0; i < bpdev->num_lanes; i++) { + init_kr_lane(&bpdev->lane[i], true); + start_kr_state_machine(&bpdev->lane[i], + KR_TIMEOUT_1); + } + } + bpdev->phy_suspended = false; + } + + return 0; +} +EXPORT_SYMBOL(backplane_resume); + +int backplane_read_status(struct phy_device *phydev) +{ + struct backplane_device *bpdev = phydev->priv; + + if (!phydev->mdio.dev.of_node) { + phydev_err(phydev, "No associated device tree node\n"); + return -EINVAL; + } + if (!bpdev) { + phydev_err(phydev, "Backplane phy info is not allocated\n"); + return -EINVAL; + } + + /* Linkup method proposal for training stability: + * Don't raise linkup until all lanes are trained + * in order to prevent interface sending packets that may + * interfere with the training packets + */ + if (backplane_is_link_up(phydev)) + if (backplane_is_mode_kr(phydev->interface)) + phydev->link = backplane_are_all_lanes_trained(bpdev); + else + phydev->link = 1; + else + phydev->link = 0; + + return 0; +} +EXPORT_SYMBOL(backplane_read_status); + +int backplane_match_phy_device(struct phy_device *phydev) +{ + struct device_node *dev_node; + + if (!phydev->is_c45) + return 0; + + dev_node = phydev->mdio.dev.of_node; + if (!dev_node) { + phydev_err(phydev, "No associated device tree node\n"); + return 0; + } + + return 1; +} +EXPORT_SYMBOL(backplane_match_phy_device); + +static int __init backplane_module_init(void) +{ + pr_info("%s: Backplane driver version %s\n", + BACKPLANE_DRIVER_NAME, BACKPLANE_DRIVER_VERSION); + + mutex_init(&backplane_lock); + backplane_features_init(); + + return 0; +} + +static void __exit backplane_module_exit(void) +{ + pr_info("%s: Backplane driver version %s unloaded\n", + BACKPLANE_DRIVER_NAME, BACKPLANE_DRIVER_VERSION); +} + +module_init(backplane_module_init); +module_exit(backplane_module_exit); + +MODULE_DESCRIPTION("Backplane driver"); +MODULE_AUTHOR("Florinel Iordache "); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/phy/backplane/backplane.h b/drivers/net/phy/backplane/backplane.h new file mode 100644 index 0000000..d142e83 --- /dev/null +++ b/drivers/net/phy/backplane/backplane.h @@ -0,0 +1,293 @@ +/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */ +/* Backplane driver + * + * Copyright 2018-2020 NXP + */ + +#ifndef __BACKPLANE_H +#define __BACKPLANE_H + +#include +#include + +#include "equalization.h" + +/* Backplane Driver name */ +#define BACKPLANE_DRIVER_NAME "backplane" + +/* Backplane Driver version */ +#define BACKPLANE_DRIVER_VERSION "1.0.0" + +/* Maximum number of lanes per phy */ +#define MAX_KR_LANES_PER_PHY 1 + +/* Lanes definitions */ +#define MASTER_LANE 0 +#define SINGLE_LANE 0 + +/* Number of device specific kr coefficients (device specific extension) */ +#define DEVICE_KR_COEF_NO 6 + +extern __ETHTOOL_DECLARE_LINK_MODE_MASK(backplane_features) __ro_after_init; + +#define BACKPLANE_FEATURES ((unsigned long *)&backplane_features) + +enum train_state { + DETECTING_LP, + TRAINED, +}; + +enum lane_req { + LANE_INVALID, + LANE_RX, + LANE_TX, + LANE_RX_TX +}; + +struct kr_coef { + u32 preq; + u32 mainq; + u32 postq; + /* device specific extension */ + u32 dev_coef[DEVICE_KR_COEF_NO]; +}; + +/* Endianness specific memory I/O */ +struct mem_io { + u32 (*read32)(void __iomem *addr); + void (*write32)(u32 value, void __iomem *addr); +}; + +/* Generic Lane operations */ +struct lane_ops { + /* device specific private extension */ + const void *priv; + /* lane memory map size */ + u32 memmap_size; + void (*reset_lane)(void __iomem *reg, enum lane_req req); + void (*tune_lane_kr)(void __iomem *reg, struct kr_coef *coef, + bool reset); + void (*read_lane_kr)(void __iomem *reg, struct kr_coef *coef); + bool (*is_cdr_lock)(void __iomem *reg); +}; + +struct training_status { + bool done_training; + bool remote_tx_complete; + bool remote_tx_running; + bool sent_init; + bool lp_rx_ready; + bool local_tx_running; +}; + +struct backplane_device; + +/* Lane kr */ +struct lane_kr { + /* KR parameters (current, default, tuned) */ + struct kr_coef crt_kr; + struct kr_coef def_kr; + struct kr_coef tuned_kr; + /* equalization */ + const struct equalization_algorithm *eq_alg; + struct eq_data_priv *eq_priv; + /* training status */ + struct training_status trst; + enum train_state state; + /* AN KR status */ + bool an_kr_detected; + u32 an_kr_wait_count; + u64 an_kr_timeout; + /* KR LD/LP updates and status */ + u32 ld_update; + u32 prev_ld_update; + /* last change (non-hold) update */ + u32 ld_last_nonhold_update; + u32 ld_status; + u32 lp_status; + /* last change (non-zero) status */ + u32 lp_last_change_status; + u32 last_lp_update_status[C_NO]; + /* link training status */ + bool lt_error; + u32 req_ld_update_init_count; + u32 repeat_request_count; + u64 init_handshake_time; + bool first_recv_init; + /* move lp back */ + bool move_back_prev; + u32 move_back_cnt; + u32 move_back_lp_status; +}; + +/* Lane device */ +struct lane_device { + /* lane memory map: registers base address */ + void __iomem *reg_base; + /* lane memory map size */ + u32 memmap_size; + /* lane address */ + u32 lane_addr; + /* lane relative index inside multi-lane PHY */ + u8 idx; + /* device specific private extension */ + void *priv; + /* phy device */ + struct phy_device *phydev; + struct backplane_device *bpdev; + struct lane_kr krln; + struct delayed_work krwk; + /* mutex between multiple lanes */ + struct mutex lane_lock; +}; + +/* KR LT MMD (MDIO Managed Device) */ +struct kr_lt_mmd { + int devad; + u32 control; + u32 status; + u32 lp_cu; + u32 lp_status; + u32 ld_cu; + u32 ld_status; +}; + +/* Backplane kr */ +struct backplane_kr { + struct kr_coef min_kr; + struct kr_coef max_kr; + struct kr_coef def_kr; + /* defaults for eq kr are initialized from DT: eq-init */ + bool valid_eq_init; + /* defaults for eq params are initialized from DT: eq-params */ + bool valid_eq_params; + /* EQ algorithm name */ + const char *eqa_name; + const struct equalizer_device *equalizer; + struct kr_lt_mmd ltmmd; +}; + +/* Backplane device specific callbacks */ +struct backplane_ops { + /* AN register ops */ + void (*an_advertisement_init)(struct lane_device *lane); + bool (*is_an_link_detected)(struct lane_device *lane); + /* default settings ops */ + void (*setup_default_settings)(struct lane_device *lane); + /* LT coefficients validation ops */ + int (*lt_validation)(struct lane_device *lane, u32 *ld_coef); +}; + +/* Backplane driver */ +struct backplane_driver { + /* serdes base address */ + u32 base_addr; + /* serdes memory map size */ + u32 memmap_size; + /* serdes endianness */ + bool is_little_endian; + /* memory I/O */ + struct mem_io io; + /* backplane ops */ + struct backplane_ops bp_ops; + /* lane ops */ + const struct lane_ops *lane_ops; + /* device specific private extension */ + void *priv; +}; + +/* Backplane device */ +struct backplane_device { + struct phy_device *phydev; + u8 num_lanes; + bool aneg_config; + bool aneg_done; + bool phy_suspended; + /* backplane management functions */ + struct backplane_driver drv; + /* backplane kr */ + struct backplane_kr bpkr; + /* kr lanes array: valid elements = num_lanes */ + struct lane_device lane[MAX_KR_LANES_PER_PHY]; + /* device specific private extension */ + void *priv; + /* bpphy mutexes */ + struct mutex bpphy_lock; + /* mutex between multiple lanes training */ + struct mutex trained_lock; +}; + +bool backplane_is_mode_kr(phy_interface_t interface); + +bool backplane_is_valid_mode(phy_interface_t interface); + +u8 backplane_num_lanes(phy_interface_t interface); + +u32 backplane_get_an_bp_eth_status_bit(phy_interface_t interface); + +u32 backplane_get_an_adv1_init(phy_interface_t interface); + +bool backplane_is_single_lane(struct backplane_device *bpdev); + +bool backplane_is_multi_lane(struct backplane_device *bpdev); + +int backplane_are_all_lanes_trained(struct backplane_device *bpdev); + +int backplane_get_lanes_trained_count(struct backplane_device *bpdev); + +int backplane_is_link_up(struct phy_device *phydev); + +void backplane_kr_lt_mmd_c45(struct backplane_kr *bpkr); + +void backplane_kr_lt_mmd_setup(struct backplane_kr *bpkr, int devad, u32 base); + +int backplane_read_mmd(struct lane_device *lane, int devad, u32 regnum); + +int backplane_write_mmd(struct lane_device *lane, int devad, u32 regnum, + u16 val); + +void backplane_default_kr_lane(struct lane_device *lane); + +void backplane_get_current_taps(struct lane_device *lane, u32 *coef); + +void backplane_set_current_taps(struct lane_device *lane, u32 *coef); + +void backplane_set_all_taps_to_max(struct lane_device *lane); + +void backplane_tune_kr_lane(struct lane_device *lane, bool reset_lane); + +/* generic main operations to be used on probe callback */ + +int backplane_create(struct phy_device *phydev); + +int backplane_parse_dt(struct phy_device *phydev); + +int backplane_setup_memio(struct phy_device *phydev); + +int backplane_setup_mmd(struct phy_device *phydev); + +int backplane_setup_lanes(struct phy_device *phydev); + +int backplane_initialize(struct phy_device *phydev); + +/* predefined phy_driver callback functions */ + +int backplane_probe(struct phy_device *phydev); + +void backplane_remove(struct phy_device *phydev); + +int backplane_config_init(struct phy_device *phydev); + +int backplane_aneg_done(struct phy_device *phydev); + +int backplane_config_aneg(struct phy_device *phydev); + +int backplane_suspend(struct phy_device *phydev); + +int backplane_resume(struct phy_device *phydev); + +int backplane_read_status(struct phy_device *phydev); + +int backplane_match_phy_device(struct phy_device *phydev); + +#endif /* __BACKPLANE_H */ diff --git a/drivers/net/phy/backplane/eq_fixed.c b/drivers/net/phy/backplane/eq_fixed.c new file mode 100644 index 0000000..827450e --- /dev/null +++ b/drivers/net/phy/backplane/eq_fixed.c @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* Fixed: No Equalization algorithm + * + * Copyright 2019-2020 NXP + */ + +#include +#include + +#include "equalization.h" + +#define ALGORITHM_NAME "backplane_fixed" +#define ALGORITHM_DESCR "Fixed Equalization" +#define ALGORITHM_VERSION "1.0.0" + +/* Fixed Algorithm API */ + +/* Create Fixed Equalization Algorithm */ +static struct eq_data_priv *create(struct equalizer_driver eqdrv) +{ + return NULL; +} + +static const struct equalization_algorithm eq_alg = { + .name = ALGORITHM_NAME, + .descr = ALGORITHM_DESCR, + .version = ALGORITHM_VERSION, + .use_local_tx_training = false, + .use_remote_tx_training = false, + .ops = { + .create = create, + .destroy = NULL, + .is_rx_ok = NULL, + .is_eq_done = NULL, + .collect_statistics = NULL, + .generate_request = NULL, + .process_bad_state = NULL, + .dump_algorithm_context = NULL, + } +}; + +static const char * const alg_keys[] = { + DEFAULT_EQ_ALGORITHM, + "bypass", +}; + +static int __init fixed_init(void) +{ + int i, err; + + pr_info("%s: %s algorithm version %s\n", + ALGORITHM_NAME, ALGORITHM_DESCR, ALGORITHM_VERSION); + + /* register Fixed algorithm: */ + for (i = 0; i < ARRAY_SIZE(alg_keys); i++) { + err = backplane_eq_register(alg_keys[i], &eq_alg); + if (err) { + pr_err("%s: '%s' equalization algorithm registration failed\n", + ALGORITHM_NAME, alg_keys[i]); + } + } + + return 0; +} + +static void __exit fixed_exit(void) +{ + int i; + + /* unregister Fixed algorithm: */ + for (i = 0; i < ARRAY_SIZE(alg_keys); i++) + backplane_eq_unregister(alg_keys[i]); + + pr_info("%s: %s algorithm version %s unloaded\n", + ALGORITHM_NAME, ALGORITHM_DESCR, ALGORITHM_VERSION); +} + +module_init(fixed_init); +module_exit(fixed_exit); + +MODULE_DESCRIPTION("Fixed Equalization Algorithm"); +MODULE_AUTHOR("Florinel Iordache "); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/phy/backplane/equalization.h b/drivers/net/phy/backplane/equalization.h new file mode 100644 index 0000000..767f0159 --- /dev/null +++ b/drivers/net/phy/backplane/equalization.h @@ -0,0 +1,275 @@ +/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */ +/* Equalization interface + * for Equalization and Link Training (IEEE802.3ap/ba) + * + * Copyright 2019-2020 NXP + */ + +#ifndef __EQUALIZATION_H +#define __EQUALIZATION_H + +#include + +/* Default equalization algorithm */ +#define DEFAULT_EQ_ALGORITHM "fixed" + +struct lane_device; +struct equalizer_driver; + +/* EQ Algorithms Interface used by Link Training + * to call equalization algorithms callbacks + */ + +/* Equalization private data + * specifically defined by each algorithm to be used internally + */ +struct eq_data_priv; + +/* Equalization Algorithm operations */ +struct equalization_ops { + /* Mandatory operations: */ + struct eq_data_priv *(*create)(struct equalizer_driver eqdrv); + void (*destroy)(struct eq_data_priv *priv); + /* Required operations for remote Tx link training: */ + bool (*is_rx_ok)(struct eq_data_priv *priv); + bool (*is_eq_done)(struct eq_data_priv *priv); + bool (*collect_statistics)(struct eq_data_priv *priv); + void (*generate_request)(struct eq_data_priv *priv); + /* Optional operations: */ + void (*process_bad_state)(struct eq_data_priv *priv); + void (*dump_algorithm_context)(struct eq_data_priv *priv); +}; + +/* Equalization Algorithm description data */ +struct equalization_algorithm { + const char *name; + const char *descr; + const char *version; + bool use_local_tx_training; + bool use_remote_tx_training; + struct equalization_ops ops; +}; + +/* Equalizer Interface for EQ Algorithms: + * Used by equalization algorithms to collect equalizer statistics + * required to take correct decisions for tuning equalization parameters + */ + +/* Equalizer counters type + * + * Equalizer Binning Counters for Data Dependent Edge Statistics: + * + * Bin(s) = (# late edges - # early edges) + * Prior/Next Edge at T -/+ #UI (Unit Interval) + * Bin_1: 1UI wide pulses: Prior Edge at T - 1UI + * final edges on short pulses: + * - contains the scoring of final edges on pulses that are 1UI long + * - represents the difference between the number of short pulse late edges + * and the number of short pulse early edges + * Bin_2: 2UI wide pulses: Prior Edge at T - 2UI + * Bin_3: 3UI (or >=3UI) wide pulses: Prior Edge at T - 3UI (or T - >=3UI) + * Bin_4: 4UI (or >=4UI) wide pulses: Prior Edge at T - 4UI (or T - >=4UI) + * Bin_Med: >=5UI and <=7UI wide pulses: + * Prior Edge in between T - >=5UI and T - <=7UI + * final edges on medium pulses: + * - contains the scoring of final edges on pulses between 5UI and 7UI long + * Bin_Long: >=8UI wide pulses: Prior Edge at T - >=8UI + * final edges on long pulses: + * - contains the scoring of final edges on pulses longer than 7UI long + * - represents the difference between the number of long pulse late edges + * and the number of long pulse early edges + * Bin_M1: 1UI wide pulses: Next Edge at T + 1UI + * initial edges on short pulses following non-single bits: + * - contains the scoring of initial edges on pulses that are 1UI long + * following non-single bits + * - the next edge is 1UI away and prior edge is more than 1UI away + * Bin_M2: 2UI wide pulses: Next Edge at T + 2UI + * Bin_M3: 3UI (or >=3UI) wide pulses: Next Edge at T + 3UI (or T + >=3UI) + * Bin_M4: 4UI (or >=4UI) wide pulses: Next Edge at T + 4UI (or T + >=4UI) + * Bin_MMed: >=5UI and <=7UI wide pulses: + * Next Edge in between T + >=5UI and T + <=7UI + * initial edges on medium pulses following non-single bits: + * - contains the scoring of initial edges on pulses between 5UI and 7UI + * following non-single bits + * Bin_MLong: >=8UI wide pulses: Next Edge at T + >=8UI + * initial edges on long pulses following non-single bits: + * - contains the scoring of initial edges on pulses longer than 7UI long + * - represents the difference between the number of long pulse late edges + * and the number of long pulse early edges + * + * Bin_Offset = [(# late rising edges + # early falling edges) - + * (# early rising edges + # late falling edges)] + * - contains the transition information for the difference between + * all bits that are narrower than expected and + * all bits that are wider than expected + * + * Bin_Avg: Low Pass Filter of Running Disparity + * - Bin_Avg provides a time weighted, filtered average of disparity which + * indicates the BLW potential of recently received data + * New Bin_Avg = Bin_Avg - Bin_Avg/8 + block_disparity + * where block_disparity = (#of ones - #of zeros) + * + * Bin_BLW: Bin Baseline Wander + * - BinBLW accumulates the correlation between Bin_Avg and Bin_Offset + * - Low frequency deficiency (LFD) causes BLW effect + * New Bin_BLW = Bin_BLW + Bin_Avg, for Bin_Offset > 0 + * = Bin_BLW - Bin_Avg, for Bin_Offset < 0 + * = Bin_BLW, for Bin_Offset = 0 + * + * Equalizer gains: + * GAIN_LF: Low-frequency gain of the equalizer amplifier + * GAIN_MF: Middle-frequency gain of the equalizer amplifier + * GAIN_HF: High-frequency gain of the equalizer amplifier + * + * Equalizer status: + * EQOFFSET: equalization offset status + * Binary coded status of RX Adaptive Equalization offset controls of lane + */ +enum eqc_type { + EQC_BIN_1, + EQC_BIN_2, + EQC_BIN_3, + EQC_BIN_4, + EQC_BIN_MED, + EQC_BIN_LONG, + EQC_BIN_M1, + EQC_BIN_M2, + EQC_BIN_M3, + EQC_BIN_M4, + EQC_BIN_MMED, + EQC_BIN_MLONG, + EQC_BIN_OFFSET, + EQC_BIN_AVG, + EQC_BIN_BLW, + EQC_GAIN_LF, + EQC_GAIN_MF, + EQC_GAIN_HF, + EQC_EQOFFSET, +}; + +/* Equalizer counters range */ +struct eqc_range { + s16 min; + s16 max; + s16 mid_low; + s16 mid_high; +}; + +/* Equalizer counters collection operations */ +struct equalizer_ops { + int (*collect_counters)(void *reg, enum eqc_type type, s16 *counters, + u8 size); + int (*collect_multiple_counters)(void *reg, enum eqc_type type[], + u8 type_no, s16 *counters, u8 size); + struct eqc_range *(*get_counter_range)(enum eqc_type type); +}; + +/* Equalizer device and operations */ +struct equalizer_device { + const char *name; + const char *version; + struct equalizer_ops ops; +}; + +/* Equalization driver */ +struct equalizer_driver { + struct phy_device *phydev; + /* lane info used as parameter for link training API */ + struct lane_device *lane; + /* lane reg base used as parameter for equalizer ops */ + void *reg_base; + const struct equalizer_device *equalizer; +}; + +/* Link Training Interface used by EQ Algorithms + * to interact with IEEE802.3ap/ba standards + */ + +/* update request type + * Identifies the LP update request type according to IEEE802.3ap-2007 + * which must be sent to LP to request coefficients update + * + * HOLD: Request LP to Hold all coefficients update + * INC: Request LP to Increment the specified coefficient + * DEC: Request LP to Decrement the specified coefficient + * INIT: Request LP to Initialize all coefficients + * PRESET: Request LP to set all coefficients to Preset + * INVALID: Invalid request type: should not be used as LP request + */ +enum req_type { + REQ_HOLD, + REQ_INC, + REQ_DEC, + REQ_INIT, + REQ_PRESET, + REQ_INVALID +}; + +/* coefficient field + * Identifies the coefficient field on which must take a desired action + * according to IEEE802.3ap-2007 + * + * coefficients: + * M1: C(-1): Pre-cursor + * Z0: C(0): Main cursor + * P1: C(+1): Post-cursor + * NO: Number of coefficients (this is not a valid coefficient field) + */ +enum coef_field { + C_M1, + C_Z0, + C_P1, + C_NO +}; + +/* coefficient status + * Specifies the coefficient status according to IEEE802.3ap-2007: + * 72.6.10.2.5 Coefficient update process + * + * NOTUPDATED: Coefficient is not updated + * UPDATED: Coefficient is updated + * MIN: Coefficient has reached the minimum threshold + * MAX: Coefficient has reached the maximum threshold + * INVALID: Invalid coefficient status + */ +enum coef_status { + COEF_NOTUPDATED, + COEF_UPDATED, + COEF_MIN, + COEF_MAX, + COEF_INVALID +}; + +void lt_lp_update(struct lane_device *lane, u32 update); + +u32 lt_encode_request(u32 base_update, enum req_type req, + enum coef_field field); + +u32 lt_encode_startup_request(enum req_type req); + +enum req_type lt_decode_coef_update(u32 update, enum coef_field field); + +bool lt_is_update_of_type(u32 update, enum req_type type); + +bool lt_is_lp_at_startup(struct lane_device *lane, enum req_type type); + +enum coef_status lt_get_lp_coef_status(struct lane_device *lane, + enum coef_field field); + +void lt_move_lp_back(struct lane_device *lane); + +void lt_set_error(struct lane_device *lane, bool err); + +/* Backplane Driver Interface for EQ Algorithms: + * Used by equalization algorithms to interact + * with backplane driver during equalization + */ + +/* equalization algorithm registration */ +int backplane_eq_register(const char *key, + const struct equalization_algorithm *eq_info); +void backplane_eq_unregister(const char *key); + +bool backplane_is_cdr_lock(struct lane_device *lane, bool retry); + +#endif /* __EQUALIZATION_H */ diff --git a/drivers/net/phy/backplane/link_training.c b/drivers/net/phy/backplane/link_training.c new file mode 100644 index 0000000..3aa26f9 --- /dev/null +++ b/drivers/net/phy/backplane/link_training.c @@ -0,0 +1,1529 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* Link Training (IEEE802.3ap/ba) + * Ethernet Operation over Electrical Backplanes + * + * Copyright 2019-2020 NXP + */ + +#include +#include +#include + +#include "link_training.h" + +/* KR LP/LD Coefficients */ +#define PRESET_MASK 0x2000 +#define INIT_MASK 0x1000 +#define COP1_MASK 0x30 +#define COP1_SHIFT 4 +#define COZ0_MASK 0xc +#define COZ0_SHIFT 2 +#define COM1_MASK 0x3 +#define COM1_SHIFT 0 +#define ALL_COEF_MASK (COP1_MASK | COZ0_MASK | COM1_MASK) +#define LD_ALL_MASK (PRESET_MASK | INIT_MASK | ALL_COEF_MASK) + +/* KR LP Status Report */ +#define LP_STATUS_ALL_COEF_UPDATED 0x15 + +/* KR LP/LD Status Report: + * RX_READY_MASK - Receiver Ready + * 0b - The LP/LD receiver is requesting that training continue + * 1b - The LP/LD receiver has determined that training is complete + * and is prepared to receive data. + */ +#define RX_READY_MASK 0x8000 + +/* Increment/Decrement Requests */ +#define HOLD 0 +#define INCREMENT 1 +#define DECREMENT 2 +#define RESERVED 3 + +/* Increment/Decrement Steps */ +#define STEP_INCREMENT_P1 -1 +#define STEP_INCREMENT_Z0 1 +#define STEP_INCREMENT_M1 -1 + +/* KR PMD Control defines */ +#define TRAIN_EN 0x3 +#define TRAIN_DISABLE 0x1 +#define PMD_RESET 0x1 + +/* KR PMD Status defines */ +#define PMD_STATUS_TRAIN_FAIL 0x8 +#define PMD_STATUS_SUP_STAT 0x4 +#define PMD_STATUS_FRAME_LOCK 0x2 +#define PMD_STATUS_RX_STAT 0x1 + +/* KR PMD control register (Register 1.150) */ +#define KR_PMD_BASE_OFFSET 150 + +/* Link training KR PMD registers offsets (relative to base) */ +#define OFFSET_KR_PMD_CTRL 0x0 +#define OFFSET_KR_PMD_STATUS 0x1 +#define OFFSET_KR_LP_CU 0x2 +#define OFFSET_KR_LP_STATUS 0x3 +#define OFFSET_KR_LD_CU 0x4 +#define OFFSET_KR_LD_STATUS 0x5 + +/* Timeouts */ +#define TIMEOUT_MOVE_BACK_PREV 6 +#define TIMEOUT_REPEAT_REQUEST 10 + +/* Training for Remote Tx */ + +static u32 get_mask_for_req(enum req_type req) +{ + u32 cmd = HOLD; + + switch (req) { + case REQ_HOLD: + cmd = HOLD; + break; + case REQ_INC: + cmd = INCREMENT; + break; + case REQ_DEC: + cmd = DECREMENT; + break; + case REQ_INIT: + cmd = INIT_MASK; + break; + case REQ_PRESET: + cmd = PRESET_MASK; + break; + case REQ_INVALID: + cmd = RESERVED; + break; + default: + cmd = HOLD; + break; + } + return cmd; +} + +static enum req_type get_req_for_mask(u32 cmd) +{ + enum req_type req = REQ_HOLD; + + switch (cmd) { + case HOLD: + req = REQ_HOLD; + break; + case INCREMENT: + req = REQ_INC; + break; + case DECREMENT: + req = REQ_DEC; + break; + case INIT_MASK: + req = REQ_INIT; + break; + case PRESET_MASK: + req = REQ_PRESET; + break; + case RESERVED: + req = REQ_INVALID; + break; + default: + req = REQ_HOLD; + break; + } + return req; +} + +/* ld_coef_status + * 72.6.10.2.5 Coefficient update process + * Once the updated, maximum, or minimum state is reported it continues + * to be reported until a hold request is received, + * after which the status reverts to not_updated. + */ +static void ld_coef_status(struct lane_device *lane) +{ + backplane_write_mmd(lane, lane->bpdev->bpkr.ltmmd.devad, + lane->bpdev->bpkr.ltmmd.ld_status, + lane->krln.ld_status); +} + +/* ld_coef_update + * LD sends to LP the specified request for coefficients update + */ +static void ld_coef_update(struct lane_device *lane) +{ + backplane_write_mmd(lane, lane->bpdev->bpkr.ltmmd.devad, + lane->bpdev->bpkr.ltmmd.ld_cu, + lane->krln.ld_update); +} + +/* get_lp_lcs + * get LP lcs (last change status) + * returns the last LP change (non-zero) status: + * meaning the last LP status resulted from a change request + * 72.6.10.2.5 Coefficient update process + * Once the updated, maximum, or minimum state is reported it continues + * to be reported until a hold request is received, + * after which the status reverts to not_updated. + */ +static u32 get_lp_lcs(struct lane_device *lane) +{ + return lane->krln.lp_last_change_status; +} + +static bool is_all_status(u32 status, enum coef_status cs) +{ + return ((status & ALL_COEF_MASK) == + (cs << COP1_SHIFT | cs << COZ0_SHIFT | cs << COM1_SHIFT)); +} + +/* Training for Local Tx */ + +static void initialize(struct lane_device *lane) +{ + backplane_default_kr_lane(lane); + + lane->krln.ld_status &= ~ALL_COEF_MASK; + lane->krln.ld_status |= COEF_UPDATED << COP1_SHIFT | + COEF_UPDATED << COZ0_SHIFT | + COEF_UPDATED << COM1_SHIFT; + + ld_coef_status(lane); +} + +/* preset + * Preset as defined by: IEEE 802.3, sub-clause 72.6.10.2.3.1 + * Setup all coefficients to MAX values from IEEE802.3 perspective + */ +static void preset(struct lane_device *lane) +{ + backplane_set_all_taps_to_max(lane); + + backplane_tune_kr_lane(lane, true); + + lane->krln.ld_status &= ~ALL_COEF_MASK; + lane->krln.ld_status |= COEF_MAX << COP1_SHIFT | + COEF_MAX << COZ0_SHIFT | + COEF_MAX << COM1_SHIFT; + + ld_coef_status(lane); +} + +static bool is_rx_ready(u32 status) +{ + return ((status & RX_READY_MASK) != 0); +} + +/* is_ld_valid + * LD coefficient values have hardware restrictions + * Check if all ld coefficients are in range + */ +static int is_ld_valid(struct lane_device *lane, u32 *ld_coef) +{ + struct backplane_kr *bpkr = &lane->bpdev->bpkr; + u32 mainq = ld_coef[C_Z0]; + u32 postq = ld_coef[C_P1]; + u32 preq = ld_coef[C_M1]; + + /* Basic HW restrictions: */ + + /* 1. tx_preq <= MIN_C(-1) */ + if (preq > bpkr->min_kr.preq) + return -ERANGE; + /* 2. tx_ratio_post1q <= MIN_C(+1) */ + if (postq > bpkr->min_kr.postq) + return -ERANGE; + /* 3. MIN_C(0) <= tx_mainq <= MAX_C(0) */ + if (mainq < bpkr->min_kr.mainq) + return -ERANGE; + if (mainq > bpkr->max_kr.mainq) + return -ERANGE; + /* 4. tx_ratio_post1q >= tx_preq */ + if (postq < preq) + return -ERANGE; + + /* Additional HW restrictions: + * LT custom HW validation: Device specific HW restrictions + */ + if (lane->bpdev->drv.bp_ops.lt_validation) + return lane->bpdev->drv.bp_ops.lt_validation(lane, ld_coef); + + return 0; +} + +static bool update_ld_status(struct lane_device *lane, enum coef_field field, + enum coef_status cs) +{ + u32 ld_cs = cs; + u32 mask, val; + + if (cs == COEF_INVALID) + return false; + + switch (field) { + case C_P1: + mask = COP1_MASK; + val = ld_cs << COP1_SHIFT; + break; + case C_Z0: + mask = COZ0_MASK; + val = ld_cs << COZ0_SHIFT; + break; + case C_M1: + mask = COM1_MASK; + val = ld_cs << COM1_SHIFT; + break; + default: + return false; + } + + lane->krln.ld_status &= ~mask; + lane->krln.ld_status |= val; + + return true; +} + +static enum coef_status inc_dec(struct lane_device *lane, + enum coef_field field, int request) +{ + u32 ld_coef[C_NO], step[C_NO], ld_limit[C_NO]; + int err; + + backplane_get_current_taps(lane, ld_coef); + + step[C_M1] = STEP_INCREMENT_M1; + step[C_Z0] = STEP_INCREMENT_Z0; + step[C_P1] = STEP_INCREMENT_P1; + + /* 72.6.10.2.5 Coefficient update process + * Upon execution of a received increment or decrement request, + * the status is reported as updated, maximum, or minimum. + */ + switch (request) { + case INCREMENT: + ld_limit[C_M1] = lane->bpdev->bpkr.max_kr.preq; + ld_limit[C_Z0] = lane->bpdev->bpkr.max_kr.mainq; + ld_limit[C_P1] = lane->bpdev->bpkr.max_kr.postq; + if (ld_coef[field] != ld_limit[field]) + ld_coef[field] += step[field]; + else + return COEF_MAX; + break; + case DECREMENT: + ld_limit[C_M1] = lane->bpdev->bpkr.min_kr.preq; + ld_limit[C_Z0] = lane->bpdev->bpkr.min_kr.mainq; + ld_limit[C_P1] = lane->bpdev->bpkr.min_kr.postq; + if (ld_coef[field] != ld_limit[field]) + ld_coef[field] -= step[field]; + else + return COEF_MIN; + break; + default: + break; + } + + err = is_ld_valid(lane, ld_coef); + if (!err) { + /* accept new ld coefficients */ + backplane_set_current_taps(lane, ld_coef); + backplane_tune_kr_lane(lane, false); + } else { + if (request == DECREMENT) + return COEF_MIN; + if (request == INCREMENT) + return COEF_MAX; + } + + /* UPDATED */ + return COEF_UPDATED; +} + +static void check_request(struct lane_device *lane, int request) +{ + enum coef_status cu = COEF_INVALID; + int cop1_req, coz0_req, com1_req; + int old_status; + + cop1_req = (request & COP1_MASK) >> COP1_SHIFT; + coz0_req = (request & COZ0_MASK) >> COZ0_SHIFT; + com1_req = (request & COM1_MASK) >> COM1_SHIFT; + + /* IEEE802.3-2008, 72.6.10.2.5 + * Ensure we only act on INCREMENT/DECREMENT when we are in NOT UPDATED + * + * 72.6.10.2.5 Coefficient update process + * An increment or decrement request will only be acted upon when + * the state of the tap is not_updated. + */ + old_status = lane->krln.ld_status; + + if (cop1_req && !(lane->krln.ld_status & COP1_MASK)) { + cu = inc_dec(lane, C_P1, cop1_req); + update_ld_status(lane, C_P1, cu); + } + + if (coz0_req && !(lane->krln.ld_status & COZ0_MASK)) { + cu = inc_dec(lane, C_Z0, coz0_req); + update_ld_status(lane, C_Z0, cu); + } + + if (com1_req && !(lane->krln.ld_status & COM1_MASK)) { + cu = inc_dec(lane, C_M1, com1_req); + update_ld_status(lane, C_M1, cu); + } + + if (old_status != lane->krln.ld_status) + ld_coef_status(lane); +} + +static void training_complete(struct lane_device *lane) +{ + struct training_status *trst = &lane->krln.trst; + + /* update training status */ + trst->remote_tx_complete = true; + trst->remote_tx_running = false; + + /* report LD status */ + lane->krln.ld_status |= RX_READY_MASK; + ld_coef_status(lane); + + /* update PMD status and tell LP we are ready */ + backplane_write_mmd(lane, lane->bpdev->bpkr.ltmmd.devad, + lane->bpdev->bpkr.ltmmd.status, + PMD_STATUS_RX_STAT); +} + +/* Link Training general API */ + +/* Setup standard KR LT MMD (MDIO Managed Device) for Clause 45 + * 45.2.1.76 10GBASE-KR PMD control register (Register 1.150) + */ +void lt_mmd_c45(struct backplane_kr *bpkr) +{ + lt_mmd_setup(bpkr, MDIO_MMD_PMAPMD, KR_PMD_BASE_OFFSET); +} + +/* Setup KR LT MMD (MDIO Managed Device) + * IEEE Std 802.3ap-2007: Table 45.3 PMA/PMD registers + */ +void lt_mmd_setup(struct backplane_kr *bpkr, int devad, u32 base) +{ + bpkr->ltmmd.devad = devad; + bpkr->ltmmd.control = base + OFFSET_KR_PMD_CTRL; + bpkr->ltmmd.status = base + OFFSET_KR_PMD_STATUS; + bpkr->ltmmd.lp_cu = base + OFFSET_KR_LP_CU; + bpkr->ltmmd.lp_status = base + OFFSET_KR_LP_STATUS; + bpkr->ltmmd.ld_cu = base + OFFSET_KR_LD_CU; + bpkr->ltmmd.ld_status = base + OFFSET_KR_LD_STATUS; +} + +/* lt_is_lp_rx_ready + * Reports if LP Receiver is ready + * false: The LP receiver is requesting that training continue + * true: The LP receiver has determined that training is complete + * and is prepared to receive data. + */ +bool lt_is_lp_rx_ready(struct lane_device *lane) +{ + struct kr_lt_mmd *ltmmd = &lane->bpdev->bpkr.ltmmd; + + /* Read LP Status */ + lane->krln.lp_status = backplane_read_mmd(lane, + ltmmd->devad, + ltmmd->lp_status); + return is_rx_ready(lane->krln.lp_status); +} + +/* lt_is_ld_rx_ready + * Reports if LD Receiver is ready + * false: The LD receiver is requesting that training continue + * true: The LD receiver has determined that training is complete + * and is prepared to receive data. + */ +bool lt_is_ld_rx_ready(struct lane_device *lane) +{ + return is_rx_ready(lane->krln.ld_status); +} + +void lt_start(struct lane_device *lane) +{ + backplane_write_mmd(lane, lane->bpdev->bpkr.ltmmd.devad, + lane->bpdev->bpkr.ltmmd.control, + TRAIN_EN); +} + +void lt_stop(struct lane_device *lane) +{ + backplane_write_mmd(lane, lane->bpdev->bpkr.ltmmd.devad, + lane->bpdev->bpkr.ltmmd.control, + TRAIN_DISABLE); +} + +void lt_reset(struct lane_device *lane) +{ + backplane_write_mmd(lane, lane->bpdev->bpkr.ltmmd.devad, MDIO_CTRL1, + PMD_RESET); + backplane_write_mmd(lane, lane->bpdev->bpkr.ltmmd.devad, + lane->bpdev->bpkr.ltmmd.control, + TRAIN_DISABLE); + backplane_write_mmd(lane, lane->bpdev->bpkr.ltmmd.devad, + lane->bpdev->bpkr.ltmmd.ld_cu, 0); + backplane_write_mmd(lane, lane->bpdev->bpkr.ltmmd.devad, + lane->bpdev->bpkr.ltmmd.ld_status, 0); + backplane_write_mmd(lane, lane->bpdev->bpkr.ltmmd.devad, + lane->bpdev->bpkr.ltmmd.status, 0); + backplane_write_mmd(lane, lane->bpdev->bpkr.ltmmd.devad, + lane->bpdev->bpkr.ltmmd.lp_cu, 0); + backplane_write_mmd(lane, lane->bpdev->bpkr.ltmmd.devad, + lane->bpdev->bpkr.ltmmd.lp_status, 0); +} + +/* lt_is_rx_trained + * IEEE Std 802.3ap-2007: Table 72.3 MDIO/PMD status variable mapping + * PMD status variable: rx_trained + */ +bool lt_is_rx_trained(struct lane_device *lane) +{ + struct phy_device *phydev = lane->phydev; + int timeout = 100; + int val; + + val = backplane_read_mmd(lane, lane->bpdev->bpkr.ltmmd.devad, + lane->bpdev->bpkr.ltmmd.status); + + if ((val & PMD_STATUS_RX_STAT) && !(val & PMD_STATUS_TRAIN_FAIL)) { + while (timeout--) { + if (backplane_is_link_up(phydev)) + return true; + + usleep_range(100, 500); + } + } + return false; +} + +/* lt_is_training_failure + * IEEE Std 802.3ap-2007: Table 72.3 MDIO/PMD status variable mapping + * PMD status variable: PMD_fault + */ +bool lt_is_training_failure(struct lane_device *lane) +{ + struct kr_lt_mmd *ltmmd = &lane->bpdev->bpkr.ltmmd; + int lt_state; + + lt_state = backplane_read_mmd(lane, ltmmd->devad, ltmmd->status); + + /* according to spec: 8023ap-2007.pdf + * training_failure + * Boolean variable that is set to TRUE when the training state machine + * has timed out due to expiration of the max_wait_timer while in the + * SEND_TRAINING, TRAIN_LOCAL, or + * TRAIN_REMOTE states and is set to FALSE otherwise. + */ + if (lt_state & PMD_STATUS_TRAIN_FAIL) + return true; + + return false; +} + +/* lt_is_frame_lock + * IEEE Std 802.3ap-2007: Table 72.3 MDIO/PMD status variable mapping + * PMD status variable: frame_lock + */ +bool lt_is_frame_lock(struct lane_device *lane) +{ + struct kr_lt_mmd *ltmmd = &lane->bpdev->bpkr.ltmmd; + int lt_state; + + lt_state = backplane_read_mmd(lane, ltmmd->devad, ltmmd->status); + + if ((lt_state & PMD_STATUS_SUP_STAT) && + (lt_state & PMD_STATUS_FRAME_LOCK)) + return true; + + return false; +} + +/* Training for Remote Tx + * This is the main routine for Remote Tx training + */ +void lt_train_remote_tx(struct lane_device *lane) +{ + const struct equalization_algorithm *eq_alg = lane->krln.eq_alg; + struct training_status *trst = &lane->krln.trst; + u32 prev_req_cp1, prev_req_cz0, prev_req_cm1; + u32 status_cp1, status_cz0, status_cm1; + u32 prev_req_init, prev_req_preset; + u64 lp_resp_time; + + /* Check stop condition for Remote Tx training */ + if (trst->remote_tx_complete) + return; + + /* Check if equalization algorithm is installed */ + if (!eq_alg) + return; + + /* Check that all required callback operations are installed */ + if (!eq_alg->ops.collect_statistics || + !eq_alg->ops.is_rx_ok || + !eq_alg->ops.generate_request || + !eq_alg->ops.is_eq_done) + return; + + /* Start new Remote Tx training step */ + trst->remote_tx_running = true; + + /* Store current state as previous state */ + lane->krln.prev_ld_update = lane->krln.ld_update; + if ((lane->krln.prev_ld_update & ALL_COEF_MASK) != HOLD) + lane->krln.ld_last_nonhold_update = lane->krln.prev_ld_update; + + prev_req_init = lane->krln.prev_ld_update & INIT_MASK; + prev_req_preset = lane->krln.prev_ld_update & PRESET_MASK; + prev_req_cp1 = (lane->krln.prev_ld_update & COP1_MASK) >> COP1_SHIFT; + prev_req_cz0 = (lane->krln.prev_ld_update & COZ0_MASK) >> COZ0_SHIFT; + prev_req_cm1 = (lane->krln.prev_ld_update & COM1_MASK) >> COM1_SHIFT; + + /* Training Done condition */ + if (eq_alg->ops.is_eq_done(lane->krln.eq_priv)) + trst->done_training = true; + + /* Check if Training is Done */ + if (trst->done_training) { + training_complete(lane); + return; + } + + /* Read LP Status */ + lane->krln.lp_status = + backplane_read_mmd(lane, lane->bpdev->bpkr.ltmmd.devad, + lane->bpdev->bpkr.ltmmd.lp_status); + + if ((lane->krln.lp_status & ALL_COEF_MASK) != 0) + lane->krln.lp_last_change_status = lane->krln.lp_status; + + status_cp1 = (lane->krln.lp_status & COP1_MASK) >> COP1_SHIFT; + status_cz0 = (lane->krln.lp_status & COZ0_MASK) >> COZ0_SHIFT; + status_cm1 = (lane->krln.lp_status & COM1_MASK) >> COM1_SHIFT; + + if (status_cp1 == COEF_UPDATED || status_cp1 == COEF_MIN || + status_cp1 == COEF_MAX) + lane->krln.last_lp_update_status[C_P1] = status_cp1; + if (status_cz0 == COEF_UPDATED || status_cz0 == COEF_MIN || + status_cz0 == COEF_MAX) + lane->krln.last_lp_update_status[C_Z0] = status_cz0; + if (status_cm1 == COEF_UPDATED || status_cm1 == COEF_MIN || + status_cm1 == COEF_MAX) + lane->krln.last_lp_update_status[C_M1] = status_cm1; + + /* IEEE802.3-2008, 72.6.10.2.3.2 + * we send initialize to the other side to ensure default settings + * for the LP. Naturally, we should do this only once. + */ + if (!trst->sent_init) { + /* All status MUST be NOTUPDATED for INIT to be executed + * otherwise send HOLD first + */ + if (status_cp1 == COEF_NOTUPDATED && + status_cz0 == COEF_NOTUPDATED && + status_cm1 == COEF_NOTUPDATED) { + trst->sent_init = true; + lane->krln.ld_update = INIT_MASK; + lane->krln.req_ld_update_init_count = 1; + lane->krln.init_handshake_time = + jiffies_to_msecs(jiffies); + } else { + /* send HOLD before sending subsequent Init requests + * this is not the very first Init sent + */ + lane->krln.ld_update = HOLD; + } + ld_coef_update(lane); + return; + } + /* continue to sent init request until LP responds to init */ + if (prev_req_init) { + if (lane->krln.lp_status == 0) { + /* nothing to do here for now + * perhaps the partner board LP has not yet started + * so continue to send INIT requests + * this will happen in the next condition anyway + */ + } + /* 72.6.10.2.3.2 Initialize + * The initialize control shall only be initially sent when all + * coefficient status fields indicate not_updated, + * and will then continue to be sent + * until no coefficient status field indicates not_updated. + */ + if (status_cp1 == COEF_NOTUPDATED || + status_cz0 == COEF_NOTUPDATED || + status_cm1 == COEF_NOTUPDATED) { + lane->krln.ld_update = INIT_MASK; + ld_coef_update(lane); + lane->krln.req_ld_update_init_count++; + } else { + /* IEEE802.3-2008, 72.6.10.2.3.2 + * We may clear INITIALIZE when no coefficients + * show NOT UPDATED. + */ + /* v1: lane->krln.ld_update &= ~INIT_MASK; */ + /* better send request: HOLD ALL + * should be equivalent since only INIT is set now + */ + lane->krln.ld_update = HOLD; + + lp_resp_time = jiffies_to_msecs(jiffies) - + lane->krln.init_handshake_time; + if (!lane->krln.first_recv_init) { + /* Init handshake not done yet, + * but will be soon + */ + lane->krln.req_ld_update_init_count = 1; + lp_resp_time = 0; + } + ld_coef_update(lane); + } + return; + } + + /* 72.6.10.2.3.1 Preset + * The preset control shall only be initially sent when all coefficient + * status fields indicate not_updated, + * and will then continue to be sent until the status for all + * coefficients indicates updated or maximum + */ + /* IEEE802.3-2008, 72.6.10.2.3.1 + * We may clear PRESET when all coefficients show UPDATED or MAX. + */ + /* check if previous request was preset */ + if (prev_req_preset) { + if ((status_cp1 == COEF_UPDATED || status_cp1 == COEF_MAX) && + (status_cz0 == COEF_UPDATED || status_cz0 == COEF_MAX) && + (status_cm1 == COEF_UPDATED || status_cm1 == COEF_MAX)) { + lane->krln.ld_update &= ~PRESET_MASK; + } else { + /* All status MUST be NOTUPDATED for INIT to be executed + * otherwise send HOLD first + */ + if (status_cp1 == COEF_NOTUPDATED && + status_cz0 == COEF_NOTUPDATED && + status_cm1 == COEF_NOTUPDATED) { + lane->krln.ld_update = PRESET_MASK; + } else { + /* send HOLD before sending subsequent + * Preset requests + */ + lane->krln.ld_update = HOLD; + } + ld_coef_update(lane); + return; + } + } + + /* IEEE802.3-2008, 72.6.10.2.3.3 + * We only request coefficient updates when no PRESET/INITIALIZE is + * pending. We also only request coefficient updates when the + * corresponding status is NOT UPDATED and nothing is pending. + */ + if (lane->krln.ld_update & (PRESET_MASK | INIT_MASK)) + return; + + /* continue to move back to previous request until LP responds to it + * Move back to previous C(-1), C(0), C(+1) and HOLD + */ + if (lane->krln.move_back_prev) { + /* can exit from here only with: DONE Training */ + if (lane->krln.move_back_cnt == TIMEOUT_MOVE_BACK_PREV) { + trst->done_training = true; + training_complete(lane); + return; + } + lane->krln.move_back_cnt++; + + if (status_cp1 == COEF_UPDATED) + lane->krln.move_back_lp_status |= + (COEF_UPDATED << COP1_SHIFT); + if (status_cz0 == COEF_UPDATED) + lane->krln.move_back_lp_status |= + (COEF_UPDATED << COZ0_SHIFT); + if (status_cm1 == COEF_UPDATED) + lane->krln.move_back_lp_status |= + (COEF_UPDATED << COM1_SHIFT); + + if ((lane->krln.move_back_lp_status & ALL_COEF_MASK) == + LP_STATUS_ALL_COEF_UPDATED) { + trst->done_training = true; + training_complete(lane); + return; + } + + /* Move back to previous C(-1), C(0), C(+1) */ + lane->krln.ld_update = lane->krln.prev_ld_update; + ld_coef_update(lane); + return; + } + + /* 72.6.10.2.5 Coefficient update process + * Once the updated, maximum, or minimum state is reported it continues + * to be reported until a hold request is received, + * after which the status reverts to not_updated. + */ + + /* IEEE802.3-2008, 72.6.10.2.3.3 + * We set coefficient requests to HOLD when we get the information + * about any updates On clearing our prior response, we also update + * our internal status. + */ + + /* send a Hold if want to send another INC same as previous + * and received status: NOTUPDATED + * 1. Continue to send previous REQ until receive status UPDATED + * 2. Continue to send HOLD until receive status NOTUPDATED + */ + + /* 3. LP can remain stuck ~42 ms in reset Rx lane: so we should wait + * around ~50 ms and only after that issue Timeout error message + */ + + switch (prev_req_cp1) { + case HOLD: + /* previous request was: HOLD */ + if (status_cp1 == COEF_NOTUPDATED) { + /* All good here: + * continue to check the other coefficient requests + * and if all are good then proceed to + * generate coefficient tuning requests + */ + } else { + /* Continue to send the same request: (2.) + * Continue to send HOLD until receive status NOTUPDATED + */ + if (lane->krln.repeat_request_count >= + TIMEOUT_REPEAT_REQUEST) { + phydev_err(lane->phydev, + "REQ Timeout: Repeating C(+1) HOLD request without LP response timeout !\n"); + /* Hold Request Timeout: + * continue to send HOLD until LP responds + * with NOTUPDATED + */ + lane->krln.repeat_request_count = 0; + } else { + /* Allow LP some time to respond + * and repeat request + */ + msleep(20); + /* Allow LP more time to respond, + * as the last chance, on the last time + * before issuing timeout error: (3.) + */ + if (lane->krln.repeat_request_count == + TIMEOUT_REPEAT_REQUEST - 1) + msleep(30); + lane->krln.repeat_request_count++; + } + ld_coef_update(lane); + return; + } + break; + case INCREMENT: + case DECREMENT: + /* previous request was: INC/DEC */ + if (status_cp1 == COEF_NOTUPDATED) { + /* Continue to send the same request: (1.) + * Continue to send previous REQ + * until receive status UPDATED + */ + if (lane->krln.repeat_request_count >= + TIMEOUT_REPEAT_REQUEST) { + if (prev_req_cp1 == INCREMENT) + phydev_err(lane->phydev, + "REQ Timeout: Repeating C(+1) INC request without LP response timeout !\n"); + else + phydev_err(lane->phydev, + "REQ Timeout: Repeating C(+1) DEC request without LP response timeout !\n"); + /* Request Timeout: + * just continue: proceed again to + * generate coefficient tuning requests + */ + } else { + /* Allow LP some time to respond + * and repeat request + */ + msleep(20); + /* Allow LP more time to respond, + * as the last chance, + * on the last time before + * issuing timeout error: (3.) + */ + if (lane->krln.repeat_request_count == + TIMEOUT_REPEAT_REQUEST - 1) + msleep(30); + lane->krln.repeat_request_count++; + ld_coef_update(lane); + return; + } + } else { + /* All good here: + * LP responded to this Request + * Sent HOLD for this coefficient + * before asking another request + * continue to check the other coefficient requests + */ + lane->krln.ld_update &= ~COP1_MASK; + } + break; + default: + /* previous request was: RESERVED: do nothing */ + break; + } + + switch (prev_req_cz0) { + case HOLD: + /* previous request was: HOLD */ + if (status_cz0 == COEF_NOTUPDATED) { + /* All good here: + * continue to check the other coefficient requests + * and if all are good then proceed to + * generate coefficient tuning requests + */ + } else { + /* Continue to send the same request: (2.) + * Continue to send HOLD until receive status NOTUPDATED + */ + if (lane->krln.repeat_request_count >= + TIMEOUT_REPEAT_REQUEST) { + phydev_err(lane->phydev, + "REQ Timeout: Repeating C(0) HOLD request without LP response timeout !\n"); + /* Hold Request Timeout: + * continue to send HOLD until LP responds + * with NOTUPDATED + */ + lane->krln.repeat_request_count = 0; + } else { + /* Allow LP some time to respond + * and repeat request + */ + msleep(20); + /* Allow LP more time to respond, + * as the last chance, + * on the last time before issuing + * timeout error: (3.) + */ + if (lane->krln.repeat_request_count == + TIMEOUT_REPEAT_REQUEST - 1) + msleep(30); + lane->krln.repeat_request_count++; + } + ld_coef_update(lane); + return; + } + break; + case INCREMENT: + case DECREMENT: + /* previous request was: INC/DEC */ + if (status_cz0 == COEF_NOTUPDATED) { + /* Continue to send the same request: (1.) + * Continue to send previous REQ until receive + * status UPDATED + */ + if (lane->krln.repeat_request_count >= + TIMEOUT_REPEAT_REQUEST) { + if (prev_req_cz0 == INCREMENT) + phydev_err(lane->phydev, + "REQ Timeout: Repeating C(0) INC request without LP response timeout !\n"); + else + phydev_err(lane->phydev, + "REQ Timeout: Repeating C(0) DEC request without LP response timeout !\n"); + /* Request Timeout: + * just continue: proceed again to + * generate coefficient tuning requests + */ + } else { + /* Allow LP some time to respond + * and repeat request + */ + msleep(20); + /* Allow LP more time to respond, as the last + * chance, on the last time before issuing + * timeout error: (3.) + */ + if (lane->krln.repeat_request_count == + TIMEOUT_REPEAT_REQUEST - 1) + msleep(30); + lane->krln.repeat_request_count++; + ld_coef_update(lane); + return; + } + } else { + /* All good here: + * LP responded to this Request + * Sent HOLD for this coefficient + * before asking another request + * continue to check the other coefficient requests + */ + lane->krln.ld_update &= ~COZ0_MASK; + } + break; + default: + /* previous request was: RESERVED: do nothing */ + break; + } + + switch (prev_req_cm1) { + case HOLD: + /* previous request was: HOLD */ + if (status_cm1 == COEF_NOTUPDATED) { + /* All good here: + * continue to check the other coefficient requests + * and if all are good then proceed to + * generate coefficient tuning requests + */ + } else { + /* Continue to send the same request: (2.) + * Continue to send HOLD until receive status + * NOTUPDATED + */ + if (lane->krln.repeat_request_count >= + TIMEOUT_REPEAT_REQUEST) { + phydev_err(lane->phydev, + "REQ Timeout: Repeating C(-1) HOLD request without LP response timeout !\n"); + /* Hold Request Timeout: + * continue to send HOLD until + * LP responds with NOTUPDATED + */ + lane->krln.repeat_request_count = 0; + } else { + /* Allow LP some time to respond + * and repeat request + */ + msleep(20); + /* Allow LP more time to respond, + * as the last chance, + * on the last time + * before issuing timeout error: (3.) + */ + if (lane->krln.repeat_request_count == + TIMEOUT_REPEAT_REQUEST - 1) + msleep(30); + lane->krln.repeat_request_count++; + } + ld_coef_update(lane); + return; + } + break; + case INCREMENT: + case DECREMENT: + /* previous request was: INC/DEC */ + if (status_cm1 == COEF_NOTUPDATED) { + /* Continue to send the same request: (1.) + * Continue to send previous REQ until receive status + * UPDATED + */ + if (lane->krln.repeat_request_count >= + TIMEOUT_REPEAT_REQUEST) { + if (prev_req_cm1 == INCREMENT) + phydev_err(lane->phydev, + "REQ Timeout: Repeating C(-1) INC request without LP response timeout !\n"); + else + phydev_err(lane->phydev, + "REQ Timeout: Repeating C(-1) DEC request without LP response timeout !\n"); + /* Request Timeout: + * just continue: proceed again to + * generate coefficient tuning requests + */ + } else { + /* Allow LP some time to respond and repeat + * request + */ + msleep(20); + /* Allow LP more time to respond, as the last + * chance, on the last time before issuing + * timeout error: (3.) + */ + if (lane->krln.repeat_request_count == + TIMEOUT_REPEAT_REQUEST - 1) + msleep(30); + lane->krln.repeat_request_count++; + ld_coef_update(lane); + return; + } + } else { + /* All good here: + * LP responded to this Request + * Sent HOLD for this coefficient + * before asking another request + * continue to check the other coefficient requests + */ + lane->krln.ld_update &= ~COM1_MASK; + } + break; + default: + /* previous request was: RESERVED: do nothing */ + break; + } + + /* Reset repeat request counter: + * must be after all prev_req verifications above + */ + lane->krln.repeat_request_count = 0; + + if (lane->krln.prev_ld_update != lane->krln.ld_update) { + ld_coef_update(lane); + /* Redo these status checks and updates until we have no more + * changes, to speed up the overall process. + */ + return; + } + + /* Do nothing if we have pending request. */ + if (prev_req_cp1 || prev_req_cz0 || prev_req_cm1) + return; + else if (lane->krln.lp_status & ALL_COEF_MASK) + /* No pending request but LP status was not reverted to + * not updated. + */ + return; + + /* Initialize status for the current step */ + lane->krln.lt_error = false; + + /* if CDR_LOCK = 0: Statistics are invalid */ + if (!backplane_is_cdr_lock(lane, true)) { + if (eq_alg->ops.process_bad_state) + eq_alg->ops.process_bad_state(lane->krln.eq_priv); + return; + } + + /* collect bit edge statistics */ + if (!eq_alg->ops.collect_statistics(lane->krln.eq_priv)) + return; + + /* if CDR_LOCK = 0: Statistics are invalid */ + if (!backplane_is_cdr_lock(lane, true)) { + if (eq_alg->ops.process_bad_state) + eq_alg->ops.process_bad_state(lane->krln.eq_priv); + return; + } + + /* Check Rx */ + if (!eq_alg->ops.is_rx_ok(lane->krln.eq_priv)) { + if (eq_alg->ops.process_bad_state) + eq_alg->ops.process_bad_state(lane->krln.eq_priv); + return; + } + eq_alg->ops.generate_request(lane->krln.eq_priv); + + /* All C are in Hold and both Bins are stopped: + * So the Training is done + */ + if (eq_alg->ops.is_eq_done(lane->krln.eq_priv)) { + trst->done_training = true; + training_complete(lane); + } +} + +/* Training for Local Tx + * Initialize LD (Local Device) + */ +void lt_init_ld(struct lane_device *lane) +{ + /* report initial ld status to lp */ + lane->krln.ld_status = 0; + ld_coef_status(lane); +} + +/* Training for Local Tx + * This is the main routine for Local Tx training + */ +void lt_train_local_tx(struct lane_device *lane) +{ + struct training_status *trst = &lane->krln.trst; + int request, old_ld_status; + + /* Check stop condition for Local Tx training */ + trst->lp_rx_ready = lt_is_lp_rx_ready(lane); + if (trst->lp_rx_ready) { + /* LP receiver is ready + * As soon as the LP shows ready, + * no need to do any more updates. + */ + lane->krln.ld_status &= ~ALL_COEF_MASK; + ld_coef_status(lane); + + trst->local_tx_running = false; + return; + } + + /* Start new Local Tx training step */ + trst->local_tx_running = true; + + /* get request from LP */ + request = backplane_read_mmd(lane, lane->bpdev->bpkr.ltmmd.devad, + lane->bpdev->bpkr.ltmmd.lp_cu) & + LD_ALL_MASK; + + old_ld_status = lane->krln.ld_status; + + /* IEEE802.3-2008, 72.6.10.2.5 + * Ensure we always go to NOT UDPATED for status reporting in + * response to HOLD requests. + * IEEE802.3-2008, 72.6.10.2.3.1/2 + * ... but only if PRESET/INITIALIZE are not active to ensure + * we keep status until they are released. + * + * 72.6.10.2.5 Coefficient update process + * Once the updated, maximum, or minimum state is reported it continues + * to be reported until a hold request is received, + * after which the status reverts to not_updated. + */ + if (!(request & (PRESET_MASK | INIT_MASK))) { + /* Reset status on HOLD request */ + if (!(request & COP1_MASK)) + lane->krln.ld_status &= ~COP1_MASK; + + if (!(request & COZ0_MASK)) + lane->krln.ld_status &= ~COZ0_MASK; + + if (!(request & COM1_MASK)) + lane->krln.ld_status &= ~COM1_MASK; + + ld_coef_status(lane); + } + + /* IEEE802.3-2008, 72.6.10.2.3.1/2 + * only act on PRESET/INITIALIZE if all status is NOT UPDATED. + */ + if (request & (PRESET_MASK | INIT_MASK)) { + if (!(lane->krln.ld_status & ALL_COEF_MASK)) { + if (request & PRESET_MASK) + preset(lane); + + if (request & INIT_MASK) { + if (!lane->krln.first_recv_init) { + lane->krln.first_recv_init = true; + /* Init requests must be counted + * from initial handshake + */ + lane->krln.req_ld_update_init_count = 1; + lane->krln.init_handshake_time = + jiffies_to_msecs(jiffies); + } + initialize(lane); + } + } else { + /* Inform the partner about current ld status + * which should be: ALL UPDATED for INIT and + * ALL MAX for PRESET + */ + ld_coef_status(lane); + } + } + + /* check if LP Coefficient are not in HOLD */ + if (request & ALL_COEF_MASK) + check_request(lane, request & ALL_COEF_MASK); + + /* Make sure the partner is always informed about the current ld status + * this will ensure avoidance of several training issues and errors: + * 'link_training_failed' + * 'Repeating request without LP response' + */ + ld_coef_status(lane); +} + +/* Training for Remote Tx API */ + +/* lt_lp_update + * + * Sends to LP the specified request for coefficients update + * + * lane: desired lane for which to send lp update + * update: desired update request to be sent to LP + * + * Returns: None + */ +void lt_lp_update(struct lane_device *lane, u32 update) +{ + lane->krln.ld_update = update; + ld_coef_update(lane); +} +EXPORT_SYMBOL(lt_lp_update); + +/* lt_encode_request + * + * Encodes a request in the update word + * and adds it to other bit requests already existent in the update word + * + * base_update: base update word used to add a new desired request + * req: desired request type to be encoded + * field: the field for which the request must be encoded + * + * Returns: the encoded update word + */ +u32 lt_encode_request(u32 base_update, enum req_type req, + enum coef_field field) +{ + u32 new_cmd = base_update; + u32 cmd; + + if (req >= REQ_INIT) + return RESERVED; + + cmd = get_mask_for_req(req); + + switch (field) { + case C_P1: + new_cmd |= (cmd << COP1_SHIFT); + break; + case C_Z0: + new_cmd |= (cmd << COZ0_SHIFT); + break; + case C_M1: + new_cmd |= (cmd << COM1_SHIFT); + break; + default: + return RESERVED; + } + return new_cmd; +} +EXPORT_SYMBOL(lt_encode_request); + +/* lt_encode_startup_request + * + * Encodes a startup request in the update word + * + * req: desired startup request type to be encoded + * + * Returns: the encoded update word + */ +u32 lt_encode_startup_request(enum req_type req) +{ + if (req == REQ_HOLD || req == REQ_INIT || req == REQ_PRESET) + return get_mask_for_req(req); + + return RESERVED; +} +EXPORT_SYMBOL(lt_encode_startup_request); + +/* lt_decode_coef_update + * + * Decodes a request update for the specified field + * + * update: update word to be decoded + * field: desired field for which to decode the update + * + * Returns: the decoded request type + */ +enum req_type lt_decode_coef_update(u32 update, enum coef_field field) +{ + u32 cmd = HOLD; + + switch (field) { + case C_P1: + cmd = (update & COP1_MASK) >> COP1_SHIFT; + break; + case C_Z0: + cmd = (update & COZ0_MASK) >> COZ0_SHIFT; + break; + case C_M1: + cmd = (update & COM1_MASK) >> COM1_SHIFT; + break; + default: + return REQ_INVALID; + } + + return get_req_for_mask(cmd); +} +EXPORT_SYMBOL(lt_decode_coef_update); + +/* lt_is_update_of_type + * + * Checks if a request update is according to the specified type + * by checking the specific request bit in update word + * + * update: desired update word to be verified + * type: desired type to check against + * + * Returns: true if update is according to asked type or false otherwise + */ +bool lt_is_update_of_type(u32 update, enum req_type type) +{ + u32 mask = HOLD; + + switch (type) { + case REQ_HOLD: + return (update == HOLD); + case REQ_INC: + mask |= (INCREMENT << COP1_SHIFT); + mask |= (INCREMENT << COZ0_SHIFT); + mask |= (INCREMENT << COM1_SHIFT); + return ((update & mask) != 0); + case REQ_DEC: + mask |= (DECREMENT << COP1_SHIFT); + mask |= (DECREMENT << COZ0_SHIFT); + mask |= (DECREMENT << COM1_SHIFT); + return ((update & mask) != 0); + case REQ_INIT: + return ((update & INIT_MASK) != 0); + case REQ_PRESET: + return ((update & PRESET_MASK) != 0); + default: + return false; + } + return false; +} +EXPORT_SYMBOL(lt_is_update_of_type); + +/* lt_is_lp_at_startup + * + * Checks if LP status is still at startup status: INIT or PRESET + * + * lane: desired lane to be verified + * req: request type to check startup status + * it makes sense only for INIT or PRESET requests + * + * Returns: true if LP status is still at startup status or false otherwise + */ +bool lt_is_lp_at_startup(struct lane_device *lane, enum req_type type) +{ + u32 lp_lcs = get_lp_lcs(lane); + u32 lp_st = lane->krln.lp_status; + bool lp_startup; + + /* LP status still at Init/Preset: + * IF now LP status is Init/Preset + * OR (now LP status is NOTUPDATED + * AND the last nonzero LP status was Init/Preset) + */ + switch (type) { + case REQ_INIT: + if (is_all_status(lp_st, COEF_UPDATED)) + lp_startup = true; + else + lp_startup = is_all_status(lp_st, COEF_NOTUPDATED) && + is_all_status(lp_lcs, COEF_UPDATED); + break; + case REQ_PRESET: + /* LP status still at Preset + * if now LP status is Preset + * OR now LP status is NOTUPDATED + * AND the last nonzero LP status was Preset + */ + if (is_all_status(lp_st, COEF_MAX) || + is_all_status(lp_st, COEF_UPDATED)) + lp_startup = true; + else + lp_startup = is_all_status(lp_st, COEF_NOTUPDATED) && + (is_all_status(lp_lcs, COEF_MAX) || + is_all_status(lp_lcs, COEF_UPDATED)); + break; + default: + return false; + } + + return lp_startup; +} +EXPORT_SYMBOL(lt_is_lp_at_startup); + +/* lt_get_lp_coef_status + * + * Determines the last LP coefficient status + * according to IEEE802.3ap-2007: + * 72.6.10.2.5 Coefficient update process + * + * lane: desired lane to be verified + * field: coefficient field to be verified + * + * Returns: the last LP coefficient status + */ +enum coef_status lt_get_lp_coef_status(struct lane_device *lane, + enum coef_field field) +{ + return lane->krln.last_lp_update_status[field]; +} +EXPORT_SYMBOL(lt_get_lp_coef_status); + +/* lt_set_error + * + * Sets or resets the LT (Link Training) Error flag + * This is used to signal to the generic kr training step procedure + * that an LT error state has occurred + * and link training cannot be successfully finished + * + * lane: desired lane to set lt error + * err: boolean value that specifies if set or reset the error flag + * + * Returns: None + */ +void lt_set_error(struct lane_device *lane, bool err) +{ + lane->krln.lt_error = err; +} +EXPORT_SYMBOL(lt_set_error); + +/* lt_move_lp_back + * Request LP to move back to previous coefficients setup and HOLD + * The procedure for sending this request is based on reverting the + * latest change request (non-hold update) for all coefficients + * This procedure should be used to exit from bad states like not CDR_Lock + * + * lane: desired lane for which to send lp update + * + * Returns: None + */ +void lt_move_lp_back(struct lane_device *lane) +{ + u32 prev_req_cp1 = (lane->krln.ld_last_nonhold_update & COP1_MASK) >> + COP1_SHIFT; + u32 prev_req_cz0 = (lane->krln.ld_last_nonhold_update & COZ0_MASK) >> + COZ0_SHIFT; + u32 prev_req_cm1 = (lane->krln.ld_last_nonhold_update & COM1_MASK) >> + COM1_SHIFT; + u32 temp; + + /* Move back to previous C(-1), C(0), C(+1) and HOLD */ + temp = HOLD; + switch (prev_req_cp1) { + case INCREMENT: + temp |= DECREMENT << COP1_SHIFT; + break; + case DECREMENT: + temp |= INCREMENT << COP1_SHIFT; + break; + } + switch (prev_req_cz0) { + case INCREMENT: + temp |= DECREMENT << COZ0_SHIFT; + break; + case DECREMENT: + temp |= INCREMENT << COZ0_SHIFT; + break; + } + switch (prev_req_cm1) { + case INCREMENT: + temp |= DECREMENT << COM1_SHIFT; + break; + case DECREMENT: + temp |= INCREMENT << COM1_SHIFT; + break; + } + + lane->krln.ld_update = temp; + ld_coef_update(lane); + + /* start the procedure for sending request to move LP back + * to previous setup until LP responds to it + */ + lane->krln.move_back_prev = true; + lane->krln.move_back_cnt = 0; + lane->krln.move_back_lp_status = 0; + if (prev_req_cp1 == HOLD) + lane->krln.move_back_lp_status |= (COEF_UPDATED << COP1_SHIFT); + if (prev_req_cz0 == HOLD) + lane->krln.move_back_lp_status |= (COEF_UPDATED << COZ0_SHIFT); + if (prev_req_cm1 == HOLD) + lane->krln.move_back_lp_status |= (COEF_UPDATED << COM1_SHIFT); +} +EXPORT_SYMBOL(lt_move_lp_back); diff --git a/drivers/net/phy/backplane/link_training.h b/drivers/net/phy/backplane/link_training.h new file mode 100644 index 0000000..fae5f35 --- /dev/null +++ b/drivers/net/phy/backplane/link_training.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */ +/* Link Training (IEEE802.3ap/ba) + * + * Copyright 2019-2020 NXP + */ + +#ifndef __LINK_TRAINING_H +#define __LINK_TRAINING_H + +#include "backplane.h" + +/* Link Training interface with backplane driver */ + +void lt_start(struct lane_device *lane); +void lt_stop(struct lane_device *lane); +void lt_reset(struct lane_device *lane); +void lt_init_ld(struct lane_device *lane); + +void lt_mmd_c45(struct backplane_kr *bpkr); +void lt_mmd_setup(struct backplane_kr *bpkr, int devad, u32 base); + +bool lt_is_rx_trained(struct lane_device *lane); +bool lt_is_training_failure(struct lane_device *lane); +bool lt_is_frame_lock(struct lane_device *lane); + +bool lt_is_lp_rx_ready(struct lane_device *lane); +bool lt_is_ld_rx_ready(struct lane_device *lane); + +void lt_train_remote_tx(struct lane_device *lane); +void lt_train_local_tx(struct lane_device *lane); + +#endif /* __LINK_TRAINING_H */ From patchwork Mon Jun 22 13:35:22 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Florinel Iordache X-Patchwork-Id: 1314388 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming-netdev@ozlabs.org Delivered-To: patchwork-incoming-netdev@ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=vger.kernel.org (client-ip=23.128.96.18; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=pass (p=none dis=none) header.from=nxp.com Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=nxp.com header.i=@nxp.com header.a=rsa-sha256 header.s=selector2 header.b=f+iLbS3A; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by ozlabs.org (Postfix) with ESMTP id 49r9Rc2dkLz9sSS for ; Mon, 22 Jun 2020 23:36:28 +1000 (AEST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1729155AbgFVNgW (ORCPT ); Mon, 22 Jun 2020 09:36:22 -0400 Received: from mail-db8eur05on2055.outbound.protection.outlook.com ([40.107.20.55]:24081 "EHLO EUR05-DB8-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1729103AbgFVNgQ (ORCPT ); Mon, 22 Jun 2020 09:36:16 -0400 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=HS4gDf0+/ynVxooDmKjcUSlOIuNuJuj3qQh+ZdcU8yFW/4w1ujZ6rhsWzgX3fdQPb1V3LtSKfPNxFwy7KRGO9jZ6DJnS9R8u/qx9vhgfA/x8y9Udz6xP5NmmZIFkMe1JMWmcRdK9jxDY6JMozeyJaNfp+52vh4Esj/DzSIhGJwEhWpqEzjTME4pRQqxBSS0eOGW1CSYiLiHly8vSwP5kQSvi460E+3AJvR7FR2J9JqoWQbt7VO6NHpxLW86WANUXLiE2yLarK6xBlxwmgZOEJfqKHkcbQUImZOmnvhdjvzE+6p4UbOSG4FxaPU4ijOnANznQnrz6qTN48gdlyJtQXw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=6Bop+vhFZMti8SjR3m5sEO7m7f3PqLGhBfZKGrfiqI8=; b=ivJ2ZAuL33RvZaRk5aCDoXMS+M7Hz/qGM+OZ1mTpv4mLqABDNdtc3wJgGtMr9tzbr/+iX5Y/FovQECOiUQX7QAQ+Koo+Q5k7C+kbFudRJsYOeCWX+3l0hQmkwBvnkruErp1F07LRcGNMRmHEex7DLLctc6P6bHigps1IVvEEd6vBBsaNKAydR9m1Xd9Zoo4YK+AhtGsjzo+Us1axRhX/WnhbfGn2Pb+1NR+f3ZUm/FUVUnx0vqVl+7K1cU2dCy6dEIpAu1fy5tQnaBpj8zRGzM6z+dJJorJ/+y1S0fSf796kveRpKBRI68pIHevCY2gJNlgCVjTauEjqDW/jGKArfA== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=nxp.com; dmarc=pass action=none header.from=nxp.com; dkim=pass header.d=nxp.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=nxp.com; s=selector2; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=6Bop+vhFZMti8SjR3m5sEO7m7f3PqLGhBfZKGrfiqI8=; b=f+iLbS3ARKF+DwEzD4pJp+vBGNscu6eumclDu74aHr9x50hZHrxnHV+BeasF0mpokxgQS3K66xzxSpTwRsSguVu0aXxUZcqU6AxrWCYBPMBLWDqw0wWRZPHmPLDb7ii2M7F9CXLJXGnSMrKN5kfz1uD64GkMWSvlIqR/vV+Gjt8= Authentication-Results: davemloft.net; dkim=none (message not signed) header.d=none;davemloft.net; dmarc=none action=none header.from=nxp.com; Received: from AM0PR04MB5443.eurprd04.prod.outlook.com (2603:10a6:208:119::33) by AM0PR04MB5075.eurprd04.prod.outlook.com (2603:10a6:208:bf::24) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.3109.22; Mon, 22 Jun 2020 13:35:57 +0000 Received: from AM0PR04MB5443.eurprd04.prod.outlook.com ([fe80::f0b7:8439:3b5a:61bd]) by AM0PR04MB5443.eurprd04.prod.outlook.com ([fe80::f0b7:8439:3b5a:61bd%7]) with mapi id 15.20.3109.027; Mon, 22 Jun 2020 13:35:57 +0000 From: Florinel Iordache To: davem@davemloft.net, netdev@vger.kernel.org, andrew@lunn.ch, f.fainelli@gmail.com, hkallweit1@gmail.com, linux@armlinux.org.uk Cc: devicetree@vger.kernel.org, linux-doc@vger.kernel.org, robh+dt@kernel.org, mark.rutland@arm.com, kuba@kernel.org, corbet@lwn.net, shawnguo@kernel.org, leoyang.li@nxp.com, madalin.bucur@oss.nxp.com, ioana.ciornei@nxp.com, linux-kernel@vger.kernel.org, Florinel Iordache Subject: [PATCH net-next v3 5/7] net: phy: enable qoriq backplane support Date: Mon, 22 Jun 2020 16:35:22 +0300 Message-Id: <1592832924-31733-6-git-send-email-florinel.iordache@nxp.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1592832924-31733-1-git-send-email-florinel.iordache@nxp.com> References: <1592832924-31733-1-git-send-email-florinel.iordache@nxp.com> Reply-to: florinel.iordache@nxp.com X-ClientProxiedBy: AM3PR07CA0086.eurprd07.prod.outlook.com (2603:10a6:207:6::20) To AM0PR04MB5443.eurprd04.prod.outlook.com (2603:10a6:208:119::33) MIME-Version: 1.0 X-MS-Exchange-MessageSentRepresentingType: 1 Received: from fsr-ub1464-128.ea.freescale.net (83.217.231.2) by AM3PR07CA0086.eurprd07.prod.outlook.com (2603:10a6:207:6::20) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256) id 15.20.3131.12 via Frontend Transport; Mon, 22 Jun 2020 13:35:56 +0000 X-Mailer: git-send-email 1.9.1 X-Originating-IP: [83.217.231.2] X-MS-PublicTrafficType: Email X-MS-Office365-Filtering-HT: Tenant X-MS-Office365-Filtering-Correlation-Id: 6676f2ec-0f96-46b3-ffd4-08d816b131be X-MS-TrafficTypeDiagnostic: AM0PR04MB5075: X-MS-Exchange-Transport-Forked: True X-Microsoft-Antispam-PRVS: X-MS-Oob-TLC-OOBClassifiers: OLM:660; X-Forefront-PRVS: 0442E569BC X-MS-Exchange-SenderADCheck: 1 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: TXAvmFUvdjtsZrLcj+6rAFt3c5XfsprHUwum2UMUGvTs0PnTkq0GtWqjwTKbxC1ltLjQV9m2j/nOWjCxas/9dIlevlPrpWzXw/IwFUq2EqaDRUP/ePxWu210yqc395pYZAAyZ4upHgjkVPFhMNuDzScxeca20P7OyiWi61a90SA7VEFDW97CLb6oG1058zCVh06wRggHBsqEP5IBQnjUWqaupVRvPygN2JwiN5W8qiUPdMMbd5jyVtjBnTUsKjcmCeauT35hoegIVNUejDLVvO9EIWFJ7HUgUbDHUT5gf2nE3LAIaFiDy0A9iZO1j/tu X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:AM0PR04MB5443.eurprd04.prod.outlook.com;PTR:;CAT:NONE;SFTY:;SFS:(4636009)(136003)(376002)(346002)(39860400002)(366004)(396003)(66476007)(66556008)(66946007)(83380400001)(5660300002)(2616005)(30864003)(956004)(44832011)(86362001)(6666004)(2906002)(8676002)(6512007)(52116002)(16526019)(186003)(26005)(3450700001)(6506007)(4326008)(6486002)(316002)(478600001)(8936002)(7416002)(36756003);DIR:OUT;SFP:1101; X-MS-Exchange-AntiSpam-MessageData: PIm69d1hgYvcjdDyFN6fyk0xlgpERIFo0VzdTcKws8un66pzPZBtOVxfqUpSRRTqWNoyk+fOJcG03dg2qnwqO8jA03WbzYRnSzNt8CLBqSEpXDYwPrM7KPs2OoFSKR326rvOTK9BTbciUIxjv/YpA2TGelJPOvlfpL/h0RndmolJUJdyHuJsiU5XKICZd0sQhG6Q0WdcP5BjCWK7zX/eHxQU5vfKfeKG6U9TWS3GBuPurJhVdQUubOUOFHK8qfal+HWsEDZLgEyIMZpDwldJB/03Y+jSB/cKaqhRBlgs0kRFUjaG2fBP4V8QvyaZAbGoJR+pNjdAz5JtzgQW+J4feR1X6vJfPjmiR9PqgoN775wNmUgtLdAHGVn2600fEZHtbHGSNswHdgddzQZfxVSXCWsSdm/Fa4RbFDn5OtbSSwJmlFs/37sOPD2l5Fn8X29qgr6Cs1WI2o85Tdp40Xiai2Pb447V7SRPLt9rWlHhXQQ= X-OriginatorOrg: nxp.com X-MS-Exchange-CrossTenant-Network-Message-Id: 6676f2ec-0f96-46b3-ffd4-08d816b131be X-MS-Exchange-CrossTenant-OriginalArrivalTime: 22 Jun 2020 13:35:57.4597 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 686ea1d3-bc2b-4c6f-a92c-d99c5c301635 X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: 7bHJnJpyjmzT0WRHGeiu2iUbHYblULIgg2fdSPLdy/fbInYZHN4LDRxG0//Im4xx/KqwwxOinSq8adR2oifLDvSjnz77Wo+Wi6aiB/joFBA= X-MS-Exchange-Transport-CrossTenantHeadersStamped: AM0PR04MB5075 Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Enable backplane support for qoriq family of devices Signed-off-by: Florinel Iordache Reported-by: kernel test robot Reported-by: kernel test robot --- drivers/net/phy/backplane/Kconfig | 11 +- drivers/net/phy/backplane/Makefile | 2 + drivers/net/phy/backplane/qoriq_backplane.c | 473 ++++++++++++++++++++++++++ drivers/net/phy/backplane/qoriq_backplane.h | 42 +++ drivers/net/phy/backplane/qoriq_serdes_10g.c | 486 +++++++++++++++++++++++++++ 5 files changed, 1013 insertions(+), 1 deletion(-) create mode 100644 drivers/net/phy/backplane/qoriq_backplane.c create mode 100644 drivers/net/phy/backplane/qoriq_backplane.h create mode 100644 drivers/net/phy/backplane/qoriq_serdes_10g.c diff --git a/drivers/net/phy/backplane/Kconfig b/drivers/net/phy/backplane/Kconfig index 9ec54b5..3e20a78 100644 --- a/drivers/net/phy/backplane/Kconfig +++ b/drivers/net/phy/backplane/Kconfig @@ -17,4 +17,13 @@ config ETH_BACKPLANE_FIXED This module provides a driver to setup fixed user configurable coefficient values for backplanes equalization. This means No Equalization algorithm is used to adapt the initial coefficients - initially set by the user. \ No newline at end of file + initially set by the user. + +config ETH_BACKPLANE_QORIQ + tristate "QorIQ Ethernet Backplane driver" + depends on ETH_BACKPLANE + help + This module provides a driver for Ethernet Operation over + Electrical Backplanes enabled for QorIQ family of devices. + This driver is using the services provided by the generic + backplane and link training modules. \ No newline at end of file diff --git a/drivers/net/phy/backplane/Makefile b/drivers/net/phy/backplane/Makefile index ded6f2d..3ae3d8b 100644 --- a/drivers/net/phy/backplane/Makefile +++ b/drivers/net/phy/backplane/Makefile @@ -5,5 +5,7 @@ obj-$(CONFIG_ETH_BACKPLANE) += eth_backplane.o obj-$(CONFIG_ETH_BACKPLANE_FIXED) += eq_fixed.o +obj-$(CONFIG_ETH_BACKPLANE_QORIQ) += eth_backplane_qoriq.o eth_backplane-objs := backplane.o link_training.o +eth_backplane_qoriq-objs := qoriq_backplane.o qoriq_serdes_10g.o diff --git a/drivers/net/phy/backplane/qoriq_backplane.c b/drivers/net/phy/backplane/qoriq_backplane.c new file mode 100644 index 0000000..eb22e02 --- /dev/null +++ b/drivers/net/phy/backplane/qoriq_backplane.c @@ -0,0 +1,473 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* QorIQ Backplane driver + * + * Copyright 2015 Freescale Semiconductor, Inc. + * Copyright 2018-2020 NXP + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "qoriq_backplane.h" + +/* QorIQ Backplane Driver name */ +#define QORIQ_BACKPLANE_DRIVER_NAME "backplane_qoriq" + +/* QorIQ Backplane Driver version */ +#define QORIQ_BACKPLANE_DRIVER_VERSION "1.0.0" + +/* PCS Device Identifier */ +#define PCS_PHY_DEVICE_ID 0x0083e400 +#define PCS_PHY_DEVICE_ID_MASK 0xffffffff + +/* Max/Min coefficients range values */ +#define PRE_COEF_MAX 0x0 +#define PRE_COEF_MIN 0x8 +#define POST_COEF_MAX 0x0 +#define POST_COEF_MIN 0x10 +#define ZERO_COEF_MIN 0x1A +#define ZERO_COEF_MAX 0x30 + +/* Coefficients sum ratio: (their sum divided by their difference) */ +#define COEF_SUM_RATIO_NUMERATOR 17 +#define COEF_SUM_RATIO_DENOMINATOR 4 + +/* Number of equalization custom parameters */ +#define EQ_PARAMS_NO 1 + +/* Serdes types supported by QorIQ devices */ +enum serdes_type { + SERDES_10G, + SERDES_INVAL +}; + +static void an_advertisement_init(struct lane_device *lane) +{ + struct backplane_device *bpdev = lane->bpdev; + struct phy_device *phydev = lane->phydev; + struct qoriq_driver *qoriq_drv; + u32 init_an_adv1; + int err; + + qoriq_drv = (struct qoriq_driver *)bpdev->drv.priv; + init_an_adv1 = backplane_get_an_adv1_init(phydev->interface); + + err = backplane_write_mmd(lane, MDIO_MMD_AN, qoriq_drv->an_adv1, + init_an_adv1); + if (err) + phydev_err(phydev, + "Setting AN register 0x%02x on lane %d failed with error code: 0x%08x\n", + qoriq_drv->an_adv1, lane->idx, err); +} + +static bool is_an_link_detected(struct lane_device *lane) +{ + struct backplane_device *bpdev = lane->bpdev; + struct phy_device *phydev = lane->phydev; + struct qoriq_driver *qoriq_drv; + struct lane_device *masterln; + u32 an_bp_eth_status; + int an_state; + u32 an_mask; + + qoriq_drv = (struct qoriq_driver *)bpdev->drv.priv; + an_bp_eth_status = qoriq_drv->an_bp_eth_status; + + /* Check AN state only on Master Lane */ + masterln = &bpdev->lane[MASTER_LANE]; + + /* The link training occurs after auto-negotiation + * has determined the link to be a Base-KR link. + * This is indicated by asserting the corresponding + * technology bit within the BP_ETH_STATUS register. + * Note that this occurs before auto-negotiation can declare + * auto-negotiation complete, + * as this requires the PCS to report a valid link. + */ + an_mask = backplane_get_an_bp_eth_status_bit(phydev->interface); + an_state = backplane_read_mmd(masterln, MDIO_MMD_AN, an_bp_eth_status); + + return (an_state & an_mask); +} + +static void qoriq_setup_default_settings(struct lane_device *lane) +{ + const struct lane_ops *lane_ops = lane->bpdev->drv.lane_ops; + struct qoriq_lane_ops *qoriq_lane_ops; + u32 def_amp_red; + + qoriq_lane_ops = (struct qoriq_lane_ops *)lane_ops->priv; + + if (lane->bpdev->bpkr.valid_eq_params) + def_amp_red = lane->bpdev->bpkr.def_kr.dev_coef[IDX_AMP_RED]; + else + def_amp_red = qoriq_lane_ops->read_amp_red(lane->reg_base); + + lane->krln.def_kr.dev_coef[IDX_AMP_RED] = def_amp_red; +} + +/* LT HW restrictions: + * Section 5.3.1 10GBaseKR Transmit Adaptive Equalization Control + * additional restrictions set down by 802.3 specification Clause 72, + * specifically 72.7.1.11 Transmitter output waveform requirements + * + * Maintaining the following relationships limit transmit equalization + * to reasonable levels compliant with the KR specification + */ +static int lt_qoriq_validation(struct lane_device *lane, u32 *ld_coef) +{ + struct backplane_kr *bpkr = &lane->bpdev->bpkr; + u32 mainq = ld_coef[C_Z0]; + u32 postq = ld_coef[C_P1]; + u32 preq = ld_coef[C_M1]; + + /* Additional HW restrictions: + * 1. MIN_C(0) <= tx_preq + tx_mainq + tx_ratio_post1q <= MAX_C(0) + */ + if ((preq + postq + mainq) < bpkr->min_kr.mainq) + return -ERANGE; + if ((preq + postq + mainq) > bpkr->max_kr.mainq) + return -ERANGE; + + /* 2. + * ( tx_mainq + tx_preq + tx_ratio_post1q ) / + * ( tx_mainq - tx_preq - tx_ratio_post1q ) < + * coef_sum_ratio_numerator / coef_sum_ratio_denominator + */ + if (((mainq + preq + postq) * COEF_SUM_RATIO_DENOMINATOR) >= + ((mainq - preq - postq) * COEF_SUM_RATIO_NUMERATOR)) + return -ERANGE; + + return 0; +} + +enum serdes_type get_serdes_type(struct device_node *serdes_node) +{ + enum serdes_type serdes = SERDES_INVAL; + const char *serdes_comp; + int comp_no, i, ret; + + comp_no = of_property_count_strings(serdes_node, "compatible"); + for (i = 0; i < comp_no; i++) { + ret = of_property_read_string_index(serdes_node, "compatible", + i, &serdes_comp); + if (ret == 0) { + if (!strcasecmp(serdes_comp, "serdes-10g")) { + serdes = SERDES_10G; + break; + } + } + } + + return serdes; +} + +/* install QorIQ specific backplane callbacks: + * for AN start/decoding, hw specific defaults and lt validation + */ +static const struct backplane_ops qoriq_ops = { + .an_advertisement_init = an_advertisement_init, + .is_an_link_detected = is_an_link_detected, + .setup_default_settings = qoriq_setup_default_settings, + .lt_validation = lt_qoriq_validation, +}; + +/* qoriq_backplane_probe + * + * Probe function for QorIQ backplane driver to provide QorIQ device specific + * behavior + * + * phydev: backplane phy device + * this is an internal phy block controlled by the software + * which contains other component blocks like: PMA/PMD, PCS, AN + * + * Return: Zero for success or error code in case of failure + */ +static int qoriq_backplane_probe(struct phy_device *phydev) +{ + pr_info_once("%s: QorIQ Backplane driver version %s\n", + QORIQ_BACKPLANE_DRIVER_NAME, + QORIQ_BACKPLANE_DRIVER_VERSION); + + /* call generic driver probe */ + return backplane_probe(phydev); +} + +/* qoriq_backplane_config_init + * + * Config_Init function for QorIQ devices to provide QorIQ specific behavior + * + * phydev: backplane phy device + * + * Return: Zero for success or error code in case of failure + */ +static int qoriq_backplane_config_init(struct phy_device *phydev) +{ + struct device_node *dev_node, *serdes_node, *lane_node; + const struct equalizer_device *qoriq_equalizer = NULL; + struct backplane_device *bpdev = phydev->priv; + struct qoriq_lane_ops *qoriq_lane_ops = NULL; + const struct qoriq_driver *qoriq_drv = NULL; + const struct lane_ops *lane_ops = NULL; + enum serdes_type serdes = SERDES_INVAL; + u32 eqparams[EQ_PARAMS_NO]; + struct resource res; + int proplen; + int i, ret; + + dev_node = phydev->mdio.dev.of_node; + if (!dev_node) { + phydev_err(phydev, "No associated device tree node\n"); + return -EINVAL; + } + if (!bpdev) { + phydev_err(phydev, "Backplane phy info is not allocated\n"); + return -EINVAL; + } + + if (!backplane_is_valid_mode(phydev->interface)) + return -EINVAL; + + /* call generic driver parse DT */ + ret = backplane_parse_dt(phydev); + if (ret) + return ret; + + bpdev->num_lanes = backplane_num_lanes(phydev->interface); + if (bpdev->num_lanes > MAX_KR_LANES_PER_PHY) { + phydev_err(phydev, "Unsupported number of lanes per phy: %d\n", + bpdev->num_lanes); + return -EINVAL; + } + + proplen = of_property_count_u32_elems(dev_node, "lane-handle"); + if (proplen < bpdev->num_lanes) { + phydev_err(phydev, "Unspecified lane handles\n"); + return -EINVAL; + } + serdes_node = NULL; + for (i = 0; i < bpdev->num_lanes; i++) { + lane_node = of_parse_phandle(dev_node, "lane-handle", i); + if (!lane_node) { + phydev_err(phydev, "parse lane-handle failed\n"); + return -EINVAL; + } + if (i == 0) + serdes_node = lane_node->parent; + ret = of_address_to_resource(lane_node, 0, &res); + if (ret) { + phydev_err(phydev, + "could not obtain lane memory map for index=%d, ret = %d\n", + i, ret); + return ret; + } + /* setup lane address */ + bpdev->lane[i].lane_addr = res.start; + + of_node_put(lane_node); + } + if (!serdes_node) { + phydev_err(phydev, "serdes node not found\n"); + return -EINVAL; + } + bpdev->drv.is_little_endian = of_property_read_bool(serdes_node, + "little-endian"); + + ret = of_address_to_resource(serdes_node, 0, &res); + if (ret) { + phydev_err(phydev, + "could not obtain serdes memory map, ret = %d\n", + ret); + return ret; + } + bpdev->drv.base_addr = res.start; + bpdev->drv.memmap_size = res.end - res.start + 1; + + serdes = get_serdes_type(serdes_node); + if (serdes == SERDES_INVAL) { + phydev_err(phydev, "Unknown serdes-type\n"); + return 0; + } + + /* if eq-params node exists then use the DTS specified values + * if eq-params node doesn't exist then use values already found in HW + * eq-params is a custom node and variable in size + */ + proplen = of_property_count_u32_elems(dev_node, "eq-params"); + if (proplen > 0) { + /* we use only 1 custom coefficient tap: amp_red */ + if (proplen > EQ_PARAMS_NO) + proplen = EQ_PARAMS_NO; + ret = of_property_read_u32_array(dev_node, "eq-params", + (u32 *)eqparams, proplen); + if (ret == 0) { + bpdev->bpkr.valid_eq_params = true; + bpdev->bpkr.def_kr.dev_coef[IDX_AMP_RED] = + eqparams[IDX_AMP_RED]; + } + } + + /* call generic driver setup memio after reading serdes endianness */ + ret = backplane_setup_memio(phydev); + if (ret) + return ret; + + /* call generic driver setup mmd */ + ret = backplane_setup_mmd(phydev); + if (ret) + return ret; + + /* override default mdio setup and get qoriq specific info */ + switch (serdes) { + case SERDES_10G: + lane_ops = qoriq_get_lane_ops_10g(); + qoriq_drv = get_qoriq_driver_10g(); + qoriq_setup_mem_io_10g(bpdev->drv.io); + qoriq_equalizer = qoriq_get_equalizer_10g(); + break; + default: + phydev_err(phydev, "Serdes type not supported\n"); + return -EINVAL; + } + if (!lane_ops) { + phydev_err(phydev, "Lane ops not available\n"); + return -EINVAL; + } + if (!qoriq_drv) { + phydev_err(phydev, "Qoriq driver not available\n"); + return -EINVAL; + } + if (!qoriq_equalizer) { + phydev_err(phydev, "Qoriq Equalizer not available\n"); + return -EINVAL; + } + + /* setup ops and equalizer */ + bpdev->drv.lane_ops = lane_ops; + bpdev->drv.bp_ops = qoriq_ops; + bpdev->drv.priv = (void *)qoriq_drv; + bpdev->bpkr.equalizer = qoriq_equalizer; + qoriq_lane_ops = (struct qoriq_lane_ops *)lane_ops->priv; + + if (!qoriq_lane_ops) { + phydev_err(phydev, "QorIQ lane ops not available\n"); + return -EINVAL; + } + + /* setup KR LT MMD registers space */ + backplane_kr_lt_mmd_setup(&bpdev->bpkr, qoriq_drv->kr_lt_devad, + qoriq_drv->kr_lt_base); + + if (backplane_is_mode_kr(phydev->interface)) { + /* setup kr coefficients limits */ + bpdev->bpkr.min_kr.preq = PRE_COEF_MIN; + bpdev->bpkr.max_kr.preq = PRE_COEF_MAX; + bpdev->bpkr.min_kr.mainq = ZERO_COEF_MIN; + bpdev->bpkr.max_kr.mainq = ZERO_COEF_MAX; + bpdev->bpkr.min_kr.postq = POST_COEF_MIN; + bpdev->bpkr.max_kr.postq = POST_COEF_MAX; + } + + /* call generic driver setup lanes */ + ret = backplane_setup_lanes(phydev); + if (ret) + return ret; + + /* call generic driver initialize + * start the lane timers used to run the algorithm + */ + ret = backplane_initialize(phydev); + if (ret) + return ret; + + return 0; +} + +static int qoriq_backplane_match_phy_device(struct phy_device *phydev) +{ + struct device_node *dev_node, *serdes_node, *lane_node; + enum serdes_type serdes = SERDES_INVAL; + int i; + + if (!phydev->mdio.dev.of_node) + return 0; + + if (!phydev->is_c45) + return 0; + + dev_node = phydev->mdio.dev.of_node; + if (!dev_node) { + phydev_err(phydev, "No associated device tree node\n"); + return 0; + } + + /* Get Master lane node */ + lane_node = of_parse_phandle(dev_node, "lane-handle", 0); + if (!lane_node) + return 0; + serdes_node = lane_node->parent; + of_node_put(lane_node); + if (!serdes_node) + return 0; + + serdes = get_serdes_type(serdes_node); + + switch (serdes) { + case SERDES_10G: + /* On LS devices we must find the c45 device with correct PHY ID + * Implementation similar with the one existent in phy_device: + * @function: phy_bus_match + */ + for (i = 1; i < ARRAY_SIZE(phydev->c45_ids.device_ids); i++) { + if (!(phydev->c45_ids.devices_in_package & (1 << i))) + continue; + + if ((PCS_PHY_DEVICE_ID & PCS_PHY_DEVICE_ID_MASK) == + (phydev->c45_ids.device_ids[i] & + PCS_PHY_DEVICE_ID_MASK)) + return 1; + } + break; + default: + phydev_err(phydev, "Unknown serdes-type\n"); + return 0; + } + return 0; +} + +static struct phy_driver qoriq_backplane_driver[] = { + { + .phy_id = PCS_PHY_DEVICE_ID, + .name = QORIQ_BACKPLANE_DRIVER_NAME, + .phy_id_mask = PCS_PHY_DEVICE_ID_MASK, + .features = BACKPLANE_FEATURES, + .probe = qoriq_backplane_probe, + .remove = backplane_remove, + .config_init = qoriq_backplane_config_init, + .aneg_done = backplane_aneg_done, + .config_aneg = backplane_config_aneg, + .read_status = backplane_read_status, + .suspend = backplane_suspend, + .resume = backplane_resume, + .match_phy_device = qoriq_backplane_match_phy_device, + }, +}; + +module_phy_driver(qoriq_backplane_driver); + +static struct mdio_device_id __maybe_unused qoriq_backplane_tbl[] = { + { PCS_PHY_DEVICE_ID, PCS_PHY_DEVICE_ID_MASK }, + { } +}; + +MODULE_DEVICE_TABLE(mdio, qoriq_backplane_tbl); + +MODULE_DESCRIPTION("QorIQ Backplane driver"); +MODULE_AUTHOR("Florinel Iordache "); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/phy/backplane/qoriq_backplane.h b/drivers/net/phy/backplane/qoriq_backplane.h new file mode 100644 index 0000000..09a3600 --- /dev/null +++ b/drivers/net/phy/backplane/qoriq_backplane.h @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */ +/* QorIQ Backplane driver + * + * Copyright 2018-2020 NXP + */ + +#ifndef __QORIQ_BACKPLANE_H +#define __QORIQ_BACKPLANE_H + +#include "backplane.h" + +/* Bins thresholds */ +#define QORIQ_BIN_M1_THRESHOLD 3 +#define QORIQ_BIN_LONG_THRESHOLD 2 + +/* Index of custom parameter: AMP_RED (amplitude reduction) */ +#define IDX_AMP_RED 0 + +struct qoriq_lane_ops { + u32 (*read_tecr0)(void __iomem *reg); + u32 (*read_tecr1)(void __iomem *reg); + u32 (*read_amp_red)(void __iomem *reg); +}; + +struct qoriq_driver { + /* KR LT MMD registers */ + int kr_lt_devad; + u32 kr_lt_base; + /* KR AN MMD registers */ + u32 an_adv1; + u32 an_bp_eth_status; +}; + +const struct lane_ops *qoriq_get_lane_ops_10g(void); + +const struct equalizer_device *qoriq_get_equalizer_10g(void); + +const struct qoriq_driver *get_qoriq_driver_10g(void); + +void qoriq_setup_mem_io_10g(struct mem_io memio); + +#endif /* __QORIQ_BACKPLANE_H */ diff --git a/drivers/net/phy/backplane/qoriq_serdes_10g.c b/drivers/net/phy/backplane/qoriq_serdes_10g.c new file mode 100644 index 0000000..e4e5991 --- /dev/null +++ b/drivers/net/phy/backplane/qoriq_serdes_10g.c @@ -0,0 +1,486 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* QorIQ Backplane driver for SerDes 10G + * + * Copyright 2018-2020 NXP + */ + +#include +#include + +#include "qoriq_backplane.h" + +#define EQUALIZER_NAME "qoriq_serdes_10g" +#define EQUALIZER_VERSION "1.0.0" + +#define BIN_1_SEL 0x00000000 +#define BIN_2_SEL 0x00010000 +#define BIN_3_SEL 0x00020000 +#define BIN_OFFSET_SEL 0x00030000 +#define BIN_BLW_SEL 0x00040000 +#define BIN_AVG_SEL 0x00050000 +#define BIN_M1_SEL 0x00060000 +#define BIN_LONG_SEL 0x00070000 +#define CDR_SEL_MASK 0x00070000 + +#define RATIO_PREQ_SHIFT 22 +#define RATIO_PST1Q_SHIFT 16 +#define ADPT_EQ_SHIFT 8 +#define AMP_RED_SHIFT 0 + +#define RATIO_PREQ_MASK 0x03c00000 +#define RATIO_PST1Q_MASK 0x001f0000 +#define ADPT_EQ_MASK 0x00003f00 +#define AMP_RED_MASK 0x0000003f + +#define TECR0_INIT 0x24200000 + +#define GCR0_RESET_MASK 0x00600000 +#define GCR0_TRST_MASK 0x00200000 +#define GCR0_RRST_MASK 0x00400000 + +#define GCR1_SNP_START_MASK 0x00000040 +#define GCR1_CTL_SNP_START_MASK 0x00002000 + +#define RECR1_CTL_SNP_DONE_MASK 0x00000002 +#define RECR1_SNP_DONE_MASK 0x00000004 +#define TCSR1_SNP_DATA_MASK 0x00007fc0 +#define TCSR1_SNP_DATA_SHIFT 6 +#define TCSR1_EQ_SNPBIN_SIGN_MASK 0x100 + +#define TCSR3_CDR_LCK_MASK 0x08000000 + +#define RECR1_GAINK2_MASK 0x0f000000 +#define RECR1_GAINK2_SHIFT 24 + +#define RECR1_GAINK3_MASK 0x000f0000 +#define RECR1_GAINK3_SHIFT 16 + +#define RECR1_EQ_OFFSET_MASK 0x00001f80 +#define RECR1_EQ_OFFSET_SHIFT 7 + +/* AN advertisement register 7.17 */ +#define AN_AD_ABILITY_1 0x11 + +/* Backplane Ethernet status (Register 7.48) */ +#define AN_BP_ETH_STATUS_OFFSET 0x30 + +/* KR PMD control register (Register 1.150) */ +#define KR_PMD_BASE_OFFSET 0x96 + +/* Bin snapshots thresholds range */ +#define EQ_BIN_MIN -256 +#define EQ_BIN_MAX 255 +/* Bin snapshots average thresholds range */ +#define EQ_BIN_SNP_AV_THR_LOW -150 +#define EQ_BIN_SNP_AV_THR_HIGH 150 + +#define EQ_GAINK_MIN 0xF +#define EQ_GAINK_MAX 0x0 +#define EQ_GAINK_MIDRANGE_LOW 0xE +#define EQ_GAINK_MIDRANGE_HIGH 0x1 + +#define EQ_OFFSET_MIN 0 +#define EQ_OFFSET_MAX 0x3F +#define EQ_OFFSET_MIDRANGE_LOW 0x10 +#define EQ_OFFSET_MIDRANGE_HIGH 0x2F + +#define MEMORY_MAP_SIZE 0x40 + +struct qoriq_lane_regs { + u32 gcr0; /* 0x00: General Control Register 0 */ + u32 gcr1; /* 0x04: General Control Register 1 */ + u32 gcr2; /* 0x08: General Control Register 2 */ + u32 res_0c; /* 0x0C: Reserved */ + u32 recr0; /* 0x10: Receive Equalization Control Register 0 */ + u32 recr1; /* 0x14: Receive Equalization Control Register 1 */ + u32 tecr0; /* 0x18: Transmit Equalization Control Register 0 */ + u32 res_1c; /* 0x1C: Reserved */ + u32 tlcr0; /* 0x20: TTL Control Register 0 */ + u32 tlcr1; /* 0x24: TTL Control Register 1 */ + u32 tlcr2; /* 0x28: TTL Control Register 2 */ + u32 tlcr3; /* 0x2C: TTL Control Register 3 */ + u32 tcsr0; /* 0x30: Test Control/Status Register 0 */ + u32 tcsr1; /* 0x34: Test Control/Status Register 1 */ + u32 tcsr2; /* 0x38: Test Control/Status Register 2 */ + u32 tcsr3; /* 0x3C: Test Control/Status Register 3 */ +}; + +static struct mem_io io; + +static void reset_lane(void __iomem *reg, enum lane_req ln_req) +{ + struct qoriq_lane_regs __iomem *reg_base = reg; + + /* reset Tx lane: send reset request */ + if (ln_req | LANE_TX) { + io.write32(io.read32(®_base->gcr0) & ~GCR0_TRST_MASK, + ®_base->gcr0); + } + /* reset Rx lane: send reset request */ + if (ln_req | LANE_RX) { + io.write32(io.read32(®_base->gcr0) & ~GCR0_RRST_MASK, + ®_base->gcr0); + } + /* unreset the lane */ + if (ln_req != LANE_INVALID) { + udelay(1); + io.write32(io.read32(®_base->gcr0) | GCR0_RESET_MASK, + ®_base->gcr0); + udelay(1); + } +} + +static u32 read_tecr0(void __iomem *reg) +{ + struct qoriq_lane_regs __iomem *reg_base = reg; + + return io.read32(®_base->tecr0); +} + +static u32 read_tecr1(void __iomem *reg) +{ + return 0; +} + +static u32 read_amp_red(void __iomem *reg) +{ + struct qoriq_lane_regs __iomem *reg_base = reg; + u32 val, amp_red; + + val = io.read32(®_base->tecr0); + amp_red = (val & AMP_RED_MASK) >> AMP_RED_SHIFT; + + return amp_red; +} + +static void read_kr_coef(void __iomem *reg, struct kr_coef *coef) +{ + struct qoriq_lane_regs __iomem *reg_base = reg; + u32 val; + + val = io.read32(®_base->tecr0); + + coef->preq = (val & RATIO_PREQ_MASK) >> RATIO_PREQ_SHIFT; + coef->postq = (val & RATIO_PST1Q_MASK) >> RATIO_PST1Q_SHIFT; + coef->mainq = (val & ADPT_EQ_MASK) >> ADPT_EQ_SHIFT; + coef->dev_coef[IDX_AMP_RED] = (val & AMP_RED_MASK) >> AMP_RED_SHIFT; +} + +static void tune_tecr(void __iomem *reg, struct kr_coef *coef, bool reset) +{ + struct qoriq_lane_regs __iomem *reg_base = reg; + u32 val; + + val = TECR0_INIT | + coef->mainq << ADPT_EQ_SHIFT | + coef->preq << RATIO_PREQ_SHIFT | + coef->postq << RATIO_PST1Q_SHIFT | + coef->dev_coef[IDX_AMP_RED] << AMP_RED_SHIFT; + + if (reset) { + /* reset the lane */ + io.write32(io.read32(®_base->gcr0) & + ~GCR0_RESET_MASK, ®_base->gcr0); + udelay(1); + } + + io.write32(val, ®_base->tecr0); + udelay(1); + + if (reset) { + /* unreset the lane */ + io.write32(io.read32(®_base->gcr0) | GCR0_RESET_MASK, + ®_base->gcr0); + udelay(1); + } +} + +/* collect_gains + * + * reg: serdes registers memory map + * gaink2: High-frequency gain of the equalizer amplifier + * the high-frequency gain of the equalizer amplifier is increased by + * decrementing the value of eq_gaink2 by one + * gaink3: Middle-frequency gain of the equalizer amplifier + * the mid-frequency gain of the equalizer amplifier is increased by + * decrementing the value of eq_gaink3 by one + * osestat: equalization offset status + * the equalizer offset is reduced by decrementing the value of osestat + * size: size of snapshots data collection + */ +static int collect_gains(void __iomem *reg, s16 *gaink2, s16 *gaink3, + s16 *osestat, u8 size) +{ + struct qoriq_lane_regs __iomem *reg_base = reg; + u32 rx_eq_snp; + int timeout; + int i; + + for (i = 0; i < size; i++) { + /* wait RECR1_CTL_SNP_DONE_MASK has cleared */ + timeout = 100; + while (io.read32(®_base->recr1) & + RECR1_CTL_SNP_DONE_MASK) { + udelay(1); + timeout--; + if (timeout == 0) + break; + } + + /* start snapshot */ + io.write32((io.read32(®_base->gcr1) | + GCR1_CTL_SNP_START_MASK), ®_base->gcr1); + + /* wait for SNP done */ + timeout = 100; + while (!(io.read32(®_base->recr1) & + RECR1_CTL_SNP_DONE_MASK)) { + udelay(1); + timeout--; + if (timeout == 0) + break; + } + + /* read and save the snapshot */ + rx_eq_snp = io.read32(®_base->recr1); + + if (gaink2) + gaink2[i] = (u8)((rx_eq_snp & RECR1_GAINK2_MASK) >> + RECR1_GAINK2_SHIFT); + if (gaink3) + gaink3[i] = (u8)((rx_eq_snp & RECR1_GAINK3_MASK) >> + RECR1_GAINK3_SHIFT); + if (osestat) + osestat[i] = (u8)((rx_eq_snp & RECR1_EQ_OFFSET_MASK) >> + RECR1_EQ_OFFSET_SHIFT); + + /* terminate the snapshot by setting GCR1[REQ_CTL_SNP] */ + io.write32((io.read32(®_base->gcr1) & + ~GCR1_CTL_SNP_START_MASK), ®_base->gcr1); + } + return i; +} + +static int collect_eq_status(void __iomem *reg, enum eqc_type type[], + u8 type_no, s16 *counters, u8 size) +{ + s16 *gaink2 = NULL, *gaink3 = NULL, *osestat = NULL; + u8 i; + + for (i = 0; i < type_no; i++) { + switch (type[i]) { + case EQC_GAIN_HF: + gaink2 = counters; + break; + case EQC_GAIN_MF: + gaink3 = counters + size; + break; + case EQC_EQOFFSET: + osestat = counters + 2 * size; + break; + default: + /* invalid type */ + break; + } + } + + return collect_gains(reg, gaink2, gaink3, osestat, size); +} + +static int collect_bin_snapshots(void __iomem *reg, enum eqc_type type, + s16 *bin_counters, u8 bin_size) +{ + struct qoriq_lane_regs __iomem *reg_base = reg; + int bin_snapshot; + int i, timeout; + u32 bin_sel; + + /* calculate TCSR1[CDR_SEL] */ + switch (type) { + case EQC_BIN_1: + bin_sel = BIN_1_SEL; + break; + case EQC_BIN_2: + bin_sel = BIN_2_SEL; + break; + case EQC_BIN_3: + bin_sel = BIN_3_SEL; + break; + case EQC_BIN_LONG: + bin_sel = BIN_LONG_SEL; + break; + case EQC_BIN_M1: + bin_sel = BIN_M1_SEL; + break; + case EQC_BIN_OFFSET: + bin_sel = BIN_OFFSET_SEL; + break; + case EQC_BIN_AVG: + bin_sel = BIN_AVG_SEL; + break; + case EQC_BIN_BLW: + bin_sel = BIN_BLW_SEL; + break; + default: + /* invalid bin type */ + return 0; + } + + for (i = 0; i < bin_size; i++) { + /* wait RECR1_SNP_DONE_MASK has cleared */ + timeout = 100; + while ((io.read32(®_base->recr1) & + RECR1_SNP_DONE_MASK)) { + udelay(1); + timeout--; + if (timeout == 0) + break; + } + + /* set TCSR1[CDR_SEL] */ + io.write32((io.read32(®_base->tcsr1) & + ~CDR_SEL_MASK) | bin_sel, ®_base->tcsr1); + + /* start snapshot */ + io.write32(io.read32(®_base->gcr1) | + GCR1_SNP_START_MASK, ®_base->gcr1); + + /* wait for SNP done */ + timeout = 100; + while (!(io.read32(®_base->recr1) & + RECR1_SNP_DONE_MASK)) { + udelay(1); + timeout--; + if (timeout == 0) + break; + } + + /* read and save the snapshot: + * 2's complement 9 bit long value (-256 to 255) + */ + bin_snapshot = (io.read32(®_base->tcsr1) & + TCSR1_SNP_DATA_MASK) >> TCSR1_SNP_DATA_SHIFT; + if (bin_snapshot & TCSR1_EQ_SNPBIN_SIGN_MASK) { + /* 2's complement 9 bit long negative number */ + bin_snapshot &= ~TCSR1_EQ_SNPBIN_SIGN_MASK; + bin_snapshot -= 256; + } + + /* save collected Bin snapshot */ + bin_counters[i] = (s16)bin_snapshot; + + /* terminate the snapshot by setting GCR1[REQ_CTL_SNP] */ + io.write32(io.read32(®_base->gcr1) & + ~GCR1_SNP_START_MASK, ®_base->gcr1); + } + return i; +} + +static struct eqc_range bin_range = { + .min = EQ_BIN_MIN, + .max = EQ_BIN_MAX, + .mid_low = EQ_BIN_SNP_AV_THR_LOW, + .mid_high = EQ_BIN_SNP_AV_THR_HIGH, +}; + +static struct eqc_range gaink_range = { + .min = EQ_GAINK_MIN, + .max = EQ_GAINK_MAX, + .mid_low = EQ_GAINK_MIDRANGE_LOW, + .mid_high = EQ_GAINK_MIDRANGE_HIGH, +}; + +static struct eqc_range osestat_range = { + .min = EQ_OFFSET_MIN, + .max = EQ_OFFSET_MAX, + .mid_low = EQ_OFFSET_MIDRANGE_LOW, + .mid_high = EQ_OFFSET_MIDRANGE_HIGH, +}; + +static struct eqc_range *get_counter_range(enum eqc_type type) +{ + switch (type) { + case EQC_BIN_1: + case EQC_BIN_2: + case EQC_BIN_3: + case EQC_BIN_LONG: + case EQC_BIN_M1: + case EQC_BIN_OFFSET: + case EQC_BIN_AVG: + case EQC_BIN_BLW: + return &bin_range; + case EQC_GAIN_HF: + case EQC_GAIN_MF: + return &gaink_range; + case EQC_EQOFFSET: + return &osestat_range; + default: + /* invalid counter type */ + return NULL; + } + return NULL; +} + +static bool is_cdr_lock_bit(void __iomem *reg) +{ + struct qoriq_lane_regs __iomem *reg_base = reg; + + if (io.read32(®_base->tcsr3) & TCSR3_CDR_LCK_MASK) + return true; + + return false; +} + +static const struct qoriq_lane_ops qoriq_lane_ops_10g = { + .read_tecr0 = read_tecr0, + .read_tecr1 = read_tecr1, + .read_amp_red = read_amp_red, +}; + +static const struct lane_ops lane_ops_10g = { + .priv = &qoriq_lane_ops_10g, + .memmap_size = MEMORY_MAP_SIZE, + .reset_lane = reset_lane, + .tune_lane_kr = tune_tecr, + .read_lane_kr = read_kr_coef, + .is_cdr_lock = is_cdr_lock_bit, +}; + +const struct lane_ops *qoriq_get_lane_ops_10g(void) +{ + return &lane_ops_10g; +} + +static const struct equalizer_device qoriq_equalizer = { + .name = EQUALIZER_NAME, + .version = EQUALIZER_VERSION, + .ops = { + .collect_counters = collect_bin_snapshots, + .collect_multiple_counters = collect_eq_status, + .get_counter_range = get_counter_range, + }, +}; + +const struct equalizer_device *qoriq_get_equalizer_10g(void) +{ + return &qoriq_equalizer; +} + +static const struct qoriq_driver qoriq_drv = { + /* KR PMD registers */ + .kr_lt_devad = MDIO_MMD_PMAPMD, + .kr_lt_base = KR_PMD_BASE_OFFSET, + /* KR AN registers: IEEE802.3 Clause 45 MMD 7 */ + .an_adv1 = AN_AD_ABILITY_1, + .an_bp_eth_status = AN_BP_ETH_STATUS_OFFSET, +}; + +const struct qoriq_driver *get_qoriq_driver_10g(void) +{ + return &qoriq_drv; +} + +void qoriq_setup_mem_io_10g(struct mem_io memio) +{ + io = memio; +} From patchwork Mon Jun 22 13:35:23 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Florinel Iordache X-Patchwork-Id: 1314391 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming-netdev@ozlabs.org Delivered-To: patchwork-incoming-netdev@ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=vger.kernel.org (client-ip=23.128.96.18; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=pass (p=none dis=none) header.from=nxp.com Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=nxp.com header.i=@nxp.com header.a=rsa-sha256 header.s=selector2 header.b=iArQ6/K6; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by ozlabs.org (Postfix) with ESMTP id 49r9S76f9Vz9sRf for ; Mon, 22 Jun 2020 23:36:55 +1000 (AEST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1729196AbgFVNgg (ORCPT ); Mon, 22 Jun 2020 09:36:36 -0400 Received: from mail-db8eur05on2055.outbound.protection.outlook.com ([40.107.20.55]:24081 "EHLO EUR05-DB8-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1729136AbgFVNgZ (ORCPT ); Mon, 22 Jun 2020 09:36:25 -0400 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=NWKBRnWJ9U1kw8DczYL+XOvuIccjL9vFOsjs2fpzCxGN+TMmuHvjYWt8PUvixJxdbxIZ3ZpKqAuQdbQBfXu5hogmV+Wa0L+uD9V64eBhFvSb706ptpCb8MumDbeup8k/ifeLAcG6dnXfXmpWcwtfEU6576IKwLz9/hlkIcJOsHMLGoHiWQNi/M9q0Spsb4eUbrB7rm/6zti8bw0vc73LI2TrUaZqsjt9ydur+Twgq8WaGk76CTS9FqfHF6LvzckvEBEqBq5lih3uZLRzkTPs4e8qhnSQ2eWW0KFWXpuZUk/+5stfYbXWJEONQk8Eo4WA+MqBr7NcjxIWi1X9lL8O6A== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=X+KaNz24Z1T7odLAQx6h9jLDv+rl8RqlKrsCEM5yaOM=; b=UYXoS0aVrffjmNdak/1JfoeWRo2xFGpYSn/I0yj+fgcGtBWXLULJ/8kv3udbkNhYDxjESNz5l/5u1gSWhe/kLk/2Bh8I2SGl5q9W37Ky/eyw15Iyhz+ctrVXfrHWnJPxm0VlulOi1vkD3EcKoNWkYQTQ7eoN3ljcBZkuw1rDwu76QfVWYhPqXvQeOTNXO02+NM5yf61Cki9ol8/E6R8VSCYMi/+ziH06nSuRYqpdGlnoI4n2k/wsozBiiOHsdNj73h+tzLfd+VbmFKXZ7l19qI7EhNXsJCCRjh7sKOHomJEPA1Dr3c6pHBNnQCqe0Y1rwfG1MmfLuUpXBI7s50fhOQ== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=nxp.com; dmarc=pass action=none header.from=nxp.com; dkim=pass header.d=nxp.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=nxp.com; s=selector2; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=X+KaNz24Z1T7odLAQx6h9jLDv+rl8RqlKrsCEM5yaOM=; b=iArQ6/K6Mrr9CGX5pzoSMIVUPaqLoFMWs+Ta2XPWGScJax2HoY7F90Mok1kg6/iG9J6iWcAeoosrJ//1CVBrc5CKPH2SVVUjjuW9+nre4Tthdsf10/+JhCWJXpAl9400/c61upbz9EslnduWVQE8yADp4P8nvK0gWCp013R4rXM= Authentication-Results: davemloft.net; dkim=none (message not signed) header.d=none;davemloft.net; dmarc=none action=none header.from=nxp.com; Received: from AM0PR04MB5443.eurprd04.prod.outlook.com (2603:10a6:208:119::33) by AM0PR04MB5075.eurprd04.prod.outlook.com (2603:10a6:208:bf::24) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.3109.22; Mon, 22 Jun 2020 13:35:58 +0000 Received: from AM0PR04MB5443.eurprd04.prod.outlook.com ([fe80::f0b7:8439:3b5a:61bd]) by AM0PR04MB5443.eurprd04.prod.outlook.com ([fe80::f0b7:8439:3b5a:61bd%7]) with mapi id 15.20.3109.027; Mon, 22 Jun 2020 13:35:58 +0000 From: Florinel Iordache To: davem@davemloft.net, netdev@vger.kernel.org, andrew@lunn.ch, f.fainelli@gmail.com, hkallweit1@gmail.com, linux@armlinux.org.uk Cc: devicetree@vger.kernel.org, linux-doc@vger.kernel.org, robh+dt@kernel.org, mark.rutland@arm.com, kuba@kernel.org, corbet@lwn.net, shawnguo@kernel.org, leoyang.li@nxp.com, madalin.bucur@oss.nxp.com, ioana.ciornei@nxp.com, linux-kernel@vger.kernel.org, Florinel Iordache Subject: [PATCH net-next v3 6/7] net: phy: add bee algorithm for kr training Date: Mon, 22 Jun 2020 16:35:23 +0300 Message-Id: <1592832924-31733-7-git-send-email-florinel.iordache@nxp.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1592832924-31733-1-git-send-email-florinel.iordache@nxp.com> References: <1592832924-31733-1-git-send-email-florinel.iordache@nxp.com> Reply-to: florinel.iordache@nxp.com X-ClientProxiedBy: AM3PR07CA0086.eurprd07.prod.outlook.com (2603:10a6:207:6::20) To AM0PR04MB5443.eurprd04.prod.outlook.com (2603:10a6:208:119::33) MIME-Version: 1.0 X-MS-Exchange-MessageSentRepresentingType: 1 Received: from fsr-ub1464-128.ea.freescale.net (83.217.231.2) by AM3PR07CA0086.eurprd07.prod.outlook.com (2603:10a6:207:6::20) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256) id 15.20.3131.12 via Frontend Transport; Mon, 22 Jun 2020 13:35:57 +0000 X-Mailer: git-send-email 1.9.1 X-Originating-IP: [83.217.231.2] X-MS-PublicTrafficType: Email X-MS-Office365-Filtering-HT: Tenant X-MS-Office365-Filtering-Correlation-Id: e3ed5271-106b-4221-0447-08d816b13274 X-MS-TrafficTypeDiagnostic: AM0PR04MB5075: X-MS-Exchange-Transport-Forked: True X-Microsoft-Antispam-PRVS: X-MS-Oob-TLC-OOBClassifiers: OLM:299; X-Forefront-PRVS: 0442E569BC X-MS-Exchange-SenderADCheck: 1 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: 0BxRfJH0gTd291Dttr9XOFYdPfxNC095gERO6tZivFpRRWjzHIWfXd9iHcqqJm1QXGlQhC1JwybTBLOyhw9wAyP9fTz28aJfNSQapmPFaN2w/3BN2cOTFNnaKXGkJMGDLrJXSOekpMkCk5yOs9oZWLUbiarCp8vIwk5pfZgXEqUfxcQy6vZyKXKhBOtpO7tGZOI5FLwPKw64Q6dFKL5b4sAu+v52uBRSbPIzrI7VqhhIB8e2lpR6a/5XbUICYVO0tBcGxUd6BKzDvg1t+G0DcCA706tj6hHqmn0DQMjRklvzLfHMUmxFiHY18VPRfbfLiTGRb3D6S+sWeSXWidCkTg== X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:AM0PR04MB5443.eurprd04.prod.outlook.com;PTR:;CAT:NONE;SFTY:;SFS:(4636009)(136003)(376002)(346002)(39860400002)(366004)(396003)(66476007)(66556008)(66946007)(83380400001)(5660300002)(2616005)(30864003)(956004)(44832011)(86362001)(6666004)(2906002)(8676002)(6512007)(52116002)(16526019)(186003)(26005)(3450700001)(6506007)(4326008)(6486002)(316002)(478600001)(8936002)(7416002)(36756003);DIR:OUT;SFP:1101; X-MS-Exchange-AntiSpam-MessageData: 8c4wmreMxASRhdiA6tAX1ZwQXFT2Zad6eKLktX0Xe9WvtODnERqosmJOzLBMapfcQ5kb9eAJs/hh1IwGc1jpyyd1k8KZK7LgO6As2v0oXy9c8DI7n4yNhx5VX6iBydk/ft3pTghpPW4QAYvxkJ6LrxYrPidQ71C+25HyaZbCPoHlAcZhPXli2d7VxzuozFg9dNkHr/MST1BR9OR22IG7S/28AWFpkBlxfPLzotH2sIVvrY17F8/pO3BCZmjgG1dUvWmovHkINpxEIyNb919/8vnMy5hr85MWnVrDHi5XhWz4JeMjdDq+WSs7c7boTkYJPJEGHHiML8c2uyurDre3gIG1V1TRfoUxwARm9ws40P35v+z7xItpT4p+VXfqfGA1f0oB+zRyj04qCWR9mmIBjWNwrDYpZlcnac3nmOIE/0FGHtc2Wt9V11OJhojyMHlxBI4DCL0CqCh1DFEfEEJBnX/IvViChFe8Iklf2pqSQsk= X-OriginatorOrg: nxp.com X-MS-Exchange-CrossTenant-Network-Message-Id: e3ed5271-106b-4221-0447-08d816b13274 X-MS-Exchange-CrossTenant-OriginalArrivalTime: 22 Jun 2020 13:35:58.6000 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 686ea1d3-bc2b-4c6f-a92c-d99c5c301635 X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: YdfJmG18tGz9eSTfgjdDdlDZKTXz0tV7BS6z1OXAqYwwOD975x/CvG+BtkLy4l381mHrKWQr++jRlmeveK42tOC6LJwGV46aFJjHA+qCQ8o= X-MS-Exchange-Transport-CrossTenantHeadersStamped: AM0PR04MB5075 Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Add support for bee equalization algorithm used by kr training: 3-Taps Bit Edge Equalization (BEE) algorithm Signed-off-by: Florinel Iordache --- drivers/net/phy/backplane/Kconfig | 11 + drivers/net/phy/backplane/Makefile | 1 + drivers/net/phy/backplane/eq_bee.c | 1076 ++++++++++++++++++++++++++++++++++++ 3 files changed, 1088 insertions(+) create mode 100644 drivers/net/phy/backplane/eq_bee.c diff --git a/drivers/net/phy/backplane/Kconfig b/drivers/net/phy/backplane/Kconfig index 3e20a78..ee5cf1c 100644 --- a/drivers/net/phy/backplane/Kconfig +++ b/drivers/net/phy/backplane/Kconfig @@ -19,6 +19,17 @@ config ETH_BACKPLANE_FIXED No Equalization algorithm is used to adapt the initial coefficients initially set by the user. +config ETH_BACKPLANE_BEE + tristate "3-Taps Bit Edge Equalization (BEE) algorithm" + depends on ETH_BACKPLANE + help + This module provides a driver for BEE algorithm: 3-Taps + Bit Edge Equalization. This algorithm is using a method + based on 3-taps coefficients for mitigating intersymbol + interference (ISI) in high-speed backplane applications. + The initial values for algorithm coefficient values are + user configurable and used as a starting point of the algorithm. + config ETH_BACKPLANE_QORIQ tristate "QorIQ Ethernet Backplane driver" depends on ETH_BACKPLANE diff --git a/drivers/net/phy/backplane/Makefile b/drivers/net/phy/backplane/Makefile index 3ae3d8b..30a2ebb 100644 --- a/drivers/net/phy/backplane/Makefile +++ b/drivers/net/phy/backplane/Makefile @@ -5,6 +5,7 @@ obj-$(CONFIG_ETH_BACKPLANE) += eth_backplane.o obj-$(CONFIG_ETH_BACKPLANE_FIXED) += eq_fixed.o +obj-$(CONFIG_ETH_BACKPLANE_BEE) += eq_bee.o obj-$(CONFIG_ETH_BACKPLANE_QORIQ) += eth_backplane_qoriq.o eth_backplane-objs := backplane.o link_training.o diff --git a/drivers/net/phy/backplane/eq_bee.c b/drivers/net/phy/backplane/eq_bee.c new file mode 100644 index 0000000..045ed7e --- /dev/null +++ b/drivers/net/phy/backplane/eq_bee.c @@ -0,0 +1,1076 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* 3-Taps Bit Edge Equalization (BEE) algorithm + * + * Copyright 2019-2020 NXP + */ + +#include +#include + +#include "equalization.h" + +#define ALGORITHM_NAME "backplane_bee_3tap" +#define ALGORITHM_DESCR "3-Taps Bit Edge Equalization" +#define ALGORITHM_VERSION "1.5.5" + +/* BEE algorithm timeouts */ +#define TIMEOUT_LONG 3 +#define TIMEOUT_M1 3 + +/* Size of equalization snapshots data collection */ +#define EQ_SNAPSHOTS_SIZE 10 + +/* Rx link quality conditions: + * The following macros are used to determine the Rx link quality + * which is used to decide if/when to proceed with BinLong/BinM1 modules. + * The code that considers Rx in good quality is always in place: + * Rx link is in 'good quality' if: + * Bin1, Bin2 and Bin3 are toggling + * + * These macros are used to enable less quality link conditions. + * If Rx link quality is considered good enough then proceed to BinLong/BinM1 + */ + +/* Rx is 'less quality' if: + * Bin1 is toggling + * AND + * Bin2 is Early, GainMF stuck at max_eq_gain and Bin3 is Late + * OR + * Bin2 is Late, GainMF stuck at min_eq_gain and Bin3 is Early + */ +#define ENABLE_LESS_QUALITY_CONDITION + +/* Rx is 'even less quality' if: + * Bin1 is Early AND GainHF stuck at max_eq_gain and Bin2 is Late AND + * GainMF stuck at min_eq_gain + * OR + * Bin1 is Late AND GainHF stuck at min_eq_gain AND + * Bin2 is Early, GainMF stuck at max_eq_gain + */ +#define ENABLE_EVEN_LESS_QUALITY_CONDITION + +/* Rx is 'seemingly quality' if: + * Bin1 is always Late for all snapshots AND + * GainHF is stuck at min_eq_gain + * AND + * Bin2 and Bin3 are both Toggling + */ +#define ENABLE_SEEMINGLY_QUALITY_CONDITION + +enum bin_state { + BIN_INVALID, + BIN_EARLY, + BIN_TOGGLE, + BIN_LATE +}; + +struct eq_data_priv { + /* Equalization Algorithm setup data */ + struct equalizer_driver eqdrv; + + /* Bin state */ + enum bin_state bin_m1_state; + enum bin_state bin_long_state; + enum bin_state prev_bin_m1_state; + enum bin_state prev_bin_long_state; + + /* Bin training status */ + bool bin_m1_stop; + bool bin_long_stop; + int m1_min_max_cnt; + int long_min_max_cnt; + + /* Algorithm controlled value */ + u32 ld_update; + + /* Bit edge statistics: Bin snapshots */ + s16 bin1_snapshot[EQ_SNAPSHOTS_SIZE]; + s16 bin2_snapshot[EQ_SNAPSHOTS_SIZE]; + s16 bin3_snapshot[EQ_SNAPSHOTS_SIZE]; + s16 bin_long_snapshot[EQ_SNAPSHOTS_SIZE]; + s16 bin_m1_snapshot[EQ_SNAPSHOTS_SIZE]; + s16 bin_offset_snapshot[EQ_SNAPSHOTS_SIZE]; + + /* Gain snapshots */ + u8 gain_hf_snapshot[EQ_SNAPSHOTS_SIZE]; + u8 gain_mf_snapshot[EQ_SNAPSHOTS_SIZE]; + + /* Offset status snapshot */ + u8 osestat_snapshot[EQ_SNAPSHOTS_SIZE]; +}; + +static enum bin_state get_bin_snapshots_state(struct eq_data_priv *priv, + enum eqc_type type) +{ + s16 bin_snp_av_thr_low, bin_snp_av_thr_high; + s16 snapshot_average, snapshot_sum = 0; + struct eqc_range *bin_range; + s16 *bin_snapshot; + int i; + + if (!priv) { + pr_err("%s: NULL private data\n", ALGORITHM_NAME); + return BIN_INVALID; + } + + switch (type) { + case EQC_BIN_1: + bin_snapshot = priv->bin1_snapshot; + break; + case EQC_BIN_2: + bin_snapshot = priv->bin2_snapshot; + break; + case EQC_BIN_3: + bin_snapshot = priv->bin3_snapshot; + break; + case EQC_BIN_LONG: + bin_snapshot = priv->bin_long_snapshot; + break; + case EQC_BIN_OFFSET: + bin_snapshot = priv->bin_offset_snapshot; + break; + case EQC_BIN_M1: + bin_snapshot = priv->bin_m1_snapshot; + break; + default: + /* invalid bin type */ + return BIN_INVALID; + } + if (!bin_snapshot) + return BIN_INVALID; + + bin_range = priv->eqdrv.equalizer->ops.get_counter_range(type); + if (!bin_range) + return BIN_INVALID; + + bin_snp_av_thr_low = bin_range->mid_low; + bin_snp_av_thr_high = bin_range->mid_high; + + for (i = 0; i < EQ_SNAPSHOTS_SIZE; i++) + snapshot_sum += bin_snapshot[i]; + + snapshot_average = (s16)(snapshot_sum / EQ_SNAPSHOTS_SIZE); + + if (snapshot_average >= -256 && snapshot_average < bin_snp_av_thr_low) + return BIN_EARLY; + else if (snapshot_average >= bin_snp_av_thr_low && + snapshot_average < bin_snp_av_thr_high) + return BIN_TOGGLE; + else if (snapshot_average >= bin_snp_av_thr_high && + snapshot_average <= 255) + return BIN_LATE; + + return BIN_INVALID; +} + +static u32 process_bin_m1_toggle(struct eq_data_priv *priv) +{ + u32 update = lt_encode_startup_request(REQ_HOLD); + enum req_type prev_req_cm1; + + if (!priv) { + pr_err("%s: NULL private data\n", ALGORITHM_NAME); + return update; + } + + prev_req_cm1 = lt_decode_coef_update(priv->ld_update, C_M1); + + /* Toggle path */ + if (priv->prev_bin_m1_state == priv->bin_m1_state) { + /* Hold C- */ + update = lt_encode_startup_request(REQ_HOLD); + } else { + update = lt_encode_startup_request(REQ_HOLD); + /* If previous step moved C- repeat C- move */ + if (prev_req_cm1 == REQ_INC || + prev_req_cm1 == REQ_DEC) + update = lt_encode_request(update, prev_req_cm1, C_M1); + } + + return update; +} + +static u32 process_bin_m1_prev_toggle(struct eq_data_priv *priv) +{ + u32 update = lt_encode_startup_request(REQ_HOLD); + enum req_type prev_req_cm1; + + if (!priv) { + pr_err("%s: NULL private data\n", ALGORITHM_NAME); + return update; + } + + prev_req_cm1 = lt_decode_coef_update(priv->ld_update, C_M1); + + update = lt_encode_startup_request(REQ_HOLD); + /* If previous step moved C- go back on C- */ + if (prev_req_cm1 == REQ_INC) + update = lt_encode_request(update, REQ_DEC, C_M1); + if (prev_req_cm1 == REQ_DEC) + update = lt_encode_request(update, REQ_INC, C_M1); + + return update; +} + +static u32 process_bin_m1_early(struct eq_data_priv *priv) +{ + u32 update = lt_encode_startup_request(REQ_HOLD); + enum coef_status lpst_cm1; + + if (!priv) { + pr_err("%s: NULL private data\n", ALGORITHM_NAME); + return update; + } + + /* Get LP coefficient status to determine + * if coefficient is in range or reached the limit thresholds + * IF the coefficient is at MIN/MAX and still want to INC/DEC + * THEN do we are done with this module + */ + lpst_cm1 = lt_get_lp_coef_status(priv->eqdrv.lane, C_M1); + + /* Early path */ + if (lpst_cm1 == COEF_MAX) { + /* Hold C- */ + update = lt_encode_startup_request(REQ_HOLD); + } else { + /* request Increment C- */ + update = lt_encode_request(update, REQ_INC, C_M1); + } + + return update; +} + +static u32 process_bin_m1_late(struct eq_data_priv *priv) +{ + u32 update = lt_encode_startup_request(REQ_HOLD); + enum coef_status lpst_cm1; + + if (!priv) { + pr_err("%s: NULL private data\n", ALGORITHM_NAME); + return update; + } + + /* Get LP coefficient status to determine + * if coefficient is in range or reached the limit thresholds + * IF the coefficient is at MIN/MAX and still want to INC/DEC + * THEN do we are done with this module + */ + lpst_cm1 = lt_get_lp_coef_status(priv->eqdrv.lane, C_M1); + + /* Late path */ + if (lpst_cm1 == COEF_MIN) { + /* Hold C- */ + update = lt_encode_startup_request(REQ_HOLD); + } else { + /* request Decrement C- */ + update = lt_encode_request(update, REQ_DEC, C_M1); + } + + return update; +} + +static u32 process_bin_m1_antipodal(struct eq_data_priv *priv) +{ + u32 update = lt_encode_startup_request(REQ_HOLD); + + if (!priv) { + pr_err("%s: NULL private data\n", ALGORITHM_NAME); + return update; + } + + if (priv->bin_m1_state == BIN_LATE) { + /* request Decrement C- */ + update = lt_encode_request(update, REQ_DEC, C_M1); + } else { + /* Hold C- */ + update = lt_encode_startup_request(REQ_HOLD); + } + + return update; +} + +/* process_bin_m1 + * + * Bin_M1: + * contains the scoring of initial edges on pulses that are 1UI long + * following non-single bits + * used to adjust LP coefficient: C_M1 + */ +static void process_bin_m1(struct eq_data_priv *priv) +{ + u32 update = lt_encode_startup_request(REQ_HOLD); + + if (!priv) { + pr_err("%s: NULL private data\n", ALGORITHM_NAME); + return; + } + + if (priv->bin_m1_state == BIN_INVALID) { + phydev_err(priv->eqdrv.phydev, "Invalid Bin_M1 state\n"); + return; + } + + if (priv->bin_m1_state == BIN_TOGGLE) { + update = process_bin_m1_toggle(priv); + } else { + if (priv->prev_bin_m1_state == BIN_TOGGLE) { + update = process_bin_m1_prev_toggle(priv); + } else { + if (priv->prev_bin_m1_state == priv->bin_m1_state) { + if (priv->bin_m1_state == BIN_LATE) + update = process_bin_m1_late(priv); + else + update = process_bin_m1_early(priv); + } else { + update = process_bin_m1_antipodal(priv); + } + } + } + + /* Store current algorithm decision + * as previous algorithm ld_update for next step + */ + priv->ld_update = update; +} + +static u32 process_bin_long_toggle(struct eq_data_priv *priv) +{ + u32 update = lt_encode_startup_request(REQ_HOLD); + enum req_type prev_req_cp1, prev_req_cz0; + + if (!priv) { + pr_err("%s: NULL private data\n", ALGORITHM_NAME); + return update; + } + + prev_req_cp1 = lt_decode_coef_update(priv->ld_update, C_P1); + prev_req_cz0 = lt_decode_coef_update(priv->ld_update, C_Z0); + + /* Toggle path */ + if (priv->prev_bin_long_state == priv->bin_long_state) { + /* Hold C+ and C0 */ + update = lt_encode_startup_request(REQ_HOLD); + } else { + update = lt_encode_startup_request(REQ_HOLD); + /* If previous step moved C+/C0 repeat C+/C0 move */ + if (prev_req_cp1 == REQ_INC || + prev_req_cp1 == REQ_DEC || + prev_req_cz0 == REQ_INC || + prev_req_cz0 == REQ_DEC) { + update = lt_encode_request(update, prev_req_cp1, C_P1); + update = lt_encode_request(update, prev_req_cz0, C_Z0); + } + } + + return update; +} + +static u32 process_bin_long_prev_toggle(struct eq_data_priv *priv) +{ + u32 update = lt_encode_startup_request(REQ_HOLD); + enum req_type prev_req_cp1, prev_req_cz0; + + if (!priv) { + pr_err("%s: NULL private data\n", ALGORITHM_NAME); + return update; + } + + prev_req_cp1 = lt_decode_coef_update(priv->ld_update, C_P1); + prev_req_cz0 = lt_decode_coef_update(priv->ld_update, C_Z0); + + /* If previous step moved C+/C0 then go back on C+/C0 */ + if (prev_req_cp1 == REQ_INC) + update = lt_encode_request(update, REQ_DEC, C_P1); + if (prev_req_cp1 == REQ_DEC) + update = lt_encode_request(update, REQ_INC, C_P1); + if (prev_req_cz0 == REQ_INC) + update = lt_encode_request(update, REQ_DEC, C_Z0); + if (prev_req_cz0 == REQ_DEC) + update = lt_encode_request(update, REQ_INC, C_Z0); + + return update; +} + +static u32 process_bin_long_early(struct eq_data_priv *priv) +{ + u32 update = lt_encode_startup_request(REQ_HOLD); + enum coef_status lpst_cp1, lpst_cz0; + + if (!priv) { + pr_err("%s: NULL private data\n", ALGORITHM_NAME); + return update; + } + + /* Get LP coefficient status to determine + * if coefficient is in range or reached the limit thresholds + * IF the coefficient is at MIN/MAX and still want to INC/DEC + * THEN do we are done with this module + */ + lpst_cp1 = lt_get_lp_coef_status(priv->eqdrv.lane, C_P1); + lpst_cz0 = lt_get_lp_coef_status(priv->eqdrv.lane, C_Z0); + + /* Early path (make edge later) */ + if (lpst_cp1 == COEF_MAX) { + if (lpst_cz0 == COEF_MAX) { + /* Hold C+, C0 */ + update = lt_encode_startup_request(REQ_HOLD); + } else { + /* request Increment C0 and + * Decrement C+ + */ + update = lt_encode_request(update, REQ_INC, C_Z0); + update = lt_encode_request(update, REQ_DEC, C_P1); + } + } else { + /* request Increment C+ */ + update = lt_encode_request(update, REQ_INC, C_P1); + } + + return update; +} + +static u32 process_bin_long_late(struct eq_data_priv *priv) +{ + u32 update = lt_encode_startup_request(REQ_HOLD); + enum coef_status lpst_cp1, lpst_cz0; + + if (!priv) { + pr_err("%s: NULL private data\n", ALGORITHM_NAME); + return update; + } + + /* Get LP coefficient status to determine + * if coefficient is in range or reached the limit thresholds + * IF the coefficient is at MIN/MAX and still want to INC/DEC + * THEN do we are done with this module + */ + lpst_cp1 = lt_get_lp_coef_status(priv->eqdrv.lane, C_P1); + lpst_cz0 = lt_get_lp_coef_status(priv->eqdrv.lane, C_Z0); + + /* Late path (make edge earlier) */ + if (lpst_cp1 == COEF_MIN) { + if (lpst_cz0 == COEF_MIN) { + /* Hold C0 */ + update = lt_encode_startup_request(REQ_HOLD); + } else { + /* request Decrement C0 */ + update = lt_encode_request(update, REQ_DEC, C_Z0); + } + } else { + /* request Decrement C+ */ + update = lt_encode_request(update, REQ_DEC, C_P1); + } + + return update; +} + +static u32 process_bin_long_antipodal(struct eq_data_priv *priv) +{ + u32 update = lt_encode_startup_request(REQ_HOLD); + enum req_type prev_req_cp1; + + if (!priv) { + pr_err("%s: NULL private data\n", ALGORITHM_NAME); + return update; + } + + prev_req_cp1 = lt_decode_coef_update(priv->ld_update, C_P1); + + /* Request move on C+ and C0 */ + /* If previous step moved C+ then go back on C+ */ + if (prev_req_cp1 == REQ_INC) + update = lt_encode_request(update, REQ_DEC, C_P1); + if (prev_req_cp1 == REQ_DEC) + update = lt_encode_request(update, REQ_INC, C_P1); + + if (priv->bin_long_state == BIN_LATE) { + /* request Decrement C0 */ + update = lt_encode_request(update, REQ_DEC, C_Z0); + } else { + /* request Increment C0 */ + update = lt_encode_request(update, REQ_INC, C_Z0); + } + + return update; +} + +/* process_bin_long + * + * Bin_Long: + * contains the scoring of final edges on pulses longer than 7UI long + * used to adjust LP coefficients: C_P1 and C_Z0 + */ +static void process_bin_long(struct eq_data_priv *priv) +{ + u32 update = lt_encode_startup_request(REQ_HOLD); + + if (!priv) { + pr_err("%s: NULL private data\n", ALGORITHM_NAME); + return; + } + + if (priv->bin_long_state == BIN_INVALID) { + phydev_err(priv->eqdrv.phydev, "Invalid Bin_Long state\n"); + return; + } + + if (priv->bin_long_state == BIN_TOGGLE) { + update = process_bin_long_toggle(priv); + } else { + if (priv->prev_bin_long_state == BIN_TOGGLE) { + update = process_bin_long_prev_toggle(priv); + } else { + if (priv->prev_bin_long_state == priv->bin_long_state) { + if (priv->bin_long_state == BIN_LATE) + update = process_bin_long_late(priv); + else + update = process_bin_long_early(priv); + } else { + update = process_bin_long_antipodal(priv); + } + } + } + + /* Store current algorithm decision + * as previous algorithm ld_update for next step + */ + priv->ld_update = update; +} + +/* Callbacks: + * required by generic equalization_algorithm + */ +static void process_bad_state(struct eq_data_priv *priv) +{ + bool lp_at_init, lp_at_preset; + u32 req; + + if (!priv) { + pr_err("%s: NULL private data\n", ALGORITHM_NAME); + return; + } + + lp_at_init = lt_is_lp_at_startup(priv->eqdrv.lane, REQ_INIT); + lp_at_preset = lt_is_lp_at_startup(priv->eqdrv.lane, REQ_PRESET); + + if (lp_at_init) { + /* Try Request Preset */ + req = lt_encode_startup_request(REQ_PRESET); + lt_lp_update(priv->eqdrv.lane, req); + } else if (lp_at_preset) { + /* LT ERROR + * set lt_error flag to prevent reaching + * training state = TRAINED + * and resume training in case of LT error + */ + lt_set_error(priv->eqdrv.lane, true); + phydev_err(priv->eqdrv.phydev, + "LT Error: CDR_LOCK is zero on Preset\n"); + } else { + /* Move LP back to previous C-, C0, C+ and HOLD */ + lt_move_lp_back(priv->eqdrv.lane); + } +} + +static bool collect_bin_counters(struct eq_data_priv *priv, + enum eqc_type type) +{ + const struct equalizer_ops *eqops; + s16 *bin_snapshot = NULL; + int snp_size; + + if (!priv) { + pr_err("%s: NULL private data\n", ALGORITHM_NAME); + return false; + } + + /* collect Bin snapshots */ + switch (type) { + case EQC_BIN_1: + bin_snapshot = priv->bin1_snapshot; + break; + case EQC_BIN_2: + bin_snapshot = priv->bin2_snapshot; + break; + case EQC_BIN_3: + bin_snapshot = priv->bin3_snapshot; + break; + case EQC_BIN_LONG: + bin_snapshot = priv->bin_long_snapshot; + break; + case EQC_BIN_OFFSET: + bin_snapshot = priv->bin_offset_snapshot; + break; + case EQC_BIN_M1: + bin_snapshot = priv->bin_m1_snapshot; + break; + default: + /* invalid bin type */ + return false; + } + if (!bin_snapshot) + return false; + + eqops = &priv->eqdrv.equalizer->ops; + if (!eqops) { + phydev_err(priv->eqdrv.phydev, + "No operations for equalizer %s %s\n", + priv->eqdrv.equalizer->name, + priv->eqdrv.equalizer->version); + return false; + } + snp_size = eqops->collect_counters(priv->eqdrv.reg_base, type, + bin_snapshot, EQ_SNAPSHOTS_SIZE); + /* Check if snapshots collection failed */ + if (snp_size < EQ_SNAPSHOTS_SIZE) { + phydev_err(priv->eqdrv.phydev, + "Counters collection failed for equalizer %s %s\n", + priv->eqdrv.equalizer->name, + priv->eqdrv.equalizer->version); + return false; + } + + /* if CDR_LOCK = 0: Statistics are invalid */ + if (!backplane_is_cdr_lock(priv->eqdrv.lane, true)) { + process_bad_state(priv); + return false; + } + + return true; +} + +static bool collect_bit_edge_statistics(struct eq_data_priv *priv) +{ + enum eqc_type st_types[] = { EQC_GAIN_HF, EQC_GAIN_MF, EQC_EQOFFSET }; + s16 status_counters[ARRAY_SIZE(st_types)][EQ_SNAPSHOTS_SIZE]; + const struct equalizer_ops *eqops; + int i, snp_size; + + if (!priv) { + pr_err("%s: NULL private data\n", ALGORITHM_NAME); + return false; + } + + /* collect Bin snapshots */ + if (!collect_bin_counters(priv, EQC_BIN_1)) + return false; + if (!collect_bin_counters(priv, EQC_BIN_2)) + return false; + if (!collect_bin_counters(priv, EQC_BIN_3)) + return false; + if (!collect_bin_counters(priv, EQC_BIN_LONG)) + return false; + if (!collect_bin_counters(priv, EQC_BIN_OFFSET)) + return false; + if (!collect_bin_counters(priv, EQC_BIN_M1)) + return false; + + /* collect Gains */ + eqops = &priv->eqdrv.equalizer->ops; + if (!eqops) { + phydev_err(priv->eqdrv.phydev, + "No operations for equalizer %s %s\n", + priv->eqdrv.equalizer->name, + priv->eqdrv.equalizer->version); + return false; + } + snp_size = eqops->collect_multiple_counters(priv->eqdrv.reg_base, + st_types, + ARRAY_SIZE(st_types), + (s16 *)status_counters, + EQ_SNAPSHOTS_SIZE); + /* Check if snapshots collection failed */ + if (snp_size < EQ_SNAPSHOTS_SIZE) { + phydev_err(priv->eqdrv.phydev, + "Counters collection failed for equalizer %s %s\n", + priv->eqdrv.equalizer->name, + priv->eqdrv.equalizer->version); + return false; + } + + for (i = 0; i < EQ_SNAPSHOTS_SIZE; i++) { + priv->gain_hf_snapshot[i] = (u8)status_counters[0][i]; + priv->gain_mf_snapshot[i] = (u8)status_counters[1][i]; + priv->osestat_snapshot[i] = (u8)status_counters[2][i]; + } + + return true; +} + +static void generate_3taps_request(struct eq_data_priv *priv) +{ + if (!priv) { + pr_err("%s: NULL private data\n", ALGORITHM_NAME); + return; + } + + /* Store current state as previous state */ + priv->prev_bin_m1_state = priv->bin_m1_state; + priv->prev_bin_long_state = priv->bin_long_state; + + priv->bin_m1_state = get_bin_snapshots_state(priv, EQC_BIN_M1); + if (priv->bin_m1_state == BIN_INVALID) { + /* invalid state: should never happen */ + return; + } + + priv->bin_long_state = get_bin_snapshots_state(priv, EQC_BIN_LONG); + if (priv->bin_long_state == BIN_INVALID) { + /* invalid state: should never happen */ + return; + } + + /* Move to BinLong/BinM1 modules: + * Bin Modules order: BinLong before BinM1 + * We try to finish BinLong before we do BinM1 + */ + + /* Process BinLong module + * decide and ask for movement of C+/C0 + */ + if (!priv->bin_long_stop) { + process_bin_long(priv); + lt_lp_update(priv->eqdrv.lane, priv->ld_update); + if (lt_is_update_of_type(priv->ld_update, REQ_HOLD)) { + /* Sent All Hold request */ + priv->long_min_max_cnt++; + if (priv->long_min_max_cnt >= TIMEOUT_LONG) + priv->bin_long_stop = true; + } else { + /* Sent C Inc/Dec request */ + priv->long_min_max_cnt = 0; + } + return; + } + + /* Process BinM1 module + * decide and ask for movement of C- + */ + if (!priv->bin_m1_stop) { + process_bin_m1(priv); + lt_lp_update(priv->eqdrv.lane, priv->ld_update); + if (lt_is_update_of_type(priv->ld_update, REQ_HOLD)) { + /* Sent All Hold request */ + priv->m1_min_max_cnt++; + if (priv->m1_min_max_cnt >= TIMEOUT_M1) + priv->bin_m1_stop = true; + } else { + /* Sent C Inc/Dec request */ + priv->m1_min_max_cnt = 0; + } + return; + } +} + +static bool is_rx_ok(struct eq_data_priv *priv) +{ + struct eqc_range *gain_range, *osestat_range; + u8 osestat_mid_low, osestat_mid_high; + enum bin_state bin1_snapshot_state; + enum bin_state bin2_snapshot_state; + enum bin_state bin3_snapshot_state; + const struct equalizer_ops *eqops; + bool rx_quality_1, rx_quality_2; + bool is_ok, rx_good_quality; + u8 min_eq_gain, max_eq_gain; + u8 min_snp, max_snp; + s16 snapshot; + int i; + + if (!priv) { + pr_err("%s: NULL private data\n", ALGORITHM_NAME); + return false; + } + + /* Checking Bins/Gains after LP has updated its TX */ + eqops = &priv->eqdrv.equalizer->ops; + if (!eqops) + return false; + gain_range = eqops->get_counter_range(EQC_GAIN_HF); + if (!gain_range) + return false; + osestat_range = eqops->get_counter_range(EQC_EQOFFSET); + if (!osestat_range) + return false; + + min_eq_gain = (u8)gain_range->min; + max_eq_gain = (u8)gain_range->max; + osestat_mid_low = (u8)osestat_range->mid_low; + osestat_mid_high = (u8)osestat_range->mid_high; + + /* CDR_LOCK must be 1 */ + if (!backplane_is_cdr_lock(priv->eqdrv.lane, true)) + return false; + + /* Offset Bin must NOT be 10 of the same value */ + rx_good_quality = false; + snapshot = priv->bin_offset_snapshot[0]; + for (i = 0; i < EQ_SNAPSHOTS_SIZE; i++) { + if (snapshot != priv->bin_offset_snapshot[i]) { + rx_good_quality = true; + break; + } + } + if (!rx_good_quality) + return false; + + /* Offset status must dither (+/-2) around MidRange value + * What we want to see is that the Offset has settled to a value + * somewhere between: mid-range low and mid-range high and that + * the series of snapshot values are +/-2 of the settled value. + */ + rx_good_quality = true; + min_snp = priv->osestat_snapshot[0]; + max_snp = priv->osestat_snapshot[0]; + for (i = 0; i < EQ_SNAPSHOTS_SIZE; i++) { + if (priv->osestat_snapshot[i] < osestat_mid_low || + priv->osestat_snapshot[i] > osestat_mid_high) { + rx_good_quality = false; + break; + } + if (priv->osestat_snapshot[i] < min_snp) + min_snp = priv->osestat_snapshot[i]; + if (priv->osestat_snapshot[i] > max_snp) + max_snp = priv->osestat_snapshot[i]; + } + if (max_snp - min_snp > 4) + rx_good_quality = false; + if (!rx_good_quality) + return false; + + /* The Rx is in good quality if: + * Bin1, Bin2, and Bin3 are toggling + * Proceed to BinLong/BinM1 modules + */ + bin1_snapshot_state = get_bin_snapshots_state(priv, EQC_BIN_1); + bin2_snapshot_state = get_bin_snapshots_state(priv, EQC_BIN_2); + bin3_snapshot_state = get_bin_snapshots_state(priv, EQC_BIN_3); + + rx_good_quality = (bin1_snapshot_state == BIN_TOGGLE && + bin2_snapshot_state == BIN_TOGGLE && + bin3_snapshot_state == BIN_TOGGLE); + + /* If Rx is in good quality then proceed to BinLong/BinM1 */ + if (rx_good_quality) + return true; + +#ifdef ENABLE_LESS_QUALITY_CONDITION + rx_quality_1 = false; + rx_quality_2 = false; + if (bin1_snapshot_state == BIN_TOGGLE) { + if (bin2_snapshot_state == BIN_EARLY && + bin3_snapshot_state == BIN_LATE) { + /* check if GainMF is stuck at max_eq_gain */ + rx_quality_1 = true; + for (i = 0; i < EQ_SNAPSHOTS_SIZE; i++) { + if (priv->gain_mf_snapshot[i] != max_eq_gain) { + rx_quality_1 = false; + break; + } + } + } + if (bin2_snapshot_state == BIN_LATE && + bin3_snapshot_state == BIN_EARLY) { + /* check if GainMF is stuck at min_eq_gain */ + rx_quality_2 = true; + for (i = 0; i < EQ_SNAPSHOTS_SIZE; i++) { + if (priv->gain_mf_snapshot[i] != min_eq_gain) { + rx_quality_2 = false; + break; + } + } + } + } + + /* If Rx is less quality then proceed to BinLong/BinM1 */ + if (rx_quality_1 || rx_quality_2) + return true; +#endif + +#ifdef ENABLE_EVEN_LESS_QUALITY_CONDITION + rx_quality_1 = false; + rx_quality_2 = false; + if (bin1_snapshot_state == BIN_EARLY && + bin2_snapshot_state == BIN_LATE) { + /* check if GainHF is stuck at max_eq_gain */ + is_ok = true; + for (i = 0; i < EQ_SNAPSHOTS_SIZE; i++) { + if (priv->gain_hf_snapshot[i] != max_eq_gain) { + is_ok = false; + break; + } + } + if (is_ok) { + /* check if GainMF is stuck at min_eq_gain */ + is_ok = true; + for (i = 0; i < EQ_SNAPSHOTS_SIZE; i++) { + if (priv->gain_mf_snapshot[i] != min_eq_gain) { + is_ok = false; + break; + } + } + if (is_ok) + rx_quality_1 = true; + } + } + if (bin1_snapshot_state == BIN_LATE && + bin2_snapshot_state == BIN_EARLY) { + /* check if GainHF is stuck at min_eq_gain */ + is_ok = true; + for (i = 0; i < EQ_SNAPSHOTS_SIZE; i++) { + if (priv->gain_hf_snapshot[i] != min_eq_gain) { + is_ok = false; + break; + } + } + if (is_ok) { + /* check if GainMF is stuck at max_eq_gain */ + is_ok = true; + for (i = 0; i < EQ_SNAPSHOTS_SIZE; i++) { + if (priv->gain_mf_snapshot[i] != max_eq_gain) { + is_ok = false; + break; + } + } + if (is_ok) + rx_quality_2 = true; + } + } + + /* If Rx is in good quality then proceed to BinLong/BinM1 */ + if (rx_quality_1 || rx_quality_2) + return true; +#endif + +#ifdef ENABLE_SEEMINGLY_QUALITY_CONDITION + rx_quality_1 = false; + if (bin1_snapshot_state == BIN_LATE && + bin2_snapshot_state == BIN_TOGGLE && + bin3_snapshot_state == BIN_TOGGLE) { + /* check if GainHF is stuck at min_eq_gain */ + rx_quality_1 = true; + for (i = 0; i < EQ_SNAPSHOTS_SIZE; i++) { + if (priv->gain_hf_snapshot[i] != min_eq_gain) { + rx_quality_1 = false; + break; + } + } + } + + if (rx_quality_1) + return true; +#endif + + return false; +} + +static bool is_eq_done(struct eq_data_priv *priv) +{ + if (!priv) { + pr_err("%s: NULL private data\n", ALGORITHM_NAME); + return false; + } + + return (priv->bin_m1_stop && priv->bin_long_stop); +} + +/* BEE 3 TAP Algorithm API */ + +/* Create BEE 3-TAP Equalization Algorithm */ +static struct eq_data_priv *create(struct equalizer_driver eqdrv) +{ + struct eq_data_priv *bee_data; + + bee_data = devm_kzalloc(&eqdrv.phydev->mdio.dev, + sizeof(*bee_data), GFP_KERNEL); + if (!bee_data) + return NULL; + + /* initialize algorithm setup data */ + bee_data->eqdrv = eqdrv; + + /* initialize specific BEE algorithm data */ + bee_data->bin_m1_state = BIN_INVALID; + bee_data->bin_long_state = BIN_INVALID; + bee_data->prev_bin_m1_state = BIN_INVALID; + bee_data->prev_bin_long_state = BIN_INVALID; + bee_data->m1_min_max_cnt = 0; + bee_data->long_min_max_cnt = 0; + bee_data->bin_m1_stop = false; + bee_data->bin_long_stop = false; + bee_data->ld_update = 0; + + return bee_data; +} + +static void destroy(struct eq_data_priv *priv) +{ + if (!priv) { + pr_err("%s: NULL private data\n", ALGORITHM_NAME); + return; + } + + kfree(priv); +} + +static const struct equalization_algorithm eq_alg = { + .name = ALGORITHM_NAME, + .descr = ALGORITHM_DESCR, + .version = ALGORITHM_VERSION, + .use_local_tx_training = true, + .use_remote_tx_training = true, + .ops = { + .create = create, + .destroy = destroy, + .is_rx_ok = is_rx_ok, + .is_eq_done = is_eq_done, + .collect_statistics = collect_bit_edge_statistics, + .generate_request = generate_3taps_request, + .process_bad_state = process_bad_state, + .dump_algorithm_context = NULL, + } +}; + +static const char * const alg_keys[] = { + "bee", + "BEE", +}; + +static int __init bee_init(void) +{ + int i, err; + + pr_info("%s: %s algorithm version %s\n", + ALGORITHM_NAME, ALGORITHM_DESCR, ALGORITHM_VERSION); + + /* register BEE algorithm: */ + for (i = 0; i < ARRAY_SIZE(alg_keys); i++) { + err = backplane_eq_register(alg_keys[i], &eq_alg); + if (err) { + pr_err("%s: '%s' equalization algorithm registration failed\n", + ALGORITHM_NAME, alg_keys[i]); + } + } + + return 0; +} + +static void __exit bee_exit(void) +{ + int i; + + /* unregister BEE algorithm: */ + for (i = 0; i < ARRAY_SIZE(alg_keys); i++) + backplane_eq_unregister(alg_keys[i]); + + pr_info("%s: %s algorithm version %s unloaded\n", + ALGORITHM_NAME, ALGORITHM_DESCR, ALGORITHM_VERSION); +} + +module_init(bee_init); +module_exit(bee_exit); + +MODULE_DESCRIPTION("Bit Edge Equalization Algorithm"); +MODULE_AUTHOR("Florinel Iordache "); +MODULE_LICENSE("Dual BSD/GPL"); From patchwork Mon Jun 22 13:35:24 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Florinel Iordache X-Patchwork-Id: 1314390 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming-netdev@ozlabs.org Delivered-To: patchwork-incoming-netdev@ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=vger.kernel.org (client-ip=23.128.96.18; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=pass (p=none dis=none) header.from=nxp.com Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=nxp.com header.i=@nxp.com header.a=rsa-sha256 header.s=selector2 header.b=sm2LpHtV; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by ozlabs.org (Postfix) with ESMTP id 49r9S04ST2z9sQt for ; Mon, 22 Jun 2020 23:36:48 +1000 (AEST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1729229AbgFVNgq (ORCPT ); Mon, 22 Jun 2020 09:36:46 -0400 Received: from mail-db8eur05on2051.outbound.protection.outlook.com ([40.107.20.51]:36110 "EHLO EUR05-DB8-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1729158AbgFVNgh (ORCPT ); Mon, 22 Jun 2020 09:36:37 -0400 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=NVo37QscCTOvWF+F8WyZIoL9xljLDVuQHZjabLBcnWmjNGwQqyiNQIDYtZHWnr4MPCImfGzXsZhmMu4qFV5aOBWiROwXC/jOhTp0GasCs4dHWOQ3fl+/z72osekYc3F9FMjMzvzFLemf7E+vUi/2ob5qyApy1asLMDKQqfNNfiDjicFqDbgdP3qx5pEoAsUXD205tCP2wYnEhnafIKHKhBbiqFQv/rfgndP/TH0bsjkVJCtZ8XrhbBdYbU9fmbJDhVrLwIi/mF865lJn6XMgpiqlylPIrAVC750idsiUMcSFm1jQXlxSkoHPF2oEhOQxOoV/5/yJI67YIgQRckB9qg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=fKDxXahmHd8WA0gzvjyKltxkC7me2NihuwXiLDSDprc=; b=mpJxE30W/Ibb6/+JUT6DqUuI5kLK/vbo6CcIilkUmrXzokscw9tfoUdWjfPIpNKSM2u0YFUtWaq1U4uWKe1pMkupV52Iwnke4tqODCpKBzv5i2eI9ML6mXzRMgGmgNSZ+VeMBxl5ZKTwvP+PthEuifZ08FqJkAIpwv96+gdjtugdAUzistGDC/FFic2PI5FAEvSm4CNAGwBi4t0mzbX8FWZgYk02IO3kYG7WnGKm1V84MW3kdO8gm/1cZ+i2BInYIKqM8MBb/CU91SqQKMtI9K9TwbiIgI8pxEl9CJ6ki0kWAJZ6VmStqMzVW3C6j0uZthv3JL+GZWBp9lqw6F1gQg== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=nxp.com; dmarc=pass action=none header.from=nxp.com; dkim=pass header.d=nxp.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=nxp.com; s=selector2; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=fKDxXahmHd8WA0gzvjyKltxkC7me2NihuwXiLDSDprc=; b=sm2LpHtVu9Xz/1qrmei8UuCHLHO5RjZug4iFOLb/JYmr3b5r98rhBDsnO0xN076DJyxeXjIXNxYAm8NSE7ABQ5XHkxWCV2tKlKDVlW4M/bci9nbAc32ticg40S+o2E6wDF/RiHbZuLEgJWKn4AY05nzPzvkZRsrLsP/XB+b1q5k= Authentication-Results: davemloft.net; dkim=none (message not signed) header.d=none;davemloft.net; dmarc=none action=none header.from=nxp.com; Received: from AM0PR04MB5443.eurprd04.prod.outlook.com (2603:10a6:208:119::33) by AM0PR04MB5075.eurprd04.prod.outlook.com (2603:10a6:208:bf::24) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.3109.22; Mon, 22 Jun 2020 13:35:59 +0000 Received: from AM0PR04MB5443.eurprd04.prod.outlook.com ([fe80::f0b7:8439:3b5a:61bd]) by AM0PR04MB5443.eurprd04.prod.outlook.com ([fe80::f0b7:8439:3b5a:61bd%7]) with mapi id 15.20.3109.027; Mon, 22 Jun 2020 13:35:59 +0000 From: Florinel Iordache To: davem@davemloft.net, netdev@vger.kernel.org, andrew@lunn.ch, f.fainelli@gmail.com, hkallweit1@gmail.com, linux@armlinux.org.uk Cc: devicetree@vger.kernel.org, linux-doc@vger.kernel.org, robh+dt@kernel.org, mark.rutland@arm.com, kuba@kernel.org, corbet@lwn.net, shawnguo@kernel.org, leoyang.li@nxp.com, madalin.bucur@oss.nxp.com, ioana.ciornei@nxp.com, linux-kernel@vger.kernel.org, Florinel Iordache Subject: [PATCH net-next v3 7/7] arm64: dts: add serdes and mdio description Date: Mon, 22 Jun 2020 16:35:24 +0300 Message-Id: <1592832924-31733-8-git-send-email-florinel.iordache@nxp.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1592832924-31733-1-git-send-email-florinel.iordache@nxp.com> References: <1592832924-31733-1-git-send-email-florinel.iordache@nxp.com> Reply-to: florinel.iordache@nxp.com X-ClientProxiedBy: AM3PR07CA0086.eurprd07.prod.outlook.com (2603:10a6:207:6::20) To AM0PR04MB5443.eurprd04.prod.outlook.com (2603:10a6:208:119::33) MIME-Version: 1.0 X-MS-Exchange-MessageSentRepresentingType: 1 Received: from fsr-ub1464-128.ea.freescale.net (83.217.231.2) by AM3PR07CA0086.eurprd07.prod.outlook.com (2603:10a6:207:6::20) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256) id 15.20.3131.12 via Frontend Transport; Mon, 22 Jun 2020 13:35:58 +0000 X-Mailer: git-send-email 1.9.1 X-Originating-IP: [83.217.231.2] X-MS-PublicTrafficType: Email X-MS-Office365-Filtering-HT: Tenant X-MS-Office365-Filtering-Correlation-Id: fb135e35-9328-4138-1634-08d816b13323 X-MS-TrafficTypeDiagnostic: AM0PR04MB5075: X-MS-Exchange-Transport-Forked: True X-Microsoft-Antispam-PRVS: X-MS-Oob-TLC-OOBClassifiers: OLM:1775; X-Forefront-PRVS: 0442E569BC X-MS-Exchange-SenderADCheck: 1 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: 6uRXHoynWBMBfpFGU8T/GHZt3SfBQWEya9B8JgIVTKepxB8WruWfkv62TOJ6uh0cGkL4eMbbFT+gKG4uKzajDAZ9hvYRlyFS6jFbZFui9GCgYESHKcVff/zD1WV3EuGcrEXwTPpIze2QAshjR+WcKn5S80McYhgB/PI5tXN/uPioluTn+BteylLM1CewRWAkbSjdK766wsHHfIBZFN2TyGCt3sfEiLAYfRTXFibEUiIouwzCxrQqfHWueSxElOdtsq6OUmQIOW1Ppn40OYPO+O9c3auKJgMfb7DV6v0Q8mJSeTVf2b6metdsvloM6KKgBBzM1DEo48YhaB92FuLp1w== X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:AM0PR04MB5443.eurprd04.prod.outlook.com;PTR:;CAT:NONE;SFTY:;SFS:(4636009)(136003)(376002)(346002)(39860400002)(366004)(396003)(66476007)(66556008)(66946007)(83380400001)(5660300002)(2616005)(956004)(44832011)(86362001)(6666004)(2906002)(8676002)(6512007)(52116002)(16526019)(186003)(26005)(3450700001)(6506007)(4326008)(6486002)(316002)(478600001)(8936002)(7416002)(36756003);DIR:OUT;SFP:1101; X-MS-Exchange-AntiSpam-MessageData: /i290F4x2zvapUVJdMk9tysCL2+UrzHcKaysZpvhjzXaC75gEKbEXs1DLv6vgS8VNMtPJ/5INrbNAGh6MGBmfPvcaoacsJeGKEvI6up2eDPwBcLURmlM+mvtOx2PLfDlHDfg2jjkM7F+0s87xNF1F8q2VXWKdeI6GSXw9NaUsLGTMqQXVSxzHbrhYwmVBvxHK/HvWAZSN4bR1PoQvL4sb+HkCeVljY0rloPiindV7o28sjulbaTdWR4N5WBNrDPj9Wc6KN6QbLO7En133v3gNWAqx+V42SySZtaBY6KGhHBsBcNClXOcWG8DMEZd6m5CfIQtLqWdY4d0kaznR9N0pUduwj3WpKqEW6lbPBhtc3x+EslhHgFGKzDTYHlMxUGiclZ/wjncUwVcWBLJlm3zIbBTu9f9Pyq1+RFPpcD6L9LzBhRUu+ntFod4kAkeUmmb4GMxXQcMx3ShsEe3VX5aHwrX1sFKAXcjmbHi/oe0Qyc= X-OriginatorOrg: nxp.com X-MS-Exchange-CrossTenant-Network-Message-Id: fb135e35-9328-4138-1634-08d816b13323 X-MS-Exchange-CrossTenant-OriginalArrivalTime: 22 Jun 2020 13:35:59.7574 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 686ea1d3-bc2b-4c6f-a92c-d99c5c301635 X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: 84dh5N35B/WbdW+MsfkBFPD9tIYuf57M3I4rWw+g2ufwQ+onqnUoE/JD//qG6BG4sLBc/b/gn6j1M0oFP/rBsAc+HxlbA/B1ohY/y+QHBRA= X-MS-Exchange-Transport-CrossTenantHeadersStamped: AM0PR04MB5075 Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Add dt nodes with serdes, lanes, mdio generic description for supported platform: ls1046. This is a prerequisite to enable backplane on device tree for these platforms. Signed-off-by: Florinel Iordache --- arch/arm64/boot/dts/freescale/fsl-ls1046a.dtsi | 33 +++++++++++++++++++++- .../boot/dts/freescale/qoriq-fman3-0-10g-0.dtsi | 5 ++-- .../boot/dts/freescale/qoriq-fman3-0-10g-1.dtsi | 5 ++-- 3 files changed, 38 insertions(+), 5 deletions(-) diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1046a.dtsi b/arch/arm64/boot/dts/freescale/fsl-ls1046a.dtsi index d4c1da3..c7d845f 100644 --- a/arch/arm64/boot/dts/freescale/fsl-ls1046a.dtsi +++ b/arch/arm64/boot/dts/freescale/fsl-ls1046a.dtsi @@ -3,7 +3,7 @@ * Device Tree Include file for Freescale Layerscape-1046A family SoC. * * Copyright 2016 Freescale Semiconductor, Inc. - * Copyright 2018 NXP + * Copyright 2018, 2020 NXP * * Mingkai Hu */ @@ -735,6 +735,37 @@ status = "disabled"; }; + serdes1: serdes@1ea0000 { + compatible = "serdes-10g"; + reg = <0x0 0x1ea0000 0 0x00002000>; + reg-names = "serdes", "serdes-10g"; + big-endian; + + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0x00 0x1ea0000 0x00002000>; + lane_a: lane@800 { + compatible = "lane-10g"; + reg = <0x800 0x40>; + reg-names = "lane", "serdes-lane"; + }; + lane_b: lane@840 { + compatible = "lane-10g"; + reg = <0x840 0x40>; + reg-names = "lane", "serdes-lane"; + }; + lane_c: lane@880 { + compatible = "lane-10g"; + reg = <0x880 0x40>; + reg-names = "lane", "serdes-lane"; + }; + lane_d: lane@8c0 { + compatible = "lane-10g"; + reg = <0x8c0 0x40>; + reg-names = "lane", "serdes-lane"; + }; + }; + pcie_ep@3600000 { compatible = "fsl,ls1046a-pcie-ep", "fsl,ls-pcie-ep"; reg = <0x00 0x03600000 0x0 0x00100000 diff --git a/arch/arm64/boot/dts/freescale/qoriq-fman3-0-10g-0.dtsi b/arch/arm64/boot/dts/freescale/qoriq-fman3-0-10g-0.dtsi index dbd2fc3..d6191f1 100644 --- a/arch/arm64/boot/dts/freescale/qoriq-fman3-0-10g-0.dtsi +++ b/arch/arm64/boot/dts/freescale/qoriq-fman3-0-10g-0.dtsi @@ -3,6 +3,7 @@ * QorIQ FMan v3 10g port #0 device tree * * Copyright 2012-2015 Freescale Semiconductor Inc. + * Copyright 2020 NXP * */ @@ -21,7 +22,7 @@ fman@1a00000 { fsl,fman-10g-port; }; - ethernet@f0000 { + mac9: ethernet@f0000 { cell-index = <0x8>; compatible = "fsl,fman-memac"; reg = <0xf0000 0x1000>; @@ -29,7 +30,7 @@ fman@1a00000 { pcsphy-handle = <&pcsphy6>; }; - mdio@f1000 { + mdio9: mdio@f1000 { #address-cells = <1>; #size-cells = <0>; compatible = "fsl,fman-memac-mdio", "fsl,fman-xmdio"; diff --git a/arch/arm64/boot/dts/freescale/qoriq-fman3-0-10g-1.dtsi b/arch/arm64/boot/dts/freescale/qoriq-fman3-0-10g-1.dtsi index 6fc5d25..1f6f28f 100644 --- a/arch/arm64/boot/dts/freescale/qoriq-fman3-0-10g-1.dtsi +++ b/arch/arm64/boot/dts/freescale/qoriq-fman3-0-10g-1.dtsi @@ -3,6 +3,7 @@ * QorIQ FMan v3 10g port #1 device tree * * Copyright 2012-2015 Freescale Semiconductor Inc. + * Copyright 2020 NXP * */ @@ -21,7 +22,7 @@ fman@1a00000 { fsl,fman-10g-port; }; - ethernet@f2000 { + mac10: ethernet@f2000 { cell-index = <0x9>; compatible = "fsl,fman-memac"; reg = <0xf2000 0x1000>; @@ -29,7 +30,7 @@ fman@1a00000 { pcsphy-handle = <&pcsphy7>; }; - mdio@f3000 { + mdio10: mdio@f3000 { #address-cells = <1>; #size-cells = <0>; compatible = "fsl,fman-memac-mdio", "fsl,fman-xmdio";