static void phylink_merge_link_mode(unsigned long *dst, const unsigned long *b) { __ETHTOOL_DECLARE_LINK_MODE_MASK(mask); linkmode_zero(mask); phylink_set_port_modes(mask); linkmode_and(dst, dst, mask); linkmode_or(dst, dst, b); }
/** * arc_emac_open - Open the network device. * @ndev: Pointer to the network device. * * returns: 0, on success or non-zero error value on failure. * * This function sets the MAC address, requests and enables an IRQ * for the EMAC device and starts the Tx queue. * It also connects to the phy device. */ static int arc_emac_open(struct net_device *ndev) { struct arc_emac_priv *priv = netdev_priv(ndev); struct phy_device *phy_dev = ndev->phydev; int i; phy_dev->autoneg = AUTONEG_ENABLE; phy_dev->speed = 0; phy_dev->duplex = 0; linkmode_and(phy_dev->advertising, phy_dev->advertising, phy_dev->supported); priv->last_rx_bd = 0; /* Allocate and set buffers for Rx BD's */ for (i = 0; i < RX_BD_NUM; i++) { dma_addr_t addr; unsigned int *last_rx_bd = &priv->last_rx_bd; struct arc_emac_bd *rxbd = &priv->rxbd[*last_rx_bd]; struct buffer_state *rx_buff = &priv->rx_buff[*last_rx_bd]; rx_buff->skb = netdev_alloc_skb_ip_align(ndev, EMAC_BUFFER_SIZE); if (unlikely(!rx_buff->skb)) return -ENOMEM; addr = dma_map_single(&ndev->dev, (void *)rx_buff->skb->data, EMAC_BUFFER_SIZE, DMA_FROM_DEVICE); if (dma_mapping_error(&ndev->dev, addr)) { netdev_err(ndev, "cannot dma map\n"); dev_kfree_skb(rx_buff->skb); return -ENOMEM; } dma_unmap_addr_set(rx_buff, addr, addr); dma_unmap_len_set(rx_buff, len, EMAC_BUFFER_SIZE); rxbd->data = cpu_to_le32(addr); /* Make sure pointer to data buffer is set */ wmb(); /* Return ownership to EMAC */ rxbd->info = cpu_to_le32(FOR_EMAC | EMAC_BUFFER_SIZE); *last_rx_bd = (*last_rx_bd + 1) % RX_BD_NUM; } priv->txbd_curr = 0; priv->txbd_dirty = 0; /* Clean Tx BD's */ memset(priv->txbd, 0, TX_RING_SZ); /* Initialize logical address filter */ arc_reg_set(priv, R_LAFL, 0); arc_reg_set(priv, R_LAFH, 0); /* Set BD ring pointers for device side */ arc_reg_set(priv, R_RX_RING, (unsigned int)priv->rxbd_dma); arc_reg_set(priv, R_TX_RING, (unsigned int)priv->txbd_dma); /* Enable interrupts */ arc_reg_set(priv, R_ENABLE, RXINT_MASK | TXINT_MASK | ERR_MASK); /* Set CONTROL */ arc_reg_set(priv, R_CTRL, (RX_BD_NUM << 24) | /* RX BD table length */ (TX_BD_NUM << 16) | /* TX BD table length */ TXRN_MASK | RXRN_MASK); napi_enable(&priv->napi); /* Enable EMAC */ arc_reg_or(priv, R_CTRL, EN_MASK); phy_start(ndev->phydev); netif_start_queue(ndev); return 0; }
int phylink_ethtool_ksettings_set(struct phylink *pl, const struct ethtool_link_ksettings *kset) { struct ethtool_link_ksettings our_kset; struct phylink_link_state config; int ret; WARN_ON(!lockdep_rtnl_is_held()); if (kset->base.autoneg != AUTONEG_DISABLE && kset->base.autoneg != AUTONEG_ENABLE) return -EINVAL; config = pl->link_config; /* Mask out unsupported advertisments */ linkmode_and(config.advertising, kset->link_modes.advertising, pl->supported); /* FIXME: should we reject autoneg if phy/mac does not support it? */ if (kset->base.autoneg == AUTONEG_DISABLE) { const struct phy_setting *s; /* Autonegotiation disabled, select a suitable speed and * duplex. */ s = phy_lookup_setting(kset->base.speed, kset->base.duplex, pl->supported, __ETHTOOL_LINK_MODE_MASK_NBITS, false); if (!s) return -EINVAL; /* If we have a fixed link (as specified by firmware), refuse * to change link parameters. */ if (pl->link_an_mode == MLO_AN_FIXED && (s->speed != pl->link_config.speed || s->duplex != pl->link_config.duplex)) return -EINVAL; config.speed = s->speed; config.duplex = s->duplex; config.an_enabled = false; __clear_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, config.advertising); } else { /* If we have a fixed link, refuse to enable autonegotiation */ if (pl->link_an_mode == MLO_AN_FIXED) return -EINVAL; config.speed = SPEED_UNKNOWN; config.duplex = DUPLEX_UNKNOWN; config.an_enabled = true; __set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, config.advertising); } if (phylink_validate(pl, pl->supported, &config)) return -EINVAL; /* If autonegotiation is enabled, we must have an advertisment */ if (config.an_enabled && phylink_is_empty_linkmode(config.advertising)) return -EINVAL; our_kset = *kset; linkmode_copy(our_kset.link_modes.advertising, config.advertising); our_kset.base.speed = config.speed; our_kset.base.duplex = config.duplex; /* If we have a PHY, configure the phy */ if (pl->phydev) { ret = phy_ethtool_ksettings_set(pl->phydev, &our_kset); if (ret) return ret; } mutex_lock(&pl->state_mutex); /* Configure the MAC to match the new settings */ linkmode_copy(pl->link_config.advertising, our_kset.link_modes.advertising); pl->link_config.speed = our_kset.base.speed; pl->link_config.duplex = our_kset.base.duplex; pl->link_config.an_enabled = our_kset.base.autoneg != AUTONEG_DISABLE; if (!test_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state)) { phylink_mac_config(pl, &pl->link_config); phylink_mac_an_restart(pl); } mutex_unlock(&pl->state_mutex); return 0; }
static int phylink_parse_fixedlink(struct phylink *pl, struct device_node *np) { struct device_node *fixed_node; const struct phy_setting *s; struct gpio_desc *desc; const __be32 *fixed_prop; u32 speed; int ret, len; fixed_node = of_get_child_by_name(np, "fixed-link"); if (fixed_node) { ret = of_property_read_u32(fixed_node, "speed", &speed); pl->link_config.speed = speed; pl->link_config.duplex = DUPLEX_HALF; if (of_property_read_bool(fixed_node, "full-duplex")) pl->link_config.duplex = DUPLEX_FULL; /* We treat the "pause" and "asym-pause" terminology as * defining the link partner's ability. */ if (of_property_read_bool(fixed_node, "pause")) pl->link_config.pause |= MLO_PAUSE_SYM; if (of_property_read_bool(fixed_node, "asym-pause")) pl->link_config.pause |= MLO_PAUSE_ASYM; if (ret == 0) { desc = fwnode_get_named_gpiod(&fixed_node->fwnode, "link-gpios", 0, GPIOD_IN, "?"); if (!IS_ERR(desc)) pl->link_gpio = desc; else if (desc == ERR_PTR(-EPROBE_DEFER)) ret = -EPROBE_DEFER; } of_node_put(fixed_node); if (ret) return ret; } else { fixed_prop = of_get_property(np, "fixed-link", &len); if (!fixed_prop) { netdev_err(pl->netdev, "broken fixed-link?\n"); return -EINVAL; } if (len == 5 * sizeof(*fixed_prop)) { pl->link_config.duplex = be32_to_cpu(fixed_prop[1]) ? DUPLEX_FULL : DUPLEX_HALF; pl->link_config.speed = be32_to_cpu(fixed_prop[2]); if (be32_to_cpu(fixed_prop[3])) pl->link_config.pause |= MLO_PAUSE_SYM; if (be32_to_cpu(fixed_prop[4])) pl->link_config.pause |= MLO_PAUSE_ASYM; } } if (pl->link_config.speed > SPEED_1000 && pl->link_config.duplex != DUPLEX_FULL) netdev_warn(pl->netdev, "fixed link specifies half duplex for %dMbps link?\n", pl->link_config.speed); bitmap_fill(pl->supported, __ETHTOOL_LINK_MODE_MASK_NBITS); linkmode_copy(pl->link_config.advertising, pl->supported); phylink_validate(pl, pl->supported, &pl->link_config); s = phy_lookup_setting(pl->link_config.speed, pl->link_config.duplex, pl->supported, __ETHTOOL_LINK_MODE_MASK_NBITS, true); linkmode_zero(pl->supported); phylink_set(pl->supported, MII); if (s) { __set_bit(s->bit, pl->supported); } else { netdev_warn(pl->netdev, "fixed link %s duplex %dMbps not recognised\n", pl->link_config.duplex == DUPLEX_FULL ? "full" : "half", pl->link_config.speed); } linkmode_and(pl->link_config.advertising, pl->link_config.advertising, pl->supported); pl->link_config.link = 1; pl->link_config.an_complete = 1; return 0; }