From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.8 required=3.0 tests=DKIM_SIGNED,DKIM_VALID, DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_PATCH,MAILING_LIST_MULTI,SIGNED_OFF_BY, SPF_PASS autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id D7EAEC43381 for ; Thu, 21 Mar 2019 20:08:56 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 9BBC2218A5 for ; Thu, 21 Mar 2019 20:08:56 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="fkFOuAXd" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728677AbfCUUIz (ORCPT ); Thu, 21 Mar 2019 16:08:55 -0400 Received: from mail-wr1-f65.google.com ([209.85.221.65]:45163 "EHLO mail-wr1-f65.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728460AbfCUUIz (ORCPT ); Thu, 21 Mar 2019 16:08:55 -0400 Received: by mail-wr1-f65.google.com with SMTP id s15so3174583wra.12 for ; Thu, 21 Mar 2019 13:08:53 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:subject:to:cc:message-id:date:user-agent:mime-version :content-language:content-transfer-encoding; bh=AIRTLjwotw7t2AP4Td7yL+ekiF8yUM3gzgErRJ8z/Oc=; b=fkFOuAXdXmqQJhTbdABwR0/H1qcCTwHYjSTBF4UwKZQp+K9ByLb1Mga/pGe2bgEgnk 6nJQNqLlfwZfUOEijgPuSp7cLlGekgnQnBNHpnccDZEG02E4FRhJ2xaukQxMLfGaeBPT 5uxMLZ2oS52OngwYz7LweUjT0ay5wZ/k0kWHhyClql3RSJfZx1/OfzSaoPwizhs39HRf jIjvEeMkAumtqhRi0CMMIiWMfN0AETUQ0G8/eky1bql+lifWpFQNZelZv3bpMvVZlXSI oKrxYWYCw99ISHsxDMNsY6gUmWY1Oi70yt7adunhm0cq2GjYVJWyl2LwTmHgTtNLDJSf 7WWw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:subject:to:cc:message-id:date:user-agent :mime-version:content-language:content-transfer-encoding; bh=AIRTLjwotw7t2AP4Td7yL+ekiF8yUM3gzgErRJ8z/Oc=; b=hfB7DXvdG07qvJVyjKUzthBr7AKqRNBb9VYQ3wWDvVRxaU/AaaRhEsFWlilMoj2piS 77FvlE5QI5Ksvlfiwtg6kkvBpGGII7shP95w1ZxXxB8Ps/GOYNCQoUsC2DJfZq5WI2sP wp70b9xolsysLc9xPLLJ34R3XqpdlpOPvjfLY50BW8q0SZjBd3/9VC2+neHVrlTrD/jr iRDCAW8GNzrJ/yVnhH7//Jr3fXcLggLUK55sAugJQRff3fDyTIlzh3lbWscJbBCPiW1W TRHNvXBUCwDLdMyUEz2lnPuuAXuXr2506fJfDReIlvoTD/TvP7K+GMj0xflHUoJ3G/P2 4Uew== X-Gm-Message-State: APjAAAXb46Dch28WNsYLtIW6xBp8cyZDL8MXq4MHkbuypBy0EI7XekuK jfQF19gmwwAT/rsCrKG4Z/uKUks5 X-Google-Smtp-Source: APXvYqymq69cf9UyOV0OYHeELSz+GT6Nv65oiR0D0DHnm6/v9ziTI+zO+4FMgcCGAVNJeVAFPmrI7Q== X-Received: by 2002:a05:6000:1110:: with SMTP id z16mr3920720wrw.28.1553198932300; Thu, 21 Mar 2019 13:08:52 -0700 (PDT) Received: from ?IPv6:2003:ea:8bc4:dc00:8497:d0ab:971a:d627? (p200300EA8BC4DC008497D0AB971AD627.dip0.t-ipconnect.de. [2003:ea:8bc4:dc00:8497:d0ab:971a:d627]) by smtp.googlemail.com with ESMTPSA id y192sm17940661wmc.6.2019.03.21.13.08.50 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Thu, 21 Mar 2019 13:08:51 -0700 (PDT) From: Heiner Kallweit Subject: [PATCH v2 net-next] net: phy: aquantia: add downshift support To: Andrew Lunn , Florian Fainelli , David Miller Cc: "netdev@vger.kernel.org" Message-ID: Date: Thu, 21 Mar 2019 21:08:35 +0100 User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:60.0) Gecko/20100101 Thunderbird/60.6.0 MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Language: en-US Content-Transfer-Encoding: 7bit Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Aquantia PHY's of the AQR107 family support the downshift feature. Add support for it as standard PHY tunable so that it can be controlled via ethtool. The AQCS109 supports a proprietary 2-pair 1Gbps mode. If two such PHY's are connected to each other with a 2-pair cable, they may not be able to establish a link if both advertise modes > 1Gbps. v2: - add downshift event detection - warn if downshift occurred - read downshifted rate from vendor register - enable downshift per default on all AQR107 family members Signed-off-by: Heiner Kallweit --- drivers/net/phy/aquantia_main.c | 155 +++++++++++++++++++++++++++++--- 1 file changed, 145 insertions(+), 10 deletions(-) diff --git a/drivers/net/phy/aquantia_main.c b/drivers/net/phy/aquantia_main.c index 034b82d41..5c148df28 100644 --- a/drivers/net/phy/aquantia_main.c +++ b/drivers/net/phy/aquantia_main.c @@ -33,17 +33,23 @@ #define MDIO_AN_VEND_PROV 0xc400 #define MDIO_AN_VEND_PROV_1000BASET_FULL BIT(15) #define MDIO_AN_VEND_PROV_1000BASET_HALF BIT(14) +#define MDIO_AN_VEND_PROV_DOWNSHIFT_EN BIT(4) +#define MDIO_AN_VEND_PROV_DOWNSHIFT_MASK GENMASK(3, 0) +#define MDIO_AN_VEND_PROV_DOWNSHIFT_DFLT 4 #define MDIO_AN_TX_VEND_STATUS1 0xc800 -#define MDIO_AN_TX_VEND_STATUS1_10BASET (0x0 << 1) -#define MDIO_AN_TX_VEND_STATUS1_100BASETX (0x1 << 1) -#define MDIO_AN_TX_VEND_STATUS1_1000BASET (0x2 << 1) -#define MDIO_AN_TX_VEND_STATUS1_10GBASET (0x3 << 1) -#define MDIO_AN_TX_VEND_STATUS1_2500BASET (0x4 << 1) -#define MDIO_AN_TX_VEND_STATUS1_5000BASET (0x5 << 1) -#define MDIO_AN_TX_VEND_STATUS1_RATE_MASK (0x7 << 1) +#define MDIO_AN_TX_VEND_STATUS1_RATE_MASK GENMASK(3, 1) +#define MDIO_AN_TX_VEND_STATUS1_10BASET 0 +#define MDIO_AN_TX_VEND_STATUS1_100BASETX 1 +#define MDIO_AN_TX_VEND_STATUS1_1000BASET 2 +#define MDIO_AN_TX_VEND_STATUS1_10GBASET 3 +#define MDIO_AN_TX_VEND_STATUS1_2500BASET 4 +#define MDIO_AN_TX_VEND_STATUS1_5000BASET 5 #define MDIO_AN_TX_VEND_STATUS1_FULL_DUPLEX BIT(0) +#define MDIO_AN_TX_VEND_INT_STATUS1 0xcc00 +#define MDIO_AN_TX_VEND_INT_STATUS1_DOWNSHIFT BIT(1) + #define MDIO_AN_TX_VEND_INT_STATUS2 0xcc01 #define MDIO_AN_TX_VEND_INT_MASK2 0xd401 @@ -186,6 +192,57 @@ static int aqr_read_status(struct phy_device *phydev) return genphy_c45_read_status(phydev); } +static int aqr107_read_downshift_event(struct phy_device *phydev) +{ + int val; + + val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_TX_VEND_INT_STATUS1); + if (val < 0) + return val; + + return !!(val & MDIO_AN_TX_VEND_INT_STATUS1_DOWNSHIFT); +} + +static int aqr107_read_rate(struct phy_device *phydev) +{ + int val; + + val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_TX_VEND_STATUS1); + if (val < 0) + return val; + + switch (FIELD_GET(MDIO_AN_TX_VEND_STATUS1_RATE_MASK, val)) { + case MDIO_AN_TX_VEND_STATUS1_10BASET: + phydev->speed = SPEED_10; + break; + case MDIO_AN_TX_VEND_STATUS1_100BASETX: + phydev->speed = SPEED_100; + break; + case MDIO_AN_TX_VEND_STATUS1_1000BASET: + phydev->speed = SPEED_1000; + break; + case MDIO_AN_TX_VEND_STATUS1_2500BASET: + phydev->speed = SPEED_2500; + break; + case MDIO_AN_TX_VEND_STATUS1_5000BASET: + phydev->speed = SPEED_5000; + break; + case MDIO_AN_TX_VEND_STATUS1_10GBASET: + phydev->speed = SPEED_10000; + break; + default: + phydev->speed = SPEED_UNKNOWN; + break; + } + + if (val & MDIO_AN_TX_VEND_STATUS1_FULL_DUPLEX) + phydev->duplex = DUPLEX_FULL; + else + phydev->duplex = DUPLEX_HALF; + + return 0; +} + static int aqr107_read_status(struct phy_device *phydev) { int val, ret; @@ -194,7 +251,7 @@ static int aqr107_read_status(struct phy_device *phydev) if (ret) return ret; - if (!phydev->link) + if (!phydev->link || phydev->autoneg == AUTONEG_DISABLE) return 0; val = phy_read_mmd(phydev, MDIO_MMD_PHYXS, MDIO_PHYXS_VEND_IF_STATUS); @@ -217,9 +274,71 @@ static int aqr107_read_status(struct phy_device *phydev) break; } + val = aqr107_read_downshift_event(phydev); + if (val <= 0) + return val; + + phydev_warn(phydev, "Downshift occurred! Cabling may be defective.\n"); + + /* Read downshifted rate from vendor register */ + return aqr107_read_rate(phydev); +} + +static int aqr107_get_downshift(struct phy_device *phydev, u8 *data) +{ + int val, cnt, enable; + + val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_VEND_PROV); + if (val < 0) + return val; + + enable = FIELD_GET(MDIO_AN_VEND_PROV_DOWNSHIFT_EN, val); + cnt = FIELD_GET(MDIO_AN_VEND_PROV_DOWNSHIFT_MASK, val); + + *data = enable && cnt ? cnt : DOWNSHIFT_DEV_DISABLE; + return 0; } +static int aqr107_set_downshift(struct phy_device *phydev, u8 cnt) +{ + int val = 0; + + if (!FIELD_FIT(MDIO_AN_VEND_PROV_DOWNSHIFT_MASK, cnt)) + return -E2BIG; + + if (cnt != DOWNSHIFT_DEV_DISABLE) { + val = MDIO_AN_VEND_PROV_DOWNSHIFT_EN; + val |= FIELD_PREP(MDIO_AN_VEND_PROV_DOWNSHIFT_MASK, cnt); + } + + return phy_modify_mmd(phydev, MDIO_MMD_AN, MDIO_AN_VEND_PROV, + MDIO_AN_VEND_PROV_DOWNSHIFT_EN | + MDIO_AN_VEND_PROV_DOWNSHIFT_MASK, val); +} + +static int aqr107_get_tunable(struct phy_device *phydev, + struct ethtool_tunable *tuna, void *data) +{ + switch (tuna->id) { + case ETHTOOL_PHY_DOWNSHIFT: + return aqr107_get_downshift(phydev, data); + default: + return -EOPNOTSUPP; + } +} + +static int aqr107_set_tunable(struct phy_device *phydev, + struct ethtool_tunable *tuna, const void *data) +{ + switch (tuna->id) { + case ETHTOOL_PHY_DOWNSHIFT: + return aqr107_set_downshift(phydev, *(const u8 *)data); + default: + return -EOPNOTSUPP; + } +} + static int aqr107_config_init(struct phy_device *phydev) { /* Check that the PHY interface type is compatible */ @@ -228,11 +347,16 @@ static int aqr107_config_init(struct phy_device *phydev) phydev->interface != PHY_INTERFACE_MODE_10GKR) return -ENODEV; - return 0; + /* ensure that a latched downshift event is cleared */ + aqr107_read_downshift_event(phydev); + + return aqr107_set_downshift(phydev, MDIO_AN_VEND_PROV_DOWNSHIFT_DFLT); } static int aqcs109_config_init(struct phy_device *phydev) { + int ret; + /* Check that the PHY interface type is compatible */ if (phydev->interface != PHY_INTERFACE_MODE_SGMII && phydev->interface != PHY_INTERFACE_MODE_2500BASEX) @@ -242,7 +366,14 @@ static int aqcs109_config_init(struct phy_device *phydev) * PMA speed ability bits are the same for all members of the family, * AQCS109 however supports speeds up to 2.5G only. */ - return phy_set_max_speed(phydev, SPEED_2500); + ret = phy_set_max_speed(phydev, SPEED_2500); + if (ret) + return ret; + + /* ensure that a latched downshift event is cleared */ + aqr107_read_downshift_event(phydev); + + return aqr107_set_downshift(phydev, MDIO_AN_VEND_PROV_DOWNSHIFT_DFLT); } static struct phy_driver aqr_driver[] = { @@ -297,6 +428,8 @@ static struct phy_driver aqr_driver[] = { .config_intr = aqr_config_intr, .ack_interrupt = aqr_ack_interrupt, .read_status = aqr107_read_status, + .get_tunable = aqr107_get_tunable, + .set_tunable = aqr107_set_tunable, }, { PHY_ID_MATCH_MODEL(PHY_ID_AQCS109), @@ -309,6 +442,8 @@ static struct phy_driver aqr_driver[] = { .config_intr = aqr_config_intr, .ack_interrupt = aqr_ack_interrupt, .read_status = aqr107_read_status, + .get_tunable = aqr107_get_tunable, + .set_tunable = aqr107_set_tunable, }, { PHY_ID_MATCH_MODEL(PHY_ID_AQR405), -- 2.21.0