--- /dev/null
+From patchwork Thu Mar 9 10:57:44 2023
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 8bit
+X-Patchwork-Submitter: Daniel Golle <daniel@makrotopia.org>
+X-Patchwork-Id: 13167235
+X-Patchwork-Delegate: kuba@kernel.org
+Return-Path: <netdev-owner@vger.kernel.org>
+Date: Thu, 9 Mar 2023 10:57:44 +0000
+From: Daniel Golle <daniel@makrotopia.org>
+To: netdev@vger.kernel.org, linux-mediatek@lists.infradead.org,
+ linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org,
+ Russell King <linux@armlinux.org.uk>,
+ Heiner Kallweit <hkallweit1@gmail.com>,
+ Lorenzo Bianconi <lorenzo@kernel.org>,
+ Mark Lee <Mark-MC.Lee@mediatek.com>,
+ John Crispin <john@phrozen.org>, Felix Fietkau <nbd@nbd.name>,
+ AngeloGioacchino Del Regno
+ <angelogioacchino.delregno@collabora.com>,
+ Matthias Brugger <matthias.bgg@gmail.com>,
+ DENG Qingfang <dqfext@gmail.com>,
+ Landen Chao <Landen.Chao@mediatek.com>,
+ Sean Wang <sean.wang@mediatek.com>,
+ Paolo Abeni <pabeni@redhat.com>,
+ Jakub Kicinski <kuba@kernel.org>,
+ Eric Dumazet <edumazet@google.com>,
+ "David S. Miller" <davem@davemloft.net>,
+ Vladimir Oltean <olteanv@gmail.com>,
+ Florian Fainelli <f.fainelli@gmail.com>,
+ Andrew Lunn <andrew@lunn.ch>,
+ Vladimir Oltean <vladimir.oltean@nxp.com>
+Cc: =?iso-8859-1?q?Bj=F8rn?= Mork <bjorn@mork.no>,
+ Frank Wunderlich <frank-w@public-files.de>,
+ Alexander Couzens <lynxis@fe80.eu>
+Subject: [PATCH net-next v13 11/16] net: dsa: mt7530: use external PCS driver
+Message-ID:
+ <2ac2ee40d3b0e705461b50613fda6a7edfdbc4b3.1678357225.git.daniel@makrotopia.org>
+References: <cover.1678357225.git.daniel@makrotopia.org>
+MIME-Version: 1.0
+Content-Disposition: inline
+In-Reply-To: <cover.1678357225.git.daniel@makrotopia.org>
+Precedence: bulk
+List-ID: <netdev.vger.kernel.org>
+X-Mailing-List: netdev@vger.kernel.org
+X-Patchwork-Delegate: kuba@kernel.org
+
+Implement regmap access wrappers, for now only to be used by the
+pcs-mtk driver.
+Make use of external PCS driver and drop the reduntant implementation
+in mt7530.c.
+As a nice side effect the SGMII registers can now also more easily be
+inspected for debugging via /sys/kernel/debug/regmap.
+
+Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Tested-by: Bjørn Mork <bjorn@mork.no>
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+Tested-by: Frank Wunderlich <frank-w@public-files.de>
+---
+ drivers/net/dsa/Kconfig | 1 +
+ drivers/net/dsa/mt7530.c | 277 ++++++++++-----------------------------
+ drivers/net/dsa/mt7530.h | 47 +------
+ 3 files changed, 71 insertions(+), 254 deletions(-)
+
+--- a/drivers/net/dsa/Kconfig
++++ b/drivers/net/dsa/Kconfig
+@@ -37,6 +37,7 @@ config NET_DSA_MT7530
+ tristate "MediaTek MT753x and MT7621 Ethernet switch support"
+ select NET_DSA_TAG_MTK
+ select MEDIATEK_GE_PHY
++ select PCS_MTK_LYNXI
+ help
+ This enables support for the MediaTek MT7530, MT7531, and MT7621
+ Ethernet switch chips.
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -14,6 +14,7 @@
+ #include <linux/of_mdio.h>
+ #include <linux/of_net.h>
+ #include <linux/of_platform.h>
++#include <linux/pcs/pcs-mtk-lynxi.h>
+ #include <linux/phylink.h>
+ #include <linux/regmap.h>
+ #include <linux/regulator/consumer.h>
+@@ -2568,128 +2569,11 @@ static int mt7531_rgmii_setup(struct mt7
+ return 0;
+ }
+
+-static void mt7531_pcs_link_up(struct phylink_pcs *pcs, unsigned int mode,
+- phy_interface_t interface, int speed, int duplex)
+-{
+- struct mt7530_priv *priv = pcs_to_mt753x_pcs(pcs)->priv;
+- int port = pcs_to_mt753x_pcs(pcs)->port;
+- unsigned int val;
+-
+- /* For adjusting speed and duplex of SGMII force mode. */
+- if (interface != PHY_INTERFACE_MODE_SGMII ||
+- phylink_autoneg_inband(mode))
+- return;
+-
+- /* SGMII force mode setting */
+- val = mt7530_read(priv, MT7531_SGMII_MODE(port));
+- val &= ~MT7531_SGMII_IF_MODE_MASK;
+-
+- switch (speed) {
+- case SPEED_10:
+- val |= MT7531_SGMII_FORCE_SPEED_10;
+- break;
+- case SPEED_100:
+- val |= MT7531_SGMII_FORCE_SPEED_100;
+- break;
+- case SPEED_1000:
+- val |= MT7531_SGMII_FORCE_SPEED_1000;
+- break;
+- }
+-
+- /* MT7531 SGMII 1G force mode can only work in full duplex mode,
+- * no matter MT7531_SGMII_FORCE_HALF_DUPLEX is set or not.
+- *
+- * The speed check is unnecessary as the MAC capabilities apply
+- * this restriction. --rmk
+- */
+- if ((speed == SPEED_10 || speed == SPEED_100) &&
+- duplex != DUPLEX_FULL)
+- val |= MT7531_SGMII_FORCE_HALF_DUPLEX;
+-
+- mt7530_write(priv, MT7531_SGMII_MODE(port), val);
+-}
+-
+ static bool mt753x_is_mac_port(u32 port)
+ {
+ return (port == 5 || port == 6);
+ }
+
+-static int mt7531_sgmii_setup_mode_force(struct mt7530_priv *priv, u32 port,
+- phy_interface_t interface)
+-{
+- u32 val;
+-
+- if (!mt753x_is_mac_port(port))
+- return -EINVAL;
+-
+- mt7530_set(priv, MT7531_QPHY_PWR_STATE_CTRL(port),
+- MT7531_SGMII_PHYA_PWD);
+-
+- val = mt7530_read(priv, MT7531_PHYA_CTRL_SIGNAL3(port));
+- val &= ~MT7531_RG_TPHY_SPEED_MASK;
+- /* Setup 2.5 times faster clock for 2.5Gbps data speeds with 10B/8B
+- * encoding.
+- */
+- val |= (interface == PHY_INTERFACE_MODE_2500BASEX) ?
+- MT7531_RG_TPHY_SPEED_3_125G : MT7531_RG_TPHY_SPEED_1_25G;
+- mt7530_write(priv, MT7531_PHYA_CTRL_SIGNAL3(port), val);
+-
+- mt7530_clear(priv, MT7531_PCS_CONTROL_1(port), MT7531_SGMII_AN_ENABLE);
+-
+- /* MT7531 SGMII 1G and 2.5G force mode can only work in full duplex
+- * mode, no matter MT7531_SGMII_FORCE_HALF_DUPLEX is set or not.
+- */
+- mt7530_rmw(priv, MT7531_SGMII_MODE(port),
+- MT7531_SGMII_IF_MODE_MASK | MT7531_SGMII_REMOTE_FAULT_DIS,
+- MT7531_SGMII_FORCE_SPEED_1000);
+-
+- mt7530_write(priv, MT7531_QPHY_PWR_STATE_CTRL(port), 0);
+-
+- return 0;
+-}
+-
+-static int mt7531_sgmii_setup_mode_an(struct mt7530_priv *priv, int port,
+- phy_interface_t interface)
+-{
+- if (!mt753x_is_mac_port(port))
+- return -EINVAL;
+-
+- mt7530_set(priv, MT7531_QPHY_PWR_STATE_CTRL(port),
+- MT7531_SGMII_PHYA_PWD);
+-
+- mt7530_rmw(priv, MT7531_PHYA_CTRL_SIGNAL3(port),
+- MT7531_RG_TPHY_SPEED_MASK, MT7531_RG_TPHY_SPEED_1_25G);
+-
+- mt7530_set(priv, MT7531_SGMII_MODE(port),
+- MT7531_SGMII_REMOTE_FAULT_DIS |
+- MT7531_SGMII_SPEED_DUPLEX_AN);
+-
+- mt7530_rmw(priv, MT7531_PCS_SPEED_ABILITY(port),
+- MT7531_SGMII_TX_CONFIG_MASK, 1);
+-
+- mt7530_set(priv, MT7531_PCS_CONTROL_1(port), MT7531_SGMII_AN_ENABLE);
+-
+- mt7530_set(priv, MT7531_PCS_CONTROL_1(port), MT7531_SGMII_AN_RESTART);
+-
+- mt7530_write(priv, MT7531_QPHY_PWR_STATE_CTRL(port), 0);
+-
+- return 0;
+-}
+-
+-static void mt7531_pcs_an_restart(struct phylink_pcs *pcs)
+-{
+- struct mt7530_priv *priv = pcs_to_mt753x_pcs(pcs)->priv;
+- int port = pcs_to_mt753x_pcs(pcs)->port;
+- u32 val;
+-
+- /* Only restart AN when AN is enabled */
+- val = mt7530_read(priv, MT7531_PCS_CONTROL_1(port));
+- if (val & MT7531_SGMII_AN_ENABLE) {
+- val |= MT7531_SGMII_AN_RESTART;
+- mt7530_write(priv, MT7531_PCS_CONTROL_1(port), val);
+- }
+-}
+-
+ static int
+ mt7531_mac_config(struct dsa_switch *ds, int port, unsigned int mode,
+ phy_interface_t interface)
+@@ -2712,11 +2596,11 @@ mt7531_mac_config(struct dsa_switch *ds,
+ phydev = dp->slave->phydev;
+ return mt7531_rgmii_setup(priv, port, interface, phydev);
+ case PHY_INTERFACE_MODE_SGMII:
+- return mt7531_sgmii_setup_mode_an(priv, port, interface);
+ case PHY_INTERFACE_MODE_NA:
+ case PHY_INTERFACE_MODE_1000BASEX:
+ case PHY_INTERFACE_MODE_2500BASEX:
+- return mt7531_sgmii_setup_mode_force(priv, port, interface);
++ /* handled in SGMII PCS driver */
++ return 0;
+ default:
+ return -EINVAL;
+ }
+@@ -2741,11 +2625,11 @@ mt753x_phylink_mac_select_pcs(struct dsa
+
+ switch (interface) {
+ case PHY_INTERFACE_MODE_TRGMII:
++ return &priv->pcs[port].pcs;
+ case PHY_INTERFACE_MODE_SGMII:
+ case PHY_INTERFACE_MODE_1000BASEX:
+ case PHY_INTERFACE_MODE_2500BASEX:
+- return &priv->pcs[port].pcs;
+-
++ return priv->ports[port].sgmii_pcs;
+ default:
+ return NULL;
+ }
+@@ -2986,86 +2870,6 @@ static void mt7530_pcs_get_state(struct
+ state->pause |= MLO_PAUSE_TX;
+ }
+
+-static int
+-mt7531_sgmii_pcs_get_state_an(struct mt7530_priv *priv, int port,
+- struct phylink_link_state *state)
+-{
+- u32 status, val;
+- u16 config_reg;
+-
+- status = mt7530_read(priv, MT7531_PCS_CONTROL_1(port));
+- state->link = !!(status & MT7531_SGMII_LINK_STATUS);
+- state->an_complete = !!(status & MT7531_SGMII_AN_COMPLETE);
+- if (state->interface == PHY_INTERFACE_MODE_SGMII &&
+- (status & MT7531_SGMII_AN_ENABLE)) {
+- val = mt7530_read(priv, MT7531_PCS_SPEED_ABILITY(port));
+- config_reg = val >> 16;
+-
+- switch (config_reg & LPA_SGMII_SPD_MASK) {
+- case LPA_SGMII_1000:
+- state->speed = SPEED_1000;
+- break;
+- case LPA_SGMII_100:
+- state->speed = SPEED_100;
+- break;
+- case LPA_SGMII_10:
+- state->speed = SPEED_10;
+- break;
+- default:
+- dev_err(priv->dev, "invalid sgmii PHY speed\n");
+- state->link = false;
+- return -EINVAL;
+- }
+-
+- if (config_reg & LPA_SGMII_FULL_DUPLEX)
+- state->duplex = DUPLEX_FULL;
+- else
+- state->duplex = DUPLEX_HALF;
+- }
+-
+- return 0;
+-}
+-
+-static void
+-mt7531_sgmii_pcs_get_state_inband(struct mt7530_priv *priv, int port,
+- struct phylink_link_state *state)
+-{
+- unsigned int val;
+-
+- val = mt7530_read(priv, MT7531_PCS_CONTROL_1(port));
+- state->link = !!(val & MT7531_SGMII_LINK_STATUS);
+- if (!state->link)
+- return;
+-
+- state->an_complete = state->link;
+-
+- if (state->interface == PHY_INTERFACE_MODE_2500BASEX)
+- state->speed = SPEED_2500;
+- else
+- state->speed = SPEED_1000;
+-
+- state->duplex = DUPLEX_FULL;
+- state->pause = MLO_PAUSE_NONE;
+-}
+-
+-static void mt7531_pcs_get_state(struct phylink_pcs *pcs,
+- struct phylink_link_state *state)
+-{
+- struct mt7530_priv *priv = pcs_to_mt753x_pcs(pcs)->priv;
+- int port = pcs_to_mt753x_pcs(pcs)->port;
+-
+- if (state->interface == PHY_INTERFACE_MODE_SGMII) {
+- mt7531_sgmii_pcs_get_state_an(priv, port, state);
+- return;
+- } else if ((state->interface == PHY_INTERFACE_MODE_1000BASEX) ||
+- (state->interface == PHY_INTERFACE_MODE_2500BASEX)) {
+- mt7531_sgmii_pcs_get_state_inband(priv, port, state);
+- return;
+- }
+-
+- state->link = false;
+-}
+-
+ static int mt753x_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
+ phy_interface_t interface,
+ const unsigned long *advertising,
+@@ -3085,18 +2889,57 @@ static const struct phylink_pcs_ops mt75
+ .pcs_an_restart = mt7530_pcs_an_restart,
+ };
+
+-static const struct phylink_pcs_ops mt7531_pcs_ops = {
+- .pcs_validate = mt753x_pcs_validate,
+- .pcs_get_state = mt7531_pcs_get_state,
+- .pcs_config = mt753x_pcs_config,
+- .pcs_an_restart = mt7531_pcs_an_restart,
+- .pcs_link_up = mt7531_pcs_link_up,
++static int mt7530_regmap_read(void *context, unsigned int reg, unsigned int *val)
++{
++ struct mt7530_priv *priv = context;
++
++ *val = mt7530_read(priv, reg);
++ return 0;
++};
++
++static int mt7530_regmap_write(void *context, unsigned int reg, unsigned int val)
++{
++ struct mt7530_priv *priv = context;
++
++ mt7530_write(priv, reg, val);
++ return 0;
++};
++
++static int mt7530_regmap_update_bits(void *context, unsigned int reg,
++ unsigned int mask, unsigned int val)
++{
++ struct mt7530_priv *priv = context;
++
++ mt7530_rmw(priv, reg, mask, val);
++ return 0;
++};
++
++static const struct regmap_bus mt7531_regmap_bus = {
++ .reg_write = mt7530_regmap_write,
++ .reg_read = mt7530_regmap_read,
++ .reg_update_bits = mt7530_regmap_update_bits,
++};
++
++#define MT7531_PCS_REGMAP_CONFIG(_name, _reg_base) \
++ { \
++ .name = _name, \
++ .reg_bits = 16, \
++ .val_bits = 32, \
++ .reg_stride = 4, \
++ .reg_base = _reg_base, \
++ .max_register = 0x17c, \
++ }
++
++static const struct regmap_config mt7531_pcs_config[] = {
++ MT7531_PCS_REGMAP_CONFIG("port5", MT7531_SGMII_REG_BASE(5)),
++ MT7531_PCS_REGMAP_CONFIG("port6", MT7531_SGMII_REG_BASE(6)),
+ };
+
+ static int
+ mt753x_setup(struct dsa_switch *ds)
+ {
+ struct mt7530_priv *priv = ds->priv;
++ struct regmap *regmap;
+ int i, ret;
+
+ /* Initialise the PCS devices */
+@@ -3104,8 +2947,6 @@ mt753x_setup(struct dsa_switch *ds)
+ priv->pcs[i].pcs.ops = priv->info->pcs_ops;
+ priv->pcs[i].priv = priv;
+ priv->pcs[i].port = i;
+- if (mt753x_is_mac_port(i))
+- priv->pcs[i].pcs.poll = 1;
+ }
+
+ ret = priv->info->sw_setup(ds);
+@@ -3120,6 +2961,16 @@ mt753x_setup(struct dsa_switch *ds)
+ if (ret && priv->irq)
+ mt7530_free_irq_common(priv);
+
++ if (priv->id == ID_MT7531)
++ for (i = 0; i < 2; i++) {
++ regmap = devm_regmap_init(ds->dev,
++ &mt7531_regmap_bus, priv,
++ &mt7531_pcs_config[i]);
++ priv->ports[5 + i].sgmii_pcs =
++ mtk_pcs_lynxi_create(ds->dev, regmap,
++ MT7531_PHYA_CTRL_SIGNAL3, 0);
++ }
++
+ return ret;
+ }
+
+@@ -3211,7 +3062,7 @@ static const struct mt753x_info mt753x_t
+ },
+ [ID_MT7531] = {
+ .id = ID_MT7531,
+- .pcs_ops = &mt7531_pcs_ops,
++ .pcs_ops = &mt7530_pcs_ops,
+ .sw_setup = mt7531_setup,
+ .phy_read = mt7531_ind_phy_read,
+ .phy_write = mt7531_ind_phy_write,
+@@ -3319,7 +3170,7 @@ static void
+ mt7530_remove(struct mdio_device *mdiodev)
+ {
+ struct mt7530_priv *priv = dev_get_drvdata(&mdiodev->dev);
+- int ret = 0;
++ int ret = 0, i;
+
+ if (!priv)
+ return;
+@@ -3338,6 +3189,10 @@ mt7530_remove(struct mdio_device *mdiode
+ mt7530_free_irq(priv);
+
+ dsa_unregister_switch(priv->ds);
++
++ for (i = 0; i < 2; ++i)
++ mtk_pcs_lynxi_destroy(priv->ports[5 + i].sgmii_pcs);
++
+ mutex_destroy(&priv->reg_mutex);
+
+ dev_set_drvdata(&mdiodev->dev, NULL);
+--- a/drivers/net/dsa/mt7530.h
++++ b/drivers/net/dsa/mt7530.h
+@@ -364,47 +364,8 @@ enum mt7530_vlan_port_acc_frm {
+ CCR_TX_OCT_CNT_BAD)
+
+ /* MT7531 SGMII register group */
+-#define MT7531_SGMII_REG_BASE 0x5000
+-#define MT7531_SGMII_REG(p, r) (MT7531_SGMII_REG_BASE + \
+- ((p) - 5) * 0x1000 + (r))
+-
+-/* Register forSGMII PCS_CONTROL_1 */
+-#define MT7531_PCS_CONTROL_1(p) MT7531_SGMII_REG(p, 0x00)
+-#define MT7531_SGMII_LINK_STATUS BIT(18)
+-#define MT7531_SGMII_AN_ENABLE BIT(12)
+-#define MT7531_SGMII_AN_RESTART BIT(9)
+-#define MT7531_SGMII_AN_COMPLETE BIT(21)
+-
+-/* Register for SGMII PCS_SPPED_ABILITY */
+-#define MT7531_PCS_SPEED_ABILITY(p) MT7531_SGMII_REG(p, 0x08)
+-#define MT7531_SGMII_TX_CONFIG_MASK GENMASK(15, 0)
+-#define MT7531_SGMII_TX_CONFIG BIT(0)
+-
+-/* Register for SGMII_MODE */
+-#define MT7531_SGMII_MODE(p) MT7531_SGMII_REG(p, 0x20)
+-#define MT7531_SGMII_REMOTE_FAULT_DIS BIT(8)
+-#define MT7531_SGMII_IF_MODE_MASK GENMASK(5, 1)
+-#define MT7531_SGMII_FORCE_DUPLEX BIT(4)
+-#define MT7531_SGMII_FORCE_SPEED_MASK GENMASK(3, 2)
+-#define MT7531_SGMII_FORCE_SPEED_1000 BIT(3)
+-#define MT7531_SGMII_FORCE_SPEED_100 BIT(2)
+-#define MT7531_SGMII_FORCE_SPEED_10 0
+-#define MT7531_SGMII_SPEED_DUPLEX_AN BIT(1)
+-
+-enum mt7531_sgmii_force_duplex {
+- MT7531_SGMII_FORCE_FULL_DUPLEX = 0,
+- MT7531_SGMII_FORCE_HALF_DUPLEX = 0x10,
+-};
+-
+-/* Fields of QPHY_PWR_STATE_CTRL */
+-#define MT7531_QPHY_PWR_STATE_CTRL(p) MT7531_SGMII_REG(p, 0xe8)
+-#define MT7531_SGMII_PHYA_PWD BIT(4)
+-
+-/* Values of SGMII SPEED */
+-#define MT7531_PHYA_CTRL_SIGNAL3(p) MT7531_SGMII_REG(p, 0x128)
+-#define MT7531_RG_TPHY_SPEED_MASK (BIT(2) | BIT(3))
+-#define MT7531_RG_TPHY_SPEED_1_25G 0x0
+-#define MT7531_RG_TPHY_SPEED_3_125G BIT(2)
++#define MT7531_SGMII_REG_BASE(p) (0x5000 + ((p) - 5) * 0x1000)
++#define MT7531_PHYA_CTRL_SIGNAL3 0x128
+
+ /* Register for system reset */
+ #define MT7530_SYS_CTRL 0x7000
+@@ -703,13 +664,13 @@ struct mt7530_fdb {
+ * @pm: The matrix used to show all connections with the port.
+ * @pvid: The VLAN specified is to be considered a PVID at ingress. Any
+ * untagged frames will be assigned to the related VLAN.
+- * @vlan_filtering: The flags indicating whether the port that can recognize
+- * VLAN-tagged frames.
++ * @sgmii_pcs: Pointer to PCS instance for SerDes ports
+ */
+ struct mt7530_port {
+ bool enable;
+ u32 pm;
+ u16 pvid;
++ struct phylink_pcs *sgmii_pcs;
+ };
+
+ /* Port 5 interface select definitions */