162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Broadcom GENET MDIO routines
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 2014-2017 Broadcom
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/acpi.h>
962306a36Sopenharmony_ci#include <linux/types.h>
1062306a36Sopenharmony_ci#include <linux/delay.h>
1162306a36Sopenharmony_ci#include <linux/wait.h>
1262306a36Sopenharmony_ci#include <linux/mii.h>
1362306a36Sopenharmony_ci#include <linux/ethtool.h>
1462306a36Sopenharmony_ci#include <linux/bitops.h>
1562306a36Sopenharmony_ci#include <linux/netdevice.h>
1662306a36Sopenharmony_ci#include <linux/platform_device.h>
1762306a36Sopenharmony_ci#include <linux/phy.h>
1862306a36Sopenharmony_ci#include <linux/phy_fixed.h>
1962306a36Sopenharmony_ci#include <linux/brcmphy.h>
2062306a36Sopenharmony_ci#include <linux/of.h>
2162306a36Sopenharmony_ci#include <linux/of_net.h>
2262306a36Sopenharmony_ci#include <linux/of_mdio.h>
2362306a36Sopenharmony_ci#include <linux/platform_data/bcmgenet.h>
2462306a36Sopenharmony_ci#include <linux/platform_data/mdio-bcm-unimac.h>
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#include "bcmgenet.h"
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_cistatic void bcmgenet_mac_config(struct net_device *dev)
2962306a36Sopenharmony_ci{
3062306a36Sopenharmony_ci	struct bcmgenet_priv *priv = netdev_priv(dev);
3162306a36Sopenharmony_ci	struct phy_device *phydev = dev->phydev;
3262306a36Sopenharmony_ci	u32 reg, cmd_bits = 0;
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	/* speed */
3562306a36Sopenharmony_ci	if (phydev->speed == SPEED_1000)
3662306a36Sopenharmony_ci		cmd_bits = CMD_SPEED_1000;
3762306a36Sopenharmony_ci	else if (phydev->speed == SPEED_100)
3862306a36Sopenharmony_ci		cmd_bits = CMD_SPEED_100;
3962306a36Sopenharmony_ci	else
4062306a36Sopenharmony_ci		cmd_bits = CMD_SPEED_10;
4162306a36Sopenharmony_ci	cmd_bits <<= CMD_SPEED_SHIFT;
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	/* duplex */
4462306a36Sopenharmony_ci	if (phydev->duplex != DUPLEX_FULL) {
4562306a36Sopenharmony_ci		cmd_bits |= CMD_HD_EN |
4662306a36Sopenharmony_ci			CMD_RX_PAUSE_IGNORE | CMD_TX_PAUSE_IGNORE;
4762306a36Sopenharmony_ci	} else {
4862306a36Sopenharmony_ci		/* pause capability defaults to Symmetric */
4962306a36Sopenharmony_ci		if (priv->autoneg_pause) {
5062306a36Sopenharmony_ci			bool tx_pause = 0, rx_pause = 0;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci			if (phydev->autoneg)
5362306a36Sopenharmony_ci				phy_get_pause(phydev, &tx_pause, &rx_pause);
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci			if (!tx_pause)
5662306a36Sopenharmony_ci				cmd_bits |= CMD_TX_PAUSE_IGNORE;
5762306a36Sopenharmony_ci			if (!rx_pause)
5862306a36Sopenharmony_ci				cmd_bits |= CMD_RX_PAUSE_IGNORE;
5962306a36Sopenharmony_ci		}
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci		/* Manual override */
6262306a36Sopenharmony_ci		if (!priv->rx_pause)
6362306a36Sopenharmony_ci			cmd_bits |= CMD_RX_PAUSE_IGNORE;
6462306a36Sopenharmony_ci		if (!priv->tx_pause)
6562306a36Sopenharmony_ci			cmd_bits |= CMD_TX_PAUSE_IGNORE;
6662306a36Sopenharmony_ci	}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	/* Program UMAC and RGMII block based on established
6962306a36Sopenharmony_ci	 * link speed, duplex, and pause. The speed set in
7062306a36Sopenharmony_ci	 * umac->cmd tell RGMII block which clock to use for
7162306a36Sopenharmony_ci	 * transmit -- 25MHz(100Mbps) or 125MHz(1Gbps).
7262306a36Sopenharmony_ci	 * Receive clock is provided by the PHY.
7362306a36Sopenharmony_ci	 */
7462306a36Sopenharmony_ci	reg = bcmgenet_ext_readl(priv, EXT_RGMII_OOB_CTRL);
7562306a36Sopenharmony_ci	reg |= RGMII_LINK;
7662306a36Sopenharmony_ci	bcmgenet_ext_writel(priv, reg, EXT_RGMII_OOB_CTRL);
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	reg = bcmgenet_umac_readl(priv, UMAC_CMD);
7962306a36Sopenharmony_ci	reg &= ~((CMD_SPEED_MASK << CMD_SPEED_SHIFT) |
8062306a36Sopenharmony_ci		       CMD_HD_EN |
8162306a36Sopenharmony_ci		       CMD_RX_PAUSE_IGNORE | CMD_TX_PAUSE_IGNORE);
8262306a36Sopenharmony_ci	reg |= cmd_bits;
8362306a36Sopenharmony_ci	if (reg & CMD_SW_RESET) {
8462306a36Sopenharmony_ci		reg &= ~CMD_SW_RESET;
8562306a36Sopenharmony_ci		bcmgenet_umac_writel(priv, reg, UMAC_CMD);
8662306a36Sopenharmony_ci		udelay(2);
8762306a36Sopenharmony_ci		reg |= CMD_TX_EN | CMD_RX_EN;
8862306a36Sopenharmony_ci	}
8962306a36Sopenharmony_ci	bcmgenet_umac_writel(priv, reg, UMAC_CMD);
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	priv->eee.eee_active = phy_init_eee(phydev, 0) >= 0;
9262306a36Sopenharmony_ci	bcmgenet_eee_enable_set(dev,
9362306a36Sopenharmony_ci				priv->eee.eee_enabled && priv->eee.eee_active,
9462306a36Sopenharmony_ci				priv->eee.tx_lpi_enabled);
9562306a36Sopenharmony_ci}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci/* setup netdev link state when PHY link status change and
9862306a36Sopenharmony_ci * update UMAC and RGMII block when link up
9962306a36Sopenharmony_ci */
10062306a36Sopenharmony_civoid bcmgenet_mii_setup(struct net_device *dev)
10162306a36Sopenharmony_ci{
10262306a36Sopenharmony_ci	struct bcmgenet_priv *priv = netdev_priv(dev);
10362306a36Sopenharmony_ci	struct phy_device *phydev = dev->phydev;
10462306a36Sopenharmony_ci	u32 reg;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	if (phydev->link) {
10762306a36Sopenharmony_ci		bcmgenet_mac_config(dev);
10862306a36Sopenharmony_ci	} else {
10962306a36Sopenharmony_ci		reg = bcmgenet_ext_readl(priv, EXT_RGMII_OOB_CTRL);
11062306a36Sopenharmony_ci		reg &= ~RGMII_LINK;
11162306a36Sopenharmony_ci		bcmgenet_ext_writel(priv, reg, EXT_RGMII_OOB_CTRL);
11262306a36Sopenharmony_ci	}
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	phy_print_status(phydev);
11562306a36Sopenharmony_ci}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_cistatic int bcmgenet_fixed_phy_link_update(struct net_device *dev,
11962306a36Sopenharmony_ci					  struct fixed_phy_status *status)
12062306a36Sopenharmony_ci{
12162306a36Sopenharmony_ci	struct bcmgenet_priv *priv;
12262306a36Sopenharmony_ci	u32 reg;
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	if (dev && dev->phydev && status) {
12562306a36Sopenharmony_ci		priv = netdev_priv(dev);
12662306a36Sopenharmony_ci		reg = bcmgenet_umac_readl(priv, UMAC_MODE);
12762306a36Sopenharmony_ci		status->link = !!(reg & MODE_LINK_STATUS);
12862306a36Sopenharmony_ci	}
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	return 0;
13162306a36Sopenharmony_ci}
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_civoid bcmgenet_phy_pause_set(struct net_device *dev, bool rx, bool tx)
13462306a36Sopenharmony_ci{
13562306a36Sopenharmony_ci	struct phy_device *phydev = dev->phydev;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	linkmode_mod_bit(ETHTOOL_LINK_MODE_Pause_BIT, phydev->advertising, rx);
13862306a36Sopenharmony_ci	linkmode_mod_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, phydev->advertising,
13962306a36Sopenharmony_ci			 rx | tx);
14062306a36Sopenharmony_ci	phy_start_aneg(phydev);
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	mutex_lock(&phydev->lock);
14362306a36Sopenharmony_ci	if (phydev->link)
14462306a36Sopenharmony_ci		bcmgenet_mac_config(dev);
14562306a36Sopenharmony_ci	mutex_unlock(&phydev->lock);
14662306a36Sopenharmony_ci}
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_civoid bcmgenet_phy_power_set(struct net_device *dev, bool enable)
14962306a36Sopenharmony_ci{
15062306a36Sopenharmony_ci	struct bcmgenet_priv *priv = netdev_priv(dev);
15162306a36Sopenharmony_ci	u32 reg = 0;
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	/* EXT_GPHY_CTRL is only valid for GENETv4 and onward */
15462306a36Sopenharmony_ci	if (GENET_IS_V4(priv) || priv->ephy_16nm) {
15562306a36Sopenharmony_ci		reg = bcmgenet_ext_readl(priv, EXT_GPHY_CTRL);
15662306a36Sopenharmony_ci		if (enable) {
15762306a36Sopenharmony_ci			reg &= ~EXT_CK25_DIS;
15862306a36Sopenharmony_ci			bcmgenet_ext_writel(priv, reg, EXT_GPHY_CTRL);
15962306a36Sopenharmony_ci			mdelay(1);
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci			reg &= ~(EXT_CFG_IDDQ_BIAS | EXT_CFG_PWR_DOWN |
16262306a36Sopenharmony_ci				 EXT_CFG_IDDQ_GLOBAL_PWR);
16362306a36Sopenharmony_ci			reg |= EXT_GPHY_RESET;
16462306a36Sopenharmony_ci			bcmgenet_ext_writel(priv, reg, EXT_GPHY_CTRL);
16562306a36Sopenharmony_ci			mdelay(1);
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci			reg &= ~EXT_GPHY_RESET;
16862306a36Sopenharmony_ci		} else {
16962306a36Sopenharmony_ci			reg |= EXT_CFG_IDDQ_BIAS | EXT_CFG_PWR_DOWN |
17062306a36Sopenharmony_ci			       EXT_GPHY_RESET | EXT_CFG_IDDQ_GLOBAL_PWR;
17162306a36Sopenharmony_ci			bcmgenet_ext_writel(priv, reg, EXT_GPHY_CTRL);
17262306a36Sopenharmony_ci			mdelay(1);
17362306a36Sopenharmony_ci			reg |= EXT_CK25_DIS;
17462306a36Sopenharmony_ci		}
17562306a36Sopenharmony_ci		bcmgenet_ext_writel(priv, reg, EXT_GPHY_CTRL);
17662306a36Sopenharmony_ci		udelay(60);
17762306a36Sopenharmony_ci	} else {
17862306a36Sopenharmony_ci		mdelay(1);
17962306a36Sopenharmony_ci	}
18062306a36Sopenharmony_ci}
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_cistatic void bcmgenet_moca_phy_setup(struct bcmgenet_priv *priv)
18362306a36Sopenharmony_ci{
18462306a36Sopenharmony_ci	if (priv->hw_params->flags & GENET_HAS_MOCA_LINK_DET)
18562306a36Sopenharmony_ci		fixed_phy_set_link_update(priv->dev->phydev,
18662306a36Sopenharmony_ci					  bcmgenet_fixed_phy_link_update);
18762306a36Sopenharmony_ci}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ciint bcmgenet_mii_config(struct net_device *dev, bool init)
19062306a36Sopenharmony_ci{
19162306a36Sopenharmony_ci	struct bcmgenet_priv *priv = netdev_priv(dev);
19262306a36Sopenharmony_ci	struct phy_device *phydev = dev->phydev;
19362306a36Sopenharmony_ci	struct device *kdev = &priv->pdev->dev;
19462306a36Sopenharmony_ci	const char *phy_name = NULL;
19562306a36Sopenharmony_ci	u32 id_mode_dis = 0;
19662306a36Sopenharmony_ci	u32 port_ctrl;
19762306a36Sopenharmony_ci	u32 reg;
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	switch (priv->phy_interface) {
20062306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_INTERNAL:
20162306a36Sopenharmony_ci		phy_name = "internal PHY";
20262306a36Sopenharmony_ci		fallthrough;
20362306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_MOCA:
20462306a36Sopenharmony_ci		/* Irrespective of the actually configured PHY speed (100 or
20562306a36Sopenharmony_ci		 * 1000) GENETv4 only has an internal GPHY so we will just end
20662306a36Sopenharmony_ci		 * up masking the Gigabit features from what we support, not
20762306a36Sopenharmony_ci		 * switching to the EPHY
20862306a36Sopenharmony_ci		 */
20962306a36Sopenharmony_ci		if (GENET_IS_V4(priv))
21062306a36Sopenharmony_ci			port_ctrl = PORT_MODE_INT_GPHY;
21162306a36Sopenharmony_ci		else
21262306a36Sopenharmony_ci			port_ctrl = PORT_MODE_INT_EPHY;
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci		if (!phy_name) {
21562306a36Sopenharmony_ci			phy_name = "MoCA";
21662306a36Sopenharmony_ci			if (!GENET_IS_V5(priv))
21762306a36Sopenharmony_ci				port_ctrl |= LED_ACT_SOURCE_MAC;
21862306a36Sopenharmony_ci			bcmgenet_moca_phy_setup(priv);
21962306a36Sopenharmony_ci		}
22062306a36Sopenharmony_ci		break;
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_MII:
22362306a36Sopenharmony_ci		phy_name = "external MII";
22462306a36Sopenharmony_ci		phy_set_max_speed(phydev, SPEED_100);
22562306a36Sopenharmony_ci		port_ctrl = PORT_MODE_EXT_EPHY;
22662306a36Sopenharmony_ci		break;
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_REVMII:
22962306a36Sopenharmony_ci		phy_name = "external RvMII";
23062306a36Sopenharmony_ci		/* of_mdiobus_register took care of reading the 'max-speed'
23162306a36Sopenharmony_ci		 * PHY property for us, effectively limiting the PHY supported
23262306a36Sopenharmony_ci		 * capabilities, use that knowledge to also configure the
23362306a36Sopenharmony_ci		 * Reverse MII interface correctly.
23462306a36Sopenharmony_ci		 */
23562306a36Sopenharmony_ci		if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
23662306a36Sopenharmony_ci				      dev->phydev->supported))
23762306a36Sopenharmony_ci			port_ctrl = PORT_MODE_EXT_RVMII_50;
23862306a36Sopenharmony_ci		else
23962306a36Sopenharmony_ci			port_ctrl = PORT_MODE_EXT_RVMII_25;
24062306a36Sopenharmony_ci		break;
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_RGMII:
24362306a36Sopenharmony_ci		/* RGMII_NO_ID: TXC transitions at the same time as TXD
24462306a36Sopenharmony_ci		 *		(requires PCB or receiver-side delay)
24562306a36Sopenharmony_ci		 *
24662306a36Sopenharmony_ci		 * ID is implicitly disabled for 100Mbps (RG)MII operation.
24762306a36Sopenharmony_ci		 */
24862306a36Sopenharmony_ci		phy_name = "external RGMII (no delay)";
24962306a36Sopenharmony_ci		id_mode_dis = BIT(16);
25062306a36Sopenharmony_ci		port_ctrl = PORT_MODE_EXT_GPHY;
25162306a36Sopenharmony_ci		break;
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_RGMII_TXID:
25462306a36Sopenharmony_ci		/* RGMII_TXID:	Add 2ns delay on TXC (90 degree shift) */
25562306a36Sopenharmony_ci		phy_name = "external RGMII (TX delay)";
25662306a36Sopenharmony_ci		port_ctrl = PORT_MODE_EXT_GPHY;
25762306a36Sopenharmony_ci		break;
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_RGMII_RXID:
26062306a36Sopenharmony_ci		phy_name = "external RGMII (RX delay)";
26162306a36Sopenharmony_ci		port_ctrl = PORT_MODE_EXT_GPHY;
26262306a36Sopenharmony_ci		break;
26362306a36Sopenharmony_ci	default:
26462306a36Sopenharmony_ci		dev_err(kdev, "unknown phy mode: %d\n", priv->phy_interface);
26562306a36Sopenharmony_ci		return -EINVAL;
26662306a36Sopenharmony_ci	}
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	bcmgenet_sys_writel(priv, port_ctrl, SYS_PORT_CTRL);
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	priv->ext_phy = !priv->internal_phy &&
27162306a36Sopenharmony_ci			(priv->phy_interface != PHY_INTERFACE_MODE_MOCA);
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	/* This is an external PHY (xMII), so we need to enable the RGMII
27462306a36Sopenharmony_ci	 * block for the interface to work, unconditionally clear the
27562306a36Sopenharmony_ci	 * Out-of-band disable since we do not need it.
27662306a36Sopenharmony_ci	 */
27762306a36Sopenharmony_ci	reg = bcmgenet_ext_readl(priv, EXT_RGMII_OOB_CTRL);
27862306a36Sopenharmony_ci	reg &= ~OOB_DISABLE;
27962306a36Sopenharmony_ci	if (priv->ext_phy) {
28062306a36Sopenharmony_ci		reg &= ~ID_MODE_DIS;
28162306a36Sopenharmony_ci		reg |= id_mode_dis;
28262306a36Sopenharmony_ci		if (GENET_IS_V1(priv) || GENET_IS_V2(priv) || GENET_IS_V3(priv))
28362306a36Sopenharmony_ci			reg |= RGMII_MODE_EN_V123;
28462306a36Sopenharmony_ci		else
28562306a36Sopenharmony_ci			reg |= RGMII_MODE_EN;
28662306a36Sopenharmony_ci	}
28762306a36Sopenharmony_ci	bcmgenet_ext_writel(priv, reg, EXT_RGMII_OOB_CTRL);
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	if (init)
29062306a36Sopenharmony_ci		dev_info(kdev, "configuring instance for %s\n", phy_name);
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	return 0;
29362306a36Sopenharmony_ci}
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ciint bcmgenet_mii_probe(struct net_device *dev)
29662306a36Sopenharmony_ci{
29762306a36Sopenharmony_ci	struct bcmgenet_priv *priv = netdev_priv(dev);
29862306a36Sopenharmony_ci	struct device *kdev = &priv->pdev->dev;
29962306a36Sopenharmony_ci	struct device_node *dn = kdev->of_node;
30062306a36Sopenharmony_ci	phy_interface_t phy_iface = priv->phy_interface;
30162306a36Sopenharmony_ci	struct phy_device *phydev;
30262306a36Sopenharmony_ci	u32 phy_flags = PHY_BRCM_AUTO_PWRDWN_ENABLE |
30362306a36Sopenharmony_ci			PHY_BRCM_DIS_TXCRXC_NOENRGY |
30462306a36Sopenharmony_ci			PHY_BRCM_IDDQ_SUSPEND;
30562306a36Sopenharmony_ci	int ret;
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	/* Communicate the integrated PHY revision */
30862306a36Sopenharmony_ci	if (priv->internal_phy)
30962306a36Sopenharmony_ci		phy_flags = priv->gphy_rev;
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	/* This is an ugly quirk but we have not been correctly interpreting
31262306a36Sopenharmony_ci	 * the phy_interface values and we have done that across different
31362306a36Sopenharmony_ci	 * drivers, so at least we are consistent in our mistakes.
31462306a36Sopenharmony_ci	 *
31562306a36Sopenharmony_ci	 * When the Generic PHY driver is in use either the PHY has been
31662306a36Sopenharmony_ci	 * strapped or programmed correctly by the boot loader so we should
31762306a36Sopenharmony_ci	 * stick to our incorrect interpretation since we have validated it.
31862306a36Sopenharmony_ci	 *
31962306a36Sopenharmony_ci	 * Now when a dedicated PHY driver is in use, we need to reverse the
32062306a36Sopenharmony_ci	 * meaning of the phy_interface_mode values to something that the PHY
32162306a36Sopenharmony_ci	 * driver will interpret and act on such that we have two mistakes
32262306a36Sopenharmony_ci	 * canceling themselves so to speak. We only do this for the two
32362306a36Sopenharmony_ci	 * modes that GENET driver officially supports on Broadcom STB chips:
32462306a36Sopenharmony_ci	 * PHY_INTERFACE_MODE_RGMII and PHY_INTERFACE_MODE_RGMII_TXID. Other
32562306a36Sopenharmony_ci	 * modes are not *officially* supported with the boot loader and the
32662306a36Sopenharmony_ci	 * scripted environment generating Device Tree blobs for those
32762306a36Sopenharmony_ci	 * platforms.
32862306a36Sopenharmony_ci	 *
32962306a36Sopenharmony_ci	 * Note that internal PHY, MoCA and fixed-link configurations are not
33062306a36Sopenharmony_ci	 * affected because they use different phy_interface_t values or the
33162306a36Sopenharmony_ci	 * Generic PHY driver.
33262306a36Sopenharmony_ci	 */
33362306a36Sopenharmony_ci	switch (priv->phy_interface) {
33462306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_RGMII:
33562306a36Sopenharmony_ci		phy_iface = PHY_INTERFACE_MODE_RGMII_ID;
33662306a36Sopenharmony_ci		break;
33762306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_RGMII_TXID:
33862306a36Sopenharmony_ci		phy_iface = PHY_INTERFACE_MODE_RGMII_RXID;
33962306a36Sopenharmony_ci		break;
34062306a36Sopenharmony_ci	default:
34162306a36Sopenharmony_ci		break;
34262306a36Sopenharmony_ci	}
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	if (dn) {
34562306a36Sopenharmony_ci		phydev = of_phy_connect(dev, priv->phy_dn, bcmgenet_mii_setup,
34662306a36Sopenharmony_ci					phy_flags, phy_iface);
34762306a36Sopenharmony_ci		if (!phydev) {
34862306a36Sopenharmony_ci			pr_err("could not attach to PHY\n");
34962306a36Sopenharmony_ci			return -ENODEV;
35062306a36Sopenharmony_ci		}
35162306a36Sopenharmony_ci	} else {
35262306a36Sopenharmony_ci		if (has_acpi_companion(kdev)) {
35362306a36Sopenharmony_ci			char mdio_bus_id[MII_BUS_ID_SIZE];
35462306a36Sopenharmony_ci			struct mii_bus *unimacbus;
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci			snprintf(mdio_bus_id, MII_BUS_ID_SIZE, "%s-%d",
35762306a36Sopenharmony_ci				 UNIMAC_MDIO_DRV_NAME, priv->pdev->id);
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci			unimacbus = mdio_find_bus(mdio_bus_id);
36062306a36Sopenharmony_ci			if (!unimacbus) {
36162306a36Sopenharmony_ci				pr_err("Unable to find mii\n");
36262306a36Sopenharmony_ci				return -ENODEV;
36362306a36Sopenharmony_ci			}
36462306a36Sopenharmony_ci			phydev = phy_find_first(unimacbus);
36562306a36Sopenharmony_ci			put_device(&unimacbus->dev);
36662306a36Sopenharmony_ci			if (!phydev) {
36762306a36Sopenharmony_ci				pr_err("Unable to find PHY\n");
36862306a36Sopenharmony_ci				return -ENODEV;
36962306a36Sopenharmony_ci			}
37062306a36Sopenharmony_ci		} else {
37162306a36Sopenharmony_ci			phydev = dev->phydev;
37262306a36Sopenharmony_ci		}
37362306a36Sopenharmony_ci		phydev->dev_flags = phy_flags;
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci		ret = phy_connect_direct(dev, phydev, bcmgenet_mii_setup,
37662306a36Sopenharmony_ci					 phy_iface);
37762306a36Sopenharmony_ci		if (ret) {
37862306a36Sopenharmony_ci			pr_err("could not attach to PHY\n");
37962306a36Sopenharmony_ci			return -ENODEV;
38062306a36Sopenharmony_ci		}
38162306a36Sopenharmony_ci	}
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	/* Configure port multiplexer based on what the probed PHY device since
38462306a36Sopenharmony_ci	 * reading the 'max-speed' property determines the maximum supported
38562306a36Sopenharmony_ci	 * PHY speed which is needed for bcmgenet_mii_config() to configure
38662306a36Sopenharmony_ci	 * things appropriately.
38762306a36Sopenharmony_ci	 */
38862306a36Sopenharmony_ci	ret = bcmgenet_mii_config(dev, true);
38962306a36Sopenharmony_ci	if (ret) {
39062306a36Sopenharmony_ci		phy_disconnect(dev->phydev);
39162306a36Sopenharmony_ci		return ret;
39262306a36Sopenharmony_ci	}
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	/* The internal PHY has its link interrupts routed to the
39562306a36Sopenharmony_ci	 * Ethernet MAC ISRs. On GENETv5 there is a hardware issue
39662306a36Sopenharmony_ci	 * that prevents the signaling of link UP interrupts when
39762306a36Sopenharmony_ci	 * the link operates at 10Mbps, so fallback to polling for
39862306a36Sopenharmony_ci	 * those versions of GENET.
39962306a36Sopenharmony_ci	 */
40062306a36Sopenharmony_ci	if (priv->internal_phy && !GENET_IS_V5(priv))
40162306a36Sopenharmony_ci		dev->phydev->irq = PHY_MAC_INTERRUPT;
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	/* Indicate that the MAC is responsible for PHY PM */
40462306a36Sopenharmony_ci	dev->phydev->mac_managed_pm = true;
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	return 0;
40762306a36Sopenharmony_ci}
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_cistatic struct device_node *bcmgenet_mii_of_find_mdio(struct bcmgenet_priv *priv)
41062306a36Sopenharmony_ci{
41162306a36Sopenharmony_ci	struct device_node *dn = priv->pdev->dev.of_node;
41262306a36Sopenharmony_ci	struct device *kdev = &priv->pdev->dev;
41362306a36Sopenharmony_ci	char *compat;
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	compat = kasprintf(GFP_KERNEL, "brcm,genet-mdio-v%d", priv->version);
41662306a36Sopenharmony_ci	if (!compat)
41762306a36Sopenharmony_ci		return NULL;
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	priv->mdio_dn = of_get_compatible_child(dn, compat);
42062306a36Sopenharmony_ci	kfree(compat);
42162306a36Sopenharmony_ci	if (!priv->mdio_dn) {
42262306a36Sopenharmony_ci		dev_err(kdev, "unable to find MDIO bus node\n");
42362306a36Sopenharmony_ci		return NULL;
42462306a36Sopenharmony_ci	}
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	return priv->mdio_dn;
42762306a36Sopenharmony_ci}
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_cistatic void bcmgenet_mii_pdata_init(struct bcmgenet_priv *priv,
43062306a36Sopenharmony_ci				    struct unimac_mdio_pdata *ppd)
43162306a36Sopenharmony_ci{
43262306a36Sopenharmony_ci	struct device *kdev = &priv->pdev->dev;
43362306a36Sopenharmony_ci	struct bcmgenet_platform_data *pd = kdev->platform_data;
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	if (pd->phy_interface != PHY_INTERFACE_MODE_MOCA && pd->mdio_enabled) {
43662306a36Sopenharmony_ci		/*
43762306a36Sopenharmony_ci		 * Internal or external PHY with MDIO access
43862306a36Sopenharmony_ci		 */
43962306a36Sopenharmony_ci		if (pd->phy_address >= 0 && pd->phy_address < PHY_MAX_ADDR)
44062306a36Sopenharmony_ci			ppd->phy_mask = 1 << pd->phy_address;
44162306a36Sopenharmony_ci		else
44262306a36Sopenharmony_ci			ppd->phy_mask = 0;
44362306a36Sopenharmony_ci	}
44462306a36Sopenharmony_ci}
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_cistatic int bcmgenet_mii_wait(void *wait_func_data)
44762306a36Sopenharmony_ci{
44862306a36Sopenharmony_ci	struct bcmgenet_priv *priv = wait_func_data;
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	wait_event_timeout(priv->wq,
45162306a36Sopenharmony_ci			   !(bcmgenet_umac_readl(priv, UMAC_MDIO_CMD)
45262306a36Sopenharmony_ci			   & MDIO_START_BUSY),
45362306a36Sopenharmony_ci			   HZ / 100);
45462306a36Sopenharmony_ci	return 0;
45562306a36Sopenharmony_ci}
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_cistatic int bcmgenet_mii_register(struct bcmgenet_priv *priv)
45862306a36Sopenharmony_ci{
45962306a36Sopenharmony_ci	struct platform_device *pdev = priv->pdev;
46062306a36Sopenharmony_ci	struct bcmgenet_platform_data *pdata = pdev->dev.platform_data;
46162306a36Sopenharmony_ci	struct device_node *dn = pdev->dev.of_node;
46262306a36Sopenharmony_ci	struct unimac_mdio_pdata ppd;
46362306a36Sopenharmony_ci	struct platform_device *ppdev;
46462306a36Sopenharmony_ci	struct resource *pres, res;
46562306a36Sopenharmony_ci	int id, ret;
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	pres = platform_get_resource(pdev, IORESOURCE_MEM, 0);
46862306a36Sopenharmony_ci	if (!pres) {
46962306a36Sopenharmony_ci		dev_err(&pdev->dev, "Invalid resource\n");
47062306a36Sopenharmony_ci		return -EINVAL;
47162306a36Sopenharmony_ci	}
47262306a36Sopenharmony_ci	memset(&res, 0, sizeof(res));
47362306a36Sopenharmony_ci	memset(&ppd, 0, sizeof(ppd));
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	ppd.wait_func = bcmgenet_mii_wait;
47662306a36Sopenharmony_ci	ppd.wait_func_data = priv;
47762306a36Sopenharmony_ci	ppd.bus_name = "bcmgenet MII bus";
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	/* Unimac MDIO bus controller starts at UniMAC offset + MDIO_CMD
48062306a36Sopenharmony_ci	 * and is 2 * 32-bits word long, 8 bytes total.
48162306a36Sopenharmony_ci	 */
48262306a36Sopenharmony_ci	res.start = pres->start + GENET_UMAC_OFF + UMAC_MDIO_CMD;
48362306a36Sopenharmony_ci	res.end = res.start + 8;
48462306a36Sopenharmony_ci	res.flags = IORESOURCE_MEM;
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci	if (dn)
48762306a36Sopenharmony_ci		id = of_alias_get_id(dn, "eth");
48862306a36Sopenharmony_ci	else
48962306a36Sopenharmony_ci		id = pdev->id;
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci	ppdev = platform_device_alloc(UNIMAC_MDIO_DRV_NAME, id);
49262306a36Sopenharmony_ci	if (!ppdev)
49362306a36Sopenharmony_ci		return -ENOMEM;
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	/* Retain this platform_device pointer for later cleanup */
49662306a36Sopenharmony_ci	priv->mii_pdev = ppdev;
49762306a36Sopenharmony_ci	ppdev->dev.parent = &pdev->dev;
49862306a36Sopenharmony_ci	if (dn)
49962306a36Sopenharmony_ci		ppdev->dev.of_node = bcmgenet_mii_of_find_mdio(priv);
50062306a36Sopenharmony_ci	else if (pdata)
50162306a36Sopenharmony_ci		bcmgenet_mii_pdata_init(priv, &ppd);
50262306a36Sopenharmony_ci	else
50362306a36Sopenharmony_ci		ppd.phy_mask = ~0;
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci	ret = platform_device_add_resources(ppdev, &res, 1);
50662306a36Sopenharmony_ci	if (ret)
50762306a36Sopenharmony_ci		goto out;
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	ret = platform_device_add_data(ppdev, &ppd, sizeof(ppd));
51062306a36Sopenharmony_ci	if (ret)
51162306a36Sopenharmony_ci		goto out;
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	ret = platform_device_add(ppdev);
51462306a36Sopenharmony_ci	if (ret)
51562306a36Sopenharmony_ci		goto out;
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	return 0;
51862306a36Sopenharmony_ciout:
51962306a36Sopenharmony_ci	platform_device_put(ppdev);
52062306a36Sopenharmony_ci	return ret;
52162306a36Sopenharmony_ci}
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_cistatic int bcmgenet_phy_interface_init(struct bcmgenet_priv *priv)
52462306a36Sopenharmony_ci{
52562306a36Sopenharmony_ci	struct device *kdev = &priv->pdev->dev;
52662306a36Sopenharmony_ci	int phy_mode = device_get_phy_mode(kdev);
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci	if (phy_mode < 0) {
52962306a36Sopenharmony_ci		dev_err(kdev, "invalid PHY mode property\n");
53062306a36Sopenharmony_ci		return phy_mode;
53162306a36Sopenharmony_ci	}
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci	priv->phy_interface = phy_mode;
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	/* We need to specifically look up whether this PHY interface is
53662306a36Sopenharmony_ci	 * internal or not *before* we even try to probe the PHY driver
53762306a36Sopenharmony_ci	 * over MDIO as we may have shut down the internal PHY for power
53862306a36Sopenharmony_ci	 * saving purposes.
53962306a36Sopenharmony_ci	 */
54062306a36Sopenharmony_ci	if (priv->phy_interface == PHY_INTERFACE_MODE_INTERNAL)
54162306a36Sopenharmony_ci		priv->internal_phy = true;
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	return 0;
54462306a36Sopenharmony_ci}
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_cistatic int bcmgenet_mii_of_init(struct bcmgenet_priv *priv)
54762306a36Sopenharmony_ci{
54862306a36Sopenharmony_ci	struct device_node *dn = priv->pdev->dev.of_node;
54962306a36Sopenharmony_ci	struct phy_device *phydev;
55062306a36Sopenharmony_ci	int ret;
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	/* Fetch the PHY phandle */
55362306a36Sopenharmony_ci	priv->phy_dn = of_parse_phandle(dn, "phy-handle", 0);
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	/* In the case of a fixed PHY, the DT node associated
55662306a36Sopenharmony_ci	 * to the PHY is the Ethernet MAC DT node.
55762306a36Sopenharmony_ci	 */
55862306a36Sopenharmony_ci	if (!priv->phy_dn && of_phy_is_fixed_link(dn)) {
55962306a36Sopenharmony_ci		ret = of_phy_register_fixed_link(dn);
56062306a36Sopenharmony_ci		if (ret)
56162306a36Sopenharmony_ci			return ret;
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci		priv->phy_dn = of_node_get(dn);
56462306a36Sopenharmony_ci	}
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci	/* Get the link mode */
56762306a36Sopenharmony_ci	ret = bcmgenet_phy_interface_init(priv);
56862306a36Sopenharmony_ci	if (ret)
56962306a36Sopenharmony_ci		return ret;
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci	/* Make sure we initialize MoCA PHYs with a link down */
57262306a36Sopenharmony_ci	if (priv->phy_interface == PHY_INTERFACE_MODE_MOCA) {
57362306a36Sopenharmony_ci		phydev = of_phy_find_device(dn);
57462306a36Sopenharmony_ci		if (phydev) {
57562306a36Sopenharmony_ci			phydev->link = 0;
57662306a36Sopenharmony_ci			put_device(&phydev->mdio.dev);
57762306a36Sopenharmony_ci		}
57862306a36Sopenharmony_ci	}
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci	return 0;
58162306a36Sopenharmony_ci}
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_cistatic int bcmgenet_mii_pd_init(struct bcmgenet_priv *priv)
58462306a36Sopenharmony_ci{
58562306a36Sopenharmony_ci	struct device *kdev = &priv->pdev->dev;
58662306a36Sopenharmony_ci	struct bcmgenet_platform_data *pd = kdev->platform_data;
58762306a36Sopenharmony_ci	char phy_name[MII_BUS_ID_SIZE + 3];
58862306a36Sopenharmony_ci	char mdio_bus_id[MII_BUS_ID_SIZE];
58962306a36Sopenharmony_ci	struct phy_device *phydev;
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	snprintf(mdio_bus_id, MII_BUS_ID_SIZE, "%s-%d",
59262306a36Sopenharmony_ci		 UNIMAC_MDIO_DRV_NAME, priv->pdev->id);
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	if (pd->phy_interface != PHY_INTERFACE_MODE_MOCA && pd->mdio_enabled) {
59562306a36Sopenharmony_ci		snprintf(phy_name, MII_BUS_ID_SIZE, PHY_ID_FMT,
59662306a36Sopenharmony_ci			 mdio_bus_id, pd->phy_address);
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci		/*
59962306a36Sopenharmony_ci		 * Internal or external PHY with MDIO access
60062306a36Sopenharmony_ci		 */
60162306a36Sopenharmony_ci		phydev = phy_attach(priv->dev, phy_name, pd->phy_interface);
60262306a36Sopenharmony_ci		if (IS_ERR(phydev)) {
60362306a36Sopenharmony_ci			dev_err(kdev, "failed to register PHY device\n");
60462306a36Sopenharmony_ci			return PTR_ERR(phydev);
60562306a36Sopenharmony_ci		}
60662306a36Sopenharmony_ci	} else {
60762306a36Sopenharmony_ci		/*
60862306a36Sopenharmony_ci		 * MoCA port or no MDIO access.
60962306a36Sopenharmony_ci		 * Use fixed PHY to represent the link layer.
61062306a36Sopenharmony_ci		 */
61162306a36Sopenharmony_ci		struct fixed_phy_status fphy_status = {
61262306a36Sopenharmony_ci			.link = 1,
61362306a36Sopenharmony_ci			.speed = pd->phy_speed,
61462306a36Sopenharmony_ci			.duplex = pd->phy_duplex,
61562306a36Sopenharmony_ci			.pause = 0,
61662306a36Sopenharmony_ci			.asym_pause = 0,
61762306a36Sopenharmony_ci		};
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci		phydev = fixed_phy_register(PHY_POLL, &fphy_status, NULL);
62062306a36Sopenharmony_ci		if (IS_ERR(phydev)) {
62162306a36Sopenharmony_ci			dev_err(kdev, "failed to register fixed PHY device\n");
62262306a36Sopenharmony_ci			return PTR_ERR(phydev);
62362306a36Sopenharmony_ci		}
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci		/* Make sure we initialize MoCA PHYs with a link down */
62662306a36Sopenharmony_ci		phydev->link = 0;
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci	}
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	priv->phy_interface = pd->phy_interface;
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	return 0;
63362306a36Sopenharmony_ci}
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_cistatic int bcmgenet_mii_bus_init(struct bcmgenet_priv *priv)
63662306a36Sopenharmony_ci{
63762306a36Sopenharmony_ci	struct device *kdev = &priv->pdev->dev;
63862306a36Sopenharmony_ci	struct device_node *dn = kdev->of_node;
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci	if (dn)
64162306a36Sopenharmony_ci		return bcmgenet_mii_of_init(priv);
64262306a36Sopenharmony_ci	else if (has_acpi_companion(kdev))
64362306a36Sopenharmony_ci		return bcmgenet_phy_interface_init(priv);
64462306a36Sopenharmony_ci	else
64562306a36Sopenharmony_ci		return bcmgenet_mii_pd_init(priv);
64662306a36Sopenharmony_ci}
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ciint bcmgenet_mii_init(struct net_device *dev)
64962306a36Sopenharmony_ci{
65062306a36Sopenharmony_ci	struct bcmgenet_priv *priv = netdev_priv(dev);
65162306a36Sopenharmony_ci	int ret;
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	ret = bcmgenet_mii_register(priv);
65462306a36Sopenharmony_ci	if (ret)
65562306a36Sopenharmony_ci		return ret;
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci	ret = bcmgenet_mii_bus_init(priv);
65862306a36Sopenharmony_ci	if (ret)
65962306a36Sopenharmony_ci		goto out;
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci	return 0;
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ciout:
66462306a36Sopenharmony_ci	bcmgenet_mii_exit(dev);
66562306a36Sopenharmony_ci	return ret;
66662306a36Sopenharmony_ci}
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_civoid bcmgenet_mii_exit(struct net_device *dev)
66962306a36Sopenharmony_ci{
67062306a36Sopenharmony_ci	struct bcmgenet_priv *priv = netdev_priv(dev);
67162306a36Sopenharmony_ci	struct device_node *dn = priv->pdev->dev.of_node;
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci	if (of_phy_is_fixed_link(dn))
67462306a36Sopenharmony_ci		of_phy_deregister_fixed_link(dn);
67562306a36Sopenharmony_ci	of_node_put(priv->phy_dn);
67662306a36Sopenharmony_ci	clk_prepare_enable(priv->clk);
67762306a36Sopenharmony_ci	platform_device_unregister(priv->mii_pdev);
67862306a36Sopenharmony_ci	clk_disable_unprepare(priv->clk);
67962306a36Sopenharmony_ci}
680