From patchwork Tue Mar 7 15:54:36 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Golle X-Patchwork-Id: 65656 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a05:7300:570d:b0:a1:2e52:cf23 with SMTP id h13csp1987214dyk; Tue, 7 Mar 2023 08:06:17 -0800 (PST) X-Google-Smtp-Source: AK7set8kHm77rb2Hg2vjrGoNV8fPO3gKMNC2G84MnlivqQrA9eBpsiLRw9FoYlRzswt8OBplLMlG X-Received: by 2002:a05:6a20:6a10:b0:c7:3c30:188f with SMTP id p16-20020a056a206a1000b000c73c30188fmr14704082pzk.20.1678205177588; Tue, 07 Mar 2023 08:06:17 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1678205177; cv=none; d=google.com; s=arc-20160816; b=JXHPQvg6YagWHXv9n8P67yokR7N1C9tQHz4SyKwfYL7EesHym/R2QsX57zE7nCfax9 KtlXQ+ZFYTCfsuLEdP42RFhzQz7Dc18q4HOK9bk0MJIVZVI/NRB6I9LuWnRdTW/uB57J UTyF3FzP7cW8VMAErmeGp9LD1l7a7i3IxVnFSBMRyuqtsfGBrfo1HOua98vCgm+dNvh/ zvw1MqPjEOntpkKhOuOxcliRkPxjCZHoEYKSLzp7d2o1kaeZiGZvWPofn/e7wr36XGSH X4fRKPZ0nCRicanOVPR3cVb5PBQTSBBaGoCUMEHirTfSwl7vaex+bWUEOVcvfUmVz1pS F99g== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:in-reply-to:content-disposition:mime-version :references:message-id:subject:cc:to:from:date; bh=e20glwwWx+plc8HMN7f4Y5E1lXMgqNY3Gaeg/MTmm0g=; b=Z3YXkGBcR1lrew82tC+2Vt1fPhzxenkneezONlg8BC5Rh1j7MAnzPEWX/kOhge4X2q LKqGhHgLiauKHsLu/xnV+ccGouwCkQ5hCFmDpi6P+opPq0IyvEg2YDgTUpjwrmoRQGIa lbXs2O3yuX7vw3UWRN573SB3jjsrRhGgRxzNyiczR50uu3T6a2eOGtPORojAChyk8Y2n 4HcOXhVdy4afRXj9vko2r5hZeZyc6lw5wEMAQhJKF8tLMy8eo3hykISQAh1xyFCTSMTS GCaet7+so2ycyfkG6W+DQOu0froMl1EG/r8H5oYlfUvXwgmMsNRSu+M6DuX3tG/dYI8h E6lA== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id 6-20020a630906000000b00502d5c32148si11320017pgj.248.2023.03.07.08.06.02; Tue, 07 Mar 2023 08:06:17 -0800 (PST) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231587AbjCGP54 (ORCPT + 99 others); Tue, 7 Mar 2023 10:57:56 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:41568 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231365AbjCGP53 (ORCPT ); Tue, 7 Mar 2023 10:57:29 -0500 Received: from fudo.makrotopia.org (fudo.makrotopia.org [IPv6:2a07:2ec0:3002::71]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 30F9F92BDD; Tue, 7 Mar 2023 07:56:16 -0800 (PST) Received: from local by fudo.makrotopia.org with esmtpsa (TLS1.3:TLS_AES_256_GCM_SHA384:256) (Exim 4.96) (envelope-from ) id 1pZZfh-0001t6-1V; Tue, 07 Mar 2023 16:56:13 +0100 Date: Tue, 7 Mar 2023 15:54:36 +0000 From: Daniel Golle To: netdev@vger.kernel.org, linux-mediatek@lists.infradead.org, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, Russell King , Heiner Kallweit , Lorenzo Bianconi , Mark Lee , John Crispin , Felix Fietkau , AngeloGioacchino Del Regno , Matthias Brugger , DENG Qingfang , Landen Chao , Sean Wang , Paolo Abeni , Jakub Kicinski , Eric Dumazet , "David S. Miller" , Vladimir Oltean , Florian Fainelli , Andrew Lunn , Vladimir Oltean Cc: Jianhui Zhao , =?iso-8859-1?q?Bj=F8rn?= Mork , Frank Wunderlich , Alexander Couzens Subject: [PATCH net-next v12 11/18] net: pcs: add driver for MediaTek SGMII PCS Message-ID: <05e6d08bba07ab2462178e51bbc146bdea508f46.1678201958.git.daniel@makrotopia.org> References: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00,SPF_HELO_NONE, SPF_PASS autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= X-GMAIL-THRID: =?utf-8?q?1759725672259544713?= X-GMAIL-MSGID: =?utf-8?q?1759725672259544713?= The SGMII core found in several MediaTek SoCs is identical to what can also be found in MediaTek's MT7531 Ethernet switch IC. As this has not always been clear, both drivers developed different implementations to deal with the PCS. Recently Alexander Couzens pointed out this fact which lead to the development of this shared driver. Add a dedicated driver, mostly by copying the code now found in the Ethernet driver. The now redundant code will be removed by a follow-up commit. Suggested-by: Alexander Couzens Suggested-by: Russell King (Oracle) Signed-off-by: Daniel Golle Tested-by: Frank Wunderlich --- MAINTAINERS | 7 + drivers/net/pcs/Kconfig | 7 + drivers/net/pcs/Makefile | 1 + drivers/net/pcs/pcs-mtk-lynxi.c | 308 ++++++++++++++++++++++++++++++ include/linux/pcs/pcs-mtk-lynxi.h | 13 ++ 5 files changed, 336 insertions(+) create mode 100644 drivers/net/pcs/pcs-mtk-lynxi.c create mode 100644 include/linux/pcs/pcs-mtk-lynxi.h diff --git a/MAINTAINERS b/MAINTAINERS index edd3d562beee..7485315a9c65 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13047,6 +13047,14 @@ L: netdev@vger.kernel.org S: Maintained F: drivers/net/ethernet/mediatek/ +MEDIATEK ETHERNET PCS DRIVER +M: Alexander Couzens +M: Daniel Golle +L: netdev@vger.kernel.org +S: Maintained +F: drivers/net/pcs/pcs-mtk-lynxi.c +F: include/linux/pcs/pcs-mtk-lynxi.h + MEDIATEK I2C CONTROLLER DRIVER M: Qii Wang L: linux-i2c@vger.kernel.org diff --git a/drivers/net/pcs/Kconfig b/drivers/net/pcs/Kconfig index 6e7e6c346a3e..7c34fb7cbf7b 100644 --- a/drivers/net/pcs/Kconfig +++ b/drivers/net/pcs/Kconfig @@ -18,6 +18,13 @@ config PCS_LYNX This module provides helpers to phylink for managing the Lynx PCS which is part of the Layerscape and QorIQ Ethernet SERDES. +config PCS_MTK_LYNXI + tristate + select REGMAP + help + This module provides helpers to phylink for managing the LynxI PCS + which is part of MediaTek's SoC and Ethernet switch ICs. + config PCS_RZN1_MIIC tristate "Renesas RZ/N1 MII converter" depends on OF && (ARCH_RZN1 || COMPILE_TEST) diff --git a/drivers/net/pcs/Makefile b/drivers/net/pcs/Makefile index 4c780d8f2e98..9b9afd6b1c22 100644 --- a/drivers/net/pcs/Makefile +++ b/drivers/net/pcs/Makefile @@ -5,5 +5,6 @@ pcs_xpcs-$(CONFIG_PCS_XPCS) := pcs-xpcs.o pcs-xpcs-nxp.o obj-$(CONFIG_PCS_XPCS) += pcs_xpcs.o obj-$(CONFIG_PCS_LYNX) += pcs-lynx.o +obj-$(CONFIG_PCS_MTK_LYNXI) += pcs-mtk-lynxi.o obj-$(CONFIG_PCS_RZN1_MIIC) += pcs-rzn1-miic.o obj-$(CONFIG_PCS_ALTERA_TSE) += pcs-altera-tse.o diff --git a/drivers/net/pcs/pcs-mtk-lynxi.c b/drivers/net/pcs/pcs-mtk-lynxi.c new file mode 100644 index 000000000000..ecee351bd646 --- /dev/null +++ b/drivers/net/pcs/pcs-mtk-lynxi.c @@ -0,0 +1,309 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2018-2019 MediaTek Inc. +/* A library for MediaTek SGMII circuit + * + * Author: Sean Wang + * Author: Alexander Couzens + * Author: Daniel Golle + * + */ + +#include +#include +#include +#include +#include + +/* SGMII subsystem config registers */ +/* BMCR (low 16) BMSR (high 16) */ +#define SGMSYS_PCS_CONTROL_1 0x0 +#define SGMII_BMCR GENMASK(15, 0) +#define SGMII_BMSR GENMASK(31, 16) + +#define SGMSYS_PCS_DEVICE_ID 0x4 +#define SGMII_LYNXI_DEV_ID 0x4d544950 + +#define SGMSYS_PCS_ADVERTISE 0x8 +#define SGMII_ADVERTISE GENMASK(15, 0) +#define SGMII_LPA GENMASK(31, 16) + +#define SGMSYS_PCS_SCRATCH 0x14 +#define SGMII_DEV_VERSION GENMASK(31, 16) + +/* Register to programmable link timer, the unit in 2 * 8ns */ +#define SGMSYS_PCS_LINK_TIMER 0x18 +#define SGMII_LINK_TIMER_MASK GENMASK(19, 0) +#define SGMII_LINK_TIMER_VAL(ns) FIELD_PREP(SGMII_LINK_TIMER_MASK, \ + (ns / 2 / 8)) + +/* Register to control remote fault */ +#define SGMSYS_SGMII_MODE 0x20 +#define SGMII_IF_MODE_SGMII BIT(0) +#define SGMII_SPEED_DUPLEX_AN BIT(1) +#define SGMII_SPEED_MASK GENMASK(3, 2) +#define SGMII_SPEED_10 FIELD_PREP(SGMII_SPEED_MASK, 0) +#define SGMII_SPEED_100 FIELD_PREP(SGMII_SPEED_MASK, 1) +#define SGMII_SPEED_1000 FIELD_PREP(SGMII_SPEED_MASK, 2) +#define SGMII_DUPLEX_HALF BIT(4) +#define SGMII_REMOTE_FAULT_DIS BIT(8) + +/* Register to reset SGMII design */ +#define SGMSYS_RESERVED_0 0x34 +#define SGMII_SW_RESET BIT(0) + +/* Register to set SGMII speed, ANA RG_ Control Signals III */ +#define SGMII_PHY_SPEED_MASK GENMASK(3, 2) +#define SGMII_PHY_SPEED_1_25G FIELD_PREP(SGMII_PHY_SPEED_MASK, 0) +#define SGMII_PHY_SPEED_3_125G FIELD_PREP(SGMII_PHY_SPEED_MASK, 1) + +/* Register to power up QPHY */ +#define SGMSYS_QPHY_PWR_STATE_CTRL 0xe8 +#define SGMII_PHYA_PWD BIT(4) + +/* Register to QPHY wrapper control */ +#define SGMSYS_QPHY_WRAP_CTRL 0xec +#define SGMII_PN_SWAP_MASK GENMASK(1, 0) +#define SGMII_PN_SWAP_TX_RX (BIT(0) | BIT(1)) + +/* struct mtk_pcs_lynxi - This structure holds each sgmii regmap andassociated + * data + * @regmap: The register map pointing at the range used to setup + * SGMII modes + * @dev: Pointer to device owning the PCS + * @ana_rgc3: The offset of register ANA_RGC3 relative to regmap + * @interface: Currently configured interface mode + * @pcs: Phylink PCS structure + * @flags: Flags indicating hardware properties + */ +struct mtk_pcs_lynxi { + struct regmap *regmap; + u32 ana_rgc3; + phy_interface_t interface; + struct phylink_pcs pcs; + u32 flags; +}; + +static struct mtk_pcs_lynxi *pcs_to_mtk_pcs_lynxi(struct phylink_pcs *pcs) +{ + return container_of(pcs, struct mtk_pcs_lynxi, pcs); +} + +static void mtk_pcs_lynxi_get_state(struct phylink_pcs *pcs, + struct phylink_link_state *state) +{ + struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs); + unsigned int bm, bmsr, adv; + + /* Read the BMSR */ + regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &bm); + bmsr = FIELD_GET(SGMII_BMSR, bm); + + /* link partner advertised link modes are not reported by the + * hardware when not operating in SGMII mode. Hence we cannot + * use phylink_mii_c22_pcs_decode_state() in this case. + */ + if (state->interface != PHY_INTERFACE_MODE_SGMII) { + state->link = !!(bmsr & BMSR_LSTATUS); + state->an_complete = !!(bmsr & BMSR_ANEGCOMPLETE); + state->speed = (state->interface == + PHY_INTERFACE_MODE_2500BASEX) ? + SPEED_2500 : SPEED_1000; + state->duplex = DUPLEX_FULL; + + return; + } + + /* Read LPA and use standard decode function for SGMII mode */ + regmap_read(mpcs->regmap, SGMSYS_PCS_ADVERTISE, &adv); + phylink_mii_c22_pcs_decode_state(state, bmsr, + FIELD_GET(SGMII_LPA, adv)); +} + +static int mtk_pcs_lynxi_config(struct phylink_pcs *pcs, unsigned int mode, + phy_interface_t interface, + const unsigned long *advertising, + bool permit_pause_to_mac) +{ + struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs); + unsigned int rgc3, sgm_mode = 0, bmcr = 0; + bool mode_changed = false, changed; + int advertise, link_timer; + + advertise = phylink_mii_c22_pcs_encode_advertisement(interface, + advertising); + if (advertise < 0) + return advertise; + + /* Clearing IF_MODE_BIT0 switches the PCS to BASE-X mode, and + * we assume that fixes it's speed at bitrate = line rate (in + * other words, 1000Mbps or 2500Mbps). + */ + if (interface == PHY_INTERFACE_MODE_SGMII) { + sgm_mode = SGMII_IF_MODE_SGMII; + if (phylink_autoneg_inband(mode)) { + sgm_mode |= SGMII_REMOTE_FAULT_DIS | + SGMII_SPEED_DUPLEX_AN; + bmcr = BMCR_ANENABLE; + } + } else if (!phylink_autoneg_inband(mode)) { + /* 1000base-X or 2500base-X without autoneg */ + sgm_mode = SGMII_REMOTE_FAULT_DIS; + } + + if (mpcs->interface != interface) { + link_timer = phylink_get_link_timer_ns(interface); + if (link_timer < 0) + return link_timer; + + /* PHYA power down */ + regmap_set_bits(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, + SGMII_PHYA_PWD); + + /* Reset SGMII PCS state */ + regmap_set_bits(mpcs->regmap, SGMSYS_RESERVED_0, + SGMII_SW_RESET); + + if (mpcs->flags & MTK_SGMII_FLAG_PN_SWAP) + regmap_update_bits(mpcs->regmap, SGMSYS_QPHY_WRAP_CTRL, + SGMII_PN_SWAP_MASK, + SGMII_PN_SWAP_TX_RX); + + if (interface == PHY_INTERFACE_MODE_2500BASEX) + rgc3 = SGMII_PHY_SPEED_3_125G; + else + rgc3 = SGMII_PHY_SPEED_1_25G; + + /* Configure the underlying interface speed */ + regmap_update_bits(mpcs->regmap, mpcs->ana_rgc3, + SGMII_PHY_SPEED_MASK, rgc3); + + /* Setup the link timer */ + regmap_write(mpcs->regmap, SGMSYS_PCS_LINK_TIMER, + SGMII_LINK_TIMER_VAL(link_timer)); + + mpcs->interface = interface; + mode_changed = true; + } + + /* Update the advertisement, noting whether it has changed */ + regmap_update_bits_check(mpcs->regmap, SGMSYS_PCS_ADVERTISE, + SGMII_ADVERTISE, advertise, &changed); + + /* Update the sgmsys mode register */ + regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE, + SGMII_REMOTE_FAULT_DIS | SGMII_SPEED_DUPLEX_AN | + SGMII_IF_MODE_SGMII, sgm_mode); + + /* Update the BMCR */ + regmap_update_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1, + BMCR_ANENABLE, bmcr); + + /* Release PHYA power down state + * Only removing bit SGMII_PHYA_PWD isn't enough. + * There are cases when the SGMII_PHYA_PWD register contains 0x9 which + * prevents SGMII from working. The SGMII still shows link but no traffic + * can flow. Writing 0x0 to the PHYA_PWD register fix the issue. 0x0 was + * taken from a good working state of the SGMII interface. + * Unknown how much the QPHY needs but it is racy without a sleep. + * Tested on mt7622 & mt7986. + */ + usleep_range(50, 100); + regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, 0); + + return changed || mode_changed; +} + +static void mtk_pcs_lynxi_restart_an(struct phylink_pcs *pcs) +{ + struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs); + + regmap_set_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1, BMCR_ANRESTART); +} + +static void mtk_pcs_lynxi_link_up(struct phylink_pcs *pcs, unsigned int mode, + phy_interface_t interface, int speed, + int duplex) +{ + struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs); + unsigned int sgm_mode; + + if (!phylink_autoneg_inband(mode)) { + /* Force the speed and duplex setting */ + if (speed == SPEED_10) + sgm_mode = SGMII_SPEED_10; + else if (speed == SPEED_100) + sgm_mode = SGMII_SPEED_100; + else + sgm_mode = SGMII_SPEED_1000; + + if (duplex != DUPLEX_FULL) + sgm_mode |= SGMII_DUPLEX_HALF; + + regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE, + SGMII_DUPLEX_HALF | SGMII_SPEED_MASK, + sgm_mode); + } +} + +static const struct phylink_pcs_ops mtk_pcs_lynxi_ops = { + .pcs_get_state = mtk_pcs_lynxi_get_state, + .pcs_config = mtk_pcs_lynxi_config, + .pcs_an_restart = mtk_pcs_lynxi_restart_an, + .pcs_link_up = mtk_pcs_lynxi_link_up, +}; + +struct phylink_pcs *mtk_pcs_lynxi_create(struct device *dev, + struct regmap *regmap, u32 ana_rgc3, + u32 flags) +{ + struct mtk_pcs_lynxi *mpcs; + u32 id, ver; + int ret; + + ret = regmap_read(regmap, SGMSYS_PCS_DEVICE_ID, &id); + if (ret < 0) + return NULL; + + if (id != SGMII_LYNXI_DEV_ID) { + dev_err(dev, "unknown PCS device id %08x\n", id); + return NULL; + } + + ret = regmap_read(regmap, SGMSYS_PCS_SCRATCH, &ver); + if (ret < 0) + return NULL; + + ver = FIELD_GET(SGMII_DEV_VERSION, ver); + if (ver != 0x1) { + dev_err(dev, "unknown PCS device version %04x\n", ver); + return NULL; + } + + dev_dbg(dev, "MediaTek LynxI SGMII PCS (id 0x%08x, ver 0x%04x)\n", id, + ver); + + mpcs = kzalloc(sizeof(*mpcs), GFP_KERNEL); + if (!mpcs) + return NULL; + + mpcs->ana_rgc3 = ana_rgc3; + mpcs->regmap = regmap; + mpcs->flags = flags; + mpcs->pcs.ops = &mtk_pcs_lynxi_ops; + mpcs->pcs.poll = true; + mpcs->interface = PHY_INTERFACE_MODE_NA; + + return &mpcs->pcs; +} +EXPORT_SYMBOL(mtk_pcs_lynxi_create); + +void mtk_pcs_lynxi_destroy(struct phylink_pcs *pcs) +{ + if (!pcs) + return; + + kfree(pcs_to_mtk_pcs_lynxi(pcs)); +} +EXPORT_SYMBOL(mtk_pcs_lynxi_destroy); + +MODULE_LICENSE("GPL"); diff --git a/include/linux/pcs/pcs-mtk-lynxi.h b/include/linux/pcs/pcs-mtk-lynxi.h new file mode 100644 index 000000000000..be3b4ab32f4a --- /dev/null +++ b/include/linux/pcs/pcs-mtk-lynxi.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __LINUX_PCS_MTK_LYNXI_H +#define __LINUX_PCS_MTK_LYNXI_H + +#include +#include + +#define MTK_SGMII_FLAG_PN_SWAP BIT(0) +struct phylink_pcs *mtk_pcs_lynxi_create(struct device *dev, + struct regmap *regmap, + u32 ana_rgc3, u32 flags); +void mtk_pcs_lynxi_destroy(struct phylink_pcs *pcs); +#endif