From patchwork Thu Mar 26 13:51:14 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Florinel Iordache X-Patchwork-Id: 1261990 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=none (no SPF record) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; 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=KvmAiZPn; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 48p5xz3FGFz9sSV for ; Fri, 27 Mar 2020 00:51:51 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727829AbgCZNvt (ORCPT ); Thu, 26 Mar 2020 09:51:49 -0400 Received: from mail-eopbgr60051.outbound.protection.outlook.com ([40.107.6.51]:56129 "EHLO EUR04-DB3-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1726359AbgCZNvs (ORCPT ); Thu, 26 Mar 2020 09:51:48 -0400 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=Ac7o1d5pdk04pvgaO4ebw0BMxonQT+/qrErGT0ib/HHCXW+ElDt6f31mDdkKuVkiimNa5/ugKhhYvTi909mT9casNSR5es+uzqS1sTdMmKUqFrbzdBr2yU9NnYNMFT8OgkkdxAsikwUQELT531FO9B2mU0KPe6aQ2DX5h1PCB14WK4YJxOt0xiQb7myYp/2YTPayLKC+wWdr6n/bmNb6ar0bnCwUa7JmWYFasTWNsJ9oI86hzaEmSQ14LNtdg5yrkVuYRh5OPDfhaPmvRgfoAfduJJlwjIl+x1ogna7HmRULZeJYcK3yg17G0SR8MTfe/XYZWVDOdz+8qHUrkslCaA== 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=IpvrJhfAnSsDLKpkCWQXafkQxI3fC3uXMs7q1r34UW0=; b=dWSFyVifbAk+Kbqkt0DYVe8LbL3DUtmoT835Vl7Qo1vaphlZLcSs2B6jog55LmSSre3310VnHqLGotgQzPr20QYw+0DsAEOnAft8j27vFfX9oJ6QsNOjkn6Mg7XoggzrVa7OsdV/lmIFTp1JM67W7Z3zWL2vlwLnvMEyPgg/OmJjlSi0S/8oSDwVN3D8dp2R7UNspV4vNKV+UJ3iLgh1dOML70BUL1XsGszx5GRvry2RDZAd/XqFi3OJbx4IZf78GI/Y1x58vxkmMzhyB2RxWLHRop0DC7AFwJ2fuC1l9NPVIRNZDpV/DDqDVYXC8P9DASdr9nDjJfE5WuKV0sGdyQ== 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=IpvrJhfAnSsDLKpkCWQXafkQxI3fC3uXMs7q1r34UW0=; b=KvmAiZPnBm8LZEmS8P4E0Mm09P5YNdX/frjHdrR013odyIiMa3knela1llzToooROQakBnJgvdCNOLjKcwl/kvK5bls1Lrd+tQULxv4W02zbom+OF9aLRMXMkIad3et5yplMllDd65tH8Ju3YHaUfXz+BpqZwwN8W52vii4bmmc= Authentication-Results: spf=none (sender IP is ) smtp.mailfrom=florinel.iordache@nxp.com; Received: from VI1PR04MB5454.eurprd04.prod.outlook.com (20.178.122.87) by VI1PR04MB4272.eurprd04.prod.outlook.com (10.171.182.33) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.2835.20; Thu, 26 Mar 2020 13:51:44 +0000 Received: from VI1PR04MB5454.eurprd04.prod.outlook.com ([fe80::69f6:5d59:b505:a6c8]) by VI1PR04MB5454.eurprd04.prod.outlook.com ([fe80::69f6:5d59:b505:a6c8%3]) with mapi id 15.20.2835.023; Thu, 26 Mar 2020 13:51:44 +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 1/9] doc: net: add backplane documentation Date: Thu, 26 Mar 2020 15:51:14 +0200 Message-Id: <1585230682-24417-2-git-send-email-florinel.iordache@nxp.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1585230682-24417-1-git-send-email-florinel.iordache@nxp.com> References: <1585230682-24417-1-git-send-email-florinel.iordache@nxp.com> Reply-to: florinel.iordache@nxp.com X-ClientProxiedBy: AM0PR01CA0142.eurprd01.prod.exchangelabs.com (2603:10a6:208:168::47) To VI1PR04MB5454.eurprd04.prod.outlook.com (2603:10a6:803:d1::23) MIME-Version: 1.0 X-MS-Exchange-MessageSentRepresentingType: 1 Received: from fsr-ub1464-128.ea.freescale.net (89.37.124.34) by AM0PR01CA0142.eurprd01.prod.exchangelabs.com (2603:10a6:208:168::47) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256) id 15.20.2835.20 via Frontend Transport; Thu, 26 Mar 2020 13:51:42 +0000 X-Mailer: git-send-email 1.9.1 X-Originating-IP: [89.37.124.34] X-MS-PublicTrafficType: Email X-MS-Office365-Filtering-HT: Tenant X-MS-Office365-Filtering-Correlation-Id: ee97d36c-fb14-497b-462d-08d7d18cd192 X-MS-TrafficTypeDiagnostic: VI1PR04MB4272:|VI1PR04MB4272: X-MS-Exchange-Transport-Forked: True X-Microsoft-Antispam-PRVS: X-MS-Oob-TLC-OOBClassifiers: OLM:9508; X-Forefront-PRVS: 0354B4BED2 X-Forefront-Antispam-Report: SFV:NSPM; SFS:(10009020)(4636009)(39860400002)(376002)(346002)(136003)(366004)(396003)(26005)(478600001)(186003)(16526019)(956004)(81166006)(3450700001)(36756003)(81156014)(8936002)(44832011)(4326008)(8676002)(5660300002)(2616005)(7416002)(2906002)(66946007)(86362001)(6486002)(66556008)(316002)(6506007)(6512007)(6666004)(66476007)(52116002); DIR:OUT; SFP:1101; SCL:1; SRVR:VI1PR04MB4272; H:VI1PR04MB5454.eurprd04.prod.outlook.com; FPR:; SPF:None; LANG:en; PTR:InfoNoRecords; Received-SPF: None (protection.outlook.com: nxp.com does not designate permitted sender hosts) X-MS-Exchange-SenderADCheck: 1 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: JRJa2k77bLC9FagSr36ElixShm/3/eG8BJqbj/rcNy8QRcjMqkwB8uJCsgmwv4zLMIiR08QBgmRiQxNUv9N4VIehe0ROUY+Y77gjuLa8pqf2QYZ1kyZIOfqEyh6y9h9NNDYSpM9sgVnVMJ8lI4a1RpsZvCyCK29ePHdwY87lXP/FSEbmqyOGN7Vsp8/ti6mQ3ZUvNoBpiSxbE3vHlHo0XBtUeKMPR+B9cQZ/DhOXKiAl+kBRS5pdtMxAyWC16M8VwDCQivS9/XKnGCckP7Fz00X1dm6q7etnwIPQInlvbOJaflGEZ700XPksFIR1tmeC1bfVUyVSbn/tOVDx/fhVCk+vb7uhsRXygx5Bak5g/hTlqmiUaLFJvyHKwenztLVH0lzGVQJxpKcTKe8+A6UGCNkYpQw/KXTsTtqAmt4AoRi6+E9cRNDdoyE4ktNnMEzy X-MS-Exchange-AntiSpam-MessageData: dcuVwOnLJIJguVw+d+H9hzDfquKuuZLuxuvWRKJapN7ofaE7hWKHgQRXnTwEwJG4MMiwqsjdgfe0Aws+sSEx1Z+fxv0qYECvH2M5pweO470Kfcxv1gLJOKArnVMPabelVgvTkAOUijSGgGsEj7/SAw== X-OriginatorOrg: nxp.com X-MS-Exchange-CrossTenant-Network-Message-Id: ee97d36c-fb14-497b-462d-08d7d18cd192 X-MS-Exchange-CrossTenant-OriginalArrivalTime: 26 Mar 2020 13:51:43.9364 (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: PmQKZwh3SglJOh2IlgjQsohqe0nevW/9rx95fqA1pwWhhb+j55sZ14YKaqHiLqTkLKXLlNUvjUbWMestL9sNhP7XeYiFh134+VyaZ+dpZg8= X-MS-Exchange-Transport-CrossTenantHeadersStamped: VI1PR04MB4272 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 | 165 +++++++++++++++++++++++++++++++++ 1 file changed, 165 insertions(+) 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..951c17e --- /dev/null +++ b/Documentation/networking/backplane.rst @@ -0,0 +1,165 @@ +.. 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 + +- Multi-lane: +40GBase-KR4 + +Supported platforms +=================== + +Ethernet Backplane is enabled on the following platforms: + +LS1046A +LS1088A +LS2088A +LX2160A \ No newline at end of file From patchwork Thu Mar 26 13:51:15 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Florinel Iordache X-Patchwork-Id: 1261992 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=none (no SPF record) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; 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=IuJVFRF2; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 48p5yD4xbqz9sSm for ; Fri, 27 Mar 2020 00:52:04 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727866AbgCZNwB (ORCPT ); Thu, 26 Mar 2020 09:52:01 -0400 Received: from mail-eopbgr60078.outbound.protection.outlook.com ([40.107.6.78]:40267 "EHLO EUR04-DB3-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1726359AbgCZNwA (ORCPT ); Thu, 26 Mar 2020 09:52:00 -0400 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=IZZyjiEiy9uXFLlvZz01tzn/8NwKiZE0h1teSGYwe2QWxTaEvNfr5uOynHu0sRLFA450EFFjOQGxvfdvZVhfIFzsz1wKRv629YLKhUSxjg96cQW7zqDjBHizHz3UWUsZ5JhS6KZvIlb9Kp88LtvYRkYacSMKa/yrGWMoYzmXtCKKRS32Dn/KXKLqzU2A8po7sXh6YPs+MHiTNXC9Gwx35S/GbnoDphzSA7cPh2+pEHSvyOtmH5+uKdc9klDpu/NvHX1FhAgmzKxu5t46tuzqqZhFiEiT0GIlGFEyNCWSwtI9JcxHslZL7tDXU2sW5e4WEsuVROhyrq8tPMrEQv+0YA== 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=8FFivp8tz3G38wecI4lXkwHnDs4U1tWrqQQc5c6et3I=; b=Ef5vyuLwYori/qgmn8iKLJFpf77fE9JZ8awwiA/JpwD3wCkrpE24a3ei9vEZNnYhKxFJ8eaKfh7IBraFA3GlQr4vyTayiedMlKUpFQoiQO2RrqesbY346uvh2l/SlMXIAXtg8/7kcmTBd7qa/teHODf8G1reWOU4p0PQjMmbNmGKxa1Q1PNA1wvLbqqltXtKVQxjd0hImZfjBKRdHHXS23MDkLfUfkmcfMRYnfqri+PvlDXHd+Aaq5CqQQQM/pCsVb3oC4Py6KrjgHSJeH4U+iYO1l9P3SD6QP86xrm5GeizyjP61GxwKq+0dI1fQV910gaVX6PwxdXHyYHffV6PqQ== 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=8FFivp8tz3G38wecI4lXkwHnDs4U1tWrqQQc5c6et3I=; b=IuJVFRF2Ov6+8g9rybeKI6I+2Y5SHvDKp399x7e4H+SMPZouen8XC+1vLD1zmUYrhpxB09Er0Y9MmrItbStXpzjNSbc7J3WC9OtF6/p+Zk059pGmOFTlAO/D/wKoxdvNLvCotA4G7pfttqGFjP7MiWQ8ARinioHa8ADMCgCjZ50= Authentication-Results: spf=none (sender IP is ) smtp.mailfrom=florinel.iordache@nxp.com; Received: from VI1PR04MB5454.eurprd04.prod.outlook.com (20.178.122.87) by VI1PR04MB4272.eurprd04.prod.outlook.com (10.171.182.33) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.2835.20; Thu, 26 Mar 2020 13:51:57 +0000 Received: from VI1PR04MB5454.eurprd04.prod.outlook.com ([fe80::69f6:5d59:b505:a6c8]) by VI1PR04MB5454.eurprd04.prod.outlook.com ([fe80::69f6:5d59:b505:a6c8%3]) with mapi id 15.20.2835.023; Thu, 26 Mar 2020 13:51: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 2/9] dt-bindings: net: add backplane dt bindings Date: Thu, 26 Mar 2020 15:51:15 +0200 Message-Id: <1585230682-24417-3-git-send-email-florinel.iordache@nxp.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1585230682-24417-1-git-send-email-florinel.iordache@nxp.com> References: <1585230682-24417-1-git-send-email-florinel.iordache@nxp.com> Reply-to: florinel.iordache@nxp.com X-ClientProxiedBy: AM0PR01CA0142.eurprd01.prod.exchangelabs.com (2603:10a6:208:168::47) To VI1PR04MB5454.eurprd04.prod.outlook.com (2603:10a6:803:d1::23) MIME-Version: 1.0 X-MS-Exchange-MessageSentRepresentingType: 1 Received: from fsr-ub1464-128.ea.freescale.net (89.37.124.34) by AM0PR01CA0142.eurprd01.prod.exchangelabs.com (2603:10a6:208:168::47) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256) id 15.20.2835.20 via Frontend Transport; Thu, 26 Mar 2020 13:51:55 +0000 X-Mailer: git-send-email 1.9.1 X-Originating-IP: [89.37.124.34] X-MS-PublicTrafficType: Email X-MS-Office365-Filtering-HT: Tenant X-MS-Office365-Filtering-Correlation-Id: 5dea79be-c766-4c07-8da8-08d7d18cd92f X-MS-TrafficTypeDiagnostic: VI1PR04MB4272:|VI1PR04MB4272: X-MS-Exchange-Transport-Forked: True X-Microsoft-Antispam-PRVS: X-MS-Oob-TLC-OOBClassifiers: OLM:6108; X-Forefront-PRVS: 0354B4BED2 X-Forefront-Antispam-Report: SFV:NSPM; SFS:(10009020)(4636009)(39860400002)(376002)(346002)(136003)(366004)(396003)(26005)(478600001)(186003)(16526019)(956004)(81166006)(3450700001)(36756003)(81156014)(8936002)(44832011)(4326008)(8676002)(5660300002)(2616005)(7416002)(2906002)(66946007)(86362001)(6486002)(966005)(66556008)(316002)(6506007)(6512007)(6666004)(66476007)(52116002); DIR:OUT; SFP:1101; SCL:1; SRVR:VI1PR04MB4272; H:VI1PR04MB5454.eurprd04.prod.outlook.com; FPR:; SPF:None; LANG:en; PTR:InfoNoRecords; Received-SPF: None (protection.outlook.com: nxp.com does not designate permitted sender hosts) X-MS-Exchange-SenderADCheck: 1 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: gkLON4Mzbk2cX/7Rq5p5FXCX0XD2i1kyABJ1LRETMLah4vCQu/hJFXu0o2LhrO5y2fP55VM+HtEfIGXSkMv6GTH2OjZ3+BYvd/ceIaM04F4i1ErRC91mp5jVit8CxCLsMHRoaI2UND822Znu76ENHR1AZfl48dvxT2KvjM3jSNYxjI/EqBw3cTKyh1Tlt371zwOVbczTApcBKuf5GhEIzCMShUGZB83q4PPU667I3gXc0PX/vi3k+jmWQvgcYyRZduBVRZVWTRrsYrcA4Tnn3a4nuOU+2C9ko8PQ7JVTR+12TI932J7sssytGQ3jTVRj+l2F/tyNBNjOd8+0u83jq0TmLsXTaUfZzOwnV17nx01TIr1fWj7d9HjtnmaAN3pfB7hXOnkAXDGNq9NHYJk4jp7CGw/WjvOfTrxSBXupQ/wKwotF+f0KHryuIW9rpkfh5CDcYbt4ASikbx/OKMpzYCSBe4j9rzFZztowwGof1lB6M5yf2ivQgWyQZK8noGaB X-MS-Exchange-AntiSpam-MessageData: dY6QC0acehI2kBNxKNaH5nyXdmjTfeSKhgdhuwXF2YrQmZHyG3DUBhPUBFG69uXWyylD76dvDlmw0gQKzzxuh+hUU45Uytm2MxKJCMlB4JEfBZhnmzc3PEvkeuJtQ2mahEiVB26zpktblb9O1jSJdw== X-OriginatorOrg: nxp.com X-MS-Exchange-CrossTenant-Network-Message-Id: 5dea79be-c766-4c07-8da8-08d7d18cd92f X-MS-Exchange-CrossTenant-OriginalArrivalTime: 26 Mar 2020 13:51:56.8961 (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: zgD/RQKAPVxlVfHEsrEXvZFviVSpigvXXu/EMFZo5ku1VNGLD3/vvjEc8LJvpZfO1SD/5a0ngy40eAWUW7owhW1SfIQ5Ez39PKKF1I8xhR8= X-MS-Exchange-Transport-CrossTenantHeadersStamped: VI1PR04MB4272 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 | 3 +- .../devicetree/bindings/net/ethernet-phy.yaml | 53 +++++++++++++ Documentation/devicetree/bindings/net/serdes.yaml | 90 ++++++++++++++++++++++ 3 files changed, 145 insertions(+), 1 deletion(-) 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 ac471b6..541cee5 100644 --- a/Documentation/devicetree/bindings/net/ethernet-controller.yaml +++ b/Documentation/devicetree/bindings/net/ethernet-controller.yaml @@ -93,8 +93,9 @@ properties: - rxaui - xaui - # 10GBASE-KR, XFI, SFI + # 10GBASE-KR, 40GBASE-KR4, XFI, SFI - 10gbase-kr + - 40gbase-kr4 - usxgmii phy-mode: diff --git a/Documentation/devicetree/bindings/net/ethernet-phy.yaml b/Documentation/devicetree/bindings/net/ethernet-phy.yaml index 8927941..2fb377e 100644 --- a/Documentation/devicetree/bindings/net/ethernet-phy.yaml +++ b/Documentation/devicetree/bindings/net/ethernet-phy.yaml @@ -158,6 +158,42 @@ properties: description: Specifies a reference to a node representing a SFP cage. + eq-algorithm: + 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 + description: + Specifies the desired equalization algorithm to be used + by the KR link training + + 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, post-cursor, main-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 @@ -180,3 +216,20 @@ examples: reset-deassert-us = <2000>; }; }; + - | + /* Backplane configurations for specific setup */ + &mdio9 { + bpphy6: ethernet-phy@0 { + compatible = "ethernet-phy-ieee802.3-c45"; + reg = <0x0>; + lane-handle = <&lane_d>; /* use lane D */ + eq-algorithm = "bee"; + /* 10G Short cables setup: up to 30 cm cable */ + eq-init = <0x2 0x5 0x29>; + eq-params = <0>; + }; + }; + &mac9 { + phy-connection-type = "10gbase-kr"; + phy-handle = <&bpphy6>; + }; diff --git a/Documentation/devicetree/bindings/net/serdes.yaml b/Documentation/devicetree/bindings/net/serdes.yaml new file mode 100644 index 0000000..965152f --- /dev/null +++ b/Documentation/devicetree/bindings/net/serdes.yaml @@ -0,0 +1,90 @@ +# SPDX-License-Identifier: GPL-2.0 +%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 + - const: serdes-28g + description: SerDes module type of 28G + + reg: + description: + Registers memory map offset and size for this serdes module + + reg-names: + description: + Names of the register map given in "reg" node. + Should be one of the following according to serdes type: + "serdes", "serdes-10g", "serdes-28g" + + little-endian: + description: + Specifies the endianness of serdes module + For complete definition see: + Documentation/devicetree/bindings/common-properties.txt + + #address-cells: + description: Must be <1> + + #size-cells: + description: Must be <1> + + ranges: + description: + Address range of serdes module. + +properties: + $nodename: + pattern: "^lane(@[a-f0-9]+)?$" + + compatible: + oneOf: + - const: lane-10g + description: Lane part of a 10G SerDes module + - const: lane-28g + description: Lane part of a 28G 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. + Should be one of the following: "lane", "serdes-lane" + +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>; + 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"; + }; + }; From patchwork Thu Mar 26 13:51:16 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Florinel Iordache X-Patchwork-Id: 1261993 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=none (no SPF record) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; 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=GjY7sALj; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 48p5yG75dyz9sSb for ; Fri, 27 Mar 2020 00:52:06 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727898AbgCZNwF (ORCPT ); Thu, 26 Mar 2020 09:52:05 -0400 Received: from mail-eopbgr60066.outbound.protection.outlook.com ([40.107.6.66]:1666 "EHLO EUR04-DB3-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1726359AbgCZNwE (ORCPT ); Thu, 26 Mar 2020 09:52:04 -0400 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=F8KN6ZsPyweDML1x8BljFnFFxcfgDLkICzujuKCxUFaGAPBbzG4GHg7m1IVl6NTrrLwKi0EQxAmdhRU5NO95Z414sZmPit2my+ceF9xQNt7ivg1EQmR0O+4+kgbhn4sr2blsw2jq4cR+RVpkXwd2aZaokHwH5i8GIhnvqCf4UaU6YtQJJfK847lRjwnNgKDOIy8wM1dOwbMoyuLeQnil9eqg/lPsZTbd+47naGOkJEfpzPLpfHQZateT7UjfkuGLADjI6/7QqHx+3lBbRV4bFhxLf8+wwnVP3X7ATyUHWqGJiZq2bGia4l2Cp1LZD8387ZvpuulXAnTCUP4dHp7Z5A== 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=RAw6aH0zPo4YrDfsW7Mz35nUyx86Z76nqlXQm8luP0s=; b=CXnwBlNB5gdyLcgEGRxHIFQ5pfP8Bzwdmc3y7WfXuOU9ddI56MRD3rHRays47Krh1bkCyPBvaPRojtrsid+rBiCQMxqxrNeZCphmJ0YId6snWHyMerc8yHo21+lDdolAk7KuZTmtkLLDFkrmgnVNn3SC2NQtfYLfF1L4FScwgTZqk5dSEMH+zW/derqvHFgKTMEZluF8RC2gCF3h9X6V18CkguaiUT/25zozvkXwC9EpXHcKJbSRLEEm0p11OoRUxOo1+aEi8Ed09FOzlwmj0Iyumsr37NQ3Wi2wrZ6h+sVqRgajLJAtuqPFpJiRvVKJUJMJTg2ZmnkQnfz5O51BpQ== 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=RAw6aH0zPo4YrDfsW7Mz35nUyx86Z76nqlXQm8luP0s=; b=GjY7sALjXDpIWSaunq4e6Yx8AYbzMguOVgOGGCqCxtmV53GULSvsuuHGnnX1JXiYYT4/vQQb3l1PHhAXj1eWv96LRWp7dC0mKlB8wndQt3jgPF7RV/h03w64s+MZ/Ow+emXEuMfNNwegI1ucUWkKkL9wEPHrXV1Jj5SUojzUX30= Authentication-Results: spf=none (sender IP is ) smtp.mailfrom=florinel.iordache@nxp.com; Received: from VI1PR04MB5454.eurprd04.prod.outlook.com (20.178.122.87) by VI1PR04MB4272.eurprd04.prod.outlook.com (10.171.182.33) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.2835.20; Thu, 26 Mar 2020 13:52:00 +0000 Received: from VI1PR04MB5454.eurprd04.prod.outlook.com ([fe80::69f6:5d59:b505:a6c8]) by VI1PR04MB5454.eurprd04.prod.outlook.com ([fe80::69f6:5d59:b505:a6c8%3]) with mapi id 15.20.2835.023; Thu, 26 Mar 2020 13:52:00 +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 3/9] net: phy: add kr phy connection type Date: Thu, 26 Mar 2020 15:51:16 +0200 Message-Id: <1585230682-24417-4-git-send-email-florinel.iordache@nxp.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1585230682-24417-1-git-send-email-florinel.iordache@nxp.com> References: <1585230682-24417-1-git-send-email-florinel.iordache@nxp.com> Reply-to: florinel.iordache@nxp.com X-ClientProxiedBy: AM0PR01CA0142.eurprd01.prod.exchangelabs.com (2603:10a6:208:168::47) To VI1PR04MB5454.eurprd04.prod.outlook.com (2603:10a6:803:d1::23) MIME-Version: 1.0 X-MS-Exchange-MessageSentRepresentingType: 1 Received: from fsr-ub1464-128.ea.freescale.net (89.37.124.34) by AM0PR01CA0142.eurprd01.prod.exchangelabs.com (2603:10a6:208:168::47) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256) id 15.20.2835.20 via Frontend Transport; Thu, 26 Mar 2020 13:51:59 +0000 X-Mailer: git-send-email 1.9.1 X-Originating-IP: [89.37.124.34] X-MS-PublicTrafficType: Email X-MS-Office365-Filtering-HT: Tenant X-MS-Office365-Filtering-Correlation-Id: 477eff74-8442-432a-ca68-08d7d18cdb6e X-MS-TrafficTypeDiagnostic: VI1PR04MB4272:|VI1PR04MB4272: X-MS-Exchange-Transport-Forked: True X-Microsoft-Antispam-PRVS: X-MS-Oob-TLC-OOBClassifiers: OLM:4941; X-Forefront-PRVS: 0354B4BED2 X-Forefront-Antispam-Report: SFV:NSPM; SFS:(10009020)(4636009)(39860400002)(376002)(346002)(136003)(366004)(396003)(26005)(478600001)(186003)(16526019)(956004)(81166006)(3450700001)(36756003)(81156014)(8936002)(44832011)(4326008)(8676002)(5660300002)(2616005)(7416002)(2906002)(66946007)(86362001)(6486002)(66556008)(316002)(6506007)(6512007)(6666004)(66476007)(52116002); DIR:OUT; SFP:1101; SCL:1; SRVR:VI1PR04MB4272; H:VI1PR04MB5454.eurprd04.prod.outlook.com; FPR:; SPF:None; LANG:en; PTR:InfoNoRecords; Received-SPF: None (protection.outlook.com: nxp.com does not designate permitted sender hosts) X-MS-Exchange-SenderADCheck: 1 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: nhAao0CsR/ePouUm7vf7krAUr368yxmZLeHNSkA1KHUVsoinNscDG2kSjTABbA5LJKBf+/Tn0w8/vbHh/2nwZ3Tb+NI3hWYvrPyn80WocaKf96ZTXDcM/Z6iL+cVhEIt3Yrb9vEuyUdbGlCLEbIINgUsoZ0sfduUANyDb/1i9PBiFj7ytSkNi9O8DS6yqkUgQMTkGucU49Yrr/uyzjT60GICG2S8btV+HpeCdq2K/uxOiV6DSNiw45VvoOcNS6HxSu0YVTxzFcfpv/2qn4U//gXQa+l1J6Z9qq3Py5bKEnQ+fo0WPo5CDjqf1EHocdsUJmqcVTvh6Jzf8JVTI8jeJ9fTHCKSSDdXAXSbnCFJQ0i4y1krTSC2n64PtF5lD57A18LON9MShs/wxa0JfWc++7Jd7mcqh3gD+4ixYg45MsGNEzI6hdARVBlLDtb6/1HL X-MS-Exchange-AntiSpam-MessageData: eT7ZPghrRWfCCPfU151RrPixd2yzeh6mOsRxWG6mkVrtxa9OSye3rDRpzduwvwxV17SV5Fu7fKEXdPJjaT86pk4JH9GxAjHTqNNn9qkmnj76quFWPXsRzoPs5x7C0mibNsn52j4n+LXlnCnpKKkr8w== X-OriginatorOrg: nxp.com X-MS-Exchange-CrossTenant-Network-Message-Id: 477eff74-8442-432a-ca68-08d7d18cdb6e X-MS-Exchange-CrossTenant-OriginalArrivalTime: 26 Mar 2020 13:52:00.4621 (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: QJwDWkO2RWuy/dN7W8az16cp526xLUfa8cAFX0MmO5guLWczAHU7bRgxsMXK9pyzSRBh+Ilojx+hILkPcx0dS5zN0k7FqFgDmwug+oXoUfY= X-MS-Exchange-Transport-CrossTenantHeadersStamped: VI1PR04MB4272 Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Add support for backplane kr phy connection types currently available (10gbase-kr, 40gbase-kr4) and the required phylink updates (cover all the cases for KR modes which are clause 45 compatible to correctly assign phy_interface and phylink#supported) Signed-off-by: Florinel Iordache --- drivers/net/phy/phylink.c | 15 ++++++++++++--- include/linux/phy.h | 6 +++++- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index fed0c59..db1bb87 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -4,6 +4,7 @@ * technologies such as SFP cages where the PHY is hot-pluggable. * * Copyright (C) 2015 Russell King + * Copyright 2020 NXP */ #include #include @@ -303,7 +304,6 @@ static int phylink_parse_mode(struct phylink *pl, struct fwnode_handle *fwnode) break; case PHY_INTERFACE_MODE_USXGMII: - case PHY_INTERFACE_MODE_10GKR: case PHY_INTERFACE_MODE_10GBASER: phylink_set(pl->supported, 10baseT_Half); phylink_set(pl->supported, 10baseT_Full); @@ -317,7 +317,6 @@ static int phylink_parse_mode(struct phylink *pl, struct fwnode_handle *fwnode) phylink_set(pl->supported, 2500baseX_Full); phylink_set(pl->supported, 5000baseT_Full); phylink_set(pl->supported, 10000baseT_Full); - phylink_set(pl->supported, 10000baseKR_Full); phylink_set(pl->supported, 10000baseKX4_Full); phylink_set(pl->supported, 10000baseCR_Full); phylink_set(pl->supported, 10000baseSR_Full); @@ -326,6 +325,14 @@ static int phylink_parse_mode(struct phylink *pl, struct fwnode_handle *fwnode) phylink_set(pl->supported, 10000baseER_Full); break; + case PHY_INTERFACE_MODE_10GKR: + phylink_set(pl->supported, 10000baseKR_Full); + break; + + case PHY_INTERFACE_MODE_40GKR4: + phylink_set(pl->supported, 40000baseKR4_Full); + break; + case PHY_INTERFACE_MODE_XLGMII: phylink_set(pl->supported, 25000baseCR_Full); phylink_set(pl->supported, 25000baseKR_Full); @@ -823,7 +830,9 @@ static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy, if (phy->is_c45 && interface != PHY_INTERFACE_MODE_RXAUI && interface != PHY_INTERFACE_MODE_XAUI && - interface != PHY_INTERFACE_MODE_USXGMII) + interface != PHY_INTERFACE_MODE_USXGMII && + interface != PHY_INTERFACE_MODE_10GKR && + interface != PHY_INTERFACE_MODE_40GKR4) config.interface = PHY_INTERFACE_MODE_NA; else config.interface = interface; diff --git a/include/linux/phy.h b/include/linux/phy.h index 2432ca4..d7cca4b 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -6,6 +6,7 @@ * Author: Andy Fleming * * Copyright (c) 2004 Freescale Semiconductor, Inc. + * Copyright 2020 NXP */ #ifndef __PHY_H @@ -107,8 +108,9 @@ /* 10GBASE-R, XFI, SFI - single lane 10G Serdes */ PHY_INTERFACE_MODE_10GBASER, PHY_INTERFACE_MODE_USXGMII, - /* 10GBASE-KR - with Clause 73 AN */ + /* Backplane KR */ PHY_INTERFACE_MODE_10GKR, + PHY_INTERFACE_MODE_40GKR4, PHY_INTERFACE_MODE_MAX, } phy_interface_t; @@ -190,6 +192,8 @@ static inline const char *phy_modes(phy_interface_t interface) return "usxgmii"; case PHY_INTERFACE_MODE_10GKR: return "10gbase-kr"; + case PHY_INTERFACE_MODE_40GKR4: + return "40gbase-kr4"; default: return "unknown"; } From patchwork Thu Mar 26 13:51:17 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Florinel Iordache X-Patchwork-Id: 1261994 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=none (no SPF record) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; 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=UaJ0QzNh; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 48p5yN504Bz9sSY for ; Fri, 27 Mar 2020 00:52:12 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727923AbgCZNwJ (ORCPT ); Thu, 26 Mar 2020 09:52:09 -0400 Received: from mail-eopbgr70054.outbound.protection.outlook.com ([40.107.7.54]:4353 "EHLO EUR04-HE1-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1727904AbgCZNwH (ORCPT ); Thu, 26 Mar 2020 09:52:07 -0400 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=jrCtr1BneTuIsdhBdNdnCltZufWA4tdumXv2DrBpZ/SDFxKwLCpE3UfPDm807u9Am0KbikuKsazr1KzkapjsOwnTZJW6xRlr6Q9wOQZ835dR8BBnr/IZNWIceG1T4SgpDpARu6/JiZDvwVaq0VjoVFsssoH1d6RrggTHmLyJZYPYDhIxE0RR7Jixm1wPi5yvPtp/KEniSj0YHEbpjNQMIPdYnlQxeDyI2vBxJkknvCLFOfs/g9QCxsnduLxDPROtnoX1CwPD5t3i8mTACln0iBfOErW9BOfrHlATBa9nlLm4V3UNsAD+iR0/CgrB2/Yzq5yCIvVPCFnv/5P04ws4+w== 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=jCgxEQBHT7EzWRsSGeTUQQsOxGXtIWgkOPATcnH9/zRou811pJ1vm7Y9RiuSOwBfM7NouWeOMd6xidbBPLTN0F/SuirnJ2FxfwuHGmFEJJyatVd+eB/tyj2id00rtXyZ/iZ6anAP4CSx6FWkWWoOdC/Y/dfF9m2uuOK4AyA4NxV1tcm2hTPQEG0mDDhAZPikr04KLxcK4nXF6mIBy9JptAxc4CH2q6QAwkStgFenAljaWu49B4LBL4Ugb7k6FTfLs2Y2mI7bHPd3qFA1zkl/gM14js/T8uuuCh/GW9tcGbx+v+wOEANQpFcMhhIl9PTjjEFTDi+ZAIs0l2Bd5/XJRw== 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=UaJ0QzNhcEPxGLvYvK3lEUvgheR5/IqLBY5Qgvk7U8i7IkugxV+M2J5odFKOmzuY+sprcPxix66G7QEVCRixqCWGX8/AmLM45NHSmVMZLa8rQK0XlQGGWq7J74d6j67iL7g+UHmMT63YCqYX99yFo/iVN8p4BVsrAfYSj4ih00c= Authentication-Results: spf=none (sender IP is ) smtp.mailfrom=florinel.iordache@nxp.com; Received: from VI1PR04MB5454.eurprd04.prod.outlook.com (20.178.122.87) by VI1PR04MB4272.eurprd04.prod.outlook.com (10.171.182.33) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.2835.20; Thu, 26 Mar 2020 13:52:04 +0000 Received: from VI1PR04MB5454.eurprd04.prod.outlook.com ([fe80::69f6:5d59:b505:a6c8]) by VI1PR04MB5454.eurprd04.prod.outlook.com ([fe80::69f6:5d59:b505:a6c8%3]) with mapi id 15.20.2835.023; Thu, 26 Mar 2020 13:52:04 +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 4/9] net: fman: add kr support for dpaa1 mac Date: Thu, 26 Mar 2020 15:51:17 +0200 Message-Id: <1585230682-24417-5-git-send-email-florinel.iordache@nxp.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1585230682-24417-1-git-send-email-florinel.iordache@nxp.com> References: <1585230682-24417-1-git-send-email-florinel.iordache@nxp.com> Reply-to: florinel.iordache@nxp.com X-ClientProxiedBy: AM0PR01CA0142.eurprd01.prod.exchangelabs.com (2603:10a6:208:168::47) To VI1PR04MB5454.eurprd04.prod.outlook.com (2603:10a6:803:d1::23) MIME-Version: 1.0 X-MS-Exchange-MessageSentRepresentingType: 1 Received: from fsr-ub1464-128.ea.freescale.net (89.37.124.34) by AM0PR01CA0142.eurprd01.prod.exchangelabs.com (2603:10a6:208:168::47) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256) id 15.20.2835.20 via Frontend Transport; Thu, 26 Mar 2020 13:52:02 +0000 X-Mailer: git-send-email 1.9.1 X-Originating-IP: [89.37.124.34] X-MS-PublicTrafficType: Email X-MS-Office365-Filtering-HT: Tenant X-MS-Office365-Filtering-Correlation-Id: 3f55782a-428e-49f8-b8df-08d7d18cdd70 X-MS-TrafficTypeDiagnostic: VI1PR04MB4272:|VI1PR04MB4272: X-MS-Exchange-Transport-Forked: True X-Microsoft-Antispam-PRVS: X-MS-Oob-TLC-OOBClassifiers: OLM:2150; X-Forefront-PRVS: 0354B4BED2 X-Forefront-Antispam-Report: SFV:NSPM; SFS:(10009020)(4636009)(39860400002)(376002)(346002)(136003)(366004)(396003)(26005)(478600001)(186003)(16526019)(956004)(81166006)(3450700001)(36756003)(81156014)(8936002)(44832011)(4326008)(8676002)(5660300002)(2616005)(7416002)(2906002)(66946007)(86362001)(6486002)(66556008)(316002)(6506007)(6512007)(6666004)(66476007)(52116002); DIR:OUT; SFP:1101; SCL:1; SRVR:VI1PR04MB4272; H:VI1PR04MB5454.eurprd04.prod.outlook.com; FPR:; SPF:None; LANG:en; PTR:InfoNoRecords; Received-SPF: None (protection.outlook.com: nxp.com does not designate permitted sender hosts) X-MS-Exchange-SenderADCheck: 1 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: KmJ7V77qs6SXMyBy+RKhtaP1sz6bvGuxOzbYKU9t5CPuwVZ4w4x17cxXjqf/O3VzEfvMkvHwLSajX9X5MwNIBVIaqlchyfR1YGmBd4d7PfRD8x1H/J3H+817t67k7PKlxODwRZgu9wQjOOdf9zxrHY8kBgUahicRC16H09O56zcM9VrNX2ZmJJAIYnVfyIPaHw64G4sDUssrHPn4yOc2UghIydkRTVGF+cONTWN9WmCinp0cLpDGwqbN38d03dgvR/FpLuGvJuI0vPNgJIVYqye5KQnph70RVqDJeBmKimjLRKbO/JtmnLvBAZZNsJx09Y2Lt9U3sSo8W4FSzjeegNzKMxrydeKwy90NieGJ9X7+zja7ZVA3HNSk62H5YFU59EVuS015O/HUoeuHgz5uZseySSguFtBnJXT1wwQ+25VWHENtMw2X53/+RhHjbZbN X-MS-Exchange-AntiSpam-MessageData: l0rwQfmhoI7uAr/9C+gOAlQrB91GysVWDz+TtkOQvgrZSRceXDdJWCdNwvSz55elTd3MhbdpN33RfN8vYiNZBMoZy2B8Fdt29sjDHQrN/21uPeUM44fh+s7LD40ef0/qZKhhLxV7BXFu2zMXEEL8UQ== X-OriginatorOrg: nxp.com X-MS-Exchange-CrossTenant-Network-Message-Id: 3f55782a-428e-49f8-b8df-08d7d18cdd70 X-MS-Exchange-CrossTenant-OriginalArrivalTime: 26 Mar 2020 13:52:03.8922 (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: S+bJpx31H1hDRU92XUBm6wivQ53mlUIokyHeMzyCVYPEP6SKU9Ph/gH8h262i6wEx1x+BBe40TroEfZTt8ZnNftJcl1hjw9Rxh8PCAQG4o4= X-MS-Exchange-Transport-CrossTenantHeadersStamped: VI1PR04MB4272 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 Thu Mar 26 13:51: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: 1261995 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=none (no SPF record) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; 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=R1XF3c44; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 48p5yR2tqyz9sT4 for ; Fri, 27 Mar 2020 00:52:15 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727940AbgCZNwO (ORCPT ); Thu, 26 Mar 2020 09:52:14 -0400 Received: from mail-eopbgr60067.outbound.protection.outlook.com ([40.107.6.67]:55298 "EHLO EUR04-DB3-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1727733AbgCZNwN (ORCPT ); Thu, 26 Mar 2020 09:52:13 -0400 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=AzNN7gAVENZQf1wcNDlOoaVWKQ3QR2JDMRn0Mcz2zmjI6deTEcNqSTAJODhO+ZA0kdwwn/j/XEfhTeYAxzRX+o21Za8ilFrbzoxKEoLM3Kw3RXzqtqSP5SgzChVmCZz/2TYn0kEToPYDT3Zxmt/R7UefzhXsyLJ+cP5IcrzidZzhWbdpFEv5Td4mTbZTjc2L1qCF576DHb0aFcViOTBfjlNSwkc9W9in+RONmXArXHRpvnDHr5mH2G3X/q/Pe2+qJU+lyEiJSHvze8Znp4XLOZxVL5oo4oYcFqKTEc+JoItz9uer9xv7J4kDNcdjdSNaRA17+IOadjcgRHHW3wcWXg== 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=/JjBwpsa1ZjaIS4MP7/u+CF5st/pQ3hnCG20ywtO62s=; b=n9KpJMYbyy1u+hTo8yb7zWxmxID/ve1Wcsp3kBB51q73QYhF535zKoFGpD2kuPypsrGS95qMI4YJwiRJE4qZ9wcW7mahC1dkYmejCcm83m43M0XJjbM7SNlAtELLFaUIua/bBTwS+bP+pV+NByQp8RGk3VWlt6vB7l4bJhknnae43PzDcXHCxWEULcRZB5mjNI5s057pzne5GcYyj9TkAS0svHrlsIyzBTNLL5CVHZDXblDNe0GJmfTirRr9o4Qivp1mBScRZbvvpBoPQekRQlDhu0ouddNRMYw+VYkefhLF7S/L9PtSGzAr0XxHTtPAaAUzGeGLQEr69RXnheZmIw== 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=/JjBwpsa1ZjaIS4MP7/u+CF5st/pQ3hnCG20ywtO62s=; b=R1XF3c44nHVvscpIHnZ8BquoTk/CUn18ptp6rsYTCOiaNFM7kccBycw/j2RUqgyjZcbUWJHHN5e4GPYWPW12zEXspt5ZnfJSmhwqYzJi6qg7iPcPe5Yw9AlL2CRU3siOgOavcX+jVocJK6SCEMFqaf9iN82uTIseWCLEOiJSMn4= Authentication-Results: spf=none (sender IP is ) smtp.mailfrom=florinel.iordache@nxp.com; Received: from VI1PR04MB5454.eurprd04.prod.outlook.com (20.178.122.87) by VI1PR04MB4272.eurprd04.prod.outlook.com (10.171.182.33) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.2835.20; Thu, 26 Mar 2020 13:52:09 +0000 Received: from VI1PR04MB5454.eurprd04.prod.outlook.com ([fe80::69f6:5d59:b505:a6c8]) by VI1PR04MB5454.eurprd04.prod.outlook.com ([fe80::69f6:5d59:b505:a6c8%3]) with mapi id 15.20.2835.023; Thu, 26 Mar 2020 13:52:09 +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 5/9] net: dpaa2: add kr support for dpaa2 mac Date: Thu, 26 Mar 2020 15:51:18 +0200 Message-Id: <1585230682-24417-6-git-send-email-florinel.iordache@nxp.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1585230682-24417-1-git-send-email-florinel.iordache@nxp.com> References: <1585230682-24417-1-git-send-email-florinel.iordache@nxp.com> Reply-to: florinel.iordache@nxp.com X-ClientProxiedBy: AM0PR01CA0142.eurprd01.prod.exchangelabs.com (2603:10a6:208:168::47) To VI1PR04MB5454.eurprd04.prod.outlook.com (2603:10a6:803:d1::23) MIME-Version: 1.0 X-MS-Exchange-MessageSentRepresentingType: 1 Received: from fsr-ub1464-128.ea.freescale.net (89.37.124.34) by AM0PR01CA0142.eurprd01.prod.exchangelabs.com (2603:10a6:208:168::47) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256) id 15.20.2835.20 via Frontend Transport; Thu, 26 Mar 2020 13:52:08 +0000 X-Mailer: git-send-email 1.9.1 X-Originating-IP: [89.37.124.34] X-MS-PublicTrafficType: Email X-MS-Office365-Filtering-HT: Tenant X-MS-Office365-Filtering-Correlation-Id: 4e1747a1-cf3f-45ec-d9c5-08d7d18ce0ca X-MS-TrafficTypeDiagnostic: VI1PR04MB4272:|VI1PR04MB4272: X-MS-Exchange-Transport-Forked: True X-Microsoft-Antispam-PRVS: X-MS-Oob-TLC-OOBClassifiers: OLM:1303; X-Forefront-PRVS: 0354B4BED2 X-Forefront-Antispam-Report: SFV:NSPM; SFS:(10009020)(4636009)(39860400002)(376002)(346002)(136003)(366004)(396003)(26005)(478600001)(186003)(16526019)(956004)(81166006)(3450700001)(36756003)(81156014)(8936002)(44832011)(4326008)(8676002)(5660300002)(2616005)(7416002)(2906002)(66946007)(86362001)(6486002)(66556008)(316002)(6506007)(6512007)(6666004)(66476007)(52116002); DIR:OUT; SFP:1101; SCL:1; SRVR:VI1PR04MB4272; H:VI1PR04MB5454.eurprd04.prod.outlook.com; FPR:; SPF:None; LANG:en; PTR:InfoNoRecords; Received-SPF: None (protection.outlook.com: nxp.com does not designate permitted sender hosts) X-MS-Exchange-SenderADCheck: 1 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: Jlt8+pA4B2hDjap5nyYuS+auEbUnUbdEcexkYEe6ffRZNVi6vQyJHDgr3wkkhSL8beRi94TIrXfFvfKCLfdFghyfvFXT1+xBAyB1FPFWbgHpURhQ1YclWTVCHeAH80KBsfBqkGLdkKydV3o0RzcfQmntGaggwkujYOiKE9lGuCRF5MiunY94a4WjlBy8EwiUjqVk1QxJ/SF0tKbXkmhptZ21rSYGsPbSIXTf2J7YLYUIZA4X2yh2IEDGFu2jX5HmpxCNBiynNZEmjnoLs8Qlhfuv0mXD6Siz36ZuJK4jgLuqsMdx5WoIk8he7qvnHqMEnaYcxuUX+19rGyp5amttkUE+ugjr+L7Z5n0cGcM1Xi42Rqvd+DYsThRmizIr5ScTZ/a2P+gyZkRQvSdRjT4XeS1BhfComKaTlX/W7gv9NX79kz882o/V4G4MDustlFN3 X-MS-Exchange-AntiSpam-MessageData: 4+fFlkOIJaV0tYpduKgN8isLGrlAxyYbaTw7cl9+8P8Q2sZfBOlQRkWK0N6UGZ5RBMFm9CkUJffW9fb2s1an8w1iCJcgujQs3UBhlx3gFjhP6PPLixLImP7ra3ppFA5cv3kF6CmcPR8jmfRZAm7zkg== X-OriginatorOrg: nxp.com X-MS-Exchange-CrossTenant-Network-Message-Id: 4e1747a1-cf3f-45ec-d9c5-08d7d18ce0ca X-MS-Exchange-CrossTenant-OriginalArrivalTime: 26 Mar 2020 13:52:09.4960 (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: fZvh4YiyQ4SKzFZtEOeiIyZvS9hji5xWrnwYNBP/jiWSCEJ0luYSVvMMpqy8AvRun4L/BfVEh6Qk/Gb90RwsJoTVxcPHi2rJ0YSiB1WPT+s= X-MS-Exchange-Transport-CrossTenantHeadersStamped: VI1PR04MB4272 Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Add kr support in mac driver for dpaa2 Signed-off-by: Florinel Iordache --- drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c index 3ee236c..f3c384c 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) -/* Copyright 2019 NXP */ +/* Copyright 2019, 2020 NXP */ #include "dpaa2-eth.h" #include "dpaa2-mac.h" @@ -71,6 +71,8 @@ static bool dpaa2_mac_phy_mode_mismatch(struct dpaa2_mac *mac, case PHY_INTERFACE_MODE_RGMII_ID: case PHY_INTERFACE_MODE_RGMII_RXID: case PHY_INTERFACE_MODE_RGMII_TXID: + case PHY_INTERFACE_MODE_10GKR: + case PHY_INTERFACE_MODE_40GKR4: return (interface != mac->if_mode); default: return true; @@ -103,6 +105,12 @@ static void dpaa2_mac_validate(struct phylink_config *config, phylink_set(mask, 100baseT_Full); phylink_set(mask, 1000baseT_Full); break; + case PHY_INTERFACE_MODE_10GKR: + phylink_set(mask, 10000baseKR_Full); + break; + case PHY_INTERFACE_MODE_40GKR4: + phylink_set(mask, 40000baseKR4_Full); + break; default: goto empty_set; } From patchwork Thu Mar 26 13:51: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: 1261996 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=none (no SPF record) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; 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=O4U98BTt; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 48p5yv5dzMz9sT1 for ; Fri, 27 Mar 2020 00:52:39 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727973AbgCZNwe (ORCPT ); Thu, 26 Mar 2020 09:52:34 -0400 Received: from mail-eopbgr60089.outbound.protection.outlook.com ([40.107.6.89]:57111 "EHLO EUR04-DB3-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1727733AbgCZNwe (ORCPT ); Thu, 26 Mar 2020 09:52:34 -0400 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=mbIaKmFpxWybnXif/pUabYu75+kHvv5cXc8Tkj0+NC79tLVt8qf7dwQX1vrwd9B3ybqLbgJlYKKcrDn6tO1sCW1oF8vqIzINoc2ozOkQhxJAUSwZD8dauvf7VfpEn/knSzNR5UAH2p7WFlkkAblaMAjdkMnnbrikrfFqJdguSumNEQO5svK3/bdwgFcVXY7Ln4eoJb0NMOE42oMobJfurbfbvxA5fLSHtdWvDWaEFDSue0sZn4txUO+k+ggXZtvX0V5s766KQOAvIeBS5/7AyUrcu01vKp56S8I7Yd6Pw4Hr8JjvHc08W1EcV7XXqzglUKcBtXhrQ1TNkuBhbXpX5w== 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=M4UM+jO7/Y+hKjL377Mm1nilPThbE8dmXjZPI7dybpU=; b=a8n7uDk/ESA3qRLulplPk1xHDyYuI5s+pJIwDwkGR243diRX5zfD7wukTNvNXOt/mdfTmE4ItcgIFtwkKowK/ZpyWAS9Nme7g/MotjxB75DclDXFwV1bEAlgBIDiaOnCHAf8mphFrmkChOkd9AU7yHUWb7t76ESgNlZij9Ac/g5zXtCZTn+i6+WNmNgnhc4Yb4ad/MW+Ecxw/EdmDi31L0t+ASuPi9YTnZK//4irGZG6yCUAjouG+3fP9OQmdTM+APajBGHDA03BJqsUhSdeIlto6Onz/QSLM9e1qv2EpQDMfuZrQTMcXtXTirjX3D/GKCQYXetQD4CQjyjuzIdm+A== 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=M4UM+jO7/Y+hKjL377Mm1nilPThbE8dmXjZPI7dybpU=; b=O4U98BTtuvc8s5ZiOoH6JxMEQpQLZXJGAFuAE797fUOvHHV0NeoLqBUT/e2BIJaa9OcdGMbmYEqEVNHyPCmsNcM12IjS0jRn37dlVq8aJ1HEIb0Y34iniLbowLBqsRGD4LWI9xbfFDUHpAr4/K1jFYLLRiElWrZEhyqma99fA1E= Authentication-Results: spf=none (sender IP is ) smtp.mailfrom=florinel.iordache@nxp.com; Received: from VI1PR04MB5454.eurprd04.prod.outlook.com (20.178.122.87) by VI1PR04MB4272.eurprd04.prod.outlook.com (10.171.182.33) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.2835.20; Thu, 26 Mar 2020 13:52:12 +0000 Received: from VI1PR04MB5454.eurprd04.prod.outlook.com ([fe80::69f6:5d59:b505:a6c8]) by VI1PR04MB5454.eurprd04.prod.outlook.com ([fe80::69f6:5d59:b505:a6c8%3]) with mapi id 15.20.2835.023; Thu, 26 Mar 2020 13:52:12 +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 6/9] net: phy: add backplane kr driver support Date: Thu, 26 Mar 2020 15:51:19 +0200 Message-Id: <1585230682-24417-7-git-send-email-florinel.iordache@nxp.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1585230682-24417-1-git-send-email-florinel.iordache@nxp.com> References: <1585230682-24417-1-git-send-email-florinel.iordache@nxp.com> Reply-to: florinel.iordache@nxp.com X-ClientProxiedBy: AM0PR01CA0142.eurprd01.prod.exchangelabs.com (2603:10a6:208:168::47) To VI1PR04MB5454.eurprd04.prod.outlook.com (2603:10a6:803:d1::23) MIME-Version: 1.0 X-MS-Exchange-MessageSentRepresentingType: 1 Received: from fsr-ub1464-128.ea.freescale.net (89.37.124.34) by AM0PR01CA0142.eurprd01.prod.exchangelabs.com (2603:10a6:208:168::47) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256) id 15.20.2835.20 via Frontend Transport; Thu, 26 Mar 2020 13:52:11 +0000 X-Mailer: git-send-email 1.9.1 X-Originating-IP: [89.37.124.34] X-MS-PublicTrafficType: Email X-MS-Office365-Filtering-HT: Tenant X-MS-Office365-Filtering-Correlation-Id: 9ecc01e6-9461-4fe3-6abb-08d7d18ce2b0 X-MS-TrafficTypeDiagnostic: VI1PR04MB4272:|VI1PR04MB4272: X-MS-Exchange-Transport-Forked: True X-Microsoft-Antispam-PRVS: X-MS-Oob-TLC-OOBClassifiers: OLM:2399; X-Forefront-PRVS: 0354B4BED2 X-Forefront-Antispam-Report: SFV:NSPM; SFS:(10009020)(4636009)(39860400002)(376002)(346002)(136003)(366004)(396003)(26005)(478600001)(186003)(16526019)(956004)(81166006)(3450700001)(36756003)(81156014)(8936002)(44832011)(4326008)(8676002)(5660300002)(2616005)(7416002)(2906002)(66946007)(86362001)(6486002)(30864003)(66556008)(316002)(6506007)(6512007)(66476007)(52116002)(579004)(559001); DIR:OUT; SFP:1101; SCL:1; SRVR:VI1PR04MB4272; H:VI1PR04MB5454.eurprd04.prod.outlook.com; FPR:; SPF:None; LANG:en; PTR:InfoNoRecords; Received-SPF: None (protection.outlook.com: nxp.com does not designate permitted sender hosts) X-MS-Exchange-SenderADCheck: 1 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: r+5uHRSBUO0jcgTrPSkP1zRIKS4cvK9gHm2y6zo6qysktwLrqiwjzmCoLn74caXKDtL/uFKQPthlRh2bDf37NuJYJvXds65G9SyIzyTQ+rA5nSSm2c9n/O2wkPx9M8Joc+KEGx+O7DOV8d1e1O0TKpsfEaDjRhOF1UJ0pQpvx8YC8SnJfUQzP66fgjaJMiWVTU0725vNh01My7Rvk/2f4VU9v4h6Z3RG+Yv4NCyBhr8hdYJhsKtORR/x+YqCdw5F4nELBDU0cKIM5X/PwZTTM+Lj6koG4A2u6hJTAqVWc0dJVVBMwrjCopQByq9/1cUMoPSQbVl4bpI5SXq60iMxClFD8VFczG6MmDEeY2AM0tLLVu6y+fhV20P2gq2ZtLEPB0nnMhjgRZ+0axZ9alJsSwBEyMPjTlL0+K84P5NJmXsInTWtQKfxESl+eU92LTjI X-MS-Exchange-AntiSpam-MessageData: XgCrfxyh8/eg9+tbfh1VQg9Xg6l+YRIYLGz11R8O685QDFZFafrIlIZpa4sAvjnn9vF6pqdZ/CKn1YB5oqTT8jJwz1ArF9Qw5lLGCbhTu9tTIu0mbT/JfUpLSaKyAKaGFEBGkMUTzV7aqvGoFkGZ0w== X-OriginatorOrg: nxp.com X-MS-Exchange-CrossTenant-Network-Message-Id: 9ecc01e6-9461-4fe3-6abb-08d7d18ce2b0 X-MS-Exchange-CrossTenant-OriginalArrivalTime: 26 Mar 2020 13:52:12.7642 (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: HDRVFaVMHnbK453d+t9WwUKybodzNsSxvePPrwUPNIcEMMUVRzyg89RH7Ml/xnwMBEUVohq/6KpxODc8ygctzitDiaft/hhK5eE/JgXqCV0= X-MS-Exchange-Transport-CrossTenantHeadersStamped: VI1PR04MB4272 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 --- 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 | 1538 +++++++++++++++++++++++++++ drivers/net/phy/backplane/backplane.h | 262 +++++ drivers/net/phy/backplane/eq_fixed.c | 83 ++ drivers/net/phy/backplane/equalization.h | 282 +++++ drivers/net/phy/backplane/link_training.c | 1604 +++++++++++++++++++++++++++++ drivers/net/phy/backplane/link_training.h | 34 + 10 files changed, 3835 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 cc7f1df..abab4e5 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -523,6 +523,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 70774ab..0b867fb 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -101,3 +101,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..1b580bc --- /dev/null +++ b/drivers/net/phy/backplane/backplane.c @@ -0,0 +1,1538 @@ +// 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_DENY_RT_INTERVAL 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 status register (Clause 45) (MMD 7): MDIO_STAT1 */ +#define AN_LINK_UP_MASK 0x04 + +/* Logging buffer size */ +#define LOG_BUFFER_SIZE 200 + +/* Backplane custom logging */ +#define BPDEV_LOG(name) \ + char log_buffer[LOG_BUFFER_SIZE]; \ + va_list args; va_start(args, msg); \ + vsnprintf(log_buffer, LOG_BUFFER_SIZE - 1, msg, args); \ + if (!bpphy->attached_dev) \ + dev_##name(&bpphy->mdio.dev, log_buffer); \ + else \ + dev_##name(&bpphy->mdio.dev, "%s: %s", \ + netdev_name(bpphy->attached_dev), log_buffer); \ + va_end(args) + +/* 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, + ETHTOOL_LINK_MODE_40000baseKR4_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 bp_mode) +{ + switch (bp_mode) { + case PHY_INTERFACE_MODE_10GKR: + return SPEED_10000; + case PHY_INTERFACE_MODE_40GKR4: + return SPEED_40000; + default: + pr_err("%s: Unsupported backplane phy 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 bp_mode) +{ + switch (bp_mode) { + case PHY_INTERFACE_MODE_10GKR: + return ETHTOOL_LINK_MODE_10000baseKR_Full_BIT; + case PHY_INTERFACE_MODE_40GKR4: + return ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT; + default: + pr_err("%s: Unsupported backplane phy 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_krln(struct kr_lane_info *krln, bool revert_default) +{ + if (revert_default) + backplane_default_kr_lane(krln); + + training_status_init(&krln->trst); + krln->state = DETECTING_LP; + krln->an_acquired = false; + + krln->ld_update = 0; + krln->prev_ld_update = 0; + krln->ld_last_nonhold_update = 0; + krln->lp_status = 0; + krln->lp_last_change_status = 0; + krln->last_lp_update_status[C_M1] = 0; + krln->last_lp_update_status[C_Z0] = 0; + krln->last_lp_update_status[C_P1] = 0; + krln->ld_status = 0; + krln->move_back_prev = false; + krln->move_back_cnt = 0; + krln->move_back_lp_status = 0; + + lt_init_ld(krln); +} + +static void setup_supported_linkmode(struct phy_device *bpphy) +{ + struct backplane_phy_info *bp_phy = bpphy->priv; + 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], + bpphy->supported); + + linkmode_set_bit(get_backplane_supported_mode(bp_phy->bp_mode), + bpphy->supported); +} + +/* Read AN Link Status */ +static int is_an_link_up(struct phy_device *bpphy) +{ + struct backplane_phy_info *bp_phy = bpphy->priv; + int ret, val = 0; + + mutex_lock(&bp_phy->bpphy_lock); + + /* Read twice because Link_Status is LL (Latched Low) bit */ + val = phy_read_mmd(bpphy, MDIO_MMD_AN, bp_phy->bp_dev.mdio.an_status); + val = phy_read_mmd(bpphy, MDIO_MMD_AN, bp_phy->bp_dev.mdio.an_status); + + mutex_unlock(&bp_phy->bpphy_lock); + + ret = (val & AN_LINK_UP_MASK) ? 1 : 0; + + return ret; +} + +static void start_kr_state_machine(struct kr_lane_info *krln, u32 timeout) +{ + /* Check if equalization algorithm is installed */ + if (!krln->eq_alg) + return; + + /* Check if link training is used */ + if (!krln->eq_alg->use_local_tx_training && + !krln->eq_alg->use_remote_tx_training) + return; + + queue_delayed_work(system_power_efficient_wq, &krln->kr_wk, + msecs_to_jiffies(timeout)); +} + +static void stop_kr_state_machine(struct kr_lane_info *krln) +{ + /* Check if equalization algorithm is installed */ + if (!krln->eq_alg) + return; + + /* Check if link training is used */ + if (!krln->eq_alg->use_local_tx_training && + !krln->eq_alg->use_remote_tx_training) + return; + + cancel_delayed_work_sync(&krln->kr_wk); +} + +static void setup_default_settings(struct kr_lane_info *krln) +{ + struct lane_kr_params krparam; + + krln->bp_phy->bp_dev.lane_ops->read_lane_kr(krln->reg_base, &krparam); + + if (krln->bp_phy->bp_dev.coef_def_dt) { + krln->def_ratio_preq = krln->bp_phy->bp_dev.cm_def; + krln->def_ratio_pstq = krln->bp_phy->bp_dev.cp_def; + krln->def_adpt_eq = krln->bp_phy->bp_dev.cz_def; + } else { + krln->def_ratio_preq = krparam.ratio_preq; + krln->def_ratio_pstq = krparam.ratio_pstq; + krln->def_adpt_eq = krparam.adpt_eq; + } + + if (krln->bp_phy->bp_dev.ampr_def_dt) + krln->def_amp_red = krln->bp_phy->bp_dev.amp_red_def; + else + krln->def_amp_red = krparam.amp_red; +} + +static void kr_reset_master_lane(struct kr_lane_info *krln) +{ + struct phy_device *bpphy = krln->bpphy; + struct backplane_phy_info *bp_phy = bpphy->priv; + const struct lane_io_ops *lane_ops = krln->bp_phy->bp_dev.lane_ops; + + if (backplane_is_multi_lane(bp_phy)) { + /* Reset only the Master Lane */ + if (krln->idx == MASTER_LANE) + lane_ops->reset_lane(krln->reg_base, LANE_RX_TX); + } else { + lane_ops->reset_lane(krln->reg_base, LANE_RX_TX); + } +} + +static void print_single_lane_trained(struct kr_lane_info *krln) +{ + struct phy_device *bpphy = krln->bpphy; + struct backplane_phy_info *bp_phy = bpphy->priv; + + bpdev_info(bpphy, + "%s link trained, Tx equalization: preq = 0x%x, pstq = 0x%x, adpt_eq = 0x%x\n", + phy_modes(bp_phy->bp_mode), + krln->tuned_ratio_preq, krln->tuned_ratio_pstq, + krln->tuned_adpt_eq); +} + +static void print_multi_lane_trained(struct kr_lane_info *krln) +{ + struct phy_device *bpphy = krln->bpphy; + struct backplane_phy_info *bp_phy = bpphy->priv; + int i; + + bpdev_info(bpphy, + "%s link trained, Tx equalization:\n", + phy_modes(bp_phy->bp_mode)); + + for (i = 0; i < bp_phy->num_lanes; i++) + bpdev_info(bpphy, + "\t|- Lane %d: preq = 0x%x, pstq = 0x%x, adpt_eq = 0x%x\n", + i + 1, bp_phy->krln[i].tuned_ratio_preq, + bp_phy->krln[i].tuned_ratio_pstq, + bp_phy->krln[i].tuned_adpt_eq); +} + +static void kr_link_trained(struct kr_lane_info *krln) +{ + struct phy_device *bpphy = krln->bpphy; + struct backplane_phy_info *bp_phy = bpphy->priv; + + mutex_lock(&bp_phy->trained_lock); + /* Setup lane state as TRAINED inside the phy trained lock + * to avoid duplicated message printed on multi-lane PHYs + */ + krln->state = TRAINED; + + mutex_lock(&backplane_lock); + + if (backplane_is_single_lane(bp_phy)) + print_single_lane_trained(krln); + else + if (backplane_are_all_lanes_trained(bp_phy)) + print_multi_lane_trained(krln); + + mutex_unlock(&backplane_lock); + mutex_unlock(&bp_phy->trained_lock); +} + +static void kr_train_step(struct kr_lane_info *krln) +{ + struct training_status *trst = &krln->trst; + u32 lt_timeout = KR_LT_TIMEOUT; + u64 dead_line; + int i = 0; + + /* Check if equalization algorithm is installed */ + if (!krln->eq_alg) + return; + + /* Check if link training is used */ + if (!krln->eq_alg->use_local_tx_training && + !krln->eq_alg->use_remote_tx_training) + return; + + lt_start(krln); + + 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(krln)) { + /* LT failed already, reset lane to avoid + * it run into hanging, then start LT again. + */ + kr_reset_master_lane(krln); + lt_start(krln); + } else if (lt_is_frame_lock(krln)) { + break; + } + /* wait frame lock (without training_failure) */ + usleep_range(100, 500); + } + + if (!lt_is_frame_lock(krln)) { + 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(krln)) { + kr_reset_master_lane(krln); + break; + } + + if (krln->eq_alg->use_local_tx_training) + lt_train_local_tx(krln); + + if (krln->eq_alg->use_remote_tx_training) + lt_train_remote_tx(krln); + + if (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 (krln->lt_error) { + init_krln(krln, false); + continue; + } else { + break; + } + } + + lt_stop(krln); + + /* check if Link is successfully TRAINED */ + if (lt_is_rx_trained(krln)) + kr_link_trained(krln); + else + kr_reset_master_lane(krln); +} + +static void an_request_restart(struct kr_lane_info *krln) +{ + struct phy_device *bpphy = krln->bpphy; + struct backplane_phy_info *bp_phy = bpphy->priv; + const struct lane_io_ops *lane_ops = krln->bp_phy->bp_dev.lane_ops; + int i; + + if (time_before(jiffies, (unsigned long)krln->rt_time)) + return; + if (!backplane_is_mode_kr(bp_phy->bp_mode)) + return; + + for (i = 0; i < bp_phy->num_lanes; i++) { + init_krln(&bp_phy->krln[i], true); + /* Reset the lane to recover from link down */ + lane_ops->reset_lane(bp_phy->krln[i].reg_base, LANE_RX_TX); + lt_reset(&bp_phy->krln[i]); + } + /* Start AN only for Master Lane */ + lt_start_an(&bp_phy->krln[MASTER_LANE]); + + krln->rt_time = jiffies + msecs_to_jiffies(KR_DENY_RT_INTERVAL); +} + +static bool detect_lp(struct kr_lane_info *krln) +{ + struct phy_device *bpphy = krln->bpphy; + struct backplane_phy_info *bp_phy = bpphy->priv; + u32 an_bp_eth_status = krln->bp_phy->bp_dev.mdio.an_bp_eth_status; + bool start_train = false; + int an_state; + + /* Check AN state on Master Lane */ + an_state = backplane_read_mmd(&bp_phy->krln[MASTER_LANE], MDIO_MMD_AN, + an_bp_eth_status); + + /* 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. + */ + if (an_state & + bp_phy->bp_dev.mdio.get_an_bp_eth_status_bit(bp_phy->bp_mode)) { + /* AN acquired: + * Train all lanes in order starting with Master Lane + */ + krln->an_acquired = true; + krln->an_wait_count = 0; + start_train = true; + } else { + /* AN lost or not yet acquired */ + if (krln->an_acquired) { + /* AN acquired first time but now was lost */ + if (!backplane_is_link_up(bpphy)) { + /* Link is down: restart training */ + krln->an_wait_count = 0; + an_request_restart(krln); + } else { + /* Link is up: + * wait few iterations for AN to be acquired + */ + if (krln->an_wait_count >= + KR_AN_WAIT_ITERATIONS) { + krln->an_wait_count = 0; + an_request_restart(krln); + } else { + krln->an_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 kr_lane_info *krln) +{ + struct phy_device *bpphy = krln->bpphy; + struct backplane_phy_info *bp_phy = bpphy->priv; + int i; + + if (krln->idx == MASTER_LANE) { + /* check if all lanes are trained + * only if current lane is Master Lane + */ + if (backplane_are_all_lanes_trained(bp_phy)) { + bpdev_info(bpphy, "Detect hotplug, restart training\n"); + for (i = 0; i < bp_phy->num_lanes; i++) { + /* initializations on Detect hotplug / restart: + * they must not be part of init_krln + */ + bp_phy->krln[i].first_recv_init = false; + } + an_request_restart(krln); + } + } +} + +static void bp_kr_state_machine(struct work_struct *work) +{ + struct delayed_work *dwork = to_delayed_work(work); + struct kr_lane_info *krln = container_of(dwork, struct kr_lane_info, + kr_wk); + struct phy_device *bpphy = krln->bpphy; + struct backplane_phy_info *bp_phy = bpphy->priv; + bool start_train = false; + u32 kr_timeout = KR_TIMEOUT_1; + + if (!backplane_is_mode_kr(bp_phy->bp_mode)) + return; + + /* Check if equalization algorithm is installed */ + if (!krln->eq_alg) + return; + + /* Check if link training is used */ + if (!krln->eq_alg->use_local_tx_training && + !krln->eq_alg->use_remote_tx_training) + return; + + if (!bp_phy->bp_dev.mdio.get_an_bp_eth_status_bit) { + bpdev_err(bpphy, + "Unknown AN_BP_ETHERNET_STATUS KR detection bit\n"); + return; + } + + mutex_lock(&krln->lane_lock); + switch (krln->state) { + case DETECTING_LP: + start_train = detect_lp(krln); + break; + case TRAINED: + kr_timeout = KR_TIMEOUT_2; + if (!backplane_is_link_up(bpphy)) { + kr_timeout = KR_TIMEOUT_1; + detect_hotplug(krln); + } + break; + } + + if (start_train) + kr_train_step(krln); + + mutex_unlock(&krln->lane_lock); + start_kr_state_machine(krln, kr_timeout); +} + +static void init_kr_state_machine(struct kr_lane_info *krln) +{ + /* Check if equalization algorithm is installed */ + if (!krln->eq_alg) + return; + + /* Check if link training is used */ + if (!krln->eq_alg->use_local_tx_training && + !krln->eq_alg->use_remote_tx_training) + return; + + INIT_DELAYED_WORK(&krln->kr_wk, bp_kr_state_machine); +} + +/* 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 kr_lane_info *krln, int devad, u32 regnum, + u16 val) +{ + struct phy_device *bpphy = krln->bpphy; + struct backplane_phy_info *bp_phy = bpphy->priv; + int mdio_addr = bpphy->mdio.addr; + int err; + + mutex_lock(&bp_phy->bpphy_lock); + + if (devad == MDIO_MMD_AN && backplane_is_multi_lane(bp_phy)) { + /* Multilane AN: prepare mdio address + * for writing bpphy AN registers on respective lane + * AN MDIO address offset for multilane is equal + * to number of lanes + */ + bpphy->mdio.addr = bp_phy->num_lanes + krln->idx; + } + + err = phy_write_mmd(bpphy, devad, regnum, val); + if (err) + bpdev_err(bpphy, + "Writing PHY (%p) MMD = 0x%02x register = 0x%02x failed with error code: 0x%08x\n", + bpphy, devad, regnum, err); + + if (devad == MDIO_MMD_AN && backplane_is_multi_lane(bp_phy)) { + /* Multilane AN: restore mdio address */ + bpphy->mdio.addr = mdio_addr; + } + + mutex_unlock(&bp_phy->bpphy_lock); + + return err; +} + +/* 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 kr_lane_info *krln, int devad, u32 regnum) +{ + struct phy_device *bpphy = krln->bpphy; + struct backplane_phy_info *bp_phy = bpphy->priv; + int mdio_addr = bpphy->mdio.addr; + int ret; + + mutex_lock(&bp_phy->bpphy_lock); + + if (devad == MDIO_MMD_AN && backplane_is_multi_lane(bp_phy)) { + /* Multilane AN: prepare mdio address + * for reading bpphy AN registers on respective lane + * AN MDIO address offset for multilane is equal to + * number of lanes + */ + bpphy->mdio.addr = bp_phy->num_lanes + krln->idx; + } + + ret = phy_read_mmd(bpphy, devad, regnum); + + if (devad == MDIO_MMD_AN && backplane_is_multi_lane(bp_phy)) { + /* Multilane AN: restore mdio address */ + bpphy->mdio.addr = mdio_addr; + } + + mutex_unlock(&bp_phy->bpphy_lock); + + return ret; +} + +/* backplane_get_current_taps + * convert coefficient taps from internal backplane driver to link training + */ +void backplane_get_current_taps(struct kr_lane_info *krln, u32 *coef) +{ + coef[C_M1] = krln->ratio_preq; + coef[C_Z0] = krln->adpt_eq; + coef[C_P1] = krln->ratio_pstq; +} + +/* backplane_set_current_taps + * convert coefficient taps from link training to internal backplane driver + */ +void backplane_set_current_taps(struct kr_lane_info *krln, u32 *coef) +{ + krln->ratio_preq = coef[C_M1]; + krln->adpt_eq = coef[C_Z0]; + krln->ratio_pstq = 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 kr_lane_info *krln) +{ + krln->ratio_pstq = krln->bp_phy->bp_dev.cp_max; + krln->adpt_eq = krln->bp_phy->bp_dev.cz_max; + krln->ratio_preq = krln->bp_phy->bp_dev.cm_max; +} + +void backplane_tune_kr_lane(struct kr_lane_info *krln, bool reset_lane) +{ + struct phy_device *bpphy = krln->bpphy; + struct backplane_phy_info *bp_phy = bpphy->priv; + struct lane_kr_params krparams; + bool reset = false; + + if (backplane_is_multi_lane(bp_phy)) { + /* Reset only the Master Lane */ + reset = (krln->idx == MASTER_LANE); + } else { + reset = true; + } + + /* Do not reset the lane if this is how it was asked */ + if (!reset_lane) + reset = false; + + krparams.ratio_preq = krln->ratio_preq; + krparams.ratio_pstq = krln->ratio_pstq; + krparams.adpt_eq = krln->adpt_eq; + krparams.amp_red = krln->def_amp_red; + krln->bp_phy->bp_dev.lane_ops->tune_lane_kr(krln->reg_base, &krparams, + reset); + + krln->tuned_ratio_preq = krln->ratio_preq; + krln->tuned_ratio_pstq = krln->ratio_pstq; + krln->tuned_adpt_eq = krln->adpt_eq; +} + +void backplane_default_kr_lane(struct kr_lane_info *krln) +{ + krln->ratio_preq = krln->def_ratio_preq; + krln->ratio_pstq = krln->def_ratio_pstq; + krln->adpt_eq = krln->def_adpt_eq; + + backplane_tune_kr_lane(krln, true); +} + +void bpdev_err(struct phy_device *bpphy, char *msg, ...) +{ + BPDEV_LOG(err); +} +EXPORT_SYMBOL(bpdev_err); + +void bpdev_warn(struct phy_device *bpphy, char *msg, ...) +{ + BPDEV_LOG(warn); +} +EXPORT_SYMBOL(bpdev_warn); + +void bpdev_info(struct phy_device *bpphy, char *msg, ...) +{ + BPDEV_LOG(info); +} +EXPORT_SYMBOL(bpdev_info); + +void bpdev_dbg(struct phy_device *bpphy, char *msg, ...) +{ + BPDEV_LOG(dbg); +} +EXPORT_SYMBOL(bpdev_dbg); + +/* 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) +{ + struct spmap_node *node, *node_tmp; + struct kr_lane_info *krln; + + 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) { + krln = (struct kr_lane_info *)node->pdata; + if (krln->eq_alg->ops.destroy) + krln->eq_alg->ops.destroy(krln->eq_priv); + krln->eq_alg = NULL; + 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); + +void backplane_setup_mdio_c45(struct backplane_dev_info *bp_dev) +{ + /* KR PMD registers */ + lt_setup_c45(bp_dev); + + bp_dev->mdio.pmd_ctrl_1 = MDIO_CTRL1; + + /* KX/KR AN registers: IEEE802.3 Clause 45 (MMD 7) */ + bp_dev->mdio.an_control = MDIO_CTRL1; + bp_dev->mdio.an_status = MDIO_STAT1; + bp_dev->mdio.an_ad_ability_0 = MDIO_PMA_EXTABLE_10GBKR; + bp_dev->mdio.an_ad_ability_1 = MDIO_PMA_EXTABLE_10GBKR + 1; + bp_dev->mdio.an_lp_base_page_ability_1 = MDIO_PMA_EXTABLE_10GBKR + 4; +} +EXPORT_SYMBOL(backplane_setup_mdio_c45); + +void backplane_setup_kr_lt_mmd(struct backplane_dev_info *bp_dev, int devad, + u32 base) +{ + lt_setup_memmap(bp_dev, devad, base); +} +EXPORT_SYMBOL(backplane_setup_kr_lt_mmd); + +bool backplane_is_mode_kr(phy_interface_t bp_mode) +{ + return (bp_mode >= PHY_INTERFACE_MODE_10GKR && + bp_mode <= PHY_INTERFACE_MODE_40GKR4); +} +EXPORT_SYMBOL(backplane_is_mode_kr); + +bool backplane_is_valid_mode(phy_interface_t bp_mode) +{ + return (bp_mode >= PHY_INTERFACE_MODE_10GKR && + bp_mode <= PHY_INTERFACE_MODE_40GKR4); +} +EXPORT_SYMBOL(backplane_is_valid_mode); + +u8 backplane_num_lanes(phy_interface_t bp_mode) +{ + const char *bp_name; + char num_lanes; + int len; + + if (!backplane_is_valid_mode(bp_mode)) + return 0; + + bp_name = phy_modes(bp_mode); + 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); + +bool backplane_is_single_lane(struct backplane_phy_info *bp_phy) +{ + return (bp_phy->num_lanes == 1); +} +EXPORT_SYMBOL(backplane_is_single_lane); + +bool backplane_is_multi_lane(struct backplane_phy_info *bp_phy) +{ + return (bp_phy->num_lanes > 1); +} +EXPORT_SYMBOL(backplane_is_multi_lane); + +/* backplane_is_cdr_lock + * + * Checks clock and data recovery bit: CDR Lock + * + * krln: 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 kr_lane_info *krln, bool retry) +{ + int i; + + if (krln->bp_phy->bp_dev.lane_ops->is_cdr_lock(krln->reg_base)) + return true; + + if (!retry) + return false; + + /* Try RX_RESET: Allow for few retries */ + for (i = 0; i < CDR_LOCK_RETRY_COUNT; i++) { + krln->bp_phy->bp_dev.lane_ops->reset_lane(krln->reg_base, + LANE_RX); + usleep_range(10, 50); + + if (krln->bp_phy->bp_dev.lane_ops->is_cdr_lock(krln->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 *bpphy) +{ + return is_an_link_up(bpphy); +} +EXPORT_SYMBOL(backplane_is_link_up); + +int backplane_get_lanes_trained_count(struct backplane_phy_info *bp_phy) +{ + int i, lanes_trained = 0; + + for (i = 0; i < bp_phy->num_lanes; i++) { + if (bp_phy->krln[i].state == TRAINED) + lanes_trained++; + } + return lanes_trained; +} +EXPORT_SYMBOL(backplane_get_lanes_trained_count); + +int backplane_are_all_lanes_trained(struct backplane_phy_info *bp_phy) +{ + int i; + + for (i = 0; i < bp_phy->num_lanes; i++) { + if (bp_phy->krln[i].state != TRAINED) + return 0; + } + return 1; +} +EXPORT_SYMBOL(backplane_are_all_lanes_trained); + +int backplane_create(struct phy_device *bpphy) +{ + struct device_node *bpphy_node; + struct backplane_phy_info *bp_phy; + + bpphy_node = bpphy->mdio.dev.of_node; + if (!bpphy_node) { + bpdev_err(bpphy, "No associated device tree node\n"); + return -EINVAL; + } + + /* allocate memory for backplane info structure */ + bp_phy = devm_kzalloc(&bpphy->mdio.dev, sizeof(*bp_phy), GFP_KERNEL); + if (!bp_phy) + return -ENOMEM; + + bpphy->priv = bp_phy; + + 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 *bpphy) +{ + struct backplane_phy_info *bp_phy = bpphy->priv; + struct device_node *bpphy_node; + const char *eqa; + u32 eqinit[4]; + int proplen; + int ret; + + if (!bp_phy) { + bpdev_err(bpphy, "Backplane phy info is not allocated\n"); + return -EINVAL; + } + + bpphy_node = bpphy->mdio.dev.of_node; + if (!bpphy_node) { + bpdev_err(bpphy, "No associated device tree node\n"); + return -EINVAL; + } + + if (!backplane_is_valid_mode(bpphy->interface)) + return -EINVAL; + + bp_phy->bp_mode = bpphy->interface; + bp_phy->num_lanes = backplane_num_lanes(bpphy->interface); + + ret = of_property_read_string(bpphy_node, "eq-algorithm", &eqa); + /* if eq-algorithm node is not found then use the default algorithm */ + if (ret == 0) + bp_phy->bp_dev.eqa_name = eqa; + else + bp_phy->bp_dev.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(bpphy_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(bpphy_node, "eq-init", + (u32 *)eqinit, proplen); + if (ret == 0) { + bp_phy->bp_dev.coef_def_dt = true; + bp_phy->bp_dev.cm_def = eqinit[0]; + bp_phy->bp_dev.cp_def = eqinit[1]; + bp_phy->bp_dev.cz_def = eqinit[2]; + } + } + + /* setup ioread/iowrite according to endianness */ + if (bp_phy->bp_dev.is_little_endian) { + bp_phy->bp_dev.io.read32 = le_ioread32; + bp_phy->bp_dev.io.write32 = le_iowrite32; + } else { + bp_phy->bp_dev.io.read32 = be_ioread32; + bp_phy->bp_dev.io.write32 = be_iowrite32; + } + + return 0; +} +EXPORT_SYMBOL(backplane_parse_dt); + +/* backplane_setup_mdio + */ +int backplane_setup_mdio(struct phy_device *bpphy) +{ + struct backplane_phy_info *bp_phy = bpphy->priv; + + if (!bp_phy) { + bpdev_err(bpphy, "Backplane phy info is not allocated\n"); + return -EINVAL; + } + + /* By default setup MDIO Clause 45 */ + backplane_setup_mdio_c45(&bp_phy->bp_dev); + + return 0; +} +EXPORT_SYMBOL(backplane_setup_mdio); + +/* backplane_setup_lanes + * Allocates lanes memory map and setup lanes relevant data + * Requires: + * - backplane_dev_info#lane_ops + * for lane access operations + * - backplane_dev_info#equalizer + * for specific Equalizer access + * - backplane_dev_info#lane_io_ops#memmap_size + * for lane memory map allocation + * - backplane_dev_info#cx_def + * for default coefficient setup + */ +int backplane_setup_lanes(struct phy_device *bpphy) +{ + struct backplane_phy_info *bp_phy = bpphy->priv; + struct kr_lane_info *krln; + struct eq_setup_info eq_setup; + int i; + + if (!bp_phy) { + bpdev_err(bpphy, "Backplane phy info is not allocated\n"); + return -EINVAL; + } + if (!bp_phy->bp_dev.lane_ops) { + bpdev_err(bpphy, "Backplane lane ops is not set\n"); + return -EINVAL; + } + if (!bp_phy->bp_dev.equalizer) { + bpdev_err(bpphy, "Backplane equalizer info is not set\n"); + return -EINVAL; + } + if (bp_phy->bp_dev.lane_ops->memmap_size == 0) { + bpdev_err(bpphy, "Lane memory map size is zero\n"); + return -EINVAL; + } + + if (backplane_is_mode_kr(bp_phy->bp_mode)) { + if (bp_phy->bp_dev.cm_def == 0 && bp_phy->bp_dev.cz_def == 0 && + bp_phy->bp_dev.cp_def == 0) + bpdev_warn(bpphy, + "All default values for KR parameters are zero\n"); + } + + for (i = 0; i < bp_phy->num_lanes; i++) { + krln = &bp_phy->krln[i]; + + /* setup lane memory map size */ + krln->memmap_size = bp_phy->bp_dev.lane_ops->memmap_size; + + krln->reg_base = devm_ioremap(&bpphy->mdio.dev, + krln->lane_addr, + krln->memmap_size); + if (!krln->reg_base) { + bpdev_err(bpphy, "Lane memory map allocation failed\n"); + return -ENOMEM; + } + + krln->idx = i; + krln->bpphy = bpphy; + krln->bp_phy = bp_phy; + krln->rt_time = jiffies + msecs_to_jiffies(KR_DENY_RT_INTERVAL); + + if (backplane_is_mode_kr(bp_phy->bp_mode)) { + setup_default_settings(krln); + + /* Find EQ Algorithm info */ + krln->eq_alg = eq_find(bp_phy->bp_dev.eqa_name); + if (!krln->eq_alg) { + /* key for desired algorithm was not found */ + bpdev_err(bpphy, + "Equalization algorithm '%s' is not registered\n", + bp_phy->bp_dev.eqa_name); + return -EINVAL; + } + if (!krln->eq_alg->ops.create) { + bpdev_err(bpphy, + "Equalization algorithm creation failed: create operation is not available\n"); + return -EINVAL; + } + + /* Setup EQ Algorithm */ + eq_setup.krlane = krln; + eq_setup.bpphy = krln->bpphy; + eq_setup.reg_base = krln->reg_base; + eq_setup.equalizer = *krln->bp_phy->bp_dev.equalizer; + + /* Create EQ Algorithm */ + krln->eq_priv = krln->eq_alg->ops.create(eq_setup); + + /* register lane attached to an algorithm */ + spmap_add(&lnalg_list, bp_phy->bp_dev.eqa_name, krln); + + if (krln->eq_alg->use_remote_tx_training) { + if (!krln->eq_alg->ops.is_rx_ok) + bpdev_warn(bpphy, + "Required operation for remote Tx training is missing: is_rx_ok\n"); + if (!krln->eq_alg->ops.is_eq_done) + bpdev_warn(bpphy, + "Required operation for remote Tx training is missing: is_eq_done\n"); + if (!krln->eq_alg->ops.collect_statistics) + bpdev_warn(bpphy, + "Required operation for remote Tx training is missing: collect_statistics\n"); + if (!krln->eq_alg->ops.generate_request) + bpdev_warn(bpphy, + "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 *bpphy) +{ + struct backplane_phy_info *bp_phy = bpphy->priv; + int i; + + if (!bp_phy) { + bpdev_err(bpphy, "Backplane phy info is not allocated\n"); + return -EINVAL; + } + + mutex_init(&bp_phy->bpphy_lock); + mutex_init(&bp_phy->trained_lock); + + for (i = 0; i < bp_phy->num_lanes; i++) + mutex_init(&bp_phy->krln[i].lane_lock); + + bpphy->speed = get_backplane_speed(bp_phy->bp_mode); + if (bpphy->speed < 0) { + bpdev_err(bpphy, "Unsupported backplane mode\n"); + return -EINVAL; + } + + if (backplane_is_mode_kr(bp_phy->bp_mode)) { + for (i = 0; i < bp_phy->num_lanes; i++) + init_kr_state_machine(&bp_phy->krln[i]); + } + + return 0; +} +EXPORT_SYMBOL(backplane_initialize); + +/* backplane_probe + * + * Probe function for backplane driver to provide generic device behavior + * + * bpphy: 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 *bpphy) +{ + return backplane_create(bpphy); +} +EXPORT_SYMBOL(backplane_probe); + +void backplane_remove(struct phy_device *bpphy) +{ + struct backplane_phy_info *bp_phy = bpphy->priv; + + if (!bp_phy) { + bpdev_err(bpphy, "Backplane phy info is not allocated\n"); + return; + } + + kfree(bp_phy); + bpphy->priv = NULL; +} +EXPORT_SYMBOL(backplane_remove); + +/* backplane_config_init + * + * Config_Init function for backplane driver to provide generic device behavior + * + * bpphy: backplane phy device + * + * Return: Zero for success or error code in case of failure + */ +int backplane_config_init(struct phy_device *bpphy) +{ + int ret; + + ret = backplane_parse_dt(bpphy); + if (ret) + return ret; + + ret = backplane_setup_mdio(bpphy); + if (ret) + return ret; + + ret = backplane_setup_lanes(bpphy); + if (ret) + return ret; + + ret = backplane_initialize(bpphy); + if (ret) + return ret; + + return 0; +} +EXPORT_SYMBOL(backplane_config_init); + +int backplane_aneg_done(struct phy_device *bpphy) +{ + struct backplane_phy_info *bp_phy = bpphy->priv; + + if (!bpphy->mdio.dev.of_node) { + bpdev_err(bpphy, "No associated device tree node\n"); + return -EINVAL; + } + if (!bp_phy) { + bpdev_err(bpphy, "Backplane phy info is not allocated\n"); + return -EINVAL; + } + + bp_phy->aneg_done = true; + bpphy->state = PHY_RUNNING; + + return 1; +} +EXPORT_SYMBOL(backplane_aneg_done); + +int backplane_config_aneg(struct phy_device *bpphy) +{ + struct backplane_phy_info *bp_phy = bpphy->priv; + struct kr_lane_info *krln; + struct equalization_ops ops; + int i; + + if (!bpphy->mdio.dev.of_node) { + bpdev_err(bpphy, "No associated device tree node\n"); + return -EINVAL; + } + if (!bp_phy) { + bpdev_err(bpphy, "Backplane phy info is not allocated\n"); + return -EINVAL; + } + if (backplane_get_lanes_trained_count(bp_phy) > 0) { + bpdev_err(bpphy, "Incorrectly trained lanes detected\n"); + return -EINVAL; + } + + for (i = 0; i < bp_phy->num_lanes; i++) { + krln = &bp_phy->krln[i]; + if (krln->eq_alg) { + ops = krln->eq_alg->ops; + if (ops.dump_algorithm_context) + ops.dump_algorithm_context(krln->eq_priv); + } + } + + if (backplane_is_mode_kr(bp_phy->bp_mode)) { + /* 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 < bp_phy->num_lanes; i++) + setup_default_settings(&bp_phy->krln[i]); + + /* Initialize all lanes and reset LT */ + for (i = 0; i < bp_phy->num_lanes; i++) { + init_krln(&bp_phy->krln[i], true); + lt_reset(&bp_phy->krln[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 */ + bpphy->speed = get_backplane_speed(bp_phy->bp_mode); + if (bpphy->speed < 0) { + bpdev_err(bpphy, "Unsupported backplane mode\n"); + return -EINVAL; + } + + setup_supported_linkmode(bpphy); + linkmode_copy(bpphy->advertising, bpphy->supported); + bpphy->duplex = DUPLEX_FULL; + + if (backplane_is_mode_kr(bp_phy->bp_mode)) { + /* Start AN only for Master Lane */ + lt_start_an(&bp_phy->krln[MASTER_LANE]); + /* start state machine on all lanes */ + for (i = 0; i < bp_phy->num_lanes; i++) + start_kr_state_machine(&bp_phy->krln[i], KR_TIMEOUT_1); + } + + bp_phy->aneg_config = true; + + return 0; +} +EXPORT_SYMBOL(backplane_config_aneg); + +int backplane_suspend(struct phy_device *bpphy) +{ + struct backplane_phy_info *bp_phy = bpphy->priv; + int i; + + if (!bpphy->mdio.dev.of_node) { + bpdev_err(bpphy, "No associated device tree node\n"); + return -EINVAL; + } + if (!bp_phy) { + bpdev_err(bpphy, "Backplane phy info is not allocated\n"); + return -EINVAL; + } + + if (bp_phy->aneg_config && !bp_phy->phy_suspended) { + if (backplane_is_mode_kr(bp_phy->bp_mode)) { + for (i = 0; i < bp_phy->num_lanes; i++) + stop_kr_state_machine(&bp_phy->krln[i]); + } + bp_phy->phy_suspended = true; + } + + return 0; +} +EXPORT_SYMBOL(backplane_suspend); + +int backplane_resume(struct phy_device *bpphy) +{ + struct backplane_phy_info *bp_phy = bpphy->priv; + int i; + + if (!bpphy->mdio.dev.of_node) { + bpdev_err(bpphy, "No associated device tree node\n"); + return -EINVAL; + } + if (!bp_phy) { + bpdev_err(bpphy, "Backplane phy info is not allocated\n"); + return -EINVAL; + } + + if (bp_phy->aneg_config && bp_phy->phy_suspended) { + if (backplane_is_mode_kr(bp_phy->bp_mode)) { + for (i = 0; i < bp_phy->num_lanes; i++) { + init_krln(&bp_phy->krln[i], true); + start_kr_state_machine(&bp_phy->krln[i], + KR_TIMEOUT_1); + } + } + bp_phy->phy_suspended = false; + } + + return 0; +} +EXPORT_SYMBOL(backplane_resume); + +int backplane_read_status(struct phy_device *bpphy) +{ + struct backplane_phy_info *bp_phy = bpphy->priv; + + if (!bpphy->mdio.dev.of_node) { + bpdev_err(bpphy, "No associated device tree node\n"); + return -EINVAL; + } + if (!bp_phy) { + bpdev_err(bpphy, "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(bpphy)) + if (backplane_is_mode_kr(bp_phy->bp_mode)) + bpphy->link = backplane_are_all_lanes_trained(bp_phy); + else + bpphy->link = 1; + else + bpphy->link = 0; + + return 0; +} +EXPORT_SYMBOL(backplane_read_status); + +int backplane_match_phy_device(struct phy_device *bpphy) +{ + struct device_node *bpphy_node; + + if (!bpphy->mdio.dev.of_node) + return 0; + + if (!bpphy->is_c45) + return 0; + + bpphy_node = bpphy->mdio.dev.of_node; + if (!bpphy_node) { + bpdev_err(bpphy, "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..911e418 --- /dev/null +++ b/drivers/net/phy/backplane/backplane.h @@ -0,0 +1,262 @@ +/* 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 4 + +/* Lanes definitions */ +#define MASTER_LANE 0 +#define SINGLE_LANE 0 + +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 lane_kr_params { + u32 ratio_preq; + u32 ratio_pstq; + u32 adpt_eq; + u32 amp_red; +}; + +/* Generic Lane operations */ +struct lane_io_ops { + const void *priv; /* device specific private info */ + u32 memmap_size; /* lane memory map size */ + void (*reset_lane)(void __iomem *reg, enum lane_req req); + void (*tune_lane_kr)(void __iomem *reg, struct lane_kr_params *params, + bool reset); + void (*read_lane_kr)(void __iomem *reg, struct lane_kr_params *params); + bool (*is_cdr_lock)(void __iomem *reg); +}; + +/* Endianness specific memory I/O operations + */ +struct mem_io_ops { + u32 (*read32)(void __iomem *addr); + void (*write32)(u32 value, void __iomem *addr); +}; + +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 kr_mdio_info { + /* MDIO_XFI_PMD Registers */ + int lt_devad; + u32 pmd_ctrl_1; + /* MDIO_XFI_PMD LT Registers */ + u32 lt_kr_pmd_control; + u32 lt_kr_pmd_status; + u32 lt_kr_lp_cu; + u32 lt_kr_lp_status; + u32 lt_kr_ld_cu; + u32 lt_kr_ld_status; + u32 lt_prbs_berr_lower; + u32 lt_prbs_berr_upper; + /* MDIO_XFI_AN Registers: MMD 7 */ + u32 an_control; + u32 an_status; + u32 an_ad_ability_0; + u32 an_ad_ability_1; + u32 an_lp_base_page_ability_1; + u32 an_bp_eth_status; + /* MDIO AN register ops */ + u32 (*get_an_bp_eth_status_bit)(phy_interface_t bp_mode); + u32 (*get_an_ad_ability_1_init)(phy_interface_t bp_mode); +}; + +/* Backplane device info */ +struct backplane_dev_info { + u32 cm_min; + u32 cm_max; + u32 cz_min; + u32 cz_max; + u32 cp_min; + u32 cp_max; + u32 sum_ratio_numer; + u32 sum_ratio_denom; + u32 cm_def; + u32 cz_def; + u32 cp_def; + u32 amp_red_def; + bool coef_def_dt; /* defaults for eq coef initialized from DT */ + bool ampr_def_dt; /* defaults for amp red initialized from DT */ + bool is_little_endian; /* serdes endianness */ + u32 base_addr; /* serdes base address */ + u32 memmap_size; /* serdes memory map size */ + const char *eqa_name; /* EQ algorithm name */ + struct mem_io_ops io; + const struct lane_io_ops *lane_ops; + const struct equalizer_info *equalizer; + struct kr_mdio_info mdio; +}; + +struct backplane_phy_info; + +/* KR Lane info */ +struct kr_lane_info { + /* generic KR data */ + void __iomem *reg_base; /* lane memory map: registers base address */ + u32 memmap_size; /* lane memory map size */ + u32 lane_addr; /* lane address */ + u8 idx; /* lane relative index inside multi-lane PHY */ + struct phy_device *bpphy; /* backplane phy device */ + struct backplane_phy_info *bp_phy; + const struct equalization_algorithm *eq_alg; + struct eq_data_priv *eq_priv; + struct training_status trst; + struct delayed_work kr_wk; + /* mutex for multiple lanes training case */ + struct mutex lane_lock; + enum train_state state; + /* KR LD/LP updates and status */ + u32 ld_update; + u32 prev_ld_update; + u32 ld_last_nonhold_update; /* last change (non-hold) update */ + u32 ld_status; + u32 lp_status; + u32 lp_last_change_status; /* last change (non-zero) status */ + u32 last_lp_update_status[C_NO]; + /* training status data */ + bool lt_error; + bool move_back_prev; + u32 move_back_cnt; + u32 move_back_lp_status; + u32 req_ld_update_init_count; + u32 repeat_request_count; + u64 init_handshake_time; + bool first_recv_init; + bool an_acquired; + u32 an_wait_count; + u64 rt_time; + /* KR parameters (current, default, tunned) */ + u32 ratio_preq; + u32 ratio_pstq; + u32 adpt_eq; + u32 def_ratio_preq; + u32 def_ratio_pstq; + u32 def_adpt_eq; + u32 def_amp_red; + u32 tuned_ratio_preq; + u32 tuned_ratio_pstq; + u32 tuned_adpt_eq; +}; + +struct backplane_phy_info { + phy_interface_t bp_mode; + u8 num_lanes; + bool aneg_config; + bool aneg_done; + bool phy_suspended; + struct backplane_dev_info bp_dev; + struct kr_lane_info krln[MAX_KR_LANES_PER_PHY]; + /* bpphy mutexes */ + struct mutex bpphy_lock; + /* mutex between multiple lanes training */ + struct mutex trained_lock; +}; + +bool backplane_is_mode_kr(phy_interface_t bp_mode); + +bool backplane_is_valid_mode(phy_interface_t bp_mode); + +u8 backplane_num_lanes(phy_interface_t bp_mode); + +bool backplane_is_single_lane(struct backplane_phy_info *bp_phy); + +bool backplane_is_multi_lane(struct backplane_phy_info *bp_phy); + +int backplane_is_link_up(struct phy_device *bpphy); + +void backplane_setup_mdio_c45(struct backplane_dev_info *bp_dev); + +void backplane_setup_kr_lt_mmd(struct backplane_dev_info *bp_dev, int devad, + u32 base); + +int backplane_read_mmd(struct kr_lane_info *krln, int devad, u32 regnum); + +int backplane_write_mmd(struct kr_lane_info *krln, int devad, u32 regnum, + u16 val); + +void backplane_default_kr_lane(struct kr_lane_info *krln); + +void backplane_get_current_taps(struct kr_lane_info *krln, u32 *coef); + +void backplane_set_current_taps(struct kr_lane_info *krln, u32 *coef); + +void backplane_set_all_taps_to_max(struct kr_lane_info *krln); + +void backplane_tune_kr_lane(struct kr_lane_info *krln, bool reset_lane); + +int backplane_are_all_lanes_trained(struct backplane_phy_info *bp_phy); + +int backplane_get_lanes_trained_count(struct backplane_phy_info *bp_phy); + +/* generic main operations to be used on probe callback */ + +int backplane_create(struct phy_device *bpphy); + +int backplane_parse_dt(struct phy_device *bpphy); + +int backplane_setup_mdio(struct phy_device *bpphy); + +int backplane_setup_lanes(struct phy_device *bpphy); + +int backplane_initialize(struct phy_device *bpphy); + +/* predefined phy_driver callback functions */ + +int backplane_probe(struct phy_device *bpphy); + +void backplane_remove(struct phy_device *bpphy); + +int backplane_config_init(struct phy_device *bpphy); + +int backplane_aneg_done(struct phy_device *bpphy); + +int backplane_config_aneg(struct phy_device *bpphy); + +int backplane_suspend(struct phy_device *bpphy); + +int backplane_resume(struct phy_device *bpphy); + +int backplane_read_status(struct phy_device *bpphy); + +int backplane_match_phy_device(struct phy_device *bpphy); + +#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..5244cec --- /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 eq_setup_info setup) +{ + 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..167c9f1 --- /dev/null +++ b/drivers/net/phy/backplane/equalization.h @@ -0,0 +1,282 @@ +/* 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 kr_lane_info; +struct eq_setup_info; + +/* 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 eq_setup_info setup); + 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 info and operations */ +struct equalizer_info { + const char *name; + const char *version; + struct equalizer_ops ops; +}; + +/* Equalization setup information */ +struct eq_setup_info { + struct phy_device *bpphy; + /* kr lane info used as parameter for link training API */ + struct kr_lane_info *krlane; + void *reg_base; + struct equalizer_info 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 kr_lane_info *krln, 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 kr_lane_info *krln, enum req_type type); + +enum coef_status lt_get_lp_coef_status(struct kr_lane_info *krln, + enum coef_field field); + +void lt_move_lp_back(struct kr_lane_info *krln); + +void lt_set_error(struct kr_lane_info *krln, 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 kr_lane_info *krln, bool retry); + +void bpdev_err(struct phy_device *bpphy, char *msg, ...); + +void bpdev_warn(struct phy_device *bpphy, char *msg, ...); + +void bpdev_info(struct phy_device *bpphy, char *msg, ...); + +void bpdev_dbg(struct phy_device *bpphy, char *msg, ...); + +#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..2afecd4 --- /dev/null +++ b/drivers/net/phy/backplane/link_training.c @@ -0,0 +1,1604 @@ +// 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 REGISTER_KR_PMD_CTRL 150 + +/* Link training KR PMD registers offsets */ +#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 +#define OFFSET_KR_PRBS_BERR_LOWER 0x7F6B +#define OFFSET_KR_PRBS_BERR_UPPER 0x7F6C + +/* Timeouts */ +#define TIMEOUT_MOVE_BACK_PREV 6 +#define TIMEOUT_REPEAT_REQUEST 10 + +/* Backplane Ethernet status (Register 7.48) */ +#define AN_BP_ETH_STATUS_OFFSET 0x30 + +/* AN registers initialization */ +#define AN_CTRL_INIT 0x1200 + +/* 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 kr_lane_info *krln) +{ + backplane_write_mmd(krln, krln->bp_phy->bp_dev.mdio.lt_devad, + krln->bp_phy->bp_dev.mdio.lt_kr_ld_status, + krln->ld_status); +} + +/* ld_coef_update + * LD sends to LP the specified request for coefficients update + */ +static void ld_coef_update(struct kr_lane_info *krln) +{ + backplane_write_mmd(krln, krln->bp_phy->bp_dev.mdio.lt_devad, + krln->bp_phy->bp_dev.mdio.lt_kr_ld_cu, + 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 kr_lane_info *krln) +{ + return 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 kr_lane_info *krln) +{ + backplane_default_kr_lane(krln); + + krln->ld_status &= ~ALL_COEF_MASK; + krln->ld_status |= COEF_UPDATED << COP1_SHIFT | + COEF_UPDATED << COZ0_SHIFT | + COEF_UPDATED << COM1_SHIFT; + + ld_coef_status(krln); +} + +/* 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 kr_lane_info *krln) +{ + backplane_set_all_taps_to_max(krln); + + backplane_tune_kr_lane(krln, true); + + krln->ld_status &= ~ALL_COEF_MASK; + krln->ld_status |= COEF_MAX << COP1_SHIFT | + COEF_MAX << COZ0_SHIFT | + COEF_MAX << COM1_SHIFT; + + ld_coef_status(krln); +} + +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 kr_lane_info *krln, u32 *ld_coef) +{ + u32 ratio_pstq = ld_coef[C_P1]; + u32 adpt_eq = ld_coef[C_Z0]; + u32 ratio_preq = ld_coef[C_M1]; + struct backplane_dev_info *bp_dev = &krln->bp_phy->bp_dev; + + /* 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 + */ + + /* 1. [condition (1) was moved below for optimization purpose] */ + + /* Basic HW restrictions: */ + + /* 2. tx_ratio_preq <= MIN_C(-1) */ + if (ratio_preq > bp_dev->cm_min) + return -ERANGE; + /* 3. tx_ratio_post1q <= MIN_C(+1) */ + if (ratio_pstq > bp_dev->cp_min) + return -ERANGE; + /* 4. MIN_C(0) <= tx_adpt_eq <= MAX_C(0) */ + if (adpt_eq < bp_dev->cz_min) + return -ERANGE; + if (adpt_eq > bp_dev->cz_max) + return -ERANGE; + /* 5. tx_ratio_post1q >= tx_ratio_preq */ + if (ratio_pstq < ratio_preq) + return -ERANGE; + + /* Additional HW restrictions: + * 1. MIN_C(0) <= tx_ratio_preq + tx_adpt_eq + + * tx_ratio_post1q <= MAX_C(0) + */ + if ((ratio_preq + ratio_pstq + adpt_eq) < bp_dev->cz_min) + return -ERANGE; + if ((ratio_preq + ratio_pstq + adpt_eq) > bp_dev->cz_max) + return -ERANGE; + /* 6. + * ( tx_adpt_eq + tx_ratio_preq + tx_ratio_post1q ) / + * ( tx_adpt_eq - tx_ratio_preq - tx_ratio_post1q ) < + * sum_ratio_numerator / sum_ratio_denominator + */ + if (((adpt_eq + ratio_preq + ratio_pstq) * bp_dev->sum_ratio_denom) >= + ((adpt_eq - ratio_preq - ratio_pstq) * bp_dev->sum_ratio_numer)) + return -ERANGE; + + return 0; +} + +static bool update_ld_status(struct kr_lane_info *krln, enum coef_field field, + enum coef_status cs) +{ + u32 mask, val; + u32 ld_cs = cs; + + 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; + } + + krln->ld_status &= ~mask; + krln->ld_status |= val; + + return true; +} + +static enum coef_status inc_dec(struct kr_lane_info *krln, + enum coef_field field, int request) +{ + u32 ld_coef[C_NO], step[C_NO], ld_limit[C_NO]; + int err; + + backplane_get_current_taps(krln, ld_coef); + + step[C_P1] = STEP_INCREMENT_P1; + step[C_Z0] = STEP_INCREMENT_Z0; + step[C_M1] = STEP_INCREMENT_M1; + + /* 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_P1] = krln->bp_phy->bp_dev.cp_max; + ld_limit[C_Z0] = krln->bp_phy->bp_dev.cz_max; + ld_limit[C_M1] = krln->bp_phy->bp_dev.cm_max; + if (ld_coef[field] != ld_limit[field]) + ld_coef[field] += step[field]; + else + return COEF_MAX; + break; + case DECREMENT: + ld_limit[C_P1] = krln->bp_phy->bp_dev.cp_min; + ld_limit[C_Z0] = krln->bp_phy->bp_dev.cz_min; + ld_limit[C_M1] = krln->bp_phy->bp_dev.cm_min; + if (ld_coef[field] != ld_limit[field]) + ld_coef[field] -= step[field]; + else + return COEF_MIN; + break; + default: + break; + } + + err = is_ld_valid(krln, ld_coef); + if (!err) { + /* accept new ld coefficients */ + backplane_set_current_taps(krln, ld_coef); + backplane_tune_kr_lane(krln, false); + } else { + if (request == DECREMENT) + return COEF_MIN; + if (request == INCREMENT) + return COEF_MAX; + } + + /* UPDATED */ + return COEF_UPDATED; +} + +static void check_request(struct kr_lane_info *krln, int request) +{ + int cop1_req, coz0_req, com1_req; + int old_status; + enum coef_status cu = COEF_INVALID; + + 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 = krln->ld_status; + + if (cop1_req && !(krln->ld_status & COP1_MASK)) { + cu = inc_dec(krln, C_P1, cop1_req); + update_ld_status(krln, C_P1, cu); + } + + if (coz0_req && !(krln->ld_status & COZ0_MASK)) { + cu = inc_dec(krln, C_Z0, coz0_req); + update_ld_status(krln, C_Z0, cu); + } + + if (com1_req && !(krln->ld_status & COM1_MASK)) { + cu = inc_dec(krln, C_M1, com1_req); + update_ld_status(krln, C_M1, cu); + } + + if (old_status != krln->ld_status) + ld_coef_status(krln); +} + +static void training_complete(struct kr_lane_info *krln) +{ + struct training_status *trst = &krln->trst; + + /* update training status */ + trst->remote_tx_complete = true; + trst->remote_tx_running = false; + + /* report LD status */ + krln->ld_status |= RX_READY_MASK; + ld_coef_status(krln); + + /* update PMD status and tell LP we are ready */ + backplane_write_mmd(krln, krln->bp_phy->bp_dev.mdio.lt_devad, + krln->bp_phy->bp_dev.mdio.lt_kr_pmd_status, + PMD_STATUS_RX_STAT); +} + +/* Link Training general API */ + +/* Setup standard KR LT memory map registers + * 45.2.1.76 10GBASE-KR PMD control register (Register 1.150) + */ +void lt_setup_c45(struct backplane_dev_info *bp_dev) +{ + bp_dev->mdio.an_bp_eth_status = AN_BP_ETH_STATUS_OFFSET; + + lt_setup_memmap(bp_dev, MDIO_MMD_PMAPMD, REGISTER_KR_PMD_CTRL); +} + +/* Setup KR LT memory map registers + * IEEE Std 802.3ap-2007: Table 45.3 PMA/PMD registers + */ +void lt_setup_memmap(struct backplane_dev_info *bp_dev, int devad, u32 base) +{ + bp_dev->mdio.lt_devad = devad; + bp_dev->mdio.lt_kr_pmd_control = base + OFFSET_KR_PMD_CTRL; + bp_dev->mdio.lt_kr_pmd_status = base + OFFSET_KR_PMD_STATUS; + bp_dev->mdio.lt_kr_lp_cu = base + OFFSET_KR_LP_CU; + bp_dev->mdio.lt_kr_lp_status = base + OFFSET_KR_LP_STATUS; + bp_dev->mdio.lt_kr_ld_cu = base + OFFSET_KR_LD_CU; + bp_dev->mdio.lt_kr_ld_status = base + OFFSET_KR_LD_STATUS; + bp_dev->mdio.lt_prbs_berr_lower = base + OFFSET_KR_PRBS_BERR_LOWER; + bp_dev->mdio.lt_prbs_berr_upper = base + OFFSET_KR_PRBS_BERR_UPPER; +} + +/* 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 kr_lane_info *krln) +{ + struct kr_mdio_info *mdio = &krln->bp_phy->bp_dev.mdio; + + /* Read LP Status */ + krln->lp_status = backplane_read_mmd(krln, + mdio->lt_devad, + mdio->lt_kr_lp_status); + return is_rx_ready(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 kr_lane_info *krln) +{ + return is_rx_ready(krln->ld_status); +} + +void lt_start(struct kr_lane_info *krln) +{ + backplane_write_mmd(krln, krln->bp_phy->bp_dev.mdio.lt_devad, + krln->bp_phy->bp_dev.mdio.lt_kr_pmd_control, + TRAIN_EN); +} + +void lt_stop(struct kr_lane_info *krln) +{ + backplane_write_mmd(krln, krln->bp_phy->bp_dev.mdio.lt_devad, + krln->bp_phy->bp_dev.mdio.lt_kr_pmd_control, + TRAIN_DISABLE); +} + +void lt_reset(struct kr_lane_info *krln) +{ + backplane_write_mmd(krln, krln->bp_phy->bp_dev.mdio.lt_devad, + krln->bp_phy->bp_dev.mdio.pmd_ctrl_1, PMD_RESET); + backplane_write_mmd(krln, krln->bp_phy->bp_dev.mdio.lt_devad, + krln->bp_phy->bp_dev.mdio.lt_kr_pmd_control, + TRAIN_DISABLE); + backplane_write_mmd(krln, krln->bp_phy->bp_dev.mdio.lt_devad, + krln->bp_phy->bp_dev.mdio.lt_kr_ld_cu, 0); + backplane_write_mmd(krln, krln->bp_phy->bp_dev.mdio.lt_devad, + krln->bp_phy->bp_dev.mdio.lt_kr_ld_status, 0); + backplane_write_mmd(krln, krln->bp_phy->bp_dev.mdio.lt_devad, + krln->bp_phy->bp_dev.mdio.lt_kr_pmd_status, 0); + backplane_write_mmd(krln, krln->bp_phy->bp_dev.mdio.lt_devad, + krln->bp_phy->bp_dev.mdio.lt_kr_lp_cu, 0); + backplane_write_mmd(krln, krln->bp_phy->bp_dev.mdio.lt_devad, + krln->bp_phy->bp_dev.mdio.lt_kr_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 kr_lane_info *krln) +{ + struct phy_device *bpphy = krln->bpphy; + int val; + int timeout = 100; + + val = backplane_read_mmd(krln, krln->bp_phy->bp_dev.mdio.lt_devad, + krln->bp_phy->bp_dev.mdio.lt_kr_pmd_status); + + if ((val & PMD_STATUS_RX_STAT) && !(val & PMD_STATUS_TRAIN_FAIL)) { + while (timeout--) { + if (backplane_is_link_up(bpphy)) + 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 kr_lane_info *krln) +{ + struct kr_mdio_info *mdio = &krln->bp_phy->bp_dev.mdio; + int lt_state; + + lt_state = backplane_read_mmd(krln, mdio->lt_devad, + mdio->lt_kr_pmd_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 kr_lane_info *krln) +{ + struct kr_mdio_info *mdio = &krln->bp_phy->bp_dev.mdio; + int lt_state; + + lt_state = backplane_read_mmd(krln, mdio->lt_devad, + mdio->lt_kr_pmd_status); + + if ((lt_state & PMD_STATUS_SUP_STAT) && + (lt_state & PMD_STATUS_FRAME_LOCK)) + return true; + + return false; +} + +void lt_start_an(struct kr_lane_info *krln) +{ + struct phy_device *bpphy = krln->bpphy; + struct backplane_phy_info *bp_phy = bpphy->priv; + struct kr_mdio_info *mdio = &bp_phy->bp_dev.mdio; + u32 an_ad_ability_1 = mdio->an_ad_ability_1; + u32 init_an_ad_ab1; + int i; + int err; + + if (!backplane_is_mode_kr(bp_phy->bp_mode)) + return; + + if (!mdio->get_an_ad_ability_1_init) { + bpdev_err(bpphy, "Unknown AN_AD_ABILITY_1 init value\n"); + return; + } + + init_an_ad_ab1 = mdio->get_an_ad_ability_1_init(bp_phy->bp_mode); + + if (krln->idx == MASTER_LANE) { + for (i = 0; i < bp_phy->num_lanes; i++) { + err = backplane_write_mmd(&bp_phy->krln[i], MDIO_MMD_AN, + an_ad_ability_1, + init_an_ad_ab1); + if (err) + bpdev_err(bpphy, + "Setting AN register 0x%02x on lane %d failed with error code: 0x%08x\n", + an_ad_ability_1, + bp_phy->krln[i].idx, err); + } + udelay(1); + err = backplane_write_mmd(krln, MDIO_MMD_AN, mdio->an_control, + AN_CTRL_INIT); + if (err) + bpdev_err(bpphy, + "Setting AN register 0x%02x on Master Lane failed with error code: 0x%08x\n", + MDIO_CTRL1, err); + } +} + +/* Training for Remote Tx + * This is the main routine for Remote Tx training + */ +void lt_train_remote_tx(struct kr_lane_info *krln) +{ + struct training_status *trst = &krln->trst; + u32 prev_req_init, prev_req_preset; + u32 prev_req_cp1, prev_req_cz0, prev_req_cm1; + u32 status_cp1, status_cz0, status_cm1; + u64 lp_resp_time; + + /* Check stop condition for Remote Tx training */ + if (trst->remote_tx_complete) + return; + + /* Check if equalization algorithm is installed */ + if (!krln->eq_alg) + return; + + /* Check that all required callback operations are installed */ + if (!krln->eq_alg->ops.collect_statistics || + !krln->eq_alg->ops.is_rx_ok || + !krln->eq_alg->ops.generate_request || + !krln->eq_alg->ops.is_eq_done) + return; + + /* Start new Remote Tx training step */ + trst->remote_tx_running = true; + + /* Store current state as previous state */ + krln->prev_ld_update = krln->ld_update; + if ((krln->prev_ld_update & ALL_COEF_MASK) != HOLD) + krln->ld_last_nonhold_update = krln->prev_ld_update; + + prev_req_init = krln->prev_ld_update & INIT_MASK; + prev_req_preset = krln->prev_ld_update & PRESET_MASK; + prev_req_cp1 = (krln->prev_ld_update & COP1_MASK) >> COP1_SHIFT; + prev_req_cz0 = (krln->prev_ld_update & COZ0_MASK) >> COZ0_SHIFT; + prev_req_cm1 = (krln->prev_ld_update & COM1_MASK) >> COM1_SHIFT; + + /* Training Done condition */ + if (krln->eq_alg->ops.is_eq_done(krln->eq_priv)) + trst->done_training = true; + + /* Check if Training is Done */ + if (trst->done_training) { + training_complete(krln); + return; + } + + /* Read LP Status */ + krln->lp_status = + backplane_read_mmd(krln, krln->bp_phy->bp_dev.mdio.lt_devad, + krln->bp_phy->bp_dev.mdio.lt_kr_lp_status); + + if ((krln->lp_status & ALL_COEF_MASK) != 0) + krln->lp_last_change_status = krln->lp_status; + + status_cp1 = (krln->lp_status & COP1_MASK) >> COP1_SHIFT; + status_cz0 = (krln->lp_status & COZ0_MASK) >> COZ0_SHIFT; + status_cm1 = (krln->lp_status & COM1_MASK) >> COM1_SHIFT; + + if (status_cp1 == COEF_UPDATED || status_cp1 == COEF_MIN || + status_cp1 == COEF_MAX) + krln->last_lp_update_status[C_P1] = status_cp1; + if (status_cz0 == COEF_UPDATED || status_cz0 == COEF_MIN || + status_cz0 == COEF_MAX) + krln->last_lp_update_status[C_Z0] = status_cz0; + if (status_cm1 == COEF_UPDATED || status_cm1 == COEF_MIN || + status_cm1 == COEF_MAX) + 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; + krln->ld_update = INIT_MASK; + krln->req_ld_update_init_count = 1; + krln->init_handshake_time = jiffies_to_msecs(jiffies); + } else { + /* send HOLD before sending subsequent Init requests + * this is not the very first Init sent + */ + krln->ld_update = HOLD; + } + ld_coef_update(krln); + return; + } + /* continue to sent init request until LP responds to init */ + if (prev_req_init) { + if (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) { + krln->ld_update = INIT_MASK; + ld_coef_update(krln); + 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: krln->ld_update &= ~INIT_MASK; */ + /* better send request: HOLD ALL + * should be equivalent since only INIT is set now + */ + krln->ld_update = HOLD; + + lp_resp_time = jiffies_to_msecs(jiffies) - + krln->init_handshake_time; + if (!krln->first_recv_init) { + /* Init handshake not done yet, + * but will be soon + */ + krln->req_ld_update_init_count = 1; + lp_resp_time = 0; + } + ld_coef_update(krln); + } + 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)) { + 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) { + krln->ld_update = PRESET_MASK; + } else { + /* send HOLD before sending subsequent + * Preset requests + */ + krln->ld_update = HOLD; + } + ld_coef_update(krln); + 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 (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 (krln->move_back_prev) { + /* can exit from here only with: DONE Training */ + if (krln->move_back_cnt == TIMEOUT_MOVE_BACK_PREV) { + trst->done_training = true; + training_complete(krln); + return; + } + krln->move_back_cnt++; + + if (status_cp1 == COEF_UPDATED) + krln->move_back_lp_status |= + (COEF_UPDATED << COP1_SHIFT); + if (status_cz0 == COEF_UPDATED) + krln->move_back_lp_status |= + (COEF_UPDATED << COZ0_SHIFT); + if (status_cm1 == COEF_UPDATED) + krln->move_back_lp_status |= + (COEF_UPDATED << COM1_SHIFT); + + if ((krln->move_back_lp_status & ALL_COEF_MASK) == + LP_STATUS_ALL_COEF_UPDATED) { + trst->done_training = true; + training_complete(krln); + return; + } + + /* Move back to previous C(-1), C(0), C(+1) */ + krln->ld_update = krln->prev_ld_update; + ld_coef_update(krln); + 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 (krln->repeat_request_count >= + TIMEOUT_REPEAT_REQUEST) { + bpdev_err(krln->bpphy, + "REQ Timeout: Repeating C(+1) HOLD request without LP response timeout !\n"); + /* Hold Request Timeout: + * continue to send HOLD until LP responds + * with NOTUPDATED + */ + 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 (krln->repeat_request_count == + TIMEOUT_REPEAT_REQUEST - 1) + msleep(30); + krln->repeat_request_count++; + } + ld_coef_update(krln); + 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 (krln->repeat_request_count >= + TIMEOUT_REPEAT_REQUEST) { + if (prev_req_cp1 == INCREMENT) + bpdev_err(krln->bpphy, + "REQ Timeout: Repeating C(+1) INC request without LP response timeout !\n"); + else + bpdev_err(krln->bpphy, + "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 (krln->repeat_request_count == + TIMEOUT_REPEAT_REQUEST - 1) + msleep(30); + krln->repeat_request_count++; + ld_coef_update(krln); + 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 + */ + 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 (krln->repeat_request_count >= + TIMEOUT_REPEAT_REQUEST) { + bpdev_err(krln->bpphy, + "REQ Timeout: Repeating C(0) HOLD request without LP response timeout !\n"); + /* Hold Request Timeout: + * continue to send HOLD until LP responds + * with NOTUPDATED + */ + 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 (krln->repeat_request_count == + TIMEOUT_REPEAT_REQUEST - 1) + msleep(30); + krln->repeat_request_count++; + } + ld_coef_update(krln); + 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 (krln->repeat_request_count >= + TIMEOUT_REPEAT_REQUEST) { + if (prev_req_cz0 == INCREMENT) + bpdev_err(krln->bpphy, + "REQ Timeout: Repeating C(0) INC request without LP response timeout !\n"); + else + bpdev_err(krln->bpphy, + "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 (krln->repeat_request_count == + TIMEOUT_REPEAT_REQUEST - 1) + msleep(30); + krln->repeat_request_count++; + ld_coef_update(krln); + 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 + */ + 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 (krln->repeat_request_count >= + TIMEOUT_REPEAT_REQUEST) { + bpdev_err(krln->bpphy, + "REQ Timeout: Repeating C(-1) HOLD request without LP response timeout !\n"); + /* Hold Request Timeout: + * continue to send HOLD until + * LP responds with NOTUPDATED + */ + 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 (krln->repeat_request_count == + TIMEOUT_REPEAT_REQUEST - 1) + msleep(30); + krln->repeat_request_count++; + } + ld_coef_update(krln); + 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 (krln->repeat_request_count >= + TIMEOUT_REPEAT_REQUEST) { + if (prev_req_cm1 == INCREMENT) + bpdev_err(krln->bpphy, + "REQ Timeout: Repeating C(-1) INC request without LP response timeout !\n"); + else + bpdev_err(krln->bpphy, + "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 (krln->repeat_request_count == + TIMEOUT_REPEAT_REQUEST - 1) + msleep(30); + krln->repeat_request_count++; + ld_coef_update(krln); + 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 + */ + 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 + */ + krln->repeat_request_count = 0; + + if (krln->prev_ld_update != krln->ld_update) { + ld_coef_update(krln); + /* 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 (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 */ + krln->lt_error = false; + + /* if CDR_LOCK = 0: Statistics are invalid */ + if (!backplane_is_cdr_lock(krln, true)) { + if (krln->eq_alg->ops.process_bad_state) + krln->eq_alg->ops.process_bad_state(krln->eq_priv); + return; + } + + /* collect bit edge statistics */ + if (!krln->eq_alg->ops.collect_statistics(krln->eq_priv)) + return; + + /* if CDR_LOCK = 0: Statistics are invalid */ + if (!backplane_is_cdr_lock(krln, true)) { + if (krln->eq_alg->ops.process_bad_state) + krln->eq_alg->ops.process_bad_state(krln->eq_priv); + return; + } + + /* Check Rx */ + if (!krln->eq_alg->ops.is_rx_ok(krln->eq_priv)) { + if (krln->eq_alg->ops.process_bad_state) + krln->eq_alg->ops.process_bad_state(krln->eq_priv); + return; + } + krln->eq_alg->ops.generate_request(krln->eq_priv); + + /* All C are in Hold and both Bins are stopped: + * So the Training is done + */ + if (krln->eq_alg->ops.is_eq_done(krln->eq_priv)) { + trst->done_training = true; + training_complete(krln); + } +} + +/* Training for Local Tx + * Initialize LD (Local Device) + */ +void lt_init_ld(struct kr_lane_info *krln) +{ + /* report initial ld status to lp */ + krln->ld_status = 0; + ld_coef_status(krln); +} + +/* Training for Local Tx + * This is the main routine for Local Tx training + */ +void lt_train_local_tx(struct kr_lane_info *krln) +{ + struct training_status *trst = &krln->trst; + int request, old_ld_status; + + /* Check stop condition for Local Tx training */ + trst->lp_rx_ready = lt_is_lp_rx_ready(krln); + if (trst->lp_rx_ready) { + /* LP receiver is ready + * As soon as the LP shows ready, + * no need to do any more updates. + */ + krln->ld_status &= ~ALL_COEF_MASK; + ld_coef_status(krln); + + 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(krln, krln->bp_phy->bp_dev.mdio.lt_devad, + krln->bp_phy->bp_dev.mdio.lt_kr_lp_cu) & + LD_ALL_MASK; + + old_ld_status = 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)) + krln->ld_status &= ~COP1_MASK; + + if (!(request & COZ0_MASK)) + krln->ld_status &= ~COZ0_MASK; + + if (!(request & COM1_MASK)) + krln->ld_status &= ~COM1_MASK; + + ld_coef_status(krln); + } + + /* 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 (!(krln->ld_status & ALL_COEF_MASK)) { + if (request & PRESET_MASK) + preset(krln); + + if (request & INIT_MASK) { + if (!krln->first_recv_init) { + krln->first_recv_init = true; + /* Init requests must be counted + * from initial handshake + */ + krln->req_ld_update_init_count = 1; + krln->init_handshake_time = + jiffies_to_msecs(jiffies); + } + initialize(krln); + } + } else { + /* Inform the partner about current ld status + * which should be: ALL UPDATED for INIT and + * ALL MAX for PRESET + */ + ld_coef_status(krln); + } + } + + /* check if LP Coefficient are not in HOLD */ + if (request & ALL_COEF_MASK) + check_request(krln, 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(krln); +} + +/* Training for Remote Tx API */ + +/* lt_lp_update + * + * Sends to LP the specified request for coefficients update + * + * krln: desired lane for which to send lp update + * update: desired update request to be sent to LP + * + * Returns: None + */ +void lt_lp_update(struct kr_lane_info *krln, u32 update) +{ + krln->ld_update = update; + ld_coef_update(krln); +} +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 + * + * krln: 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 kr_lane_info *krln, enum req_type type) +{ + u32 lp_st = krln->lp_status; + u32 lp_lcs = get_lp_lcs(krln); + 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 + * + * krln: 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 kr_lane_info *krln, + enum coef_field field) +{ + return 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 + * + * krln: 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 kr_lane_info *krln, bool err) +{ + 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 + * + * krln: desired lane for which to send lp update + * + * Returns: None + */ +void lt_move_lp_back(struct kr_lane_info *krln) +{ + u32 prev_req_cp1 = (krln->ld_last_nonhold_update & COP1_MASK) >> + COP1_SHIFT; + u32 prev_req_cz0 = (krln->ld_last_nonhold_update & COZ0_MASK) >> + COZ0_SHIFT; + u32 prev_req_cm1 = (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; + } + + krln->ld_update = temp; + ld_coef_update(krln); + + /* start the procedure for sending request to move LP back + * to previous setup until LP responds to it + */ + krln->move_back_prev = true; + krln->move_back_cnt = 0; + krln->move_back_lp_status = 0; + if (prev_req_cp1 == HOLD) + krln->move_back_lp_status |= (COEF_UPDATED << COP1_SHIFT); + if (prev_req_cz0 == HOLD) + krln->move_back_lp_status |= (COEF_UPDATED << COZ0_SHIFT); + if (prev_req_cm1 == HOLD) + 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..54374aa --- /dev/null +++ b/drivers/net/phy/backplane/link_training.h @@ -0,0 +1,34 @@ +/* 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_setup_c45(struct backplane_dev_info *bp_dev); +void lt_setup_memmap(struct backplane_dev_info *bp_dev, int devad, u32 base); + +void lt_start(struct kr_lane_info *krln); +void lt_stop(struct kr_lane_info *krln); +void lt_reset(struct kr_lane_info *krln); + +bool lt_is_rx_trained(struct kr_lane_info *krln); +bool lt_is_training_failure(struct kr_lane_info *krln); +bool lt_is_frame_lock(struct kr_lane_info *krln); + +bool lt_is_lp_rx_ready(struct kr_lane_info *krln); +bool lt_is_ld_rx_ready(struct kr_lane_info *krln); + +void lt_init_ld(struct kr_lane_info *krln); +void lt_start_an(struct kr_lane_info *krln); + +void lt_train_remote_tx(struct kr_lane_info *krln); +void lt_train_local_tx(struct kr_lane_info *krln); + +#endif /* __LINK_TRAINING_H */ From patchwork Thu Mar 26 13:51: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: 1261997 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=none (no SPF record) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; 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=nn+ZoL8X; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 48p5z22zZFz9sT1 for ; Fri, 27 Mar 2020 00:52:46 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727998AbgCZNwm (ORCPT ); Thu, 26 Mar 2020 09:52:42 -0400 Received: from mail-eopbgr60089.outbound.protection.outlook.com ([40.107.6.89]:57111 "EHLO EUR04-DB3-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1727771AbgCZNwl (ORCPT ); Thu, 26 Mar 2020 09:52:41 -0400 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=H8u8P4lAD1UcDq7aQhwTbAeHTmIBA4RuqLVyP9rKkyTSYxXqBuDkjWzUX8nwZW0ZNAH1S7H+R8Y+DTNIgCw37SQuOsW8WYqdN6D7eOZyqay4YVseLUpI1opAAlJNSofkAE8RfCwSLA5FGv8BSrRBAMtgIZY04RxQ6I9jom7YJm3heFDmlGTxZHf1+exTw4HNwDNhdTOjDFhSdRAh4Hgj5qVwIZKL2X+gL8DwZitp+MVB0b/xSNjuwOwiXahx8WjjGP4aVo2MaMUF5xCLROu5Zrr0I6hClNy/r53Qkq+o/oWZcJgyaFHTythjYdEhleXSD13V+cu+X0AmJqLbdPot2Q== 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=gDYoVNeFlyMXDF9d3GNHOBxLOmD9cLlShBqYQlKhFgY=; b=IzpiHfj62XXr+GEnG4KvNWFw9Tn9dT8a8W/JXaC8JzAHj18t/lLIqDQ6QXJGSHaQ5ILJR/qHPqkZz1pky0NhyAf0pHYWlN6j8x1sz6cq3RYXhs2Xf4lXjQf3Vv2neqMLor99SvSEAdjwTUQa6mI5cK+5Bp6NUvmuHeRMqvK3Noh36r/mDnK9aIjLhgecW6Q3MAAEBKo1/BeEHpbjDB4aUXI1Su9x2bhS3eW80lmczMHtxTYSE+lVxSuxUdsyibz6xMcykKzFRi2OncS2dlJs4t/7qo4vog89P4kcV8PLAiLe6tmHNg2wydgM++PozbJbAHlpXAnHYHSTy1SqbTuvGQ== 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=gDYoVNeFlyMXDF9d3GNHOBxLOmD9cLlShBqYQlKhFgY=; b=nn+ZoL8XzWz7AQRcT77DO+B0jXhfDjG7N+cIwRToqlH4sL47IdT4iG1hknrnaA2/IVIBo9d9rw/MengmDgHgCB6vxhKqdrukfOXxoOHb59FgGJOb2OM1PcY02B5TnSzV8vbRADcfBai5ce3JvS/tTx6GYjbSYUyGEQlUFUH/ThQ= Authentication-Results: spf=none (sender IP is ) smtp.mailfrom=florinel.iordache@nxp.com; Received: from VI1PR04MB5454.eurprd04.prod.outlook.com (20.178.122.87) by VI1PR04MB4272.eurprd04.prod.outlook.com (10.171.182.33) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.2835.20; Thu, 26 Mar 2020 13:52:16 +0000 Received: from VI1PR04MB5454.eurprd04.prod.outlook.com ([fe80::69f6:5d59:b505:a6c8]) by VI1PR04MB5454.eurprd04.prod.outlook.com ([fe80::69f6:5d59:b505:a6c8%3]) with mapi id 15.20.2835.023; Thu, 26 Mar 2020 13:52:16 +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 7/9] net: phy: enable qoriq backplane support Date: Thu, 26 Mar 2020 15:51:20 +0200 Message-Id: <1585230682-24417-8-git-send-email-florinel.iordache@nxp.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1585230682-24417-1-git-send-email-florinel.iordache@nxp.com> References: <1585230682-24417-1-git-send-email-florinel.iordache@nxp.com> Reply-to: florinel.iordache@nxp.com X-ClientProxiedBy: AM0PR01CA0142.eurprd01.prod.exchangelabs.com (2603:10a6:208:168::47) To VI1PR04MB5454.eurprd04.prod.outlook.com (2603:10a6:803:d1::23) MIME-Version: 1.0 X-MS-Exchange-MessageSentRepresentingType: 1 Received: from fsr-ub1464-128.ea.freescale.net (89.37.124.34) by AM0PR01CA0142.eurprd01.prod.exchangelabs.com (2603:10a6:208:168::47) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256) id 15.20.2835.20 via Frontend Transport; Thu, 26 Mar 2020 13:52:14 +0000 X-Mailer: git-send-email 1.9.1 X-Originating-IP: [89.37.124.34] X-MS-PublicTrafficType: Email X-MS-Office365-Filtering-HT: Tenant X-MS-Office365-Filtering-Correlation-Id: b070d47e-0bbf-4530-e2e9-08d7d18ce4c3 X-MS-TrafficTypeDiagnostic: VI1PR04MB4272:|VI1PR04MB4272: X-MS-Exchange-Transport-Forked: True X-Microsoft-Antispam-PRVS: X-MS-Oob-TLC-OOBClassifiers: OLM:568; X-Forefront-PRVS: 0354B4BED2 X-Forefront-Antispam-Report: SFV:NSPM; SFS:(10009020)(4636009)(39860400002)(376002)(346002)(136003)(366004)(396003)(26005)(478600001)(186003)(16526019)(956004)(81166006)(3450700001)(36756003)(81156014)(8936002)(44832011)(4326008)(8676002)(5660300002)(2616005)(7416002)(2906002)(66946007)(86362001)(6486002)(30864003)(66556008)(316002)(6506007)(6512007)(6666004)(66476007)(52116002)(579004); DIR:OUT; SFP:1101; SCL:1; SRVR:VI1PR04MB4272; H:VI1PR04MB5454.eurprd04.prod.outlook.com; FPR:; SPF:None; LANG:en; PTR:InfoNoRecords; Received-SPF: None (protection.outlook.com: nxp.com does not designate permitted sender hosts) X-MS-Exchange-SenderADCheck: 1 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: 2K8nihQLkjuZk4T60XKdI2rpBMq2yyGlGM/pi6v+AwUo1dJNcy9m0nLsZD5ffFlcTuuyu8F+BNRyvGuV/us6b612ADRvxp/ZXtY19rbDeSl0iEQzY7x7b8CSg2eDKKC74R6QfyI8iQ/VpsACVko7Jjo3kbXO10iBjL0PDfXkrFg4MLP7Z8PALBKXvjrxUhEkR8gohFkdYF/fe4OSdfcjkcaKFa0QKz0cV2hcshzSe+Iq5e182sUnZoK91rRjdKsT/cOSvLsbwdAfq8MTR/cDMyWP9qYYIN697qvfUNUC90o/8j1fbroM+9brKnspJjRCTwVsR8bkIhkJ2cdJcSvPsEvLcyTF18Q1QHD8tIqzvZ3X1Qy9NZy4qebJRoB2u9ynuCNZZif8i6IphE+Fcp18i4CstwV+mh9jHfWRV/abXvnAfyTjZt2dIHhHcGNNcdlA X-MS-Exchange-AntiSpam-MessageData: 3dFUKU6xNqPmh51H7l9CfcoPhaVVG481ttA8DLagzYvOGxnAEmF8knKoSrFHwZVvQ4DwwMOKd0qU6geyzhroT8C8MV5BUilAh4svlSla1joIphGTm7nV/PoHsE15F0MVM4sA6L2H3cOHxA8nWCrzww== X-OriginatorOrg: nxp.com X-MS-Exchange-CrossTenant-Network-Message-Id: b070d47e-0bbf-4530-e2e9-08d7d18ce4c3 X-MS-Exchange-CrossTenant-OriginalArrivalTime: 26 Mar 2020 13:52:16.1643 (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: 0DYZ3+8b23ZvkvO4rDm52rJDHz4lrsBayAgFsv0jq2RY7rtkyp7wQOOwjW+uf2kWDe/zr/xJVDGxJmfsIcA1dwCfK1uL1B0rcqFTdj5n2R4= X-MS-Exchange-Transport-CrossTenantHeadersStamped: VI1PR04MB4272 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 --- drivers/net/phy/backplane/Kconfig | 11 +- drivers/net/phy/backplane/Makefile | 2 + drivers/net/phy/backplane/qoriq_backplane.c | 442 ++++++++++++++++++++++ drivers/net/phy/backplane/qoriq_backplane.h | 33 ++ drivers/net/phy/backplane/qoriq_serdes_10g.c | 470 +++++++++++++++++++++++ drivers/net/phy/backplane/qoriq_serdes_28g.c | 533 +++++++++++++++++++++++++++ 6 files changed, 1490 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 create mode 100644 drivers/net/phy/backplane/qoriq_serdes_28g.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..d8f95ac 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 qoriq_serdes_28g.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..9a2cd4e --- /dev/null +++ b/drivers/net/phy/backplane/qoriq_backplane.c @@ -0,0 +1,442 @@ +// 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 + +/* AN registers initialization */ +#define KR_AN_AD1_INIT_10G 0x85 +#define KR_AN_AD1_INIT_40G 0x105 + +/* AN masks: Backplane Ethernet status (Register 7.48) */ +#define AN_MASK_10GBASE_KR 0x08 +#define AN_MASK_40GBASE_KR4 0x20 + +/* 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_28G, + SERDES_INVAL +}; + +/* Backplane Ethernet Status Register (an_bp_eth_status) + * chapter: 45.2.7.12 Backplane Ethernet status (Register 7.48) + * - bit AN_MASK_10GBASE_KR for 10GBase-KR + * - bit AN_MASK_40GBASE_KR4 for 40GBase-KR4 + */ +static u32 get_an_bp_eth_status_bit(phy_interface_t mode) +{ + u32 an_mask = 0; + + switch (mode) { + case PHY_INTERFACE_MODE_10GKR: + an_mask = AN_MASK_10GBASE_KR; + break; + case PHY_INTERFACE_MODE_40GKR4: + an_mask = AN_MASK_40GBASE_KR4; + break; + /* add AN support for other backplane modes here */ + default: + an_mask = 0; + break; + } + return an_mask; +} + +static u32 get_an_ad_ability_1_init(phy_interface_t mode) +{ + u32 init_value = 0; + + switch (mode) { + case PHY_INTERFACE_MODE_10GKR: + init_value = KR_AN_AD1_INIT_10G; + break; + case PHY_INTERFACE_MODE_40GKR4: + init_value = KR_AN_AD1_INIT_40G; + break; + /* add AN support for other backplane modes here */ + default: + init_value = 0; + break; + } + return init_value; +} + +/* qoriq_backplane_probe + * + * Probe function for QorIQ backplane driver to provide QorIQ device specific + * behavior + * + * bpphy: 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 *bpphy) +{ + static bool one_time_action = true; + + if (one_time_action) { + one_time_action = false; + pr_info("%s: QorIQ Backplane driver version %s\n", + QORIQ_BACKPLANE_DRIVER_NAME, + QORIQ_BACKPLANE_DRIVER_VERSION); + } + + /* call generic driver probe */ + return backplane_probe(bpphy); +} + +/* qoriq_backplane_config_init + * + * Config_Init function for QorIQ devices to provide QorIQ specific behavior + * + * bpphy: backplane phy device + * + * Return: Zero for success or error code in case of failure + */ +static int qoriq_backplane_config_init(struct phy_device *bpphy) +{ + struct backplane_phy_info *bp_phy = bpphy->priv; + struct device_node *bpphy_node, *serdes_node, *lane_node; + struct resource res; + const char *serdes_comp; + const struct lane_io_ops *lane_ops = NULL; + struct qoriq_lane_ops *qoriq_lane = NULL; + const struct equalizer_info *equalizer = NULL; + int comp_no, i, ret; + int serdes_type = SERDES_INVAL; + u32 eqparams[EQ_PARAMS_NO]; + int proplen; + + bpphy_node = bpphy->mdio.dev.of_node; + if (!bpphy_node) { + bpdev_err(bpphy, "No associated device tree node\n"); + return -EINVAL; + } + + if (!bp_phy) { + bpdev_err(bpphy, "Backplane phy info is not allocated\n"); + return -EINVAL; + } + + if (!backplane_is_valid_mode(bpphy->interface)) + return -EINVAL; + + bp_phy->bp_mode = bpphy->interface; + bp_phy->num_lanes = backplane_num_lanes(bpphy->interface); + + proplen = of_property_count_u32_elems(bpphy_node, "lane-handle"); + if (proplen < bp_phy->num_lanes) { + bpdev_err(bpphy, "Unspecified lane handles\n"); + return -EINVAL; + } + serdes_node = NULL; + for (i = 0; i < bp_phy->num_lanes; i++) { + lane_node = of_parse_phandle(bpphy_node, "lane-handle", i); + if (!lane_node) { + bpdev_err(bpphy, "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) { + bpdev_err(bpphy, + "could not obtain lane memory map for index=%d, ret = %d\n", + i, ret); + return ret; + } + /* setup lane address */ + bp_phy->krln[i].lane_addr = res.start; + + of_node_put(lane_node); + } + if (!serdes_node) { + bpdev_err(bpphy, "serdes node not found\n"); + return -EINVAL; + } + bp_phy->bp_dev.is_little_endian = of_property_read_bool(serdes_node, + "little-endian"); + + ret = of_address_to_resource(serdes_node, 0, &res); + if (ret) { + bpdev_err(bpphy, + "could not obtain serdes memory map, ret = %d\n", + ret); + return ret; + } + bp_phy->bp_dev.base_addr = res.start; + bp_phy->bp_dev.memmap_size = res.end - res.start + 1; + + 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_type = SERDES_10G; + break; + } else if (!strcasecmp(serdes_comp, "serdes-28g")) { + serdes_type = SERDES_28G; + break; + } + } + } + + if (serdes_type == SERDES_INVAL) { + bpdev_err(bpphy, "Unknown serdes-type\n"); + return 0; + } + + /* call generic driver parse DT */ + ret = backplane_parse_dt(bpphy); + if (ret) + return ret; + + /* call generic driver setup mdio */ + ret = backplane_setup_mdio(bpphy); + if (ret) + return ret; + + /* override default mdio setup and initialize lane ops */ + switch (serdes_type) { + case SERDES_10G: + qoriq_setup_mdio_10g(&bp_phy->bp_dev); + lane_ops = qoriq_get_lane_ops_10g(); + qoriq_setup_mem_io_10g(bp_phy->bp_dev.io); + equalizer = qoriq_get_equalizer_info_10g(); + break; + case SERDES_28G: + qoriq_setup_mdio_28g(&bp_phy->bp_dev); + lane_ops = qoriq_get_lane_ops_28g(); + qoriq_setup_mem_io_28g(bp_phy->bp_dev.io); + equalizer = qoriq_get_equalizer_info_28g(); + break; + default: + bpdev_err(bpphy, "Serdes type not supported\n"); + return -EINVAL; + } + if (!lane_ops) { + bpdev_err(bpphy, "Lane ops not available\n"); + return -EINVAL; + } + if (!equalizer) { + bpdev_err(bpphy, "Equalizer not available\n"); + return -EINVAL; + } + + bp_phy->bp_dev.lane_ops = lane_ops; + bp_phy->bp_dev.equalizer = equalizer; + qoriq_lane = (struct qoriq_lane_ops *)lane_ops->priv; + + /* install AN bp_eth_status decoding callback */ + bp_phy->bp_dev.mdio.get_an_bp_eth_status_bit = get_an_bp_eth_status_bit; + bp_phy->bp_dev.mdio.get_an_ad_ability_1_init = get_an_ad_ability_1_init; + + if (backplane_is_mode_kr(bp_phy->bp_mode)) { + /* setup coefficient limits */ + bp_phy->bp_dev.cm_min = PRE_COEF_MIN; + bp_phy->bp_dev.cm_max = PRE_COEF_MAX; + bp_phy->bp_dev.cz_min = ZERO_COEF_MIN; + bp_phy->bp_dev.cz_max = ZERO_COEF_MAX; + bp_phy->bp_dev.cp_min = POST_COEF_MIN; + bp_phy->bp_dev.cp_max = POST_COEF_MAX; + bp_phy->bp_dev.sum_ratio_numer = COEF_SUM_RATIO_NUMERATOR; + bp_phy->bp_dev.sum_ratio_denom = COEF_SUM_RATIO_DENOMINATOR; + } + + /* 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(bpphy_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(bpphy_node, "eq-params", + (u32 *)eqparams, proplen); + if (ret == 0) { + bp_phy->bp_dev.ampr_def_dt = true; + bp_phy->bp_dev.amp_red_def = eqparams[0]; + } + } + + /* call generic driver setup lanes */ + ret = backplane_setup_lanes(bpphy); + if (ret) + return ret; + + /* call generic driver initialize + * start the lane timers used to run the algorithm + */ + ret = backplane_initialize(bpphy); + if (ret) + return ret; + + return 0; +} + +static int qoriq_backplane_match_phy_device(struct phy_device *bpphy) +{ + struct device_node *bpphy_node, *serdes_node, *lane_node; + const char *serdes_comp; + int comp_no, i, ret; + int serdes_type = SERDES_INVAL; + + if (!bpphy->mdio.dev.of_node) + return 0; + + if (!bpphy->is_c45) + return 0; + + bpphy_node = bpphy->mdio.dev.of_node; + if (!bpphy_node) { + bpdev_err(bpphy, "No associated device tree node\n"); + return 0; + } + + /* Get Master lane node */ + lane_node = of_parse_phandle(bpphy_node, "lane-handle", 0); + if (!lane_node) + return 0; + serdes_node = lane_node->parent; + of_node_put(lane_node); + if (!serdes_node) + return 0; + + 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_type = SERDES_10G; + break; + } else if (!strcasecmp(serdes_comp, "serdes-28g")) { + serdes_type = SERDES_28G; + break; + } + } + } + + if (serdes_type == SERDES_INVAL) { + bpdev_err(bpphy, "Unknown serdes-type\n"); + return 0; + } + + switch (serdes_type) { + 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(bpphy->c45_ids.device_ids); i++) { + if (!(bpphy->c45_ids.devices_in_package & (1 << i))) + continue; + + if ((PCS_PHY_DEVICE_ID & PCS_PHY_DEVICE_ID_MASK) == + (bpphy->c45_ids.device_ids[i] & + PCS_PHY_DEVICE_ID_MASK)) + return 1; + } + break; + case SERDES_28G: + /* WORKAROUND: + * Required for LX2 devices + * where PHY ID cannot be verified in PCS + * because PCS Device Identifier Upper and Lower registers are + * hidden and always return 0 when they are read: + * 2 02 Device_ID0 RO Bits 15:0 0 + * val = phy_read_mmd(bpphy, MDIO_MMD_PCS, 0x2); + * 3 03 Device_ID1 RO Bits 31:16 0 + * val = phy_read_mmd(bpphy, MDIO_MMD_PCS, 0x3); + * + * To be removed: After the issue will be fixed on LX2 devices + */ + + /* On LX devices we cannot verify PHY ID + * phy id because registers are hidden + * so we are happy only with preliminary verifications + * already made: mdio.dev.of_node, is_c45 + * and lane-handle with valid serdes parent + * because we already filtered other undesired devices: + * non clause 45 + */ + return 1; + default: + bpdev_err(bpphy, "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..bbfdfb5 --- /dev/null +++ b/drivers/net/phy/backplane/qoriq_backplane.h @@ -0,0 +1,33 @@ +/* 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 + +struct qoriq_lane_ops { + u32 (*read_tecr0)(void *reg); + u32 (*read_tecr1)(void *reg); +}; + +const struct lane_io_ops *qoriq_get_lane_ops_10g(void); +const struct lane_io_ops *qoriq_get_lane_ops_28g(void); + +const struct equalizer_info *qoriq_get_equalizer_info_10g(void); +const struct equalizer_info *qoriq_get_equalizer_info_28g(void); + +void qoriq_setup_mem_io_10g(struct mem_io_ops io); +void qoriq_setup_mem_io_28g(struct mem_io_ops io); + +void qoriq_setup_mdio_10g(struct backplane_dev_info *bp_dev); +void qoriq_setup_mdio_28g(struct backplane_dev_info *bp_dev); + +#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..8ee7308 --- /dev/null +++ b/drivers/net/phy/backplane/qoriq_serdes_10g.c @@ -0,0 +1,470 @@ +// 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 + +#define AN_AD_ABILITY_0 0x10 +#define AN_AD_ABILITY_1 0x11 +#define AN_BP_ETH_STATUS_OFFSET 0x30 +#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_ops io_ops; + +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_ops.write32(io_ops.read32(®_base->gcr0) & ~GCR0_TRST_MASK, + ®_base->gcr0); + } + /* reset Rx lane: send reset request */ + if (ln_req | LANE_RX) { + io_ops.write32(io_ops.read32(®_base->gcr0) & ~GCR0_RRST_MASK, + ®_base->gcr0); + } + /* unreset the lane */ + if (ln_req != LANE_INVALID) { + udelay(1); + io_ops.write32(io_ops.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_ops.read32(®_base->tecr0); +} + +static u32 read_tecr1(void __iomem *reg) +{ + return 0; +} + +static void read_tecr_params(void __iomem *reg, struct lane_kr_params *params) +{ + struct qoriq_lane_regs __iomem *reg_base = reg; + u32 val; + + val = io_ops.read32(®_base->tecr0); + + params->ratio_preq = (val & RATIO_PREQ_MASK) >> RATIO_PREQ_SHIFT; + params->ratio_pstq = (val & RATIO_PST1Q_MASK) >> RATIO_PST1Q_SHIFT; + params->adpt_eq = (val & ADPT_EQ_MASK) >> ADPT_EQ_SHIFT; + params->amp_red = (val & AMP_RED_MASK) >> AMP_RED_SHIFT; +} + +static void setup_tecr(void __iomem *reg, struct lane_kr_params *params, + bool reset) +{ + struct qoriq_lane_regs __iomem *reg_base = reg; + u32 val; + + val = TECR0_INIT | + params->adpt_eq << ADPT_EQ_SHIFT | + params->ratio_preq << RATIO_PREQ_SHIFT | + params->ratio_pstq << RATIO_PST1Q_SHIFT | + params->amp_red << AMP_RED_SHIFT; + + if (reset) { + /* reset the lane */ + io_ops.write32(io_ops.read32(®_base->gcr0) & + ~GCR0_RESET_MASK, ®_base->gcr0); + udelay(1); + } + + io_ops.write32(val, ®_base->tecr0); + udelay(1); + + if (reset) { + /* unreset the lane */ + io_ops.write32(io_ops.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) +{ + u32 rx_eq_snp; + struct qoriq_lane_regs __iomem *reg_base = reg; + int timeout; + int i; + + for (i = 0; i < size; i++) { + /* wait RECR1_CTL_SNP_DONE_MASK has cleared */ + timeout = 100; + while (io_ops.read32(®_base->recr1) & + RECR1_CTL_SNP_DONE_MASK) { + udelay(1); + timeout--; + if (timeout == 0) + break; + } + + /* start snapshot */ + io_ops.write32((io_ops.read32(®_base->gcr1) | + GCR1_CTL_SNP_START_MASK), ®_base->gcr1); + + /* wait for SNP done */ + timeout = 100; + while (!(io_ops.read32(®_base->recr1) & + RECR1_CTL_SNP_DONE_MASK)) { + udelay(1); + timeout--; + if (timeout == 0) + break; + } + + /* read and save the snapshot */ + rx_eq_snp = io_ops.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_ops.write32((io_ops.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) +{ + int bin_snapshot; + u32 bin_sel; + int i, timeout; + struct qoriq_lane_regs __iomem *reg_base = reg; + + /* 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_ops.read32(®_base->recr1) & + RECR1_SNP_DONE_MASK)) { + udelay(1); + timeout--; + if (timeout == 0) + break; + } + + /* set TCSR1[CDR_SEL] */ + io_ops.write32((io_ops.read32(®_base->tcsr1) & + ~CDR_SEL_MASK) | bin_sel, ®_base->tcsr1); + + /* start snapshot */ + io_ops.write32(io_ops.read32(®_base->gcr1) | + GCR1_SNP_START_MASK, ®_base->gcr1); + + /* wait for SNP done */ + timeout = 100; + while (!(io_ops.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_ops.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_ops.write32(io_ops.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_ops.read32(®_base->tcsr3) & TCSR3_CDR_LCK_MASK) + return true; + + return false; +} + +static const struct qoriq_lane_ops qoriq_lane = { + .read_tecr0 = read_tecr0, + .read_tecr1 = read_tecr1, +}; + +static const struct lane_io_ops lane_ops = { + .priv = &qoriq_lane, + .memmap_size = MEMORY_MAP_SIZE, + .reset_lane = reset_lane, + .tune_lane_kr = setup_tecr, + .read_lane_kr = read_tecr_params, + .is_cdr_lock = is_cdr_lock_bit, +}; + +const struct lane_io_ops *qoriq_get_lane_ops_10g(void) +{ + return &lane_ops; +} + +static const struct equalizer_info 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_info *qoriq_get_equalizer_info_10g(void) +{ + return &equalizer; +} + +void qoriq_setup_mem_io_10g(struct mem_io_ops io) +{ + io_ops = io; +} + +void qoriq_setup_mdio_10g(struct backplane_dev_info *bp_dev) +{ + /* IEEE802.3 Clause 45 register spaces */ + + /* KR PMD registers */ + backplane_setup_kr_lt_mmd(bp_dev, MDIO_MMD_PMAPMD, KR_PMD_BASE_OFFSET); + + /* KX/KR AN registers: IEEE802.3 Clause 45 MMD 7 */ + bp_dev->mdio.an_ad_ability_0 = AN_AD_ABILITY_0; + bp_dev->mdio.an_ad_ability_1 = AN_AD_ABILITY_1; + bp_dev->mdio.an_bp_eth_status = AN_BP_ETH_STATUS_OFFSET; +} diff --git a/drivers/net/phy/backplane/qoriq_serdes_28g.c b/drivers/net/phy/backplane/qoriq_serdes_28g.c new file mode 100644 index 0000000..5d847a4 --- /dev/null +++ b/drivers/net/phy/backplane/qoriq_serdes_28g.c @@ -0,0 +1,533 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* QorIQ Backplane driver for SerDes 28G + * + * Copyright 2018-2020 NXP + */ + +#include +#include +#include + +#include "qoriq_backplane.h" + +#define EQUALIZER_NAME "qoriq_serdes_28g" +#define EQUALIZER_VERSION "1.0.0" + +#define BIN_1_SEL 0x00000000 +#define BIN_2_SEL 0x00001000 +#define BIN_3_SEL 0x00002000 +#define BIN_4_SEL 0x00003000 +#define BIN_OFFSET_SEL 0x00004000 +#define BIN_BLW_SEL 0x00008000 +#define BIN_AVG_SEL 0x00009000 +#define BIN_M1_SEL 0x0000c000 +#define BIN_LONG_SEL 0x0000d000 +#define CDR_SEL_MASK 0x0000f000 + +#define RATIO_PREQ_SHIFT 16 +#define RATIO_PST1Q_SHIFT 8 +#define AMP_RED_SHIFT 0 +#define ADPT_EQ_SHIFT 24 + +#define RATIO_PREQ_MASK 0x000f0000 +#define RATIO_PST1Q_MASK 0x00001f00 +#define ADPT_EQ_MASK 0x3f000000 +#define AMP_RED_MASK 0x0000003f + +#define TECR0_INIT 0x20808000 + +#define RESET_REQ_MASK 0x80000000 + +#define TCSR0_SD_STAT_OBS_EN_MASK 0x80000000 +#define RECR3_SNP_START_MASK 0x80000000 +#define RECR3_SNP_DONE_MASK 0x40000000 + +#define RECR4_SNP_DATA_MASK 0x000001ff +#define RECR4_SNP_DATA_SHIFT 0 +#define RECR4_EQ_SNPBIN_SIGN_MASK 0x100 + +#define RECR3_GAINK2_MASK 0x1f000000 +#define RECR3_GAINK2_SHIFT 24 + +#define RECR3_GAINK3_MASK 0x001f0000 +#define RECR3_GAINK3_SHIFT 16 + +#define RECR4_EQ_OFFSET_MASK 0x003f0000 +#define RECR4_EQ_OFFSET_SHIFT 16 + +#define RRSTCTL_CDR_LOCK_MASK 0x00001000 + +#define AN_AD_ABILITY_0 0x02 +#define AN_AD_ABILITY_1 0x03 +#define AN_BP_ETH_STATUS_OFFSET 0x0F +#define KR_PMD_BASE_OFFSET 0x100 + +/* 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 0x1F +#define EQ_GAINK_MAX 0x0 +#define EQ_GAINK_MIDRANGE_LOW 0x1E +#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 0x100 + +struct qoriq_lane_regs { + u32 gcr0; /* 0x00: General Control Register 0 */ + u32 res_04[7]; /* 0x04: Reserved */ + u32 trstctl; /* 0x20: TX Reset Control Register */ + u32 tgcr0; /* 0x24: TX General Control Register 0 */ + u32 tgcr1; /* 0x28: TX General Control Register 1 */ + u32 tgcr2; /* 0x2C: TX General Control Register 2 */ + u32 tecr0; /* 0x30: Transmit Equalization Control Register 0 */ + u32 tecr1; /* 0x34: Transmit Equalization Control Register 1 */ + u32 res_38[2]; /* 0x38: Reserved */ + u32 rrstctl; /* 0x40: RX Reset Control Register */ + u32 rgcr0; /* 0x44: RX General Control Register 0 */ + u32 rxgcr1; /* 0x48: RX General Control Register 1 */ + u32 res_4c; /* 0x4C: Reserved */ + u32 recr0; /* 0x50: RX Equalization Register 0 */ + u32 recr1; /* 0x54: RX Equalization Register 1 */ + u32 recr2; /* 0x58: RX Equalization Register 2 */ + u32 recr3; /* 0x5C: RX Equalization Register 3 */ + u32 recr4; /* 0x60: RX Equalization Register 4 */ + u32 res_64; /* 0x64: Reserved */ + u32 rccr0; /* 0x68: RX Calibration Register 0 */ + u32 rccr1; /* 0x6C: RX Calibration Register 1 */ + u32 rcpcr0; /* 0x70: RX Clock Path Register 0 */ + u32 rsccr0; /* 0x74: RX Sampler Calibration Control Register 0 */ + u32 rsccr1; /* 0x78: RX Sampler Calibration Control Register 1 */ + u32 res_7c; /* 0x7C: Reserved */ + u32 ttlcr0; /* 0x80: Transition Tracking Loop Register 0 */ + u32 ttlcr1; /* 0x84: Transition Tracking Loop Register 1 */ + u32 ttlcr2; /* 0x88: Transition Tracking Loop Register 2 */ + u32 ttlcr3; /* 0x8C: Transition Tracking Loop Register 3 */ + u32 res_90[4]; /* 0x90: Reserved */ + u32 tcsr0; /* 0xA0: Test Control/Status Register 0 */ + u32 tcsr1; /* 0xA4: Test Control/Status Register 1 */ + u32 tcsr2; /* 0xA8: Test Control/Status Register 2 */ + u32 tcsr3; /* 0xAC: Test Control/Status Register 3 */ + u32 tcsr4; /* 0xB0: Test Control/Status Register 4 */ + u32 res_b4[3]; /* 0xB4: Reserved */ + u32 rxcb0; /* 0xC0: RX Control Block Register 0 */ + u32 rxcb1; /* 0xC4: RX Control Block Register 1 */ + u32 res_c8[2]; /* 0xC8: Reserved */ + u32 rxss0; /* 0xD0: RX Speed Switch Register 0 */ + u32 rxss1; /* 0xD4: RX Speed Switch Register 1 */ + u32 rxss2; /* 0xD8: RX Speed Switch Register 2 */ + u32 res_dc; /* 0xDC: Reserved */ + u32 txcb0; /* 0xE0: TX Control Block Register 0 */ + u32 txcb1; /* 0xE4: TX Control Block Register 1 */ + u32 res_e8[2]; /* 0xE8: Reserved */ + u32 txss0; /* 0xF0: TX Speed Switch Register 0 */ + u32 txss1; /* 0xF4: TX Speed Switch Register 1 */ + u32 txss2; /* 0xF8: TX Speed Switch Register 2 */ + u32 res_fc; /* 0xFC: Reserved */ +}; + +static struct mem_io_ops io_ops; + +static void reset_lane(void __iomem *reg, enum lane_req ln_req) +{ + struct qoriq_lane_regs __iomem *reg_base = reg; + u32 val; + u64 timeout; + + /* reset Tx lane: send reset request */ + if (ln_req | LANE_TX) { + io_ops.write32(io_ops.read32(®_base->trstctl) | + RESET_REQ_MASK, ®_base->trstctl); + udelay(1); + timeout = 10; + while (timeout--) { + val = io_ops.read32(®_base->trstctl); + if (!(val & RESET_REQ_MASK)) + break; + usleep_range(5, 20); + } + } + + /* reset Rx lane: send reset request */ + if (ln_req | LANE_RX) { + io_ops.write32(io_ops.read32(®_base->rrstctl) | + RESET_REQ_MASK, ®_base->rrstctl); + udelay(1); + timeout = 10; + while (timeout--) { + val = io_ops.read32(®_base->rrstctl); + if (!(val & RESET_REQ_MASK)) + break; + usleep_range(5, 20); + } + } + + /* wait for a while after reset */ + if (ln_req != LANE_INVALID) { + timeout = jiffies + 10; + while (time_before(jiffies, (unsigned long)timeout)) { + schedule(); + usleep_range(5, 20); + } + } +} + +static u32 read_tecr0(void __iomem *reg) +{ + struct qoriq_lane_regs __iomem *reg_base = reg; + + return io_ops.read32(®_base->tecr0); +} + +static u32 read_tecr1(void __iomem *reg) +{ + struct qoriq_lane_regs __iomem *reg_base = reg; + + return io_ops.read32(®_base->tecr1); +} + +static void read_tecr_params(void __iomem *reg, struct lane_kr_params *params) +{ + struct qoriq_lane_regs __iomem *reg_base = reg; + u32 val; + + val = io_ops.read32(®_base->tecr0); + params->ratio_preq = (val & RATIO_PREQ_MASK) >> RATIO_PREQ_SHIFT; + params->ratio_pstq = (val & RATIO_PST1Q_MASK) >> RATIO_PST1Q_SHIFT; + params->amp_red = (val & AMP_RED_MASK) >> AMP_RED_SHIFT; + + val = io_ops.read32(®_base->tecr1); + params->adpt_eq = (val & ADPT_EQ_MASK) >> ADPT_EQ_SHIFT; +} + +static void setup_tecr(void __iomem *reg, struct lane_kr_params *params, + bool reset) +{ + struct qoriq_lane_regs __iomem *reg_base = reg; + u32 val; + + /* reset lanes */ + if (reset) + reset_lane(reg, LANE_RX_TX); + + val = TECR0_INIT | + params->ratio_preq << RATIO_PREQ_SHIFT | + params->ratio_pstq << RATIO_PST1Q_SHIFT | + params->amp_red << AMP_RED_SHIFT; + io_ops.write32(val, ®_base->tecr0); + + val = params->adpt_eq << ADPT_EQ_SHIFT; + io_ops.write32(val, ®_base->tecr1); + + 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) +{ + u32 recr3, recr4; + struct qoriq_lane_regs __iomem *reg_base = reg; + int timeout; + int i; + + /* Enable observation of SerDes status on all status registers */ + io_ops.write32(io_ops.read32(®_base->tcsr0) | + TCSR0_SD_STAT_OBS_EN_MASK, ®_base->tcsr0); + + for (i = 0; i < size; i++) { + /* wait RECR3_SNP_DONE_MASK has cleared */ + timeout = 100; + while (io_ops.read32(®_base->recr3) & RECR3_SNP_DONE_MASK) { + udelay(1); + timeout--; + if (timeout == 0) + break; + } + + /* start snapshot */ + io_ops.write32((io_ops.read32(®_base->recr3) | + RECR3_SNP_START_MASK), ®_base->recr3); + + /* wait for SNP done */ + timeout = 100; + while (!(io_ops.read32(®_base->recr3) & + RECR3_SNP_DONE_MASK)) { + udelay(1); + timeout--; + if (timeout == 0) + break; + } + + /* read and save the snapshot */ + recr3 = io_ops.read32(®_base->recr3); + recr4 = io_ops.read32(®_base->recr4); + + if (gaink2) + gaink2[i] = (u8)((recr3 & RECR3_GAINK2_MASK) >> + RECR3_GAINK2_SHIFT); + if (gaink3) + gaink3[i] = (u8)((recr3 & RECR3_GAINK3_MASK) >> + RECR3_GAINK3_SHIFT); + if (osestat) + osestat[i] = (u8)((recr4 & RECR4_EQ_OFFSET_MASK) >> + RECR4_EQ_OFFSET_SHIFT); + + /* terminate the snapshot by setting GCR1[REQ_CTL_SNP] */ + io_ops.write32((io_ops.read32(®_base->recr3) & + ~RECR3_SNP_START_MASK), ®_base->recr3); + } + 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) +{ + int bin_snapshot; + u32 bin_sel; + int i, timeout; + struct qoriq_lane_regs __iomem *reg_base = reg; + + /* calculate RECR4[EQ_BIN_DATA_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_4: + bin_sel = BIN_4_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; + } + + /* Enable observation of SerDes status on all status registers */ + io_ops.write32(io_ops.read32(®_base->tcsr0) | + TCSR0_SD_STAT_OBS_EN_MASK, ®_base->tcsr0); + + for (i = 0; i < bin_size; i++) { + /* wait RECR3_SNP_DONE_MASK has cleared */ + timeout = 100; + while ((io_ops.read32(®_base->recr3) & + RECR3_SNP_DONE_MASK)) { + udelay(1); + timeout--; + if (timeout == 0) + break; + } + + /* set RECR4[EQ_BIN_DATA_SEL] */ + io_ops.write32((io_ops.read32(®_base->recr4) & + ~CDR_SEL_MASK) | bin_sel, ®_base->recr4); + + /* start snapshot */ + io_ops.write32(io_ops.read32(®_base->recr3) | + RECR3_SNP_START_MASK, ®_base->recr3); + + /* wait for SNP done */ + timeout = 100; + while (!(io_ops.read32(®_base->recr3) & + RECR3_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_ops.read32(®_base->recr4) & + RECR4_SNP_DATA_MASK) >> RECR4_SNP_DATA_SHIFT; + if (bin_snapshot & RECR4_EQ_SNPBIN_SIGN_MASK) { + /* 2's complement 9 bit long negative number */ + bin_snapshot &= ~RECR4_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_ops.write32(io_ops.read32(®_base->recr3) & + ~RECR3_SNP_START_MASK, ®_base->recr3); + } + 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_4: + 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_ops.read32(®_base->rrstctl) & RRSTCTL_CDR_LOCK_MASK) + return true; + + return false; +} + +static const struct qoriq_lane_ops qoriq_lane = { + .read_tecr0 = read_tecr0, + .read_tecr1 = read_tecr1, +}; + +static const struct lane_io_ops lane_ops = { + .priv = &qoriq_lane, + .memmap_size = MEMORY_MAP_SIZE, + .reset_lane = reset_lane, + .tune_lane_kr = setup_tecr, + .read_lane_kr = read_tecr_params, + .is_cdr_lock = is_cdr_lock_bit, +}; + +const struct lane_io_ops *qoriq_get_lane_ops_28g(void) +{ + return &lane_ops; +} + +static const struct equalizer_info 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_info *qoriq_get_equalizer_info_28g(void) +{ + return &equalizer; +} + +void qoriq_setup_mem_io_28g(struct mem_io_ops io) +{ + io_ops = io; +} + +void qoriq_setup_mdio_28g(struct backplane_dev_info *bp_dev) +{ + /* Auto-Negotiation and Link Training Core Registers: + * IEEE802.3 Clauses 72, 73, 92 + */ + + /* Link Training Control and Status Registers: page 1: 0x100 */ + backplane_setup_kr_lt_mmd(bp_dev, MDIO_MMD_AN, KR_PMD_BASE_OFFSET); + + /* Auto-Negotiation Control and Status Registers: page 0 */ + bp_dev->mdio.an_ad_ability_0 = AN_AD_ABILITY_0; + bp_dev->mdio.an_ad_ability_1 = AN_AD_ABILITY_1; + bp_dev->mdio.an_bp_eth_status = AN_BP_ETH_STATUS_OFFSET; +} From patchwork Thu Mar 26 13:51: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: 1261998 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=none (no SPF record) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; 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=V+Y/bh8+; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 48p5z83n2pz9sT9 for ; Fri, 27 Mar 2020 00:52:52 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728071AbgCZNwr (ORCPT ); Thu, 26 Mar 2020 09:52:47 -0400 Received: from mail-eopbgr60089.outbound.protection.outlook.com ([40.107.6.89]:57111 "EHLO EUR04-DB3-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1727707AbgCZNwr (ORCPT ); Thu, 26 Mar 2020 09:52:47 -0400 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=TM/6CMcYDHZrSA8ACkRdu4Mce8OORFb+0deNIL/6c9+CikKna2uKa2pE9y3Y1Qp2kCsOIxLumZUvccoKTQlTmAZiVeTbGWPwwa7r4iNiiIiikWPFp5rHdI9zZ/D0pBjse/VPs1nEcNF+QysNAp4omkBVTSE2yEE1EmvjdfLwA9K5Jo5J0Ql/MJMKCSdPQX2Wl/8lRXQwF3hndEu8sn96NJiSdpaHZAlUPz2nfLMollRbfsQ9J7soRekFfOVECHmsCqJa6jEuARSjpy6JbGtDK4RYiH49GnA59AvoIu1hOV4Zs4z+1N1KxV2cxibRBz93+LM6n01Pd1Pb9IBnSoYHmQ== 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=P0wOW7awuHLwlx8uSLjJCqcIhR8hxr2Npy0wqXk/P7s=; b=NvQVgGiItra5PCFqUazxFhzRxa4Gdu3q6nRQvM4c7Oo1seve9Rx6RDfFpnG/356PiozJQ6QtWGTqCarP4rVl0r62ZjH3dcyRVffncQK7XuxfrZZDETnRa3VPeAkGh+AJrnumj9Clrioaoz0i/O7Od5NRcE2Dg9GnMAgxe5Rvle0GXoCAEXvTunPTcI071dA6LMCEOpQL6zlPQZwrIeoQVJs7Uz8wR/sShzJvQ9pr41/Yl9aOUM69iB5b/yNFYXqpMqjam2aQPFg6bHrIIsy7MaltvjNp0mcPlRbIRlcVVJDDGAYWuVxsc0zz0kCmqxCHt/oqNYc+dEFtuQqeNQrqzw== 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=P0wOW7awuHLwlx8uSLjJCqcIhR8hxr2Npy0wqXk/P7s=; b=V+Y/bh8+Wq7u8htoQnE7OyfLLKs5hMI3CKNOcwFvQY/7dzUL9J2RkUHla913gebH2Y3CVSFjY04373ypojeg63x6Hxc0JNRGR5Nu16f1dWjNdYWK7FyiGvCIV0TjnS8AQG9X3gQlU8EpPqWdEwd2bTB0NXEzle44kv6UCH/MBzc= Authentication-Results: spf=none (sender IP is ) smtp.mailfrom=florinel.iordache@nxp.com; Received: from VI1PR04MB5454.eurprd04.prod.outlook.com (20.178.122.87) by VI1PR04MB4272.eurprd04.prod.outlook.com (10.171.182.33) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.2835.20; Thu, 26 Mar 2020 13:52:19 +0000 Received: from VI1PR04MB5454.eurprd04.prod.outlook.com ([fe80::69f6:5d59:b505:a6c8]) by VI1PR04MB5454.eurprd04.prod.outlook.com ([fe80::69f6:5d59:b505:a6c8%3]) with mapi id 15.20.2835.023; Thu, 26 Mar 2020 13:52:19 +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 8/9] net: phy: add bee algorithm for kr training Date: Thu, 26 Mar 2020 15:51:21 +0200 Message-Id: <1585230682-24417-9-git-send-email-florinel.iordache@nxp.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1585230682-24417-1-git-send-email-florinel.iordache@nxp.com> References: <1585230682-24417-1-git-send-email-florinel.iordache@nxp.com> Reply-to: florinel.iordache@nxp.com X-ClientProxiedBy: AM0PR01CA0142.eurprd01.prod.exchangelabs.com (2603:10a6:208:168::47) To VI1PR04MB5454.eurprd04.prod.outlook.com (2603:10a6:803:d1::23) MIME-Version: 1.0 X-MS-Exchange-MessageSentRepresentingType: 1 Received: from fsr-ub1464-128.ea.freescale.net (89.37.124.34) by AM0PR01CA0142.eurprd01.prod.exchangelabs.com (2603:10a6:208:168::47) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256) id 15.20.2835.20 via Frontend Transport; Thu, 26 Mar 2020 13:52:17 +0000 X-Mailer: git-send-email 1.9.1 X-Originating-IP: [89.37.124.34] X-MS-PublicTrafficType: Email X-MS-Office365-Filtering-HT: Tenant X-MS-Office365-Filtering-Correlation-Id: 589b2268-cbe1-4efe-3ec9-08d7d18ce6b8 X-MS-TrafficTypeDiagnostic: VI1PR04MB4272:|VI1PR04MB4272: X-MS-Exchange-Transport-Forked: True X-Microsoft-Antispam-PRVS: X-MS-Oob-TLC-OOBClassifiers: OLM:299; X-Forefront-PRVS: 0354B4BED2 X-Forefront-Antispam-Report: SFV:NSPM; SFS:(10009020)(4636009)(39860400002)(376002)(346002)(136003)(366004)(396003)(26005)(478600001)(186003)(16526019)(956004)(81166006)(3450700001)(36756003)(81156014)(8936002)(44832011)(4326008)(8676002)(5660300002)(2616005)(7416002)(2906002)(66946007)(86362001)(6486002)(30864003)(66556008)(316002)(6506007)(6512007)(6666004)(66476007)(52116002); DIR:OUT; SFP:1101; SCL:1; SRVR:VI1PR04MB4272; H:VI1PR04MB5454.eurprd04.prod.outlook.com; FPR:; SPF:None; LANG:en; PTR:InfoNoRecords; Received-SPF: None (protection.outlook.com: nxp.com does not designate permitted sender hosts) X-MS-Exchange-SenderADCheck: 1 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: mUxlXsEvayJCzjDF1ud5BmHGt0437xk+0tACO2g678UiA/R0MVC5aNcbcefSFltBWCDxCVFDycIanyIMWPcZQ38h2XeAqmbtakkzktkdMpeP9/PR/CUXGm5Sd7Br96kmlpfu1DG3iZ0qorgi0fIB/8WHy9GvUT+o+fWddhp60uVwcYjCOCGdbaqxvaJqSGELqEAsBM7dY9si/P+Mq5NASlEdseeQgkXOg8E3kkNfJMH4K6J10GHDhZkZ9sZhx8ZI38sswkTyZIXoHnKyDggFOst+s5VO1Xya1xP4ZtMc/q2UdObuicHHL5VWTeKG5ZGcJh6GSHPJGwWNXCR5dlCjm/qdGQ9fs0eeIAwAP6cPneVynkgHvUV3GAQjNzUdzL5wNdoOpgDSuPcKLMUwiinEQO7cSPDP/NsF4EHDgZHGpdyL7Y13gBEmTaXf/zmhvuk5 X-MS-Exchange-AntiSpam-MessageData: bTIWg3mLoc5rXudKXMvFQlEQbnyMjDXoQqCy7E+lm1uSdew+iIx+JXxisyGSPTa05GI54kMX4ZOlO8ysqgasE/lhCbo9lAjKmp34Y0ImY+EzRvaBiZYGO4mwEFISCqVfesuGv+4BUSelRACjuNSulA== X-OriginatorOrg: nxp.com X-MS-Exchange-CrossTenant-Network-Message-Id: 589b2268-cbe1-4efe-3ec9-08d7d18ce6b8 X-MS-Exchange-CrossTenant-OriginalArrivalTime: 26 Mar 2020 13:52:19.4314 (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: I6iFsFmNe17qqQN/hGPECRE4mTd23sOprMRtnJ+/gYFWsOgpMia8dVeTG6a0PgCBCLGDf0KArZ2MXE0A+yOvEsHdU8M6ldG1LkDocJ+znkw= X-MS-Exchange-Transport-CrossTenantHeadersStamped: VI1PR04MB4272 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 | 1078 ++++++++++++++++++++++++++++++++++++ 3 files changed, 1090 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 d8f95ac..242e938 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..f62b777 --- /dev/null +++ b/drivers/net/phy/backplane/eq_bee.c @@ -0,0 +1,1078 @@ +// 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 eq_setup_info eq_setup; + + /* 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; + s16 *bin_snapshot; + struct eqc_range *bin_range; + 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->eq_setup.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) +{ + enum req_type prev_req_cm1; + u32 update = lt_encode_startup_request(REQ_HOLD); + + 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) +{ + enum req_type prev_req_cm1; + u32 update = lt_encode_startup_request(REQ_HOLD); + + 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) +{ + enum coef_status lpst_cm1; + u32 update = lt_encode_startup_request(REQ_HOLD); + + 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->eq_setup.krlane, 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) +{ + enum coef_status lpst_cm1; + u32 update = lt_encode_startup_request(REQ_HOLD); + + 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->eq_setup.krlane, 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) { + bpdev_err(priv->eq_setup.bpphy, "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) +{ + enum req_type prev_req_cp1, prev_req_cz0; + u32 update = lt_encode_startup_request(REQ_HOLD); + + 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) +{ + enum req_type prev_req_cp1, prev_req_cz0; + u32 update = lt_encode_startup_request(REQ_HOLD); + + 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) +{ + enum coef_status lpst_cp1, lpst_cz0; + u32 update = lt_encode_startup_request(REQ_HOLD); + + 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->eq_setup.krlane, C_P1); + lpst_cz0 = lt_get_lp_coef_status(priv->eq_setup.krlane, 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) +{ + enum coef_status lpst_cp1, lpst_cz0; + u32 update = lt_encode_startup_request(REQ_HOLD); + + 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->eq_setup.krlane, C_P1); + lpst_cz0 = lt_get_lp_coef_status(priv->eq_setup.krlane, 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) +{ + enum req_type prev_req_cp1; + u32 update = lt_encode_startup_request(REQ_HOLD); + + 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) { + bpdev_err(priv->eq_setup.bpphy, "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->eq_setup.krlane, REQ_INIT); + lp_at_preset = lt_is_lp_at_startup(priv->eq_setup.krlane, REQ_PRESET); + + if (lp_at_init) { + /* Try Request Preset */ + req = lt_encode_startup_request(REQ_PRESET); + lt_lp_update(priv->eq_setup.krlane, 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->eq_setup.krlane, true); + bpdev_err(priv->eq_setup.bpphy, + "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->eq_setup.krlane); + } +} + +static bool collect_bin_counters(struct eq_data_priv *priv, + enum eqc_type type) +{ + 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->eq_setup.equalizer.ops; + if (!eqops) { + bpdev_err(priv->eq_setup.bpphy, + "No operations for equalizer %s %s\n", + priv->eq_setup.equalizer.name, + priv->eq_setup.equalizer.version); + return false; + } + snp_size = eqops->collect_counters(priv->eq_setup.reg_base, type, + bin_snapshot, EQ_SNAPSHOTS_SIZE); + /* Check if snapshots collection failed */ + if (snp_size < EQ_SNAPSHOTS_SIZE) { + bpdev_err(priv->eq_setup.bpphy, + "Counters collection failed for equalizer %s %s\n", + priv->eq_setup.equalizer.name, + priv->eq_setup.equalizer.version); + return false; + } + + /* if CDR_LOCK = 0: Statistics are invalid */ + if (!backplane_is_cdr_lock(priv->eq_setup.krlane, true)) { + process_bad_state(priv); + return false; + } + + return true; +} + +static bool collect_bit_edge_statistics(struct eq_data_priv *priv) +{ + struct equalizer_ops *eqops; + enum eqc_type status_types[] = { + EQC_GAIN_HF, EQC_GAIN_MF, EQC_EQOFFSET + }; + s16 status_counters[ARRAY_SIZE(status_types)][EQ_SNAPSHOTS_SIZE]; + 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->eq_setup.equalizer.ops; + if (!eqops) { + bpdev_err(priv->eq_setup.bpphy, + "No operations for equalizer %s %s\n", + priv->eq_setup.equalizer.name, + priv->eq_setup.equalizer.version); + return false; + } + snp_size = eqops->collect_multiple_counters(priv->eq_setup.reg_base, + status_types, + ARRAY_SIZE(status_types), + (s16 *)status_counters, + EQ_SNAPSHOTS_SIZE); + /* Check if snapshots collection failed */ + if (snp_size < EQ_SNAPSHOTS_SIZE) { + bpdev_err(priv->eq_setup.bpphy, + "Counters collection failed for equalizer %s %s\n", + priv->eq_setup.equalizer.name, + priv->eq_setup.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->eq_setup.krlane, 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->eq_setup.krlane, 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) +{ + enum bin_state bin1_snapshot_state; + enum bin_state bin2_snapshot_state; + enum bin_state bin3_snapshot_state; + struct eqc_range *gain_range, *osestat_range; + 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 osestat_mid_low, osestat_mid_high; + 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->eq_setup.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->eq_setup.krlane, 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 eq_setup_info setup) +{ + struct eq_data_priv *bee_data; + + bee_data = devm_kzalloc(&setup.bpphy->mdio.dev, + sizeof(*bee_data), GFP_KERNEL); + if (!bee_data) + return NULL; + + /* initialize algorithm setup data */ + bee_data->eq_setup = setup; + + /* 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 Thu Mar 26 13:51: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: 1261999 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=none (no SPF record) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; 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=I1G+to0S; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 48p5zC2CHfz9sSG for ; Fri, 27 Mar 2020 00:52:55 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728120AbgCZNww (ORCPT ); Thu, 26 Mar 2020 09:52:52 -0400 Received: from mail-eopbgr60089.outbound.protection.outlook.com ([40.107.6.89]:57111 "EHLO EUR04-DB3-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1727771AbgCZNwu (ORCPT ); Thu, 26 Mar 2020 09:52:50 -0400 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=GJeoLCAtvg9CrywoqFxc/bZ++DC/xFVfHyBziN68WDK1PU+ZkwtOS/zXLoWyx/Lmyx3veqPjTnzMt6XlRRO+fZ0kpSmvfMsKHndO4hNzg79YMlXwlfuhXsy8V+3Vgkt2kcbgBavCS7r/hstB/ap77lQnNnsxNxfDn0CLGhGkX39rfO2DSrHbVjtVPK6sTkfRsiZ3kOXAz1CDo90c8j5UA2cvySODAE23S3iV2uk4ePx4ydTJGmXGqhpSTlgv9GfhVqatGx4hnhE9bCxVAQgBdJNKHR5KvC16trOZBiWjJFjofMgBPA2DcF5+5jhG08EEy3SIHHxEx9SPiVd8YCslnw== 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=Vzia31wwMREO0iGK9LHLrdOHEkjh6lJ8Fm6evrVJtS8=; b=noEaGr12CXK1FObbrbVZg3UAbCDgTlUXhpfLmgWWPE27clYBNx1GLBNBnCNSQuzfQLEjuavEEU7daer9DTJMCFRQqSfRkZgd2FH/fX+YV2ZDQVnSmlXqvhOt56BmX3fysNxx6Frr+ZeEoYpGJzqrQJrzij2OwBapd4MPLIJb4ZhzSf4x00P1QumSfanNi3Yeb+mNNhF9VG9w002Wy0RQeA8+5tl8vy5ibccVLptPkniDs9hlG1hECF+f45MGK4JtNuXsbQn7eml6Z0Py/6bGRcvUXh1mGyn+bs8RQMAH5QGdF+HTky/4Q7IlQnGtvLuyp0uePqstZJMoRzY0reRyzA== 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=Vzia31wwMREO0iGK9LHLrdOHEkjh6lJ8Fm6evrVJtS8=; b=I1G+to0ShLSWnEOh7l1O1XM18VBK9nuKmaqHx68xG+E0Gf1Ly5sB4J1cSuy+zHfboQQzBP1DYWYJBA+WFSH9oGvAwIeO7x6RGdn0U/kAW74+5nJjYHby1NT4d5UmvmfZHbIi7s5yJHn6ThUWKTLrgMNstfhQLMCgMAS/r3MbSlc= Authentication-Results: spf=none (sender IP is ) smtp.mailfrom=florinel.iordache@nxp.com; Received: from VI1PR04MB5454.eurprd04.prod.outlook.com (20.178.122.87) by VI1PR04MB4272.eurprd04.prod.outlook.com (10.171.182.33) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.2835.20; Thu, 26 Mar 2020 13:52:22 +0000 Received: from VI1PR04MB5454.eurprd04.prod.outlook.com ([fe80::69f6:5d59:b505:a6c8]) by VI1PR04MB5454.eurprd04.prod.outlook.com ([fe80::69f6:5d59:b505:a6c8%3]) with mapi id 15.20.2835.023; Thu, 26 Mar 2020 13:52:22 +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 9/9] arm64: dts: add serdes and mdio description Date: Thu, 26 Mar 2020 15:51:22 +0200 Message-Id: <1585230682-24417-10-git-send-email-florinel.iordache@nxp.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1585230682-24417-1-git-send-email-florinel.iordache@nxp.com> References: <1585230682-24417-1-git-send-email-florinel.iordache@nxp.com> Reply-to: florinel.iordache@nxp.com X-ClientProxiedBy: AM0PR01CA0142.eurprd01.prod.exchangelabs.com (2603:10a6:208:168::47) To VI1PR04MB5454.eurprd04.prod.outlook.com (2603:10a6:803:d1::23) MIME-Version: 1.0 X-MS-Exchange-MessageSentRepresentingType: 1 Received: from fsr-ub1464-128.ea.freescale.net (89.37.124.34) by AM0PR01CA0142.eurprd01.prod.exchangelabs.com (2603:10a6:208:168::47) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256) id 15.20.2835.20 via Frontend Transport; Thu, 26 Mar 2020 13:52:21 +0000 X-Mailer: git-send-email 1.9.1 X-Originating-IP: [89.37.124.34] X-MS-PublicTrafficType: Email X-MS-Office365-Filtering-HT: Tenant X-MS-Office365-Filtering-Correlation-Id: 83692443-297e-4d08-8a06-08d7d18ce8a2 X-MS-TrafficTypeDiagnostic: VI1PR04MB4272:|VI1PR04MB4272: X-MS-Exchange-Transport-Forked: True X-Microsoft-Antispam-PRVS: X-MS-Oob-TLC-OOBClassifiers: OLM:6430; X-Forefront-PRVS: 0354B4BED2 X-Forefront-Antispam-Report: SFV:NSPM; SFS:(10009020)(4636009)(39860400002)(376002)(346002)(136003)(366004)(396003)(26005)(478600001)(186003)(16526019)(956004)(81166006)(3450700001)(36756003)(81156014)(8936002)(44832011)(4326008)(8676002)(5660300002)(2616005)(7416002)(2906002)(66946007)(86362001)(6486002)(30864003)(66556008)(316002)(6506007)(6512007)(66476007)(52116002); DIR:OUT; SFP:1101; SCL:1; SRVR:VI1PR04MB4272; H:VI1PR04MB5454.eurprd04.prod.outlook.com; FPR:; SPF:None; LANG:en; PTR:InfoNoRecords; Received-SPF: None (protection.outlook.com: nxp.com does not designate permitted sender hosts) X-MS-Exchange-SenderADCheck: 1 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: OsqFsruZBZcNKffM0YujYqCLmvVWIcuxgQB1zyZhNWv35AMeNtlc3ta3vOZ9NVDVfeqMyfRN/jWGWxcMs0BORDzx7++ImXVzwGfA3sGrcka7HXAj1Co7LTTMDK4tpcyfXzRCIPMTIPLjBrnrAN9bZLcye29bl9lnzhJo3+wm9BqZhnEqWqN7Z+k+U7yulGSjF6qhMSsOXfDgz1WLUPGCtkdSYvMfp5VNbY4SSX8oBW+EVsINwnOop//rwA/dCadKW2MPHKk/8pWADS/lRDL169GqWU1nNw6W7P4oMextRx7qzyW3YZXA++3QtocfdPpzceFFngGJWKaRFbfa5UL65InEIgVLqrTp873nuQQdogAFm0SuZ+V5fbC5CsRDzQFleQusrk1pjmebIjKqnf70T5sflGFXybZ9GGbvp6yJCpNFgVBEopLVCP0YYEEEsmyT X-MS-Exchange-AntiSpam-MessageData: YaAeoHgwUxYIQVltskfX3MScf2t8OlC8vAQaPurrj54bWhibPhu3dICDWWiI9hrd3D3W7FPFejC/GMnr7IMWXklWEWiqiVIb60etHkw8IbDAnljnxJLQ38I5HyHa3eB1p62q9JQpmMNmURG1Fn7T2A== X-OriginatorOrg: nxp.com X-MS-Exchange-CrossTenant-Network-Message-Id: 83692443-297e-4d08-8a06-08d7d18ce8a2 X-MS-Exchange-CrossTenant-OriginalArrivalTime: 26 Mar 2020 13:52:22.6496 (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: cwYY28niTqwbFfoa2MB5tBrQlqj4udESkIPlrdLWrSyQApi5JliQPNNobk3g2NWPo/2+3BAHvQGZuvauynZzls2xoK53xx7gk3MjTRZ+QJA= X-MS-Exchange-Transport-CrossTenantHeadersStamped: VI1PR04MB4272 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 platforms: ls1046, ls1088, ls2088, lx2160. 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 ++++- arch/arm64/boot/dts/freescale/fsl-ls1088a.dtsi | 97 ++++++++++++- arch/arm64/boot/dts/freescale/fsl-ls208xa.dtsi | 160 ++++++++++++++++++++- arch/arm64/boot/dts/freescale/fsl-lx2160a.dtsi | 128 ++++++++++++++++- .../boot/dts/freescale/qoriq-fman3-0-10g-0.dtsi | 5 +- .../boot/dts/freescale/qoriq-fman3-0-10g-1.dtsi | 5 +- 6 files changed, 418 insertions(+), 10 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/fsl-ls1088a.dtsi b/arch/arm64/boot/dts/freescale/fsl-ls1088a.dtsi index 5945662..474464e 100644 --- a/arch/arm64/boot/dts/freescale/fsl-ls1088a.dtsi +++ b/arch/arm64/boot/dts/freescale/fsl-ls1088a.dtsi @@ -2,7 +2,7 @@ /* * Device Tree Include file for NXP Layerscape-1088A family SoC. * - * Copyright 2017 NXP + * Copyright 2017, 2020 NXP * * Harninder Rai * @@ -325,6 +325,69 @@ #interrupt-cells = <2>; }; + /* WRIOP0: 0x8B8_0000, E-MDIO1: 0x1_6000 */ + emdio1: mdio@8B96000 { + compatible = "fsl,fman-memac-mdio"; + reg = <0x0 0x8B96000 0x0 0x1000>; + device_type = "mdio"; + little-endian; /* force the driver in LE mode */ + + /* Not necessary on the QDS, but needed on the RDB */ + #address-cells = <1>; + #size-cells = <0>; + }; + + /* WRIOP0: 0x8B8_0000, E-MDIO2: 0x1_7000 */ + emdio2: mdio@8B97000 { + compatible = "fsl,fman-memac-mdio"; + reg = <0x0 0x8B97000 0x0 0x1000>; + device_type = "mdio"; + little-endian; /* force the driver in LE mode */ + + #address-cells = <1>; + #size-cells = <0>; + }; + + pcs_mdio1: mdio@0x8c07000 { + compatible = "fsl,fman-memac-mdio"; + reg = <0x0 0x8c07000 0x0 0x1000>; + device_type = "mdio"; + little-endian; + + #address-cells = <1>; + #size-cells = <0>; + }; + + pcs_mdio2: mdio@0x8c0b000 { + compatible = "fsl,fman-memac-mdio"; + reg = <0x0 0x8c0b000 0x0 0x1000>; + device_type = "mdio"; + little-endian; + + #address-cells = <1>; + #size-cells = <0>; + }; + + pcs_mdio3: mdio@0x8c0f000 { + compatible = "fsl,fman-memac-mdio"; + reg = <0x0 0x8c0f000 0x0 0x1000>; + device_type = "mdio"; + little-endian; + + #address-cells = <1>; + #size-cells = <0>; + }; + + pcs_mdio4: mdio@0x8c13000 { + compatible = "fsl,fman-memac-mdio"; + reg = <0x0 0x8c13000 0x0 0x1000>; + device_type = "mdio"; + little-endian; + + #address-cells = <1>; + #size-cells = <0>; + }; + ifc: ifc@2240000 { compatible = "fsl,ifc", "simple-bus"; reg = <0x0 0x2240000 0x0 0x20000>; @@ -777,6 +840,38 @@ }; }; }; + + serdes1: serdes@1ea0000 { + compatible = "serdes-10g"; + reg = <0x0 0x1ea0000 0 0x00002000>; + reg-names = "serdes", "serdes-10g"; + little-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"; + }; + }; + }; firmware { diff --git a/arch/arm64/boot/dts/freescale/fsl-ls208xa.dtsi b/arch/arm64/boot/dts/freescale/fsl-ls208xa.dtsi index f96d06d..e8f3026 100644 --- a/arch/arm64/boot/dts/freescale/fsl-ls208xa.dtsi +++ b/arch/arm64/boot/dts/freescale/fsl-ls208xa.dtsi @@ -3,7 +3,7 @@ * Device Tree Include file for Freescale Layerscape-2080A family SoC. * * Copyright 2016 Freescale Semiconductor, Inc. - * Copyright 2017 NXP + * Copyright 2017, 2020 NXP * * Abhimanyu Saini * @@ -560,6 +560,113 @@ #interrupt-cells = <2>; }; + /* WRIOP0: 0x8B8_0000, E-MDIO1: 0x1_6000 */ + emdio1: mdio@8B96000 { + compatible = "fsl,fman-memac-mdio"; + reg = <0x0 0x8B96000 0x0 0x1000>; + device_type = "mdio"; + little-endian; /* force the driver in LE mode */ + + /* Not necessary on the QDS, but needed on the RDB */ + #address-cells = <1>; + #size-cells = <0>; + }; + + /* WRIOP0: 0x8B8_0000, E-MDIO2: 0x1_7000 */ + emdio2: mdio@8B97000 { + compatible = "fsl,fman-memac-mdio"; + reg = <0x0 0x8B97000 0x0 0x1000>; + device_type = "mdio"; + little-endian; /* force the driver in LE mode */ + + #address-cells = <1>; + #size-cells = <0>; + }; + + pcs_mdio1: mdio@0x8c07000 { + compatible = "fsl,fman-memac-mdio"; + reg = <0x0 0x8c07000 0x0 0x1000>; + device_type = "mdio"; + little-endian; + + #address-cells = <1>; + #size-cells = <0>; + }; + + pcs_mdio2: mdio@0x8c0b000 { + compatible = "fsl,fman-memac-mdio"; + reg = <0x0 0x8c0b000 0x0 0x1000>; + device_type = "mdio"; + little-endian; + + #address-cells = <1>; + #size-cells = <0>; + }; + + pcs_mdio3: mdio@0x8c0f000 { + compatible = "fsl,fman-memac-mdio"; + reg = <0x0 0x8c0f000 0x0 0x1000>; + device_type = "mdio"; + little-endian; + + #address-cells = <1>; + #size-cells = <0>; + }; + + pcs_mdio4: mdio@0x8c13000 { + compatible = "fsl,fman-memac-mdio"; + reg = <0x0 0x8c13000 0x0 0x1000>; + device_type = "mdio"; + little-endian; + + #address-cells = <1>; + #size-cells = <0>; + }; + + pcs_mdio5: mdio@0x8c17000 { + status = "disabled"; + compatible = "fsl,fman-memac-mdio"; + reg = <0x0 0x8c17000 0x0 0x1000>; + device_type = "mdio"; + little-endian; + + #address-cells = <1>; + #size-cells = <0>; + }; + + pcs_mdio6: mdio@0x8c1b000 { + status = "disabled"; + compatible = "fsl,fman-memac-mdio"; + reg = <0x0 0x8c1b000 0x0 0x1000>; + device_type = "mdio"; + little-endian; + + #address-cells = <1>; + #size-cells = <0>; + }; + + pcs_mdio7: mdio@0x8c1f000 { + status = "disabled"; + compatible = "fsl,fman-memac-mdio"; + reg = <0x0 0x8c1f000 0x0 0x1000>; + device_type = "mdio"; + little-endian; + + #address-cells = <1>; + #size-cells = <0>; + }; + + pcs_mdio8: mdio@0x8c23000 { + status = "disabled"; + compatible = "fsl,fman-memac-mdio"; + reg = <0x0 0x8c23000 0x0 0x1000>; + device_type = "mdio"; + little-endian; + + #address-cells = <1>; + #size-cells = <0>; + }; + i2c0: i2c@2000000 { status = "disabled"; compatible = "fsl,vf610-i2c"; @@ -754,6 +861,57 @@ snps,incr-burst-type-adjustment = <1>, <4>, <8>, <16>; }; + serdes1: serdes@1ea0000 { + compatible = "serdes-10g"; + reg = <0x0 0x1ea0000 0 0x00002000>; + reg-names = "serdes", "serdes-10g"; + little-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"; + }; + lane_e: lane@900 { + compatible = "lane-10g"; + reg = <0x900 0x40>; + reg-names = "lane", "serdes-lane"; + }; + lane_f: lane@940 { + compatible = "lane-10g"; + reg = <0x940 0x40>; + reg-names = "lane", "serdes-lane"; + }; + lane_g: lane@980 { + compatible = "lane-10g"; + reg = <0x980 0x40>; + reg-names = "lane", "serdes-lane"; + }; + lane_h: lane@9c0 { + compatible = "lane-10g"; + reg = <0x9c0 0x40>; + reg-names = "lane", "serdes-lane"; + }; + }; + ccn@4000000 { compatible = "arm,ccn-504"; reg = <0x0 0x04000000 0x0 0x01000000>; diff --git a/arch/arm64/boot/dts/freescale/fsl-lx2160a.dtsi b/arch/arm64/boot/dts/freescale/fsl-lx2160a.dtsi index e5ee559..2815908 100644 --- a/arch/arm64/boot/dts/freescale/fsl-lx2160a.dtsi +++ b/arch/arm64/boot/dts/freescale/fsl-lx2160a.dtsi @@ -2,7 +2,7 @@ // // Device Tree Include file for Layerscape-LX2160A family SoC. // -// Copyright 2018 NXP +// Copyright 2018, 2020 NXP #include #include @@ -947,9 +947,9 @@ #address-cells = <1>; #size-cells = <0>; little-endian; - status = "disabled"; }; + /* WRIOP0: 0x8b8_0000, E-MDIO2: 0x1_7000 */ emdio2: mdio@8b97000 { compatible = "fsl,fman-memac-mdio"; reg = <0x0 0x8b97000 0x0 0x1000>; @@ -957,7 +957,129 @@ little-endian; #address-cells = <1>; #size-cells = <0>; - status = "disabled"; + }; + + pcs_mdio1: mdio@0x8c07000 { + compatible = "fsl,fman-memac-mdio"; + reg = <0x0 0x8c07000 0x0 0x1000>; + device_type = "mdio"; + little-endian; + #address-cells = <1>; + #size-cells = <0>; + }; + + pcs_mdio2: mdio@0x8c0b000 { + compatible = "fsl,fman-memac-mdio"; + reg = <0x0 0x8c0b000 0x0 0x1000>; + device_type = "mdio"; + little-endian; + #address-cells = <1>; + #size-cells = <0>; + }; + + pcs_mdio3: mdio@0x8c0f000 { + compatible = "fsl,fman-memac-mdio"; + reg = <0x0 0x8c0f000 0x0 0x1000>; + device_type = "mdio"; + little-endian; + #address-cells = <1>; + #size-cells = <0>; + }; + + pcs_mdio4: mdio@0x8c13000 { + compatible = "fsl,fman-memac-mdio"; + reg = <0x0 0x8c13000 0x0 0x1000>; + device_type = "mdio"; + little-endian; + #address-cells = <1>; + #size-cells = <0>; + }; + + pcs_mdio5: mdio@0x8c17000 { + compatible = "fsl,fman-memac-mdio"; + reg = <0x0 0x8c17000 0x0 0x1000>; + device_type = "mdio"; + little-endian; + #address-cells = <1>; + #size-cells = <0>; + }; + + pcs_mdio6: mdio@0x8c1b000 { + compatible = "fsl,fman-memac-mdio"; + reg = <0x0 0x8c1b000 0x0 0x1000>; + device_type = "mdio"; + little-endian; + #address-cells = <1>; + #size-cells = <0>; + }; + + pcs_mdio7: mdio@0x8c1f000 { + compatible = "fsl,fman-memac-mdio"; + reg = <0x0 0x8c1f000 0x0 0x1000>; + device_type = "mdio"; + little-endian; + #address-cells = <1>; + #size-cells = <0>; + }; + + pcs_mdio8: mdio@0x8c23000 { + compatible = "fsl,fman-memac-mdio"; + reg = <0x0 0x8c23000 0x0 0x1000>; + device_type = "mdio"; + little-endian; + #address-cells = <1>; + #size-cells = <0>; + }; + + serdes1: serdes@1ea0000 { + compatible = "serdes-28g"; + reg = <0x0 0x1ea0000 0 0x00002000>; + reg-names = "serdes", "serdes-28g"; + little-endian; + + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0x00 0x1ea0000 0x00002000>; + lane_a: lane@800 { + compatible = "lane-28g"; + reg = <0x800 0x100>; + reg-names = "lane", "serdes-lane"; + }; + lane_b: lane@900 { + compatible = "lane-28g"; + reg = <0x900 0x100>; + reg-names = "lane", "serdes-lane"; + }; + lane_c: lane@a00 { + compatible = "lane-28g"; + reg = <0xa00 0x100>; + reg-names = "lane", "serdes-lane"; + }; + lane_d: lane@b00 { + compatible = "lane-28g"; + reg = <0xb00 0x100>; + reg-names = "lane", "serdes-lane"; + }; + lane_e: lane@c00 { + compatible = "lane-28g"; + reg = <0xc00 0x100>; + reg-names = "lane", "serdes-lane"; + }; + lane_f: lane@d00 { + compatible = "lane-28g"; + reg = <0xd00 0x100>; + reg-names = "lane", "serdes-lane"; + }; + lane_g: lane@e00 { + compatible = "lane-28g"; + reg = <0xe00 0x100>; + reg-names = "lane", "serdes-lane"; + }; + lane_h: lane@f00 { + compatible = "lane-28g"; + reg = <0xf00 0x100>; + reg-names = "lane", "serdes-lane"; + }; }; fsl_mc: fsl-mc@80c000000 { 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";