Commit 7b600f8d authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'mt7988-support'



Daniel Golle says:

====================
net: dsa: add support for MT7988

The MediaTek MT7988 SoC comes with a built-in switch very similar to
previous MT7530 and MT7531. However, the switch address space is mapped
into the SoCs memory space rather than being connected via MDIO.
Using MMIO simplifies register access and also removes the need for a bus
lock, and for that reason also makes interrupt handling more light-weight.

Note that this is different from previous SoCs like MT7621 and MT7623N
which also came with an integrated MT7530-like switch which yet had to be
accessed via MDIO.

Split-off the part of the driver registering an MDIO driver, then add
another module acting as MMIO/platform driver.

The whole series has been tested on various MediaTek boards:
 * MT7623A + MT7530 (BPi-R2)
 * MT7986A + MT7531 (BPi-R3)
 * MT7988A reference board

Changes since v1:
 * use 'internal' PHY mode where appropriate
 * use regmap_update_bits in mt7530_rmw
 * improve dt-bindings

Changes since RFC v3:
 * WARN_ON_ONCE if register read fails
 * move probing of the reset GPIO and reset controller link out of
   common  probe function, as they are not actually common

Changes since RFC v2:
 * split into many small commits to ease review
 * introduce helper functions to reduce code duplication
 * use helpers for locking to make lock-skipping easier and less ugly
   to implement.
 * add dt-bindings for mediatek,mt7988-switch

Changes since initial RFC:
 * use regmap for register access and move register access to bus-
   specific driver
 * move initialization of MT7531 SGMII PCS to MDIO driver
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 56b029dd 386f5fc9
Loading
Loading
Loading
Loading
+24 −2
Original line number Diff line number Diff line
@@ -11,16 +11,23 @@ maintainers:
  - Landen Chao <Landen.Chao@mediatek.com>
  - DENG Qingfang <dqfext@gmail.com>
  - Sean Wang <sean.wang@mediatek.com>
  - Daniel Golle <daniel@makrotopia.org>

description: |
  There are two versions of MT7530, standalone and in a multi-chip module.
  There are three versions of MT7530, standalone, in a multi-chip module and
  built-into a SoC.

  MT7530 is a part of the multi-chip module in MT7620AN, MT7620DA, MT7620DAN,
  MT7620NN, MT7621AT, MT7621DAT, MT7621ST and MT7623AI SoCs.

  The MT7988 SoC comes with a built-in switch similar to MT7531 as well as four
  Gigabit Ethernet PHYs. The switch registers are directly mapped into the SoC's
  memory map rather than using MDIO. The switch got an internally connected 10G
  CPU port and 4 user ports connected to the built-in Gigabit Ethernet PHYs.

  MT7530 in MT7620AN, MT7620DA, MT7620DAN and MT7620NN SoCs has got 10/100 PHYs
  and the switch registers are directly mapped into SoC's memory map rather than
  using MDIO. The DSA driver currently doesn't support this.
  using MDIO. The DSA driver currently doesn't support MT7620 variants.

  There is only the standalone version of MT7531.

@@ -81,6 +88,10 @@ properties:
          Multi-chip module MT7530 in MT7621AT, MT7621DAT and MT7621ST SoCs
        const: mediatek,mt7621

      - description:
          Built-in switch of the MT7988 SoC
        const: mediatek,mt7988-switch

  reg:
    maxItems: 1

@@ -268,6 +279,17 @@ allOf:
      required:
        - mediatek,mcm

  - if:
      properties:
        compatible:
          const: mediatek,mt7988-switch
    then:
      $ref: "#/$defs/mt7530-dsa-port"
      properties:
        gpio-controller: false
        mediatek,mcm: false
        reset-names: false

unevaluatedProperties: false

examples:
+3 −0
Original line number Diff line number Diff line
@@ -13175,8 +13175,11 @@ MEDIATEK SWITCH DRIVER
M:	Sean Wang <sean.wang@mediatek.com>
M:	Landen Chao <Landen.Chao@mediatek.com>
M:	DENG Qingfang <dqfext@gmail.com>
M:	Daniel Golle <daniel@makrotopia.org>
L:	netdev@vger.kernel.org
S:	Maintained
F:	drivers/net/dsa/mt7530-mdio.c
F:	drivers/net/dsa/mt7530-mmio.c
F:	drivers/net/dsa/mt7530.*
F:	net/dsa/tag_mtk.c
+25 −2
Original line number Diff line number Diff line
@@ -38,11 +38,34 @@ config NET_DSA_MT7530
	tristate "MediaTek MT7530 and MT7531 Ethernet switch support"
	select NET_DSA_TAG_MTK
	select MEDIATEK_GE_PHY
	select PCS_MTK_LYNXI
	imply NET_DSA_MT7530_MDIO
	imply NET_DSA_MT7530_MMIO
	help
	  This enables support for the MediaTek MT7530 and MT7531 Ethernet
	  switch chips. Multi-chip module MT7530 in MT7621AT, MT7621DAT,
	  MT7621ST and MT7623AI SoCs is supported.
	  MT7621ST and MT7623AI SoCs, and built-in switch in MT7988 SoC are
	  supported as well.

config NET_DSA_MT7530_MDIO
	tristate "MediaTek MT7530 MDIO interface driver"
	depends on NET_DSA_MT7530
	select PCS_MTK_LYNXI
	help
	  This enables support for the MediaTek MT7530 and MT7531 switch
	  chips which are connected via MDIO, as well as multi-chip
	  module MT7530 which can be found in the MT7621AT, MT7621DAT,
	  MT7621ST and MT7623AI SoCs.

config NET_DSA_MT7530_MMIO
	tristate "MediaTek MT7530 MMIO interface driver"
	depends on NET_DSA_MT7530
	depends on HAS_IOMEM
	help
	  This enables support for the built-in Ethernet switch found
	  in the MediaTek MT7988 SoC.
	  The switch is a similar design as MT7531, but the switch registers
	  are directly mapped into the SoCs register space rather than being
	  accessible via MDIO.

config NET_DSA_MV88E6060
	tristate "Marvell 88E6060 ethernet switch chip support"
+2 −0
Original line number Diff line number Diff line
@@ -7,6 +7,8 @@ obj-$(CONFIG_FIXED_PHY) += dsa_loop_bdinfo.o
endif
obj-$(CONFIG_NET_DSA_LANTIQ_GSWIP) += lantiq_gswip.o
obj-$(CONFIG_NET_DSA_MT7530)	+= mt7530.o
obj-$(CONFIG_NET_DSA_MT7530_MDIO) += mt7530-mdio.o
obj-$(CONFIG_NET_DSA_MT7530_MMIO) += mt7530-mmio.o
obj-$(CONFIG_NET_DSA_MV88E6060) += mv88e6060.o
obj-$(CONFIG_NET_DSA_RZN1_A5PSW) += rzn1_a5psw.o
obj-$(CONFIG_NET_DSA_SMSC_LAN9303) += lan9303-core.o
+271 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only

#include <linux/gpio/consumer.h>
#include <linux/mdio.h>
#include <linux/module.h>
#include <linux/pcs/pcs-mtk-lynxi.h>
#include <linux/of_irq.h>
#include <linux/of_mdio.h>
#include <linux/of_net.h>
#include <linux/of_platform.h>
#include <linux/regmap.h>
#include <linux/reset.h>
#include <linux/regulator/consumer.h>
#include <net/dsa.h>

#include "mt7530.h"

static int
mt7530_regmap_write(void *context, unsigned int reg, unsigned int val)
{
	struct mii_bus *bus = context;
	u16 page, r, lo, hi;
	int ret;

	page = (reg >> 6) & 0x3ff;
	r  = (reg >> 2) & 0xf;
	lo = val & 0xffff;
	hi = val >> 16;

	/* MT7530 uses 31 as the pseudo port */
	ret = bus->write(bus, 0x1f, 0x1f, page);
	if (ret < 0)
		return ret;

	ret = bus->write(bus, 0x1f, r,  lo);
	if (ret < 0)
		return ret;

	ret = bus->write(bus, 0x1f, 0x10, hi);
	return ret;
}

static int
mt7530_regmap_read(void *context, unsigned int reg, unsigned int *val)
{
	struct mii_bus *bus = context;
	u16 page, r, lo, hi;
	int ret;

	page = (reg >> 6) & 0x3ff;
	r = (reg >> 2) & 0xf;

	/* MT7530 uses 31 as the pseudo port */
	ret = bus->write(bus, 0x1f, 0x1f, page);
	if (ret < 0)
		return ret;

	lo = bus->read(bus, 0x1f, r);
	hi = bus->read(bus, 0x1f, 0x10);

	*val = (hi << 16) | (lo & 0xffff);

	return 0;
}

static void
mt7530_mdio_regmap_lock(void *mdio_lock)
{
	mutex_lock_nested(mdio_lock, MDIO_MUTEX_NESTED);
}

static void
mt7530_mdio_regmap_unlock(void *mdio_lock)
{
	mutex_unlock(mdio_lock);
}

static const struct regmap_bus mt7530_regmap_bus = {
	.reg_write = mt7530_regmap_write,
	.reg_read = mt7530_regmap_read,
};

static int
mt7531_create_sgmii(struct mt7530_priv *priv)
{
	struct regmap_config *mt7531_pcs_config[2];
	struct phylink_pcs *pcs;
	struct regmap *regmap;
	int i, ret = 0;

	for (i = 0; i < 2; i++) {
		mt7531_pcs_config[i] = devm_kzalloc(priv->dev,
						    sizeof(struct regmap_config),
						    GFP_KERNEL);
		if (!mt7531_pcs_config[i]) {
			ret = -ENOMEM;
			break;
		}

		mt7531_pcs_config[i]->name = i ? "port6" : "port5";
		mt7531_pcs_config[i]->reg_bits = 16;
		mt7531_pcs_config[i]->val_bits = 32;
		mt7531_pcs_config[i]->reg_stride = 4;
		mt7531_pcs_config[i]->reg_base = MT7531_SGMII_REG_BASE(5 + i);
		mt7531_pcs_config[i]->max_register = 0x17c;
		mt7531_pcs_config[i]->lock = mt7530_mdio_regmap_lock;
		mt7531_pcs_config[i]->unlock = mt7530_mdio_regmap_unlock;
		mt7531_pcs_config[i]->lock_arg = &priv->bus->mdio_lock;

		regmap = devm_regmap_init(priv->dev,
					  &mt7530_regmap_bus, priv->bus,
					  mt7531_pcs_config[i]);
		if (IS_ERR(regmap)) {
			ret = PTR_ERR(regmap);
			break;
		}
		pcs = mtk_pcs_lynxi_create(priv->dev, regmap,
					   MT7531_PHYA_CTRL_SIGNAL3, 0);
		if (!pcs) {
			ret = -ENXIO;
			break;
		}
		priv->ports[5 + i].sgmii_pcs = pcs;
	}

	if (ret && i)
		mtk_pcs_lynxi_destroy(priv->ports[5].sgmii_pcs);

	return ret;
}

static const struct of_device_id mt7530_of_match[] = {
	{ .compatible = "mediatek,mt7621", .data = &mt753x_table[ID_MT7621], },
	{ .compatible = "mediatek,mt7530", .data = &mt753x_table[ID_MT7530], },
	{ .compatible = "mediatek,mt7531", .data = &mt753x_table[ID_MT7531], },
	{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, mt7530_of_match);

static int
mt7530_probe(struct mdio_device *mdiodev)
{
	static struct regmap_config *regmap_config;
	struct mt7530_priv *priv;
	struct device_node *dn;
	int ret;

	dn = mdiodev->dev.of_node;

	priv = devm_kzalloc(&mdiodev->dev, sizeof(*priv), GFP_KERNEL);
	if (!priv)
		return -ENOMEM;

	priv->bus = mdiodev->bus;
	priv->dev = &mdiodev->dev;

	ret = mt7530_probe_common(priv);
	if (ret)
		return ret;

	/* Use medatek,mcm property to distinguish hardware type that would
	 * cause a little bit differences on power-on sequence.
	 * Not MCM that indicates switch works as the remote standalone
	 * integrated circuit so the GPIO pin would be used to complete
	 * the reset, otherwise memory-mapped register accessing used
	 * through syscon provides in the case of MCM.
	 */
	priv->mcm = of_property_read_bool(dn, "mediatek,mcm");
	if (priv->mcm) {
		dev_info(&mdiodev->dev, "MT7530 adapts as multi-chip module\n");

		priv->rstc = devm_reset_control_get(&mdiodev->dev, "mcm");
		if (IS_ERR(priv->rstc)) {
			dev_err(&mdiodev->dev, "Couldn't get our reset line\n");
			return PTR_ERR(priv->rstc);
		}
	} else {
		priv->reset = devm_gpiod_get_optional(&mdiodev->dev, "reset",
						      GPIOD_OUT_LOW);
		if (IS_ERR(priv->reset)) {
			dev_err(&mdiodev->dev, "Couldn't get our reset line\n");
			return PTR_ERR(priv->reset);
		}
	}

	if (priv->id == ID_MT7530) {
		priv->core_pwr = devm_regulator_get(&mdiodev->dev, "core");
		if (IS_ERR(priv->core_pwr))
			return PTR_ERR(priv->core_pwr);

		priv->io_pwr = devm_regulator_get(&mdiodev->dev, "io");
		if (IS_ERR(priv->io_pwr))
			return PTR_ERR(priv->io_pwr);
	}

	regmap_config = devm_kzalloc(&mdiodev->dev, sizeof(*regmap_config),
				     GFP_KERNEL);
	if (!regmap_config)
		return -ENOMEM;

	regmap_config->reg_bits = 16;
	regmap_config->val_bits = 32;
	regmap_config->reg_stride = 4;
	regmap_config->max_register = MT7530_CREV;
	regmap_config->disable_locking = true;
	priv->regmap = devm_regmap_init(priv->dev, &mt7530_regmap_bus,
					priv->bus, regmap_config);
	if (IS_ERR(priv->regmap))
		return PTR_ERR(priv->regmap);

	if (priv->id == ID_MT7531) {
		ret = mt7531_create_sgmii(priv);
		if (ret)
			return ret;
	}

	return dsa_register_switch(priv->ds);
}

static void
mt7530_remove(struct mdio_device *mdiodev)
{
	struct mt7530_priv *priv = dev_get_drvdata(&mdiodev->dev);
	int ret = 0, i;

	if (!priv)
		return;

	ret = regulator_disable(priv->core_pwr);
	if (ret < 0)
		dev_err(priv->dev,
			"Failed to disable core power: %d\n", ret);

	ret = regulator_disable(priv->io_pwr);
	if (ret < 0)
		dev_err(priv->dev, "Failed to disable io pwr: %d\n",
			ret);

	mt7530_remove_common(priv);

	for (i = 0; i < 2; ++i)
		mtk_pcs_lynxi_destroy(priv->ports[5 + i].sgmii_pcs);
}

static void mt7530_shutdown(struct mdio_device *mdiodev)
{
	struct mt7530_priv *priv = dev_get_drvdata(&mdiodev->dev);

	if (!priv)
		return;

	dsa_switch_shutdown(priv->ds);

	dev_set_drvdata(&mdiodev->dev, NULL);
}

static struct mdio_driver mt7530_mdio_driver = {
	.probe  = mt7530_probe,
	.remove = mt7530_remove,
	.shutdown = mt7530_shutdown,
	.mdiodrv.driver = {
		.name = "mt7530-mdio",
		.of_match_table = mt7530_of_match,
	},
};

mdio_module_driver(mt7530_mdio_driver);

MODULE_AUTHOR("Sean Wang <sean.wang@mediatek.com>");
MODULE_DESCRIPTION("Driver for Mediatek MT7530 Switch (MDIO)");
MODULE_LICENSE("GPL");
Loading