Exemple #1
0
static int bcm7xxx_28nm_ephy_config_init(struct phy_device *phydev)
{
	u8 rev = phydev->phy_id & ~phydev->drv->phy_id_mask;
	int ret = 0;

	pr_info_once("%s: %s PHY revision: 0x%02x\n",
		     phydev_name(phydev), phydev->drv->name, rev);

	/* Dummy read to a register to workaround a possible issue upon reset
	 * where the internal inverter may not allow the first MDIO transaction
	 * to pass the MDIO management controller and make us return 0xffff for
	 * such reads.
	 */
	phy_read(phydev, MII_BMSR);

	/* Apply AFE software work-around if necessary */
	if (rev == 0x01) {
		ret = bcm7xxx_28nm_ephy_01_afe_config_init(phydev);
		if (ret)
			return ret;
	}

	ret = bcm7xxx_28nm_ephy_eee_enable(phydev);
	if (ret)
		return ret;

	return bcm7xxx_28nm_ephy_apd_enable(phydev);
}
Exemple #2
0
static int mii_probe(struct net_device *dev, int phy_mode)
{
	struct bfin_mac_local *lp = netdev_priv(dev);
	struct phy_device *phydev;
	unsigned short sysctl;
	u32 sclk, mdc_div;

	/* Enable PHY output early */
	if (!(bfin_read_VR_CTL() & CLKBUFOE))
		bfin_write_VR_CTL(bfin_read_VR_CTL() | CLKBUFOE);

	sclk = get_sclk();
	mdc_div = ((sclk / MDC_CLK) / 2) - 1;

	sysctl = bfin_read_EMAC_SYSCTL();
	sysctl = (sysctl & ~MDCDIV) | SET_MDCDIV(mdc_div);
	bfin_write_EMAC_SYSCTL(sysctl);

	phydev = phy_find_first(lp->mii_bus);
	if (!phydev) {
		netdev_err(dev, "no phy device found\n");
		return -ENODEV;
	}

	if (phy_mode != PHY_INTERFACE_MODE_RMII &&
		phy_mode != PHY_INTERFACE_MODE_MII) {
		netdev_err(dev, "invalid phy interface mode\n");
		return -EINVAL;
	}

	phydev = phy_connect(dev, phydev_name(phydev),
			     &bfin_mac_adjust_link, phy_mode);

	if (IS_ERR(phydev)) {
		netdev_err(dev, "could not attach PHY\n");
		return PTR_ERR(phydev);
	}

	/* mask with MAC supported features */
	phydev->supported &= (SUPPORTED_10baseT_Half
			      | SUPPORTED_10baseT_Full
			      | SUPPORTED_100baseT_Half
			      | SUPPORTED_100baseT_Full
			      | SUPPORTED_Autoneg
			      | SUPPORTED_Pause | SUPPORTED_Asym_Pause
			      | SUPPORTED_MII
			      | SUPPORTED_TP);

	phydev->advertising = phydev->supported;

	lp->old_link = 0;
	lp->old_speed = 0;
	lp->old_duplex = -1;
	lp->phydev = phydev;

	phy_attached_print(phydev, "mdc_clk=%dHz(mdc_div=%d)@sclk=%dMHz)\n",
			   MDC_CLK, mdc_div, sclk / 1000000);

	return 0;
}
Exemple #3
0
/**
 * stmmac_mdio_register
 * @ndev: net device structure
 * Description: it registers the MII bus
 */
int stmmac_mdio_register(struct net_device *ndev)
{
	int err = 0;
	struct mii_bus *new_bus;
	struct stmmac_priv *priv = netdev_priv(ndev);
	int addr, found;

	new_bus = mdiobus_alloc();
	if (new_bus == NULL)
		return -ENOMEM;

	/* Assign IRQ to phy at address phy_addr */
	if (priv->phy_addr != -1)
		new_bus->irq[priv->phy_addr] = priv->phy_irq;

	new_bus->name = "STMMAC MII Bus";
	new_bus->read = &stmmac_mdio_read;
	new_bus->write = &stmmac_mdio_write;
	new_bus->reset = &stmmac_mdio_reset;
	snprintf(new_bus->id, MII_BUS_ID_SIZE, "%x", priv->plat->bus_id);
	new_bus->priv = ndev;
	new_bus->phy_mask = priv->phy_mask;
	new_bus->parent = priv->device;
	err = mdiobus_register(new_bus);
	if (err != 0) {
		pr_err("%s: Cannot register as MDIO bus\n", new_bus->name);
		goto bus_register_fail;
	}

	priv->mii = new_bus;

	found = 0;
	for (addr = 0; addr < 32; addr++) {
		struct phy_device *phydev = mdiobus_get_phy(new_bus, addr);
		if (phydev) {
			if (priv->phy_addr == -1) {
				priv->phy_addr = addr;
				phydev->irq = priv->phy_irq;
				new_bus->irq[addr] = priv->phy_irq;
			}
			if (addr == priv->phy_addr) {
				pr_info("%s: PHY ID %08x at %d IRQ %d (%s)%s\n",
					   ndev->name, phydev->phy_id, addr,
				       phydev->irq, phydev_name(phydev),
				       (addr == priv->phy_addr) ? " active" : "");
			}
			found = 1;
		}
	}

	if (!found)
		pr_warning("%s: No PHY found\n", ndev->name);

	return 0;
bus_register_fail:
	kfree(new_bus);
	return err;
}
Exemple #4
0
static int bcm7xxx_28nm_config_init(struct phy_device *phydev)
{
	u8 rev = PHY_BRCM_7XXX_REV(phydev->dev_flags);
	u8 patch = PHY_BRCM_7XXX_PATCH(phydev->dev_flags);
	u8 count;
	int ret = 0;

	/* Newer devices have moved the revision information back into a
	 * standard location in MII_PHYS_ID[23]
	 */
	if (rev == 0)
		rev = phydev->phy_id & ~phydev->drv->phy_id_mask;

	pr_info_once("%s: %s PHY revision: 0x%02x, patch: %d\n",
		     phydev_name(phydev), phydev->drv->name, rev, patch);

	/* Dummy read to a register to workaround an issue upon reset where the
	 * internal inverter may not allow the first MDIO transaction to pass
	 * the MDIO management controller and make us return 0xffff for such
	 * reads.
	 */
	phy_read(phydev, MII_BMSR);

	switch (rev) {
	case 0xb0:
		ret = bcm7xxx_28nm_b0_afe_config_init(phydev);
		break;
	case 0xd0:
		ret = bcm7xxx_28nm_d0_afe_config_init(phydev);
		break;
	case 0xe0:
	case 0xf0:
	/* Rev G0 introduces a roll over */
	case 0x10:
		ret = bcm7xxx_28nm_e0_plus_afe_config_init(phydev);
		break;
	case 0x01:
		ret = bcm7xxx_28nm_a0_patch_afe_config_init(phydev);
		break;
	default:
		break;
	}

	if (ret)
		return ret;

	ret = bcm_phy_downshift_get(phydev, &count);
	if (ret)
		return ret;

	/* Only enable EEE if Wirespeed/downshift is disabled */
	ret = bcm_phy_set_eee(phydev, count == DOWNSHIFT_DEV_DISABLE);
	if (ret)
		return ret;

	return bcm_phy_enable_apd(phydev, true);
}
Exemple #5
0
static void phy_init(struct mtk_eth *eth, struct mtk_mac *mac,
		     struct phy_device *phy)
{
	phy_attach(eth->netdev[mac->id], phydev_name(phy),
		   PHY_INTERFACE_MODE_MII);

	phy->autoneg = AUTONEG_ENABLE;
	phy->speed = 0;
	phy->duplex = 0;
	phy->supported &= PHY_BASIC_FEATURES;
	phy->advertising = phy->supported | ADVERTISED_Autoneg;

	phy_start_aneg(phy);
}
Exemple #6
0
static int ag71xx_phy_connect_fixed(struct ag71xx *ag)
{
	struct platform_device *pdev = ag->pdev;
	struct device *dev = NULL;
	struct ag71xx_platform_data *pdata = ag71xx_get_pdata(ag);
	int ret = 0;

	if (!pdev)
		return -ENODEV;

	dev = &pdev->dev;

	if (!dev)
		return -ENODEV;

	if (!ag->phy_dev) {
		pr_err("Missing PHY for %s", dev_name(dev));
		return -ENODEV;
	}

	/* use fixed settings */
	switch (pdata->speed) {
	case SPEED_10:
	case SPEED_100:
	case SPEED_1000:
		break;
	default:
		dev_err(dev, "invalid speed specified\n");
		ret = -EINVAL;
		break;
	}

	dev_dbg(dev, "using fixed link parameters\n");

	ag->duplex = pdata->duplex;
	ag->speed = pdata->speed;

	if (!ret) {
		dev_info(dev, "connected to fixed PHY at %s [uid=%08x, driver=%s]\n",
			    phydev_name(ag->phy_dev),
			    ag->phy_dev->phy_id, ag->phy_dev->drv->name);
	} else {
		pr_err("Failed to connect to fixed PHY\n");
	}
	return ret;
}
Exemple #7
0
static int tc_mii_probe(struct net_device *dev)
{
	struct tc35815_local *lp = netdev_priv(dev);
	struct phy_device *phydev;
	u32 dropmask;

	phydev = phy_find_first(lp->mii_bus);
	if (!phydev) {
		printk(KERN_ERR "%s: no PHY found\n", dev->name);
		return -ENODEV;
	}

	/* attach the mac to the phy */
	phydev = phy_connect(dev, phydev_name(phydev),
			     &tc_handle_link_change,
			     lp->chiptype == TC35815_TX4939 ? PHY_INTERFACE_MODE_RMII : PHY_INTERFACE_MODE_MII);
	if (IS_ERR(phydev)) {
		printk(KERN_ERR "%s: Could not attach to PHY\n", dev->name);
		return PTR_ERR(phydev);
	}

	phy_attached_info(phydev);

	/* mask with MAC supported features */
	phydev->supported &= PHY_BASIC_FEATURES;
	dropmask = 0;
	if (options.speed == 10)
		dropmask |= SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full;
	else if (options.speed == 100)
		dropmask |= SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full;
	if (options.duplex == 1)
		dropmask |= SUPPORTED_10baseT_Full | SUPPORTED_100baseT_Full;
	else if (options.duplex == 2)
		dropmask |= SUPPORTED_10baseT_Half | SUPPORTED_100baseT_Half;
	phydev->supported &= ~dropmask;
	phydev->advertising = phydev->supported;

	lp->link = 0;
	lp->speed = 0;
	lp->duplex = -1;

	return 0;
}
Exemple #8
0
int mtk_connect_phy_node(struct mtk_eth *eth, struct mtk_mac *mac,
			 struct device_node *phy_node)
{
	const __be32 *_port = NULL;
	struct phy_device *phydev;
	int phy_mode, port;

	_port = of_get_property(phy_node, "reg", NULL);

	if (!_port || (be32_to_cpu(*_port) >= 0x20)) {
		pr_err("%s: invalid port id\n", phy_node->name);
		return -EINVAL;
	}
	port = be32_to_cpu(*_port);
	phy_mode = of_get_phy_mode(phy_node);
	if (phy_mode < 0) {
		dev_err(eth->dev, "incorrect phy-mode %d\n", phy_mode);
		eth->phy->phy_node[port] = NULL;
		return -EINVAL;
	}

	phydev = of_phy_connect(eth->netdev[mac->id], phy_node,
				mtk_phy_link_adjust, 0, phy_mode);
	if (!phydev) {
		dev_err(eth->dev, "could not connect to PHY\n");
		eth->phy->phy_node[port] = NULL;
		return -ENODEV;
	}

	phydev->supported &= PHY_GBIT_FEATURES;
	phydev->advertising = phydev->supported;

	dev_info(eth->dev,
		 "connected port %d to PHY at %s [uid=%08x, driver=%s]\n",
		 port, phydev_name(phydev), phydev->phy_id,
		 phydev->drv->name);

	eth->phy->phy[port] = phydev;
	eth->link[port] = 0;

	return 0;
}
Exemple #9
0
static int bcm7xxx_28nm_config_init(struct phy_device *phydev)
{
	u8 rev = PHY_BRCM_7XXX_REV(phydev->dev_flags);
	u8 patch = PHY_BRCM_7XXX_PATCH(phydev->dev_flags);
	int ret = 0;

	pr_info_once("%s: %s PHY revision: 0x%02x, patch: %d\n",
		     phydev_name(phydev), phydev->drv->name, rev, patch);

	/* Dummy read to a register to workaround an issue upon reset where the
	 * internal inverter may not allow the first MDIO transaction to pass
	 * the MDIO management controller and make us return 0xffff for such
	 * reads.
	 */
	phy_read(phydev, MII_BMSR);

	switch (rev) {
	case 0xb0:
		ret = bcm7xxx_28nm_b0_afe_config_init(phydev);
		break;
	case 0xd0:
		ret = bcm7xxx_28nm_d0_afe_config_init(phydev);
		break;
	case 0xe0:
	case 0xf0:
	/* Rev G0 introduces a roll over */
	case 0x10:
		ret = bcm7xxx_28nm_e0_plus_afe_config_init(phydev);
		break;
	default:
		break;
	}

	if (ret)
		return ret;

	ret = bcm_phy_enable_eee(phydev);
	if (ret)
		return ret;

	return bcm_phy_enable_apd(phydev, true);
}
Exemple #10
0
static int lpc_mii_probe(struct net_device *ndev)
{
	struct netdata_local *pldat = netdev_priv(ndev);
	struct phy_device *phydev = phy_find_first(pldat->mii_bus);

	if (!phydev) {
		netdev_err(ndev, "no PHY found\n");
		return -ENODEV;
	}

	/* Attach to the PHY */
	if (lpc_phy_interface_mode(&pldat->pdev->dev) == PHY_INTERFACE_MODE_MII)
		netdev_info(ndev, "using MII interface\n");
	else
		netdev_info(ndev, "using RMII interface\n");
	phydev = phy_connect(ndev, phydev_name(phydev),
			     &lpc_handle_link_change,
			     lpc_phy_interface_mode(&pldat->pdev->dev));

	if (IS_ERR(phydev)) {
		netdev_err(ndev, "Could not attach to PHY\n");
		return PTR_ERR(phydev);
	}

	/* mask with MAC supported features */
	phydev->supported &= PHY_BASIC_FEATURES;

	phydev->advertising = phydev->supported;

	pldat->link = 0;
	pldat->speed = 0;
	pldat->duplex = -1;

	phy_attached_info(phydev);

	return 0;
}
Exemple #11
0
static int pci_eth_mii_probe(struct net_device *dev)
{
	struct pci_eth_private *priv = netdev_priv(dev);
	struct phy_device *phydev = NULL;

	phydev = phy_find_first(priv->mii_bus);
	if (!phydev) {
		dev_err(&priv->pdev->dev, "no PHY found\n");
		return -ENODEV;
	}

	phydev = phy_connect(dev, phydev_name(phydev), &pci_eth_adjust_link,
			     PHY_INTERFACE_MODE_MII);

	if (IS_ERR(phydev)) {
		dev_err(&priv->pdev->dev, "could not attach to PHY\n");
		return PTR_ERR(phydev);
	}

	/* mask with MAC supported features */
	phydev->supported &= (SUPPORTED_10baseT_Half
				| SUPPORTED_10baseT_Full
				| SUPPORTED_100baseT_Half
				| SUPPORTED_100baseT_Full
				| SUPPORTED_Autoneg
				| SUPPORTED_MII
				| SUPPORTED_TP);

	phydev->advertising = phydev->supported;
	priv->phydev = phydev;
	priv->old_link = 0;
	priv->old_duplex = -1;

	phy_attached_info(phydev);

	return 0;
}
Exemple #12
0
static int ag71xx_phy_connect_multi(struct ag71xx *ag)
{
	struct device *dev = &ag->pdev->dev;
	struct ag71xx_platform_data *pdata = ag71xx_get_pdata(ag);
	struct phy_device *phydev = NULL;
	int phy_addr;
	int ret = 0;

	for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++) {
		if (!(pdata->phy_mask & (1 << phy_addr)))
			continue;

#if LINUX_VERSION_CODE < KERNEL_VERSION(4,5,0)
		if (ag->mii_bus->phy_map[phy_addr] == NULL)
			continue;

		DBG("%s: PHY found at %s, uid=%08x\n",
			dev_name(dev),
			dev_name(&ag->mii_bus->phy_map[phy_addr]->dev),
			&ag->mii_bus->phy_map[phy_addr]->phy_id),
			&ag->mii_bus->phy_map[phy_addr]->phy_id : 0);

		if (phydev == NULL)
			phydev = ag->mii_bus->phy_map[phy_addr];
#else
		if (ag->mii_bus->mdio_map[phy_addr] == NULL)
			continue;

		DBG("%s: PHY found at %s, uid=%08x\n",
			dev_name(dev),
			dev_name(&ag->mii_bus->mdio_map[phy_addr]->dev),
			mdiobus_get_phy(ag->mii_bus, phy_addr) ?
			mdiobus_get_phy(ag->mii_bus, phy_addr)->phy_id : 0);

		if (phydev == NULL)
			phydev = mdiobus_get_phy(ag->mii_bus, phy_addr);
#endif
	}

	if (!phydev) {
		dev_err(dev, "no PHY found with phy_mask=%08x\n",
			   pdata->phy_mask);
		return -ENODEV;
	}

#if LINUX_VERSION_CODE < KERNEL_VERSION(4,5,0)
	ag->phy_dev = phy_connect(ag->dev, dev_name(&phydev->dev),
#else
	ag->phy_dev = phy_connect(ag->dev, phydev_name(phydev),
#endif
				  &ag71xx_phy_link_adjust,
				  pdata->phy_if_mode);

	if (IS_ERR(ag->phy_dev)) {
		dev_err(dev, "could not connect to PHY at %s\n",
#if LINUX_VERSION_CODE < KERNEL_VERSION(4,5,0)
			   dev_name(&phydev->dev));
#else
			   phydev_name(phydev));
#endif
		return PTR_ERR(ag->phy_dev);
	}
/**
 * stmmac_mdio_register
 * @ndev: net device structure
 * Description: it registers the MII bus
 */
int stmmac_mdio_register(struct net_device *ndev)
{
    int err = 0;
    struct mii_bus *new_bus;
    struct stmmac_priv *priv = netdev_priv(ndev);
    struct stmmac_mdio_bus_data *mdio_bus_data = priv->plat->mdio_bus_data;
    struct device_node *mdio_node = priv->plat->mdio_node;
    int addr, found;

    if (!mdio_bus_data)
        return 0;

    new_bus = mdiobus_alloc();
    if (new_bus == NULL)
        return -ENOMEM;

    if (mdio_bus_data->irqs)
        memcpy(new_bus->irq, mdio_bus_data, sizeof(new_bus->irq));

#ifdef CONFIG_OF
    if (priv->device->of_node)
        mdio_bus_data->reset_gpio = -1;
#endif

    new_bus->name = "stmmac";
    new_bus->read = &stmmac_mdio_read;
    new_bus->write = &stmmac_mdio_write;
    new_bus->reset = &stmmac_mdio_reset;
    snprintf(new_bus->id, MII_BUS_ID_SIZE, "%s-%x",
             new_bus->name, priv->plat->bus_id);
    new_bus->priv = ndev;
    new_bus->phy_mask = mdio_bus_data->phy_mask;
    new_bus->parent = priv->device;

    if (mdio_node)
        err = of_mdiobus_register(new_bus, mdio_node);
    else
        err = mdiobus_register(new_bus);
    if (err != 0) {
        pr_err("%s: Cannot register as MDIO bus\n", new_bus->name);
        goto bus_register_fail;
    }

    if (priv->plat->phy_node || mdio_node)
        goto bus_register_done;

    found = 0;
    for (addr = 0; addr < PHY_MAX_ADDR; addr++) {
        struct phy_device *phydev = mdiobus_get_phy(new_bus, addr);
        if (phydev) {
            int act = 0;
            char irq_num[4];
            char *irq_str;

            /*
             * If an IRQ was provided to be assigned after
             * the bus probe, do it here.
             */
            if ((mdio_bus_data->irqs == NULL) &&
                    (mdio_bus_data->probed_phy_irq > 0)) {
                new_bus->irq[addr] =
                    mdio_bus_data->probed_phy_irq;
                phydev->irq = mdio_bus_data->probed_phy_irq;
            }

            /*
             * If we're going to bind the MAC to this PHY bus,
             * and no PHY number was provided to the MAC,
             * use the one probed here.
             */
            if (priv->plat->phy_addr == -1)
                priv->plat->phy_addr = addr;

            act = (priv->plat->phy_addr == addr);
            switch (phydev->irq) {
            case PHY_POLL:
                irq_str = "POLL";
                break;
            case PHY_IGNORE_INTERRUPT:
                irq_str = "IGNORE";
                break;
            default:
                sprintf(irq_num, "%d", phydev->irq);
                irq_str = irq_num;
                break;
            }
            pr_info("%s: PHY ID %08x at %d IRQ %s (%s)%s\n",
                    ndev->name, phydev->phy_id, addr,
                    irq_str, phydev_name(phydev),
                    act ? " active" : "");
            found = 1;
        }
    }

    if (!found && !mdio_node) {
        pr_warn("%s: No PHY found\n", ndev->name);
        mdiobus_unregister(new_bus);
        mdiobus_free(new_bus);
        return -ENODEV;
    }

bus_register_done:
    priv->mii = new_bus;

    return 0;

bus_register_fail:
    mdiobus_free(new_bus);
    return err;
}
Exemple #14
0
int dsa_port_bridge_join(struct dsa_port *dp, struct net_device *br)
{
	struct dsa_notifier_bridge_info info = {
		.sw_index = dp->ds->index,
		.port = dp->index,
		.br = br,
	};
	int err;

	/* Here the port is already bridged. Reflect the current configuration
	 * so that drivers can program their chips accordingly.
	 */
	dp->bridge_dev = br;

	err = dsa_port_notify(dp, DSA_NOTIFIER_BRIDGE_JOIN, &info);

	/* The bridging is rolled back on error */
	if (err)
		dp->bridge_dev = NULL;

	return err;
}

void dsa_port_bridge_leave(struct dsa_port *dp, struct net_device *br)
{
	struct dsa_notifier_bridge_info info = {
		.sw_index = dp->ds->index,
		.port = dp->index,
		.br = br,
	};
	int err;

	/* Here the port is already unbridged. Reflect the current configuration
	 * so that drivers can program their chips accordingly.
	 */
	dp->bridge_dev = NULL;

	err = dsa_port_notify(dp, DSA_NOTIFIER_BRIDGE_LEAVE, &info);
	if (err)
		pr_err("DSA: failed to notify DSA_NOTIFIER_BRIDGE_LEAVE\n");

	/* Port left the bridge, put in BR_STATE_DISABLED by the bridge layer,
	 * so allow it to be in BR_STATE_FORWARDING to be kept functional
	 */
	dsa_port_set_state_now(dp, BR_STATE_FORWARDING);
}

int dsa_port_vlan_filtering(struct dsa_port *dp, bool vlan_filtering,
			    struct switchdev_trans *trans)
{
	struct dsa_switch *ds = dp->ds;

	/* bridge skips -EOPNOTSUPP, so skip the prepare phase */
	if (switchdev_trans_ph_prepare(trans))
		return 0;

	if (ds->ops->port_vlan_filtering)
		return ds->ops->port_vlan_filtering(ds, dp->index,
						    vlan_filtering);

	return 0;
}

int dsa_port_ageing_time(struct dsa_port *dp, clock_t ageing_clock,
			 struct switchdev_trans *trans)
{
	unsigned long ageing_jiffies = clock_t_to_jiffies(ageing_clock);
	unsigned int ageing_time = jiffies_to_msecs(ageing_jiffies);
	struct dsa_notifier_ageing_time_info info = {
		.ageing_time = ageing_time,
		.trans = trans,
	};

	if (switchdev_trans_ph_prepare(trans))
		return dsa_port_notify(dp, DSA_NOTIFIER_AGEING_TIME, &info);

	dp->ageing_time = ageing_time;

	return dsa_port_notify(dp, DSA_NOTIFIER_AGEING_TIME, &info);
}

int dsa_port_fdb_add(struct dsa_port *dp, const unsigned char *addr,
		     u16 vid)
{
	struct dsa_notifier_fdb_info info = {
		.sw_index = dp->ds->index,
		.port = dp->index,
		.addr = addr,
		.vid = vid,
	};

	return dsa_port_notify(dp, DSA_NOTIFIER_FDB_ADD, &info);
}

int dsa_port_fdb_del(struct dsa_port *dp, const unsigned char *addr,
		     u16 vid)
{
	struct dsa_notifier_fdb_info info = {
		.sw_index = dp->ds->index,
		.port = dp->index,
		.addr = addr,
		.vid = vid,

	};

	return dsa_port_notify(dp, DSA_NOTIFIER_FDB_DEL, &info);
}

int dsa_port_fdb_dump(struct dsa_port *dp, dsa_fdb_dump_cb_t *cb, void *data)
{
	struct dsa_switch *ds = dp->ds;
	int port = dp->index;

	if (!ds->ops->port_fdb_dump)
		return -EOPNOTSUPP;

	return ds->ops->port_fdb_dump(ds, port, cb, data);
}

int dsa_port_mdb_add(const struct dsa_port *dp,
		     const struct switchdev_obj_port_mdb *mdb,
		     struct switchdev_trans *trans)
{
	struct dsa_notifier_mdb_info info = {
		.sw_index = dp->ds->index,
		.port = dp->index,
		.trans = trans,
		.mdb = mdb,
	};

	return dsa_port_notify(dp, DSA_NOTIFIER_MDB_ADD, &info);
}

int dsa_port_mdb_del(const struct dsa_port *dp,
		     const struct switchdev_obj_port_mdb *mdb)
{
	struct dsa_notifier_mdb_info info = {
		.sw_index = dp->ds->index,
		.port = dp->index,
		.mdb = mdb,
	};

	return dsa_port_notify(dp, DSA_NOTIFIER_MDB_DEL, &info);
}

int dsa_port_vlan_add(struct dsa_port *dp,
		      const struct switchdev_obj_port_vlan *vlan,
		      struct switchdev_trans *trans)
{
	struct dsa_notifier_vlan_info info = {
		.sw_index = dp->ds->index,
		.port = dp->index,
		.trans = trans,
		.vlan = vlan,
	};

	if (netif_is_bridge_master(vlan->obj.orig_dev))
		return -EOPNOTSUPP;

	if (br_vlan_enabled(dp->bridge_dev))
		return dsa_port_notify(dp, DSA_NOTIFIER_VLAN_ADD, &info);

	return 0;
}

int dsa_port_vlan_del(struct dsa_port *dp,
		      const struct switchdev_obj_port_vlan *vlan)
{
	struct dsa_notifier_vlan_info info = {
		.sw_index = dp->ds->index,
		.port = dp->index,
		.vlan = vlan,
	};

	if (netif_is_bridge_master(vlan->obj.orig_dev))
		return -EOPNOTSUPP;

	if (br_vlan_enabled(dp->bridge_dev))
		return dsa_port_notify(dp, DSA_NOTIFIER_VLAN_DEL, &info);

	return 0;
}

static struct phy_device *dsa_port_get_phy_device(struct dsa_port *dp)
{
	struct device_node *phy_dn;
	struct phy_device *phydev;

	phy_dn = of_parse_phandle(dp->dn, "phy-handle", 0);
	if (!phy_dn)
		return NULL;

	phydev = of_phy_find_device(phy_dn);
	if (!phydev) {
		of_node_put(phy_dn);
		return ERR_PTR(-EPROBE_DEFER);
	}

	return phydev;
}

static int dsa_port_setup_phy_of(struct dsa_port *dp, bool enable)
{
	struct dsa_switch *ds = dp->ds;
	struct phy_device *phydev;
	int port = dp->index;
	int err = 0;

	phydev = dsa_port_get_phy_device(dp);
	if (!phydev)
		return 0;

	if (IS_ERR(phydev))
		return PTR_ERR(phydev);

	if (enable) {
		err = genphy_config_init(phydev);
		if (err < 0)
			goto err_put_dev;

		err = genphy_resume(phydev);
		if (err < 0)
			goto err_put_dev;

		err = genphy_read_status(phydev);
		if (err < 0)
			goto err_put_dev;
	} else {
		err = genphy_suspend(phydev);
		if (err < 0)
			goto err_put_dev;
	}

	if (ds->ops->adjust_link)
		ds->ops->adjust_link(ds, port, phydev);

	dev_dbg(ds->dev, "enabled port's phy: %s", phydev_name(phydev));

err_put_dev:
	put_device(&phydev->mdio.dev);
	return err;
}

static int dsa_port_fixed_link_register_of(struct dsa_port *dp)
{
	struct device_node *dn = dp->dn;
	struct dsa_switch *ds = dp->ds;
	struct phy_device *phydev;
	int port = dp->index;
	int mode;
	int err;

	err = of_phy_register_fixed_link(dn);
	if (err) {
		dev_err(ds->dev,
			"failed to register the fixed PHY of port %d\n",
			port);
		return err;
	}

	phydev = of_phy_find_device(dn);

	mode = of_get_phy_mode(dn);
	if (mode < 0)
		mode = PHY_INTERFACE_MODE_NA;
	phydev->interface = mode;

	genphy_config_init(phydev);
	genphy_read_status(phydev);

	if (ds->ops->adjust_link)
		ds->ops->adjust_link(ds, port, phydev);

	put_device(&phydev->mdio.dev);

	return 0;
}

int dsa_port_link_register_of(struct dsa_port *dp)
{
	if (of_phy_is_fixed_link(dp->dn))
		return dsa_port_fixed_link_register_of(dp);
	else
		return dsa_port_setup_phy_of(dp, true);
}

void dsa_port_link_unregister_of(struct dsa_port *dp)
{
	if (of_phy_is_fixed_link(dp->dn))
		of_phy_deregister_fixed_link(dp->dn);
	else
		dsa_port_setup_phy_of(dp, false);
}

int dsa_port_get_phy_strings(struct dsa_port *dp, uint8_t *data)
{
	struct phy_device *phydev;
	int ret = -EOPNOTSUPP;

	if (of_phy_is_fixed_link(dp->dn))
		return ret;

	phydev = dsa_port_get_phy_device(dp);
	if (IS_ERR_OR_NULL(phydev))
		return ret;

	ret = phy_ethtool_get_strings(phydev, data);
	put_device(&phydev->mdio.dev);

	return ret;
}
EXPORT_SYMBOL_GPL(dsa_port_get_phy_strings);

int dsa_port_get_ethtool_phy_stats(struct dsa_port *dp, uint64_t *data)
{
	struct phy_device *phydev;
	int ret = -EOPNOTSUPP;

	if (of_phy_is_fixed_link(dp->dn))
		return ret;

	phydev = dsa_port_get_phy_device(dp);
	if (IS_ERR_OR_NULL(phydev))
		return ret;

	ret = phy_ethtool_get_stats(phydev, NULL, data);
	put_device(&phydev->mdio.dev);

	return ret;
}
EXPORT_SYMBOL_GPL(dsa_port_get_ethtool_phy_stats);

int dsa_port_get_phy_sset_count(struct dsa_port *dp)
{
	struct phy_device *phydev;
	int ret = -EOPNOTSUPP;

	if (of_phy_is_fixed_link(dp->dn))
		return ret;

	phydev = dsa_port_get_phy_device(dp);
	if (IS_ERR_OR_NULL(phydev))
		return ret;

	ret = phy_ethtool_get_sset_count(phydev);
	put_device(&phydev->mdio.dev);

	return ret;
}
EXPORT_SYMBOL_GPL(dsa_port_get_phy_sset_count);