Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9ffee4a8 authored by Sean Wang's avatar Sean Wang Committed by David S. Miller
Browse files

net: ethernet: mediatek: Extend SGMII related functions



Add SGMII related logic into a separate file, and also provides options for
forcing 1G, 2.5, AN mode for the target PHY, that can be determined from
SGMII node in DTS.

Signed-off-by: default avatarSean Wang <sean.wang@mediatek.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 3277fc68
Loading
Loading
Loading
Loading
+1 −1
Original line number Original line Diff line number Diff line
@@ -3,4 +3,4 @@
# Makefile for the Mediatek SoCs built-in ethernet macs
# Makefile for the Mediatek SoCs built-in ethernet macs
#
#


obj-$(CONFIG_NET_MEDIATEK_SOC)			+= mtk_eth_soc.o
obj-$(CONFIG_NET_MEDIATEK_SOC)			+= mtk_eth_soc.o mtk_sgmii.o
+43 −32
Original line number Original line Diff line number Diff line
@@ -165,37 +165,38 @@ static void mtk_gmac0_rgmii_adjust(struct mtk_eth *eth, int speed)
	mtk_w32(eth, val, TRGMII_TCK_CTRL);
	mtk_w32(eth, val, TRGMII_TCK_CTRL);
}
}


static void mtk_gmac_sgmii_hw_setup(struct mtk_eth *eth, int mac_id)
static int mtk_gmac_sgmii_hw_setup(struct mtk_eth *eth, int mac_id)
{
{
	int sid, err;
	u32 val;
	u32 val;


	/* Setup the link timer and QPHY power up inside SGMIISYS */
	/* Enable GMAC with SGMII once we finish the SGMII setup. */
	regmap_write(eth->sgmiisys, SGMSYS_PCS_LINK_TIMER,
	regmap_read(eth->ethsys, ETHSYS_SYSCFG0, &val);
		     SGMII_LINK_TIMER_DEFAULT);
	val &= ~SYSCFG0_SGMII_MASK;
	regmap_write(eth->ethsys, ETHSYS_SYSCFG0, val);


	regmap_read(eth->sgmiisys, SGMSYS_SGMII_MODE, &val);
	if (MTK_HAS_CAPS(eth->soc->caps, MTK_GMAC_SHARED_SGMII))
	val |= SGMII_REMOTE_FAULT_DIS;
		sid = 0;
	regmap_write(eth->sgmiisys, SGMSYS_SGMII_MODE, val);
	else
		sid = mac_id;


	regmap_read(eth->sgmiisys, SGMSYS_PCS_CONTROL_1, &val);
	if (MTK_HAS_FLAGS(eth->sgmii->flags[sid], MTK_SGMII_PHYSPEED_AN))
	val |= SGMII_AN_RESTART;
		err = mtk_sgmii_setup_mode_an(eth->sgmii, sid);
	regmap_write(eth->sgmiisys, SGMSYS_PCS_CONTROL_1, val);
	else
		err = mtk_sgmii_setup_mode_force(eth->sgmii, sid);


	regmap_read(eth->sgmiisys, SGMSYS_QPHY_PWR_STATE_CTRL, &val);
	if (err)
	val &= ~SGMII_PHYA_PWD;
		return err;
	regmap_write(eth->sgmiisys, SGMSYS_QPHY_PWR_STATE_CTRL, val);


	/* Determine MUX for which GMAC uses the SGMII interface */
	/* Determine MUX for which GMAC uses the SGMII interface */
	if (MTK_HAS_CAPS(eth->soc->caps, MTK_DUAL_GMAC_SHARED_SGMII)) {
	regmap_read(eth->ethsys, ETHSYS_SYSCFG0, &val);
	regmap_read(eth->ethsys, ETHSYS_SYSCFG0, &val);
		val &= ~SYSCFG0_SGMII_MASK;
	if (!mac_id)
		val |= !mac_id ? SYSCFG0_SGMII_GMAC1 : SYSCFG0_SGMII_GMAC2;
		val |= SYSCFG0_SGMII_GMAC1;
	else
		val |= MTK_HAS_CAPS(eth->soc->caps, MTK_GMAC_SHARED_SGMII) ?
		SYSCFG0_SGMII_GMAC2 : SYSCFG0_SGMII_GMAC2_V2;
	regmap_write(eth->ethsys, ETHSYS_SYSCFG0, val);
	regmap_write(eth->ethsys, ETHSYS_SYSCFG0, val);


		dev_info(eth->dev, "setup shared sgmii for gmac=%d\n",
			 mac_id);
	}

	/* Setup the GMAC1 going through SGMII path when SoC also support
	/* Setup the GMAC1 going through SGMII path when SoC also support
	 * ESW on GMAC1
	 * ESW on GMAC1
	 */
	 */
@@ -204,6 +205,8 @@ static void mtk_gmac_sgmii_hw_setup(struct mtk_eth *eth, int mac_id)
		mtk_w32(eth, 0, MTK_MAC_MISC);
		mtk_w32(eth, 0, MTK_MAC_MISC);
		dev_info(eth->dev, "setup gmac1 going through sgmii");
		dev_info(eth->dev, "setup gmac1 going through sgmii");
	}
	}

	return 0;
}
}


static void mtk_phy_link_adjust(struct net_device *dev)
static void mtk_phy_link_adjust(struct net_device *dev)
@@ -295,6 +298,7 @@ static int mtk_phy_connect(struct net_device *dev)
	struct mtk_eth *eth;
	struct mtk_eth *eth;
	struct device_node *np;
	struct device_node *np;
	u32 val;
	u32 val;
	int err;


	eth = mac->hw;
	eth = mac->hw;
	np = of_parse_phandle(mac->of_node, "phy-handle", 0);
	np = of_parse_phandle(mac->of_node, "phy-handle", 0);
@@ -314,8 +318,11 @@ static int mtk_phy_connect(struct net_device *dev)
	case PHY_INTERFACE_MODE_RGMII:
	case PHY_INTERFACE_MODE_RGMII:
		break;
		break;
	case PHY_INTERFACE_MODE_SGMII:
	case PHY_INTERFACE_MODE_SGMII:
		if (MTK_HAS_CAPS(eth->soc->caps, MTK_SGMII))
		if (MTK_HAS_CAPS(eth->soc->caps, MTK_SGMII)) {
			mtk_gmac_sgmii_hw_setup(eth, mac->id);
			err = mtk_gmac_sgmii_hw_setup(eth, mac->id);
			if (err)
				goto err_phy;
		}
		break;
		break;
	case PHY_INTERFACE_MODE_MII:
	case PHY_INTERFACE_MODE_MII:
		mac->ge_mode = 1;
		mac->ge_mode = 1;
@@ -2483,13 +2490,16 @@ static int mtk_probe(struct platform_device *pdev)
	}
	}


	if (MTK_HAS_CAPS(eth->soc->caps, MTK_SGMII)) {
	if (MTK_HAS_CAPS(eth->soc->caps, MTK_SGMII)) {
		eth->sgmiisys =
		eth->sgmii = devm_kzalloc(eth->dev, sizeof(*eth->sgmii),
		syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
					  GFP_KERNEL);
						"mediatek,sgmiisys");
		if (!eth->sgmii)
		if (IS_ERR(eth->sgmiisys)) {
			return -ENOMEM;
			dev_err(&pdev->dev, "no sgmiisys regmap found\n");

			return PTR_ERR(eth->sgmiisys);
		err = mtk_sgmii_init(eth->sgmii, pdev->dev.of_node,
		}
				     eth->soc->ana_rgc3);

		if (err)
			return err;
	}
	}


	if (eth->soc->required_pctl) {
	if (eth->soc->required_pctl) {
@@ -2642,7 +2652,8 @@ static const struct mtk_soc_data mt7621_data = {
};
};


static const struct mtk_soc_data mt7622_data = {
static const struct mtk_soc_data mt7622_data = {
	.caps = MTK_DUAL_GMAC_SHARED_SGMII | MTK_GMAC1_ESW | MTK_HWLRO,
	.ana_rgc3 = 0x2028,
	.caps = MTK_GMAC_SHARED_SGMII | MTK_GMAC1_ESW | MTK_HWLRO,
	.required_clks = MT7622_CLKS_BITMAP,
	.required_clks = MT7622_CLKS_BITMAP,
	.required_pctl = false,
	.required_pctl = false,
};
};
+35 −4
Original line number Original line Diff line number Diff line
@@ -15,6 +15,10 @@
#ifndef MTK_ETH_H
#ifndef MTK_ETH_H
#define MTK_ETH_H
#define MTK_ETH_H


#include <linux/dma-mapping.h>
#include <linux/netdevice.h>
#include <linux/of_net.h>
#include <linux/u64_stats_sync.h>
#include <linux/refcount.h>
#include <linux/refcount.h>


#define MTK_QDMA_PAGE_SIZE	2048
#define MTK_QDMA_PAGE_SIZE	2048
@@ -372,6 +376,7 @@
#define SYSCFG0_SGMII_MASK	(3 << 8)
#define SYSCFG0_SGMII_MASK	(3 << 8)
#define SYSCFG0_SGMII_GMAC1	((2 << 8) & GENMASK(9, 8))
#define SYSCFG0_SGMII_GMAC1	((2 << 8) & GENMASK(9, 8))
#define SYSCFG0_SGMII_GMAC2	((3 << 8) & GENMASK(9, 8))
#define SYSCFG0_SGMII_GMAC2	((3 << 8) & GENMASK(9, 8))
#define SYSCFG0_SGMII_GMAC2_V2  ((1 << 8) & GENMASK(9, 8))


/* ethernet subsystem clock register */
/* ethernet subsystem clock register */
#define ETHSYS_CLKCFG0		0x2c
#define ETHSYS_CLKCFG0		0x2c
@@ -567,7 +572,7 @@ struct mtk_rx_ring {
#define MTK_SGMII			BIT(8)
#define MTK_SGMII			BIT(8)
#define MTK_GMAC1_SGMII			(BIT(9) | MTK_SGMII)
#define MTK_GMAC1_SGMII			(BIT(9) | MTK_SGMII)
#define MTK_GMAC2_SGMII			(BIT(10) | MTK_SGMII)
#define MTK_GMAC2_SGMII			(BIT(10) | MTK_SGMII)
#define MTK_DUAL_GMAC_SHARED_SGMII	(BIT(11) | MTK_GMAC1_SGMII | \
#define MTK_GMAC_SHARED_SGMII		(BIT(11) | MTK_GMAC1_SGMII | \
					 MTK_GMAC2_SGMII)
					 MTK_GMAC2_SGMII)
#define MTK_HWLRO			BIT(12)
#define MTK_HWLRO			BIT(12)
#define MTK_SHARED_INT			BIT(13)
#define MTK_SHARED_INT			BIT(13)
@@ -575,6 +580,8 @@ struct mtk_rx_ring {


/* struct mtk_eth_data -	This is the structure holding all differences
/* struct mtk_eth_data -	This is the structure holding all differences
 *				among various plaforms
 *				among various plaforms
 * @ana_rgc3:                   The offset for register ANA_RGC3 related to
 *				sgmiisys syscon
 * @caps			Flags shown the extra capability for the SoC
 * @caps			Flags shown the extra capability for the SoC
 * @required_clks		Flags shown the bitmap for required clocks on
 * @required_clks		Flags shown the bitmap for required clocks on
 *				the target SoC
 *				the target SoC
@@ -582,6 +589,7 @@ struct mtk_rx_ring {
 *				the extra setup for those pins used by GMAC.
 *				the extra setup for those pins used by GMAC.
 */
 */
struct mtk_soc_data {
struct mtk_soc_data {
	u32             ana_rgc3;
	u32		caps;
	u32		caps;
	u32		required_clks;
	u32		required_clks;
	bool		required_pctl;
	bool		required_pctl;
@@ -590,6 +598,26 @@ struct mtk_soc_data {
/* currently no SoC has more than 2 macs */
/* currently no SoC has more than 2 macs */
#define MTK_MAX_DEVS			2
#define MTK_MAX_DEVS			2


#define MTK_SGMII_PHYSPEED_AN          BIT(31)
#define MTK_SGMII_PHYSPEED_MASK        GENMASK(0, 2)
#define MTK_SGMII_PHYSPEED_1000        BIT(0)
#define MTK_SGMII_PHYSPEED_2500        BIT(1)
#define MTK_HAS_FLAGS(flags, _x)       (((flags) & (_x)) == (_x))

/* struct mtk_sgmii -  This is the structure holding sgmii regmap and its
 *                     characteristics
 * @regmap:            The register map pointing at the range used to setup
 *                     SGMII modes
 * @flags:             The enum refers to which mode the sgmii wants to run on
 * @ana_rgc3:          The offset refers to register ANA_RGC3 related to regmap
 */

struct mtk_sgmii {
	struct regmap   *regmap[MTK_MAX_DEVS];
	u32             flags[MTK_MAX_DEVS];
	u32             ana_rgc3;
};

/* struct mtk_eth -	This is the main datasructure for holding the state
/* struct mtk_eth -	This is the main datasructure for holding the state
 *			of the driver
 *			of the driver
 * @dev:		The device pointer
 * @dev:		The device pointer
@@ -605,8 +633,6 @@ struct mtk_soc_data {
 * @msg_enable:		Ethtool msg level
 * @msg_enable:		Ethtool msg level
 * @ethsys:		The register map pointing at the range used to setup
 * @ethsys:		The register map pointing at the range used to setup
 *			MII modes
 *			MII modes
 * @sgmiisys:		The register map pointing at the range used to setup
 *			SGMII modes
 * @pctl:		The register map pointing at the range used to setup
 * @pctl:		The register map pointing at the range used to setup
 *			GMAC port drive/slew values
 *			GMAC port drive/slew values
 * @dma_refcnt:		track how many netdevs are using the DMA engine
 * @dma_refcnt:		track how many netdevs are using the DMA engine
@@ -638,7 +664,7 @@ struct mtk_eth {
	u32				msg_enable;
	u32				msg_enable;
	unsigned long			sysclk;
	unsigned long			sysclk;
	struct regmap			*ethsys;
	struct regmap			*ethsys;
	struct regmap			*sgmiisys;
	struct mtk_sgmii                *sgmii;
	struct regmap			*pctl;
	struct regmap			*pctl;
	bool				hwlro;
	bool				hwlro;
	refcount_t			dma_refcnt;
	refcount_t			dma_refcnt;
@@ -689,4 +715,9 @@ void mtk_stats_update_mac(struct mtk_mac *mac);
void mtk_w32(struct mtk_eth *eth, u32 val, unsigned reg);
void mtk_w32(struct mtk_eth *eth, u32 val, unsigned reg);
u32 mtk_r32(struct mtk_eth *eth, unsigned reg);
u32 mtk_r32(struct mtk_eth *eth, unsigned reg);


int mtk_sgmii_init(struct mtk_sgmii *ss, struct device_node *np,
		   u32 ana_rgc3);
int mtk_sgmii_setup_mode_an(struct mtk_sgmii *ss, int id);
int mtk_sgmii_setup_mode_force(struct mtk_sgmii *ss, int id);

#endif /* MTK_ETH_H */
#endif /* MTK_ETH_H */
+105 −0
Original line number Original line Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2018-2019 MediaTek Inc.

/* A library for MediaTek SGMII circuit
 *
 * Author: Sean Wang <sean.wang@mediatek.com>
 *
 */

#include <linux/mfd/syscon.h>
#include <linux/of.h>
#include <linux/regmap.h>

#include "mtk_eth_soc.h"

int mtk_sgmii_init(struct mtk_sgmii *ss, struct device_node *r, u32 ana_rgc3)
{
	struct device_node *np;
	const char *str;
	int i, err;

	ss->ana_rgc3 = ana_rgc3;

	for (i = 0; i < MTK_MAX_DEVS; i++) {
		np = of_parse_phandle(r, "mediatek,sgmiisys", i);
		if (!np)
			break;

		ss->regmap[i] = syscon_node_to_regmap(np);
		if (IS_ERR(ss->regmap[i]))
			return PTR_ERR(ss->regmap[i]);

		err = of_property_read_string(np, "mediatek,physpeed", &str);
		if (err)
			return err;

		if (!strcmp(str, "2500"))
			ss->flags[i] |= MTK_SGMII_PHYSPEED_2500;
		else if (!strcmp(str, "1000"))
			ss->flags[i] |= MTK_SGMII_PHYSPEED_1000;
		else if (!strcmp(str, "auto"))
			ss->flags[i] |= MTK_SGMII_PHYSPEED_AN;
		else
			return -EINVAL;
	}

	return 0;
}

int mtk_sgmii_setup_mode_an(struct mtk_sgmii *ss, int id)
{
	unsigned int val;

	if (!ss->regmap[id])
		return -EINVAL;

	/* Setup the link timer and QPHY power up inside SGMIISYS */
	regmap_write(ss->regmap[id], SGMSYS_PCS_LINK_TIMER,
		     SGMII_LINK_TIMER_DEFAULT);

	regmap_read(ss->regmap[id], SGMSYS_SGMII_MODE, &val);
	val |= SGMII_REMOTE_FAULT_DIS;
	regmap_write(ss->regmap[id], SGMSYS_SGMII_MODE, val);

	regmap_read(ss->regmap[id], SGMSYS_PCS_CONTROL_1, &val);
	val |= SGMII_AN_RESTART;
	regmap_write(ss->regmap[id], SGMSYS_PCS_CONTROL_1, val);

	regmap_read(ss->regmap[id], SGMSYS_QPHY_PWR_STATE_CTRL, &val);
	val &= ~SGMII_PHYA_PWD;
	regmap_write(ss->regmap[id], SGMSYS_QPHY_PWR_STATE_CTRL, val);

	return 0;
}

int mtk_sgmii_setup_mode_force(struct mtk_sgmii *ss, int id)
{
	unsigned int val;
	int mode;

	if (!ss->regmap[id])
		return -EINVAL;

	regmap_read(ss->regmap[id], ss->ana_rgc3, &val);
	val &= ~GENMASK(2, 3);
	mode = ss->flags[id] & MTK_SGMII_PHYSPEED_MASK;
	val |= (mode == MTK_SGMII_PHYSPEED_1000) ? 0 : BIT(2);
	regmap_write(ss->regmap[id], ss->ana_rgc3, val);

	/* Disable SGMII AN */
	regmap_read(ss->regmap[id], SGMSYS_PCS_CONTROL_1, &val);
	val &= ~BIT(12);
	regmap_write(ss->regmap[id], SGMSYS_PCS_CONTROL_1, val);

	/* SGMII force mode setting */
	val = 0x31120019;
	regmap_write(ss->regmap[id], SGMSYS_SGMII_MODE, val);

	/* Release PHYA power down state */
	regmap_read(ss->regmap[id], SGMSYS_QPHY_PWR_STATE_CTRL, &val);
	val &= ~SGMII_PHYA_PWD;
	regmap_write(ss->regmap[id], SGMSYS_QPHY_PWR_STATE_CTRL, val);

	return 0;
}