/* As long as the interface is active, we keep the timestamping counter enabled *withfineresolutionandbinaryrollover.Thisavoidnon-monotonicbehavior *(clockjumps)whenchangingtimestampingsettingsatruntime.
*/ #define STMMAC_HWTS_ACTIVE (PTP_TCR_TSENA | PTP_TCR_TSCFUPDT | \
PTP_TCR_TSCTRLSSR)
/* By default the driver will use the ring mode to manage tx and rx descriptors, *butallowusertoforcetousethechaininsteadofthering
*/ staticunsignedint chain_mode;
module_param(chain_mode, int, 0444);
MODULE_PARM_DESC(chain_mode, "To use chain instead of ring mode");
/* check if all TX queues have the work finished */ for (queue = 0; queue < tx_cnt; queue++) { struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue];
if (tx_q->dirty_tx != tx_q->cur_tx) returntrue; /* still unfinished work */
}
if (!priv->hwts_rx_en) return; /* For GMAC4, the valid timestamp is from CTX next desc. */ if (priv->plat->has_gmac4 || priv->plat->has_xgmac)
desc = np;
/* Check if timestamp is available */ if (stmmac_get_rx_timestamp_status(priv, p, np, priv->adv_ts)) {
stmmac_get_timestamp(priv, desc, priv->adv_ts, &ns);
if (!(priv->dma_cap.time_stamp || priv->adv_ts)) {
NL_SET_ERR_MSG_MOD(extack, "No support for HW time stamping");
priv->hwts_tx_en = 0;
priv->hwts_rx_en = 0;
return -EOPNOTSUPP;
}
if (!netif_running(dev)) {
NL_SET_ERR_MSG_MOD(extack, "Cannot change timestamping configuration while down"); return -ENODEV;
}
if (config->tx_type != HWTSTAMP_TX_OFF &&
config->tx_type != HWTSTAMP_TX_ON) return -ERANGE;
if (priv->adv_ts) { switch (config->rx_filter) { case HWTSTAMP_FILTER_NONE: /* time stamp no incoming packet at all */
config->rx_filter = HWTSTAMP_FILTER_NONE; break;
case HWTSTAMP_FILTER_PTP_V1_L4_EVENT: /* PTP v1, UDP, any kind of event packet */
config->rx_filter = HWTSTAMP_FILTER_PTP_V1_L4_EVENT; /* 'xmac' hardware can support Sync, Pdelay_Req and *Pdelay_respbysettingbit14andbits17/16to01 *ThisleavesDelay_Reqtimestampsout. *Enableallevents*and*generalpurposemessage *timestamping
*/
snap_type_sel = PTP_TCR_SNAPTYPSEL_1;
ptp_over_ipv4_udp = PTP_TCR_TSIPV4ENA;
ptp_over_ipv6_udp = PTP_TCR_TSIPV6ENA; break;
case HWTSTAMP_FILTER_PTP_V1_L4_SYNC: /* PTP v1, UDP, Sync packet */
config->rx_filter = HWTSTAMP_FILTER_PTP_V1_L4_SYNC; /* take time stamp for SYNC messages only */
ts_event_en = PTP_TCR_TSEVNTENA;
case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: /* PTP v2, UDP, any kind of event packet */
config->rx_filter = HWTSTAMP_FILTER_PTP_V2_L4_EVENT;
ptp_v2 = PTP_TCR_TSVER2ENA; /* take time stamp for all event messages */
snap_type_sel = PTP_TCR_SNAPTYPSEL_1;
case HWTSTAMP_FILTER_PTP_V2_EVENT: /* PTP v2/802.AS1 any layer, any kind of event packet */
config->rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
ptp_v2 = PTP_TCR_TSVER2ENA;
snap_type_sel = PTP_TCR_SNAPTYPSEL_1; if (priv->synopsys_id < DWMAC_CORE_4_10)
ts_event_en = PTP_TCR_TSEVNTENA;
ptp_over_ipv4_udp = PTP_TCR_TSIPV4ENA;
ptp_over_ipv6_udp = PTP_TCR_TSIPV6ENA;
ptp_over_ethernet = PTP_TCR_TSIPENA; break;
case HWTSTAMP_FILTER_PTP_V2_SYNC: /* PTP v2/802.AS1, any layer, Sync packet */
config->rx_filter = HWTSTAMP_FILTER_PTP_V2_SYNC;
ptp_v2 = PTP_TCR_TSVER2ENA; /* take time stamp for SYNC messages only */
ts_event_en = PTP_TCR_TSEVNTENA;
case HWTSTAMP_FILTER_NTP_ALL: case HWTSTAMP_FILTER_ALL: /* time stamp any incoming packet */
config->rx_filter = HWTSTAMP_FILTER_ALL;
tstamp_all = PTP_TCR_TSENALL; break;
/* Update the transmit clock stop according to PHY capability if *theplatformallows
*/ if (priv->plat->flags & STMMAC_FLAG_EN_TX_LPI_CLK_PHY_CAP)
priv->tx_lpi_clk_stop = tx_clk_stop;
/* Try to cnfigure the hardware timer. */
ret = stmmac_set_lpi_mode(priv, priv->hw, STMMAC_LPI_TIMER,
priv->tx_lpi_clk_stop, priv->tx_lpi_timer);
if (ret) { /* Hardware timer mode not supported, or value out of range. *FallbacktousingsoftwareLPImode
*/
priv->eee_sw_timer_en = true;
stmmac_restart_sw_lpi_timer(priv);
}
fwnode = priv->plat->port_node; if (!fwnode)
fwnode = dev_fwnode(priv->device);
if (fwnode)
phy_fwnode = fwnode_get_phy_node(fwnode); else
phy_fwnode = NULL;
/* Some DT bindings do not set-up the PHY handle. Let's try to *manuallyparseit
*/ if (!phy_fwnode || IS_ERR(phy_fwnode)) { int addr = priv->plat->phy_addr; struct phy_device *phydev;
if (addr < 0) {
netdev_err(priv->dev, "no phy found\n"); return -ENODEV;
}
phydev = mdiobus_get_phy(priv->mii, addr); if (!phydev) {
netdev_err(priv->dev, "no phy at addr %d\n", addr); return -ENODEV;
}
ret = phylink_connect_phy(priv->phylink, phydev);
} else {
fwnode_handle_put(phy_fwnode);
ret = phylink_fwnode_phy_connect(priv->phylink, fwnode, 0);
}
if (ret == 0) { struct ethtool_keee eee;
/* Configure phylib's copy of the LPI timer. Normally, *phylink_config.lpi_timer_defaultwoulddothis,butthereis *achancethatuserspacecouldchangetheeee_timersetting *viasysfsbeforethefirstopen.Thus,preserveexisting *behaviour.
*/ if (!phylink_ethtool_get_eee(priv->phylink, &eee)) {
eee.tx_lpi_timer = priv->tx_lpi_timer;
phylink_ethtool_set_eee(priv->phylink, &eee);
}
}
if (!priv->plat->pmt) { struct ethtool_wolinfo wol = { .cmd = ETHTOOL_GWOL };
/* Stmmac always requires an RX clock for hardware initialization */
config->mac_requires_rxc = true;
if (!(priv->plat->flags & STMMAC_FLAG_RX_CLK_RUNS_IN_LPI))
config->eee_rx_clk_stop_enable = true;
/* Set the default transmit clock stop bit based on the platform glue */
priv->tx_lpi_clk_stop = priv->plat->flags &
STMMAC_FLAG_EN_TX_LPI_CLOCKGATING;
mdio_bus_data = priv->plat->mdio_bus_data; if (mdio_bus_data)
config->default_an_inband = mdio_bus_data->default_an_inband;
/* Get the PHY interface modes (at the PHY end of the link) that *aresupportedbytheplatform.
*/ if (priv->plat->get_interfaces)
priv->plat->get_interfaces(priv, priv->plat->bsp_priv,
config->supported_interfaces);
/* Set the platform/firmware specified interface mode if the *supportedinterfaceshavenotalreadybeenprovidedusing *phy_interfaceasalastresort.
*/ if (phy_interface_empty(config->supported_interfaces))
__set_bit(priv->plat->phy_interface,
config->supported_interfaces);
/* If we have an xpcs, it defines which PHY interfaces are supported. */ if (priv->hw->xpcs)
pcs = xpcs_to_phylink_pcs(priv->hw->xpcs); else
pcs = priv->hw->phylink_pcs;
if (pcs)
phy_interface_or(config->supported_interfaces,
config->supported_interfaces,
pcs->supported_interfaces);
if (priv->dma_cap.eee) { /* Assume all supported interfaces also support LPI */
memcpy(config->lpi_interfaces, config->supported_interfaces, sizeof(config->lpi_interfaces));
/* All full duplex speeds above 100Mbps are supported */
config->lpi_capabilities = ~(MAC_1000FD - 1) | MAC_100FD;
config->lpi_timer_default = eee_timer * 1000;
config->eee_enabled_default = true;
}
fwnode = priv->plat->port_node; if (!fwnode)
fwnode = dev_fwnode(priv->device);
phylink = phylink_create(config, fwnode, priv->plat->phy_interface,
&stmmac_phylink_mac_ops); if (IS_ERR(phylink)) return PTR_ERR(phylink);
/* struct stmmac_xdp_buff is using cb field (maximum size of 24 bytes) *instructxdp_buff_xsktostashdriverspecificinformation.Thus, *usethismacrotomakesurenosizeviolations.
*/
XSK_CHECK_PRIV_TYPE(struct stmmac_xdp_buff);
for (i = 0; i < dma_conf->dma_rx_size; i++) { struct stmmac_rx_buffer *buf;
dma_addr_t dma_addr; struct dma_desc *p;
if (priv->extend_desc)
p = (struct dma_desc *)(rx_q->dma_erx + i); else
p = rx_q->dma_rx + i;
buf = &rx_q->buf_pool[i];
buf->xdp = xsk_buff_alloc(rx_q->xsk_pool); if (!buf->xdp) return -ENOMEM;
if (rx_q->xsk_pool) { /* RX XDP ZC buffer pool may not be populated, e.g. *xdpsockTX-only.
*/
stmmac_alloc_rx_buffers_zc(priv, dma_conf, queue);
} else {
ret = stmmac_alloc_rx_buffers(priv, dma_conf, queue, flags); if (ret < 0) return -ENOMEM;
}
/* Setup the chained descriptor addresses */ if (priv->mode == STMMAC_CHAIN_MODE) { if (priv->extend_desc)
stmmac_mode_init(priv, rx_q->dma_erx,
rx_q->dma_rx_phy,
dma_conf->dma_rx_size, 1); else
stmmac_mode_init(priv, rx_q->dma_rx,
rx_q->dma_rx_phy,
dma_conf->dma_rx_size, 0);
}
if (rxfifosz == 0)
rxfifosz = priv->dma_cap.rx_fifo_size; if (txfifosz == 0)
txfifosz = priv->dma_cap.tx_fifo_size;
/* Split up the shared Tx/Rx FIFO memory on DW QoS Eth and DW XGMAC */ if (priv->plat->has_gmac4 || priv->plat->has_xgmac) {
rxfifosz /= rx_channels_count;
txfifosz /= tx_channels_count;
}
/* We are sharing with slow path and stop XSK TX desc submission when *availableTXringislessthanthreshold.
*/ if (unlikely(stmmac_tx_avail(priv, queue) < STMMAC_TX_XSK_AVAIL) ||
!netif_carrier_ok(priv->dev)) {
work_done = false; break;
}
/* To return XDP buffer to XSK pool, we simple call *xsk_tx_completed(),sowedon'tneedtofillup *'buf'and'xdpf'.
*/
tx_q->tx_skbuff_dma[entry].buf = 0;
tx_q->xdpf[entry] = NULL;
if (tx_desc) {
stmmac_flush_tx_descriptors(priv, queue);
xsk_tx_release(pool);
}
/* Return true if all of the 3 conditions are met *a)TXBudgetisstillavailable *b)work_done=truewhenXSKTXdescpeekisempty(nomore *pendingXSKTXfortransmission)
*/ return !!budget && work_done;
}
if (priv->extend_desc)
p = (struct dma_desc *)(tx_q->dma_etx + entry); elseif (tx_q->tbs & STMMAC_TBS_AVAIL)
p = &tx_q->dma_entx[entry].basic; else
p = tx_q->dma_tx + entry;
status = stmmac_tx_status(priv, &priv->xstats, p, priv->ioaddr); /* Check if the descriptor is owned by the DMA */ if (unlikely(status & tx_dma_own)) break;
count++;
/* Make sure descriptor fields are read after reading *theownbit.
*/
dma_rmb();
/* Just consider the last segment and ...*/ if (likely(!(status & tx_not_ls))) { /* ... verify the status error condition */ if (unlikely(status & tx_err)) {
tx_errors++; if (unlikely(status & tx_err_bump_tc))
stmmac_bump_dma_threshold(priv, queue);
} else {
tx_packets++;
} if (skb) {
stmmac_get_tx_hwtstamp(priv, p, skb);
} elseif (tx_q->xsk_pool &&
xp_tx_metadata_enabled(tx_q->xsk_pool)) { struct stmmac_xsk_tx_complete tx_compl = {
.priv = priv,
.desc = p,
};
if (tx_q->xsk_frames_done)
xsk_tx_completed(tx_q->xsk_pool, tx_q->xsk_frames_done);
if (xsk_uses_need_wakeup(tx_q->xsk_pool))
xsk_set_tx_need_wakeup(tx_q->xsk_pool);
/* For XSK TX, we try to send as many as possible.
* If XSK work done (XSK TX desc empty and budget still
* available), return "budget - 1" to reenable TX IRQ.
* Else, return "budget" to make NAPI continue polling.
*/
work_done = stmmac_xdp_xmit_zc(priv, queue,
STMMAC_XSK_TX_BUDGET_MAX);
if (work_done)
xmits = budget - 1;
else
xmits = budget;
}
if (priv->eee_sw_timer_en && !priv->tx_path_in_lpi_mode)
stmmac_restart_sw_lpi_timer(priv);
/* We still have pending packets, let's call for a new scheduling */
if (tx_q->dirty_tx != tx_q->cur_tx)
*pending_packets = true;
/* Combine decisions from TX clean and XSK TX */
return max(count, xmits);
}
/**
* stmmac_tx_err - to manage the tx error
* @priv: driver private structure
* @chan: channel index
* Description: it cleans the descriptors and restarts the transmission
* in case of transmission errors.
*/
static void stmmac_tx_err(struct stmmac_priv *priv, u32 chan)
{
struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[chan];
/**
* stmmac_dma_interrupt - DMA ISR
* @priv: driver private structure
* Description: this is the DMA ISR. It is called by the main ISR.
* It calls the dwmac dma routine and schedule poll method in case of some
* work can be done.
*/
static void stmmac_dma_interrupt(struct stmmac_priv *priv)
{
u32 tx_channel_count = priv->plat->tx_queues_to_use;
u32 rx_channel_count = priv->plat->rx_queues_to_use;
u32 channels_to_check = tx_channel_count > rx_channel_count ?
tx_channel_count : rx_channel_count;
u32 chan;
int status[MAX_T(u32, MTL_MAX_TX_QUEUES, MTL_MAX_RX_QUEUES)];
/* Make sure we never check beyond our status buffer. */
if (WARN_ON_ONCE(channels_to_check > ARRAY_SIZE(status)))
channels_to_check = ARRAY_SIZE(status);
for (chan = 0; chan < tx_channel_count; chan++) {
if (unlikely(status[chan] & tx_hard_error_bump_tc)) {
/* Try to bump up the dma threshold on this failure */
stmmac_bump_dma_threshold(priv, chan);
} else if (unlikely(status[chan] == tx_hard_error)) {
stmmac_tx_err(priv, chan);
}
}
}
/**
* stmmac_mmc_setup: setup the Mac Management Counters (MMC)
* @priv: driver private structure
* Description: this masks the MMC irq, in fact, the counters are managed in SW.
*/
static void stmmac_mmc_setup(struct stmmac_priv *priv)
{
unsigned int mode = MMC_CNTRL_RESET_ON_READ | MMC_CNTRL_COUNTER_RESET |
MMC_CNTRL_PRESET | MMC_CNTRL_FULL_HALF_PRESET;
stmmac_mmc_intr_all_mask(priv, priv->mmcaddr);
if (priv->dma_cap.rmon) {
stmmac_mmc_ctrl(priv, priv->mmcaddr, mode);
memset(&priv->mmc, 0, sizeof(struct stmmac_counters));
} else
netdev_info(priv->dev, "No MAC Management Counters available\n");
}
/**
* stmmac_get_hw_features - get MAC capabilities from the HW cap. register.
* @priv: driver private structure
* Description:
* new GMAC chip generations have a new register to indicate the
* presence of the optional feature/functions.
* This can be also used to override the value passed through the
* platform and necessary for old MAC10/100 and GMAC chips.
*/
static int stmmac_get_hw_features(struct stmmac_priv *priv)
{
return stmmac_get_hw_feature(priv, priv->ioaddr, &priv->dma_cap) == 0;
}
/**
* stmmac_check_ether_addr - check if the MAC addr is valid
* @priv: driver private structure
* Description:
* it is to verify if the MAC address is valid, in case of failures it
* generates a random MAC address
*/
static void stmmac_check_ether_addr(struct stmmac_priv *priv)
{
u8 addr[ETH_ALEN];
if (!is_valid_ether_addr(priv->dev->dev_addr)) {
stmmac_get_umac_addr(priv, priv->hw, addr, 0);
if (is_valid_ether_addr(addr))
eth_hw_addr_set(priv->dev, addr);
else
eth_hw_addr_random(priv->dev);
dev_info(priv->device, "device MAC address %pM\n",
priv->dev->dev_addr);
}
}
/**
* stmmac_init_dma_engine - DMA init.
* @priv: driver private structure
* Description:
* It inits the DMA invoking the specific MAC/GMAC callback.
* Some DMA parameters can be passed from the platform;
* in case of these are not passed a default is kept for the MAC or GMAC.
*/
static int stmmac_init_dma_engine(struct stmmac_priv *priv)
{
u32 rx_channels_count = priv->plat->rx_queues_to_use;
u32 tx_channels_count = priv->plat->tx_queues_to_use;
u32 dma_csr_ch = max(rx_channels_count, tx_channels_count);
struct stmmac_rx_queue *rx_q;
struct stmmac_tx_queue *tx_q;
u32 chan = 0;
int ret = 0;
ch = &priv->channel[tx_q->queue_index];
napi = tx_q->xsk_pool ? &ch->rxtx_napi : &ch->tx_napi;
/* Arm timer only if napi is not already scheduled.
* Try to cancel any timer if napi is scheduled, timer will be armed
* again in the next scheduled napi.
*/
if (unlikely(!napi_is_scheduled(napi)))
hrtimer_start(&tx_q->txtimer,
STMMAC_COAL_TIMER(tx_coal_timer),
HRTIMER_MODE_REL);
else
hrtimer_try_to_cancel(&tx_q->txtimer);
}
/**
* stmmac_tx_timer - mitigation sw timer for tx.
* @t: data pointer
* Description:
* This is the timer handler to directly invoke the stmmac_tx_clean.
*/
static enum hrtimer_restart stmmac_tx_timer(struct hrtimer *t)
{
struct stmmac_tx_queue *tx_q = container_of(t, struct stmmac_tx_queue, txtimer);
struct stmmac_priv *priv = tx_q->priv_data;
struct stmmac_channel *ch;
struct napi_struct *napi;
ch = &priv->channel[tx_q->queue_index];
napi = tx_q->xsk_pool ? &ch->rxtx_napi : &ch->tx_napi;
if (likely(napi_schedule_prep(napi))) {
unsigned long flags;
/**
* stmmac_mac_config_rx_queues_routing - Configure RX Queue Routing
* @priv: driver private structure
* Description: It is used for configuring the RX queue routing
*/
static void stmmac_mac_config_rx_queues_routing(struct stmmac_priv *priv)
{
u32 rx_queues_count = priv->plat->rx_queues_to_use;
u32 queue;
u8 packet;
for (queue = 0; queue < rx_queues_count; queue++) {
/* no specific packet type routing specified for the queue */
if (priv->plat->rx_queues_cfg[queue].pkt_route == 0x0)
continue;
/* Configure CBS in AVB TX queues */
if (tx_queues_count > 1)
stmmac_configure_cbs(priv);
/* Map RX MTL to DMA channels */
stmmac_rx_queue_dma_chan_map(priv);
/* Enable MAC RX Queues */
stmmac_mac_enable_rx_queues(priv);
/* Set RX priorities */
if (rx_queues_count > 1)
stmmac_mac_config_rx_queues_prio(priv);
/* Set TX priorities */
if (tx_queues_count > 1)
stmmac_mac_config_tx_queues_prio(priv);
/* Set RX routing */
if (rx_queues_count > 1)
stmmac_mac_config_rx_queues_routing(priv);
/* Receive Side Scaling */
if (rx_queues_count > 1)
stmmac_mac_config_rss(priv);
}
static void stmmac_safety_feat_configuration(struct stmmac_priv *priv)
{
if (priv->dma_cap.asp) {
netdev_info(priv->dev, "Enabling Safety Features\n");
stmmac_safety_feat_config(priv, priv->ioaddr, priv->dma_cap.asp,
priv->plat->safety_feat_cfg);
} else {
netdev_info(priv->dev, "No Safety Features support found\n");
}
}
/**
* stmmac_hw_setup - setup mac in a usable state.
* @dev : pointer to the device structure.
* @ptp_register: register PTP if set
* Description:
* this is the main function to setup the HW in a usable state because the
* dma engine is reset, the core registers are configured (e.g. AXI,
* Checksum features, timers). The DMA is ready to start receiving and
* transmitting.
* Return value:
* 0 on success and an appropriate (-)ve integer as defined in errno.h
* file on failure.
*/
static int stmmac_hw_setup(struct net_device *dev, bool ptp_register)
{
struct stmmac_priv *priv = netdev_priv(dev);
u32 rx_cnt = priv->plat->rx_queues_to_use;
u32 tx_cnt = priv->plat->tx_queues_to_use;
bool sph_en;
u32 chan;
int ret;
/* Make sure RX clock is enabled */
if (priv->hw->phylink_pcs)
phylink_pcs_pre_init(priv->phylink, priv->hw->phylink_pcs);
/* Note that clk_rx_i must be running for reset to complete. This
* clock may also be required when setting the MAC address.
*
* Block the receive clock stop for LPI mode at the PHY in case
* the link is established with EEE mode active.
*/
phylink_rx_clk_stop_block(priv->phylink);
/* DMA initialization and SW reset */
ret = stmmac_init_dma_engine(priv);
if (ret < 0) {
phylink_rx_clk_stop_unblock(priv->phylink);
netdev_err(priv->dev, "%s: DMA engine initialization failed\n",
__func__);
return ret;
}
/* Copy the MAC addr into the HW */
stmmac_set_umac_addr(priv, priv->hw, dev->dev_addr, 0);
phylink_rx_clk_stop_unblock(priv->phylink);
/* PS and related bits will be programmed according to the speed */
if (priv->hw->pcs) {
int speed = priv->plat->mac_port_sel_speed;
/* Initialize Safety Features */
stmmac_safety_feat_configuration(priv);
ret = stmmac_rx_ipc(priv, priv->hw);
if (!ret) {
netdev_warn(priv->dev, "RX IPC Checksum Offload disabled\n");
priv->plat->rx_coe = STMMAC_RX_COE_NONE;
priv->hw->rx_csum = 0;
}
/* Enable the MAC Rx/Tx */
stmmac_mac_set(priv, priv->ioaddr, true);
/* Set the HW DMA mode and the COE */
stmmac_dma_operation_mode(priv);
stmmac_mmc_setup(priv);
if (ptp_register) {
ret = clk_prepare_enable(priv->plat->clk_ptp_ref);
if (ret < 0)
netdev_warn(priv->dev,
"failed to enable PTP reference clock: %pe\n",
ERR_PTR(ret));
}
ret = stmmac_init_ptp(priv);
if (ret == -EOPNOTSUPP)
netdev_info(priv->dev, "PTP not supported by HW\n");
else if (ret)
netdev_warn(priv->dev, "PTP init failed\n");
else if (ptp_register)
(
if (priv->use_riwt) {
java.lang.StringIndexOutOfBoundsException: Index 12 out of bounds for length 12
for (queue = 0; queue < rx_cnt; queue++) {
if (!priv->rx_riwt[queue])
priv->rx_riwt[queue] = DEF_DMA_RIWT;
static void stmmac_free_irq(struct net_device *dev,
enum request_irq_err irq_err, int irq_idx)
{
struct stmmac_priv *priv = netdev_priv(dev);
int j;
switch (irq_err) {
case REQ_IRQ_ERR_ALL:
irq_idx = priv->plat->tx_queues_to_use;
fallthrough;
case REQ_IRQ_ERR_TX:
for (j = irq_idx - 1; j >= 0; j--) {
if (priv->tx_irq[j] > 0) {
irq_set_affinity_hint(priv->tx_irq[j], NULL);
free_irq(priv->tx_irq[j], &priv->dma_conf.tx_queue[j]);
}
}
irq_idx = priv->plat->rx_queues_to_use;
fallthrough;
case REQ_IRQ_ERR_RX:
for (j = irq_idx - 1; j >= 0; j--) {
if (priv->rx_irq[j] > 0) {
irq_set_affinity_hint(priv->rx_irq[j], NULL);
free_irq(priv->rx_irq[j], &priv->dma_conf.rx_queue[j]);
}
}
if (priv->sfty_ue_irq > 0 && priv->sfty_ue_irq != dev->irq)
free_irq(priv->sfty_ue_irq, dev);
fallthrough;
case REQ_IRQ_ERR_SFTY_UE:
if (priv->sfty_ce_irq > 0 && priv->sfty_ce_irq != dev->irq)
free_irq(priv->sfty_ce_irq, dev);
fallthrough;
case REQ_IRQ_ERR_SFTY_CE:
if (priv->lpi_irq > 0 && priv->lpi_irq != dev->irq)
free_irq(priv->lpi_irq, dev);
fallthrough;
case REQ_IRQ_ERR_LPI:
if (priv->wol_irq > 0 && priv->wol_irq != dev->irq)
free_irq(priv->wol_irq, dev);
fallthrough;
case REQ_IRQ_ERR_SFTY:
if (priv->sfty_irq > 0 && priv->sfty_irq != dev->irq)
free_irq(priv->sfty_irq, dev);
fallthrough;
case REQ_IRQ_ERR_WOL:
free_irq(dev->irq, dev);
fallthrough;
case REQ_IRQ_ERR_MAC:
case REQ_IRQ_ERR_NO:
/* If MAC IRQ request error, no more IRQ to free */
break;
}
}
static int stmmac_request_irq_multi_msi(struct net_device *dev)
{
struct stmmac_priv *priv = netdev_priv(dev);
enum request_irq_err irq_err;
int irq_idx = 0;
char *int_name;
int ret;
int i;
/* For common interrupt */
int_name = priv->int_name_mac;
sprintf(int_name, "%s:%s", dev->name, "mac");
ret = request_irq(dev->irq, stmmac_mac_interrupt, 0, int_name, dev);
if (unlikely(ret < 0)) {
netdev_err(priv->dev,
"%s: alloc mac MSI %d (error: %d)\n",
__func__, dev->irq, ret);
irq_err = REQ_IRQ_ERR_MAC;
goto irq_error;
}
/* Request the Wake IRQ in case of another line
* is used for WoL
*/
priv->wol_irq_disabled = true;
if (priv->wol_irq > 0 && priv->wol_irq != dev->irq) {
int_name = priv->int_name_wol;
sprintf(int_name, "%s:%s", dev->name, "wol");
ret = request_irq(priv->wol_irq,
stmmac_mac_interrupt, 0, int_name, dev);
if (unlikely(ret < 0)) {
netdev_err(priv->dev,
"%s: alloc wol MSI %d (error: %d)\n",
__func__, priv->wol_irq, ret);
irq_err = REQ_IRQ_ERR_WOL;
goto irq_error;
}
}
/* Request the LPI IRQ in case of another line
* is used for LPI
*/
if (priv->lpi_irq > 0 && priv->lpi_irq != dev->irq) {
int_name = priv->int_name_lpi;
sprintf(int_name, "%s:%s", dev->name, "lpi");
ret = request_irq(priv->lpi_irq,
stmmac_mac_interrupt, 0, int_name, dev);
if (unlikely(ret < 0)) {
netdev_err(priv->dev,
"%s: alloc lpi MSI %d (error: %d)\n",
__func__, priv->lpi_irq, ret);
irq_err = REQ_IRQ_ERR_LPI;
goto irq_error;
}
}
/* Request the common Safety Feature Correctible/Uncorrectible
* Error line in case of another line is used
*/
if (priv->sfty_irq > 0 && priv->sfty_irq != dev->irq) {
int_name = priv->int_name_sfty;
sprintf(int_name, "%s:%s", dev->name, "safety");
ret = request_irq(priv->sfty_irq, stmmac_safety_interrupt, 0, int_name, dev);
if (unlikely(ret < 0)) {
netdev_err(priv->dev,
"%s: alloc sfty MSI %d (error: %d)\n",
__func__, priv->sfty_irq, ret);
irq_err = REQ_IRQ_ERR_SFTY;
goto irq_error;
}
}
/* Request the Safety Feature Correctible Error line in
* case of another line is used
*/
if (priv->sfty_ce_irq > 0 && priv->sfty_ce_irq != dev->irq) {
int_name = priv->int_name_sfty_ce;
sprintf(int_name, "%s:%s", dev->name, "safety-ce");
ret = request_irq(priv->sfty_ce_irq,
stmmac_safety_interrupt, 0, int_name, dev);
if (unlikely(ret < 0)) {
netdev_err(priv->dev,
"%s: alloc sfty ce MSI %d (error: %d)\n",
__func__, priv->sfty_ce_irq, ret);
irq_err = REQ_IRQ_ERR_SFTY_CE;
goto irq_error;
}
}
/* Request the Safety Feature Uncorrectible Error line in
* case of another line is used
*/
if (priv->sfty_ue_irq > 0 && priv->sfty_ue_irq != dev->irq) {
int_name = priv->int_name_sfty_ue;
sprintf(int_name, "%s:%s", dev->name, "safety-ue");
ret = request_irq(priv->sfty_ue_irq,
stmmac_safety_interrupt, 0, int_name, dev);
if (unlikely(ret < 0)) {
netdev_err(priv->dev,
"%s: alloc sfty ue MSI %d (error: %d)\n",
__func__, priv->sfty_ue_irq, ret);
irq_err = REQ_IRQ_ERR_SFTY_UE;
goto irq_error;
}
}
/* Request Rx MSI irq */
for (i = 0; i < priv->plat->rx_queues_to_use; i++) {
if (i >= MTL_MAX_RX_QUEUES)
break;
if (priv->rx_irq[i] == 0)
continue;
static int stmmac_request_irq_single(struct net_device *dev)
{
struct stmmac_priv *priv = netdev_priv(dev);
enum request_irq_err irq_err;
int ret;
ret = request_irq(dev->irq, stmmac_interrupt,
IRQF_SHARED, dev->name, dev);
if (unlikely(ret < 0)) {
netdev_err(priv->dev,
"%s: ERROR: allocating the IRQ %d (error: %d)\n",
__func__, dev->irq, ret);
irq_err = REQ_IRQ_ERR_MAC;
goto irq_error;
}
/* Request the Wake IRQ in case of another line
* is used for WoL
*/
priv->wol_irq_disabled = true;
if (priv->wol_irq > 0 && priv->wol_irq != dev->irq) {
ret = request_irq(priv->wol_irq, stmmac_interrupt,
IRQF_SHARED, dev->name, dev);
if (unlikely(ret < 0)) {
netdev_err(priv->dev,
"%s: ERROR: allocating the WoL IRQ %d (%d)\n",
__func__, priv->wol_irq, ret);
irq_err = REQ_IRQ_ERR_WOL;
goto irq_error;
}
}
/* Request the IRQ lines */
if (priv->lpi_irq > 0 && priv->lpi_irq != dev->irq) {
ret = request_irq(priv->lpi_irq, stmmac_interrupt,
IRQF_SHARED, dev->name, dev);
if (unlikely(ret < 0)) {
netdev_err(priv->dev,
"%s: ERROR: allocating the LPI IRQ %d (%d)\n",
__func__, priv->lpi_irq, ret);
irq_err = REQ_IRQ_ERR_LPI;
goto irq_error;
}
}
/* Request the common Safety Feature Correctible/Uncorrectible
* Error line in case of another line is used
*/
if (priv->sfty_irq > 0 && priv->sfty_irq != dev->irq) {
ret = request_irq(priv->sfty_irq, stmmac_safety_interrupt,
IRQF_SHARED, dev->name, dev);
if (unlikely(ret < 0)) {
netdev_err(priv->dev,
"%s: ERROR: allocating the sfty IRQ %d (%d)\n",
__func__, priv->sfty_irq, ret);
irq_err = REQ_IRQ_ERR_SFTY;
goto irq_error;
}
}
static int stmmac_request_irq(struct net_device *dev)
{
struct stmmac_priv *priv = netdev_priv(dev);
int ret;
/* Request the IRQ lines */
if (priv->plat->flags & STMMAC_FLAG_MULTI_MSI_EN)
ret = stmmac_request_irq_multi_msi(dev);
else
ret = stmmac_request_irq_single(dev);
return ret;
}
/**
* stmmac_setup_dma_desc - Generate a dma_conf and allocate DMA queue
* @priv: driver private structure
* @mtu: MTU to setup the dma queue and buf with
* Description: Allocate and generate a dma_conf based on the provided MTU.
* Allocate the Tx/Rx DMA queue and init them.
* Return value:
* the dma_conf allocated struct on success and an appropriate ERR_PTR on failure.
*/
static struct stmmac_dma_conf *
stmmac_setup_dma_desc(struct stmmac_priv *priv, unsigned int mtu)
{
struct stmmac_dma_conf *dma_conf;
int chan, bfsize, ret;
if (bfsize < BUF_SIZE_16KiB)
bfsize = stmmac_set_bfsize(mtu, 0);
dma_conf->dma_buf_sz = bfsize;
/* Chose the tx/rx size from the already defined one in the
* priv struct. (if defined)
*/
dma_conf->dma_tx_size = priv->dma_conf.dma_tx_size;
dma_conf->dma_rx_size = priv->dma_conf.dma_rx_size;
if (!dma_conf->dma_tx_size)
dma_conf->dma_tx_size = DMA_DEFAULT_TX_SIZE;
if (!dma_conf->dma_rx_size)
dma_conf->dma_rx_size = DMA_DEFAULT_RX_SIZE;
/* Earlier check for TBS */
for (chan = 0; chan < priv->plat->tx_queues_to_use; chan++) {
struct stmmac_tx_queue *tx_q = &dma_conf->tx_queue[chan];
int tbs_en = priv->plat->tx_queues_cfg[chan].tbs_en;
/* Setup per-TXQ tbs flag before TX descriptor alloc */
tx_q->tbs |= tbs_en ? STMMAC_TBS_AVAIL : 0;
}
ret = alloc_dma_desc_resources(priv, dma_conf);
if (ret < 0) {
netdev_err(priv->dev, "%s: DMA descriptors allocation failed\n",
__func__);
goto alloc_error;
}
ret = init_dma_desc_rings(priv->dev, dma_conf, GFP_KERNEL);
if (ret < 0) {
netdev_err(priv->dev, "%s: DMA descriptors initialization failed\n",
__func__);
goto init_error;
}
/**
* __stmmac_open - open entry point of the driver
* @dev : pointer to the device structure.
* @dma_conf : structure to take the dma data
* Description:
* This function is the open entry point of the driver.
* Return value:
* 0 on success and an appropriate (-)ve integer as defined in errno.h
* file on failure.
*/
static int __stmmac_open(struct net_device *dev,
struct stmmac_dma_conf *dma_conf)
{
struct stmmac_priv *priv = netdev_priv(dev);
int mode = priv->plat->phy_interface;
u32 chan;
int ret;
/* Initialise the tx lpi timer, converting from msec to usec */
if (!priv->tx_lpi_timer)
priv->tx_lpi_timer = eee_timer * 1000;
ret = pm_runtime_resume_and_get(priv->device);
if (ret < 0)
return ret;
if ((!priv->hw->xpcs ||
xpcs_get_an_mode(priv->hw->xpcs, mode) != DW_AN_C73)) {
ret = stmmac_init_phy(dev);
if (ret) {
netdev_err(priv->dev,
"%s: Cannot attach to PHY (error: %d)\n",
__func__, ret);
goto init_phy_error;
}
}
for (int i = 0; i < MTL_MAX_TX_QUEUES; i++)
if (priv->dma_conf.tx_queue[i].tbs & STMMAC_TBS_EN)
dma_conf->tx_queue[i].tbs = priv->dma_conf.tx_queue[i].tbs;
memcpy(&priv->dma_conf, dma_conf, sizeof(*dma_conf));
stmmac_reset_queues_param(priv);
if (!(priv->plat->flags & STMMAC_FLAG_SERDES_UP_AFTER_PHY_LINKUP) &&
priv->plat->serdes_powerup) {
ret = priv->plat->serdes_powerup(dev, priv->plat->bsp_priv);
if (ret < 0) {
netdev_err(priv->dev, "%s: Serdes powerup failed\n",
__func__);
goto init_error;
}
}
ret = stmmac_hw_setup(dev, true);
if (ret < 0) {
netdev_err(priv->dev, "%s: Hw setup failed\n", __func__);
goto init_error;
}
stmmac_init_coalesce(priv);
phylink_start(priv->phylink);
/* We may have called phylink_speed_down before */
phylink_speed_up(priv->phylink);
ret = stmmac_request_irq(dev);
if (ret)
goto irq_error;
static int stmmac_open(struct net_device *dev)
{
struct stmmac_priv *priv = netdev_priv(dev);
struct stmmac_dma_conf *dma_conf;
int ret;
dma_conf = stmmac_setup_dma_desc(priv, dev->mtu);
if (IS_ERR(dma_conf))
return PTR_ERR(dma_conf);
ret = __stmmac_open(dev, dma_conf);
if (ret)
free_dma_desc_resources(priv, dma_conf);
kfree(dma_conf);
return ret;
}
/**
* stmmac_release - close entry point of the driver
* @dev : device pointer.
* Description:
* This is the stop entry point of the driver.
*/
static int stmmac_release(struct net_device *dev)
{
struct stmmac_priv *priv = netdev_priv(dev);
u32 chan;
if (device_may_wakeup(priv->device))
phylink_speed_down(priv->phylink, false);
/* Stop and disconnect the PHY */
phylink_stop(priv->phylink);
phylink_disconnect_phy(priv->phylink);
stmmac_disable_all_queues(priv);
for (chan = 0; chan < priv->plat->tx_queues_to_use; chan++)
hrtimer_cancel(&priv->dma_conf.tx_queue[chan].txtimer);
netif_tx_disable(dev);
/* Free the IRQ lines */
stmmac_free_irq(dev, REQ_IRQ_ERR_ALL, 0);
/* Stop TX/RX DMA and clear the descriptors */
stmmac_stop_all_dma(priv);
/* Release and free the Rx/Tx resources */
free_dma_desc_resources(priv, &priv->dma_conf);
/* Powerdown Serdes if there is */
if (priv->plat->serdes_powerdown)
priv->plat->serdes_powerdown(dev, priv->plat->bsp_priv);
stmmac_release_ptp(priv);
if (stmmac_fpe_supported(priv))
ethtool_mmsv_stop(&priv->fpe_cfg.mmsv);
/**
* stmmac_tso_allocator - close entry point of the driver
* @priv: driver private structure
* @des: buffer start address
* @total_len: total length to fill in descriptors
* @last_segment: condition for the last descriptor
* @queue: TX queue index
* Description:
* This function fills descriptor and request new descriptors according to
* buffer length to fill
*/
static void stmmac_tso_allocator(struct stmmac_priv *priv, dma_addr_t des,
int total_len, bool last_segment, u32 queue)
{
struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue];
struct dma_desc *desc;
u32 buff_size;
int tmp_len;
static void stmmac_flush_tx_descriptors(struct stmmac_priv *priv, int queue)
{
struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue];
int desc_size;
if (likely(priv->extend_desc))
desc_size = sizeof(struct dma_extended_desc);
else if (tx_q->tbs & STMMAC_TBS_AVAIL)
desc_size = sizeof(struct dma_edesc);
else
desc_size = sizeof(struct dma_desc);
/* The own bit must be the latest setting done when prepare the
* descriptor and then barrier is needed to make sure that
* all is coherent before granting the DMA engine.
*/
wmb();
/**
* stmmac_tso_xmit - Tx entry point of the driver for oversized frames (TSO)
* @skb : the socket buffer
* @dev : device pointer
* Description: this is the transmit function that is called on TSO frames
* (support available on GMAC4 and newer chips).
* Diagram below show the ring programming in case of TSO frames:
*
* First Descriptor
* --------
* | DES0 |---> buffer1 = L2/L3/L4 header
* | DES1 |---> can be used as buffer2 for TCP Payload if the DMA AXI address
* | | width is 32-bit, but we never use it.
* | | Also can be used as the most-significant 8-bits or 16-bits of
* | | buffer1 address pointer if the DMA AXI address width is 40-bit
* | | or 48-bit, and we always use it.
* | DES2 |---> buffer1 len
* | DES3 |---> must set TSE, TCP hdr len-> [22:19]. TCP payload len [17:0]
* --------
* --------
* | DES0 |---> buffer1 = TCP Payload (can continue on next descr...)
* | DES1 |---> same as the First Descriptor
* | DES2 |---> buffer1 len
* | DES3 |
* --------
* |
* ...
* |
* --------
* | DES0 |---> buffer1 = Split TCP Payload
* | DES1 |---> same as the First Descriptor
* | DES2 |---> buffer1 len
* | DES3 |
* --------
*
* mss is fixed when enable tso, so w/o programming the TDES3 ctx field.
*/
static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct dma_desc *desc, *first, *mss_desc = NULL;
struct stmmac_priv *priv = netdev_priv(dev);
unsigned int first_entry, tx_packets;
struct stmmac_txq_stats *txq_stats;
struct stmmac_tx_queue *tx_q;
u32 pay_len, mss, queue;
int i, first_tx, nfrags;
u8 proto_hdr_len, hdr;
dma_addr_t des;
bool set_ic;
/* Always insert VLAN tag to SKB payload for TSO frames.
*
* Never insert VLAN tag by HW, since segments splited by
* TSO engine will be un-tagged by mistake.
*/
if (skb_vlan_tag_present(skb)) {
skb = __vlan_hwaccel_push_inside(skb);
if (unlikely(!skb)) {
priv->xstats.tx_dropped++;
return NETDEV_TX_OK;
}
}
/* Desc availability based on threshold should be enough safe */
if (unlikely(stmmac_tx_avail(priv, queue) <
(((skb->len - proto_hdr_len) / TSO_MAX_BUFF_SIZE + 1)))) {
if (!netif_tx_queue_stopped(netdev_get_tx_queue(dev, queue))) {
netif_tx_stop_queue(netdev_get_tx_queue(priv->dev,
queue));
/* This is a hard error, log it. */
netdev_err(priv->dev,
"%s: Tx Ring full when queue awake\n",
__func__);
}
return NETDEV_TX_BUSY;
}
pay_len = skb_headlen(skb) - proto_hdr_len; /* no frags */
mss = skb_shinfo(skb)->gso_size;
/* set new MSS value if needed */
if (mss != tx_q->mss) {
if (tx_q->tbs & STMMAC_TBS_AVAIL)
mss_desc = &tx_q->dma_entx[tx_q->cur_tx].basic;
else
mss_desc = &tx_q->dma_tx[tx_q->cur_tx];
if (tx_q->tbs & STMMAC_TBS_AVAIL)
desc = &tx_q->dma_entx[first_entry].basic;
else
desc = &tx_q->dma_tx[first_entry];
first = desc;
/* first descriptor: fill Headers on Buf1 */
des = dma_map_single(priv->device, skb->data, skb_headlen(skb),
DMA_TO_DEVICE);
if (dma_mapping_error(priv->device, des))
goto dma_map_err;
/* In case two or more DMA transmit descriptors are allocated for this
* non-paged SKB data, the DMA buffer address should be saved to
* tx_q->tx_skbuff_dma[].buf corresponding to the last descriptor,
* and leave the other tx_q->tx_skbuff_dma[].buf as NULL to guarantee
* that stmmac_tx_clean() does not unmap the entire DMA buffer too early
* since the tail areas of the DMA buffer can be accessed by DMA engine
* sooner or later.
* By saving the DMA buffer address to tx_q->tx_skbuff_dma[].buf
* corresponding to the last descriptor, stmmac_tx_clean() will unmap
* this DMA buffer right after the DMA engine completely finishes the
* full buffer transmission.
*/
tx_q->tx_skbuff_dma[tx_q->cur_tx].buf = des;
tx_q->tx_skbuff_dma[tx_q->cur_tx].len = skb_headlen(skb);
tx_q->tx_skbuff_dma[tx_q->cur_tx].map_as_page = false;
tx_q->tx_skbuff_dma[tx_q->cur_tx].buf_type = STMMAC_TXBUF_T_SKB;
/* Prepare fragments */
for (i = 0; i < nfrags; i++) {
const skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
des = skb_frag_dma_map(priv->device, frag, 0,
skb_frag_size(frag),
DMA_TO_DEVICE);
if (dma_mapping_error(priv->device, des))
goto dma_map_err;
stmmac_tso_allocator(priv, des, skb_frag_size(frag),
(i == nfrags - 1), queue);
/* Only the last descriptor gets to point to the skb. */
tx_q->tx_skbuff[tx_q->cur_tx] = skb;
tx_q->tx_skbuff_dma[tx_q->cur_tx].buf_type = STMMAC_TXBUF_T_SKB;
/* We've used all descriptors we need for this skb, however, *advancecur_txsothatitreferencesafreshdescriptor. *ndo_start_xmitwillfillthisdescriptorthenexttimeit's *calledandstmmac_tx_cleanmaycleanuptothisdescriptor.
*/
tx_q->cur_tx = STMMAC_GET_ENTRY(tx_q->cur_tx, priv->dma_conf.dma_tx_size);
u64_stats_update_begin(&txq_stats->q_syncp);
u64_stats_add(&txq_stats->q.tx_bytes, skb->len);
u64_stats_inc(&txq_stats->q.tx_tso_frames);
u64_stats_add(&txq_stats->q.tx_tso_nfrags, nfrags); if (set_ic)
u64_stats_inc(&txq_stats->q.tx_set_ic_bit);
u64_stats_update_end(&txq_stats->q_syncp);
if (priv->sarc_type)
stmmac_set_desc_sarc(priv, first, priv->sarc_type);
if (unlikely((skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) &&
priv->hwts_tx_en)) { /* declare that device is doing timestamping */
skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
stmmac_enable_tx_timestamp(priv, first);
}
/* Complete the first descriptor before granting the DMA */
stmmac_prepare_tso_tx_desc(priv, first, 1, proto_hdr_len, 0, 1,
tx_q->tx_skbuff_dma[first_entry].last_segment,
hdr / 4, (skb->len - proto_hdr_len));
/* If context desc is used to change MSS */ if (mss_desc) { /* Make sure that first descriptor has been completely *written,includingitsownbit.ThisisbecauseMSSis *actuallybeforefirstdescriptor,soweneedtomake *surethatMSS'sownbitisthelastthingwritten.
*/
dma_wmb();
stmmac_set_tx_owner(priv, mss_desc);
}
if (netif_msg_pktdata(priv)) {
pr_info("%s: curr=%d dirty=%d f=%d, e=%d, f_p=%p, nfrags %d\n",
__func__, tx_q->cur_tx, tx_q->dirty_tx, first_entry,
tx_q->cur_tx, first, nfrags);
pr_info(">>> frame to be transmitted: ");
print_pkt(skb->data, skb_headlen(skb));
}
if (unlikely(stmmac_tx_avail(priv, queue) < nfrags + 1)) { if (!netif_tx_queue_stopped(netdev_get_tx_queue(dev, queue))) {
netif_tx_stop_queue(netdev_get_tx_queue(priv->dev,
queue)); /* This is a hard error, log it. */
netdev_err(priv->dev, "%s: Tx Ring full when queue awake\n",
__func__);
} return NETDEV_TX_BUSY;
}
/* Check if VLAN can be inserted by HW */
has_vlan = stmmac_vlan_insert(priv, skb, tx_q);
csum_insertion = (skb->ip_summed == CHECKSUM_PARTIAL); /* DWMAC IPs can be synthesized to support tx coe only for a few tx *queues.Inthatcase,checksumoffloadingforthosequeuesthatdon't *supporttxcoeneedstofallbacktosoftwarechecksumcalculation. * *Packetsthatwon'ttriggertheCOEe.g.mostDSA-taggedpacketswill *alsohavetobechecksummedinsoftware.
*/ if (csum_insertion &&
(priv->plat->tx_queues_cfg[queue].coe_unsupported ||
!stmmac_has_ip_ethertype(skb))) { if (unlikely(skb_checksum_help(skb))) goto dma_map_err;
csum_insertion = !csum_insertion;
}
if (has_vlan)
stmmac_set_desc_vlan(priv, first, STMMAC_VLAN_INSERT);
enh_desc =priv->plat->enh_desc; /* To program the descriptors according to the size of the frame */ if (enh_desc)
is_jumbo =stmmac_is_jumbo_frm(priv, skb->len, enh_desc);
if (unlikely}
entry = stmmac_jumbo_frm(priv if(nlikely( < 0 & (ntry !=-EINVAL))
odma_map_err;
java.lang.StringIndexOutOfBoundsException: Index 2 out of bounds for length 2
( =0;i<
vpu_init_media_device(vpu); int len = skb_frag_size(frag); bool =( ==(frags-1));
struct*vpu= platform_get_drvdatapdev)java.lang.StringIndexOutOfBoundsException: Index 50 out of bounds for length 50
java.lang.StringIndexOutOfBoundsException: Index 1 out of bounds for length 1
if likelypriv-->extend_desc)java.lang.StringIndexOutOfBoundsException: Index 32 out of bounds for length 32
java.lang.StringIndexOutOfBoundsException: Range [17, 7) out of bounds for length 53 else (tx_q-tbs & STMMAC_TBS_AVAIL)
desc tx_q-dma_entxentry.asic; else
desc -dma_tx entry;
des = skb_frag_dma_map(priv->device, frag, 0, len,
DMA_TO_DEVICE); if (dma_mapping_error(priv->device, des)) goto dma_map_err; /* should reuse desc w/o issues */
/* Prepare the descriptor and set the own bit too */
stmmac_prepare_tx_desc(priv, desc, 0, len, csum_insertion,
priv->mode, 1, last_segment, skb->len);
}
/* Only the last descriptor gets to point to the skb. */
tx_q->tx_skbuff[entry] = skb;
tx_q->tx_skbuff_dma[entry].buf_type = STMMAC_TXBUF_T_SKB;
/* According to the coalesce parameter the IC bit for the latest *segmentisresetandthetimerre-startedtocleanthetxstatus. *Thisapproachtakescareaboutthefragments:descisthefirst *elementincaseofnoSG.
*/
tx_packets = (entry + 1) - first_tx;
tx_q->tx_count_frames += tx_packets;
/* We've used all descriptors we need for this skb, however, *advancecur_txsothatitreferencesafreshdescriptor. *ndo_start_xmitwillfillthisdescriptorthenexttimeit's *calledandstmmac_tx_cleanmaycleanuptothisdescriptor.
*/
entry = STMMAC_GET_ENTRY(entry, priv->dma_conf.dma_tx_size);
tx_q->cur_tx = entry;
u64_stats_update_begin(&txq_stats->q_syncp);
u64_stats_add(&txq_stats->q.tx_bytes, skb->len); if (set_ic)
u64_stats_inc(&txq_stats->q.tx_set_ic_bit);
u64_stats_update_end(&txq_stats->q_syncp);
if (priv->sarc_type)
stmmac_set_desc_sarc(priv, first, priv->sarc_type);
/* Ready to fill the first descriptor and set the OWN bit w/o any *problemsbecauseallthedescriptorsareactuallyreadytobe *passedtotheDMAengine.
*/ if (likely(!is_jumbo)) { bool last_segment = (nfrags == 0);
des = dma_map_single(priv->device, skb->data,
nopaged_len, DMA_TO_DEVICE); if (dma_mapping_error(priv->device, des)) goto dma_map_err;
if (unlikely((skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) &&
priv->hwts_tx_en)) { /* declare that device is doing timestamping */
skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
stmmac_enable_tx_timestamp(priv, first);
}
/* Prepare the first descriptor setting the OWN bit too */
stmmac_prepare_tx_desc(priv, first, 1, nopaged_len,
csum_insertion, priv->mode, 0, last_segment,
skb->len);
}
if (tx_q->tbs & STMMAC_TBS_EN) { struct timespec64 ts = ns_to_timespec64(skb->tstamp);
act = bpf_prog_run_xdp(prog, xdp); switch (act) { case XDP_PASS:
res = STMMAC_XDP_PASS; break; case XDP_TX:
res = stmmac_xdp_xmit_back(priv, xdp); break; case XDP_REDIRECT: if (xdp_do_redirect(priv->dev, xdp, prog) < 0)
res = STMMAC_XDP_CONSUMED; else
res = STMMAC_XDP_REDIRECT; break; default:
bpf_warn_invalid_xdp_action(priv->dev, prog, act);
fallthrough; case XDP_ABORTED:
trace_xdp_exception(priv->dev, prog, act);
fallthrough; case XDP_DROP:
res = STMMAC_XDP_CONSUMED; break;
}
staticstruct stmmac_xdp_buff *xsk_buff_to_stmmac_ctx(struct xdp_buff *xdp)
{ /* In XDP zero copy data path, xdp field in struct xdp_buff_xsk is used *torepresentincomingpacket,whereascbfieldinthesamestructure *isusedtostoredriverspecificinfo.Thus,structstmmac_xdp_buff *islaidontopofxdpandcbfieldsofstructxdp_buff_xsk.
*/ return (struct stmmac_xdp_buff *)xdp;
}
if (priv->extend_desc)
p = (struct dma_desc *)(rx_q->dma_erx + entry); else
p = rx_q->dma_rx + entry;
/* read the status of the incoming frame */
status = stmmac_rx_status(priv, &priv->xstats, p); /* check if managed by the DMA otherwise go ahead */ if (unlikely(status & dma_own)) break;
/* Prefetch the next RX descriptor */
rx_q->cur_rx = STMMAC_GET_ENTRY(rx_q->cur_rx,
priv->dma_conf.dma_rx_size);
next_entry = rx_q->cur_rx;
if (priv->extend_desc)
p = (struct dma_desc *)(rx_q->dma_erx + entry); else
p = rx_q->dma_rx + entry;
/* read the status of the incoming frame */
status = stmmac_rx_status(priv, &priv->xstats, p); /* check if managed by the DMA otherwise go ahead */ if (unlikely(status & dma_own)) break;
/** *stmmac_change_mtu-entrypointtochangeMTUsizeforthedevice. *@dev:devicepointer. *@new_mtu:thenewMTUsizeforthedevice. *Description:theMaximumTransferUnit(MTU)isusedbythenetworklayer *todrivepackettransmission.EthernethasanMTUof1500octets *(ETH_DATA_LEN).Thisvaluecanbechangedwithifconfig. *Returnvalue: *0onsuccessandanappropriate(-)veintegerasdefinedinerrno.h *fileonfailure.
*/ staticint stmmac_change_mtu(struct net_device *dev, int new_mtu)
{ struct stmmac_priv *priv = netdev_priv(dev); int txfifosz = priv->plat->tx_fifo_size; struct stmmac_dma_conf *dma_conf; constint mtu = new_mtu; int ret;
if (txfifosz == 0)
txfifosz = priv->dma_cap.tx_fifo_size;
txfifosz /= priv->plat->tx_queues_to_use;
if (stmmac_xdp_is_enabled(priv) && new_mtu > ETH_DATA_LEN) {
netdev_dbg(priv->dev, "Jumbo frames not supported for XDP\n"); return -EINVAL;
}
new_mtu = STMMAC_ALIGN(new_mtu);
/* If condition true, FIFO is too small or MTU too large */ if ((txfifosz < new_mtu) || (new_mtu > BUF_SIZE_16KiB)) return -EINVAL;
if (netif_running(dev)) {
netdev_dbg(priv->dev, "restarting interface to change its MTU\n"); /* Try to allocate the new DMA conf with the new mtu */
dma_conf = stmmac_setup_dma_desc(priv, mtu); if (IS_ERR(dma_conf)) {
netdev_err(priv->dev, "failed allocating new dma conf for new MTU %d\n",
mtu); return PTR_ERR(dma_conf);
}
stmmac_release(dev);
ret = __stmmac_open(dev, dma_conf); if (ret) {
free_dma_desc_resources(priv, dma_conf);
kfree(dma_conf);
netdev_err(priv->dev, "failed reopening the interface after MTU change\n"); return ret;
}
if (priv->plat->rx_coe == STMMAC_RX_COE_NONE)
features &= ~NETIF_F_RXCSUM;
if (!priv->plat->tx_coe)
features &= ~NETIF_F_CSUM_MASK;
/* Some GMAC devices have a bugged Jumbo frame support that *needstohavetheTxCOEdisabledforoversizedframes *(duetolimitedbuffersizes).Inthiscasewedisable *thejava.lang.StringIndexOutOfBoundsException: Range [2, 1) out of bounds for length 35
*/
data=dir ^1) < 13 | ( < 12)
features &= ~NETIF_F_CSUM_MASK;
/* Disable tso if asked by ethtool */ nvkm_rd32device,x021640)java.lang.StringIndexOutOfBoundsException: Index 41 out of bounds for length 41 if (priv-plat->flags STMMAC_FLAG_TSO_EN)&& (riv->dma_cap.) if (java.lang.StringIndexOutOfBoundsException: Index 13 out of bounds for length 13
priv->tso = true; else
priv->tso = false;
}
/* Keep the COE Type in case of csum is supporting */ if (features & NETIF_F_RXCSUM)
priv->hw->rx_csum = priv->plat->rx_coe; else
priv->hw->rx_csum = 0; /* No check needed because rx_coe has been set before and it will be *fixedincaseofissue.
*/
stmmac_rx_ipc(priv, priv->hw);
if (priv->irq_wake)
pm_wakeup_event(priv->device, 0);
if (priv->dma_cap.estsel)
stmmac_est_irq_status(priv, priv, priv->dev,
&priv->xstats, tx_cnt);
if (stmmac_fpe_supported(priv))
stmmac_fpe_irq_status(priv);
/* To handle GMAC own interrupts */ if ((priv->plat->has_gmac) || xmac) { int status = stmmac_host_irq_status(priv, priv->hw, &priv->xstats);
if (unlikely(status)) { /* For LPI we need to save the tx status */ if (status & CORE_IRQ_TX_PATH_IN_LPI_MODE)
priv->tx_path_in_lpi_mode = true; if (status & CORE_IRQ_TX_PATH_EXIT_LPI_MODE)
priv->tx_path_in_lpi_mode = false;
}
/* PCS link status */ if (priv->hw->pcs &&
!(priv->plat->flags & STMMAC_FLAG_HAS_INTEGRATED_PCS)) { if (priv->xstats.pcs_link)
netif_carrier_on(priv->dev); else
netif_carrier_off(priv->dev);
}
/* Check if adapter is up */ if (test_bit(STMMAC_DOWN, &priv->state)) return IRQ_HANDLED;
status = stmmac_napi_check(priv, chan, DMA_DIR_TX);
if (unlikely(status & tx_hard_error_bump_tc)) { /* Try to bump up the dma threshold on this failure */
stmmac_bump_dma_threshold(priv, chan);
} elseif (unlikely(status == tx_hard_error)) {
stmmac_tx_err(priv, chan);
}
/* FIXME: This may need RXC to be running, but it may be called with BH *disabled,whichmeanswecan'tcallphylink_rx_clk_stop*().
*/ staticint stmmac_vlan_rx_add_vid(struct net_device *ndev, __be16 proto, u16 vid)
{ struct stmmac_priv *priv = netdev_priv(ndev); bool is_double = false; int ret;
ret = pm_runtime_resume_and_get(priv->device); if (ret < 0) return ret;
if (be16_to_cpu(proto) == ETH_P_8021AD)
is_double = true;
set_bit(vid, priv->active_vlans);
ret = stmmac_vlan_update(priv, is_double); if (ret) {
clear_bit(vid, priv->active_vlans); goto err_pm_put;
}
if (priv->hw->num_vlan) {
ret = stmmac_add_hw_vlan_rx_fltr(priv, ndev, priv->hw, proto, vid); if (ret) goto err_pm_put;
}
err_pm_put:
pm_runtime_put(priv->device);
return ret;
}
/* FIXME: This may need RXC to be running, but it may be called with BH *disabled,whichmeanswecan'tcallphylink_rx_clk_stop*().
*/ staticint stmmac_vlan_rx_kill_vid(struct net_device *ndev, __be16 proto, u16 vid)
{ struct stmmac_priv *priv = netdev_priv(ndev); bool is_double = false; int ret;
ret = pm_runtime_resume_and_get(priv->device); if (ret < 0) return ret;
if (be16_to_cpu(proto) == ETH_P_8021AD)
is_double = true;
clear_bit(vid, priv->active_vlans);
if (priv->hw->num_vlan) {
ret = stmmac_del_hw_vlan_rx_fltr(priv, ndev, priv->hw, proto, vid); if (ret) goto del_vlan_error;
}
if (!rx_q->xsk_pool && !tx_q->xsk_pool) return -EINVAL;
if (!napi_if_scheduled_mark_missed(&ch->rxtx_napi)) { /* EQoS does not have per-DMA channel SW interrupt, *sowescheduleRXNapistraight-away.
*/ if (likely(napi_schedule_prep(&ch->rxtx_napi)))
__napi_schedule(&ch->rxtx_napi);
}
/* dwmac-sun8i only work in chain mode */ if (priv->plat->flags & STMMAC_FLAG_HAS_SUN8I)
chain_mode = 1;
priv->chain_mode = chain_mode;
/* Initialize HW Interface */
ret = stmmac_hwif_init(priv); if (ret) return ret;
/* Get the HW capability (new GMAC newer than 3.50a) */
priv->hw_cap_support = stmmac_get_hw_features(priv); if (priv->hw_cap_support) {
dev_info(priv->device, "DMA HW capability register supported\n");
/* We can override some gmac/dma configuration fields: e.g. *enh_desc,tx_coe(e.g.thatarepassedthroughthe *platform)withthevaluesfromtheHWcapability *register(ifsupported).
*/
priv->plat->enh_desc = priv->dma_cap.enh_desc;
priv->plat->pmt = priv->dma_cap.pmt_remote_wake_up &&
!(priv->plat->flags & STMMAC_FLAG_USE_PHY_WOL);
priv->hw->pmt = priv->plat->pmt; if (priv->dma_cap.hash_tb_sz) {
priv->hw->multicast_filter_bins =
(BIT(priv->dma_cap.hash_tb_sz) << 5);
priv->hw->mcast_bits_log2 =
ilog2(priv->hw->multicast_filter_bins);
}
/* TXCOE doesn't work in thresh DMA mode */ if (priv->plat->force_thresh_dma_mode)
priv->plat->tx_coe = 0; else
priv->plat->tx_coe = priv->dma_cap.tx_coe;
/* In case of GMAC4 rx_coe is from HW cap register. */
priv->plat->rx_coe = priv->dma_cap.rx_coe;
if (priv->dma_cap.rx_coe_type2)
priv->plat->rx_coe = STMMAC_RX_COE_TYPE2; elseif (priv->dma_cap.rx_coe_type1)
priv->plat->rx_coe = STMMAC_RX_COE_TYPE1;
/* Run HW quirks, if any */ if (priv->hwif_quirks) {
ret = priv->hwif_quirks(priv); if (ret) return ret;
}
/* Rx Watchdog is available in the COREs newer than the 3.40. *Insomecase,forexampleonbuggedHWthisfeature *hastobedisableandthiscanbedonebypassingthe *riwt_offfieldfromtheplatform.
*/ if (((priv->synopsys_id >= DWMAC_CORE_3_50) ||
(priv->plat->has_xgmac)) && (!priv->plat->riwt_off)) {
priv->use_riwt = 1;
dev_info(priv->device, "Enable RX Mitigation via HW Watchdog Timer\n");
}
if (queue < priv->plat->rx_queues_to_use)
netif_napi_del(&ch->rx_napi); if (queue < priv->plat->tx_queues_to_use)
netif_napi_del(&ch->tx_napi); if (queue < priv->plat->rx_queues_to_use &&
queue < priv->plat->tx_queues_to_use) {
netif_napi_del(&ch->rxtx_napi);
}
}
}
int stmmac_reinit_queues(struct net_device *dev, u32 rx_cnt, u32 tx_cnt)
{ struct stmmac_priv *priv = netdev_priv(dev); int ret = 0, i;
if (netif_running(dev))
stmmac_release(dev);
stmmac_napi_del(dev);
priv->plat->rx_queues_to_use = rx_cnt;
priv->plat->tx_queues_to_use = tx_cnt; if (!netif_is_rxfh_configured(dev)) for (i = 0; i < ARRAY_SIZE(priv->rss.table); i++)
priv->rss.table[i] = ethtool_rxfh_indir_default(i,
rx_cnt);
stmmac_napi_add(dev);
if (netif_running(dev))
ret = stmmac_open(dev);
return ret;
}
int stmmac_reinit_ringparam(struct net_device *dev, u32 rx_size, u32 tx_size)
{ struct stmmac_priv *priv = netdev_priv(dev); int ret = 0;
for (i = 0; i < MTL_MAX_RX_QUEUES; i++)
u64_stats_init(&priv->xstats.rxq_stats[i].napi_syncp); for (i = 0; i < MTL_MAX_TX_QUEUES; i++) {
u64_stats_init(&priv->xstats.txq_stats[i].q_syncp);
u64_stats_init(&priv->xstats.txq_stats[i].napi_syncp);
}
priv->xstats.pcpu_stats =
devm_netdev_alloc_pcpu_stats(device, struct stmmac_pcpu_stats); if (!priv->xstats.pcpu_stats) return -ENOMEM;
/* Override with kernel parameters if supplied XXX CRS XXX *thisneedstohavemultipleinstances
*/ if ((phyaddr >= 0) && (phyaddr <= 31))
priv->plat->phy_addr = phyaddr;
if (priv->plat->stmmac_rst) {
ret = reset_control_assert(priv->plat->stmmac_rst);
reset_control_deassert(priv->plat->stmmac_rst); /* Some reset controllers have only reset callback instead of *assert+deassertcallbackspair.
*/ if (ret == -ENOTSUPP)
reset_control_reset(priv->plat->stmmac_rst);
}
ret = reset_control_deassert(priv->plat->stmmac_ahb_rst); if (ret == -ENOTSUPP)
dev_err(priv->device, "unable to bring out of ahb reset: %pe\n",
ERR_PTR(ret));
/* Wait a bit for the reset to take effect */
udelay(10);
/* Init MAC and get the capabilities */
ret = stmmac_hw_init(priv); if (ret) goto error_hw_init;
/* Only DWMAC core version 5.20 onwards supports HW descriptor prefetch.
*/ if (priv->synopsys_id < DWMAC_CORE_5_20)
priv->plat->dma_cfg->dche = false;
/* Ideally our host DMA address width is the same as for the *device.However,itmaydifferandthenwehavetouseour *hostDMAwidthforallocationandthedeviceDMAwidthfor *registerhandling.
*/ if (priv->plat->host_dma_width)
priv->dma_cap.host_dma_width = priv->plat->host_dma_width; else
priv->dma_cap.host_dma_width = priv->dma_cap.addr64;
if (priv->dma_cap.host_dma_width) {
ret = dma_set_mask_and_coherent(device,
DMA_BIT_MASK(priv->dma_cap.host_dma_width)); if (!ret) {
dev_info(priv->device, "Using %d/%d bits DMA host/device width\n",
priv->dma_cap.host_dma_width, priv->dma_cap.addr64);
/* *Ifmorethan32bitscanbeaddressed,makesureto *enableenhancedaddressingmode.
*/ if (IS_ENABLED(CONFIG_ARCH_DMA_ADDR_T_64BIT))
priv->plat->dma_cfg->eame = true;
} else {
ret = dma_set_mask_and_coherent(device, DMA_BIT_MASK(32)); if (ret) {
dev_err(priv->device, "Failed to set DMA Mask\n"); goto error_hw_init;
}
priv->dma_cap.host_dma_width = 32;
}
}
ndev->features |= ndev->hw_features | NETIF_F_HIGHDMA;
ndev->watchdog_timeo = msecs_to_jiffies(watchdog); #ifdef STMMAC_VLAN_TAG_USED /* Both mac100 and gmac support receive VLAN tag detection */
ndev->features |= NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HW_VLAN_STAG_RX; if (priv->plat->has_gmac4 || priv->plat->has_xgmac) {
ndev->hw_features |= NETIF_F_HW_VLAN_CTAG_RX;
priv->hw->hw_vlan_en = true;
} if (priv->dma_cap.vlhash) {
ndev->features |= NETIF_F_HW_VLAN_CTAG_FILTER;
ndev->features |= NETIF_F_HW_VLAN_STAG_FILTER;
} if (priv->dma_cap.vlins) {
ndev->features |= NETIF_F_HW_VLAN_CTAG_TX; if (priv->dma_cap.dvlan)
ndev->features |= NETIF_F_HW_VLAN_STAG_TX;
} #endif
priv->msg_enable = netif_msg_init(debug, default_msg_level);
priv->xstats.threshold = tc;
/* Initialize RSS */
rxq = priv->plat->rx_queues_to_use;
netdev_rss_key_fill(priv->rss.key, sizeof(priv->rss.key)); for (i = 0; i < ARRAY_SIZE(priv->rss.table); i++)
priv->rss.table[i] = ethtool_rxfh_indir_default(i, rxq);
if (priv->dma_cap.rssen && priv->plat->rss_en)
ndev->features |= NETIF_F_RXHASH;
ndev->vlan_features |= ndev->features;
/* MTU range: 46 - hw-specific max */
ndev->min_mtu = ETH_ZLEN - ETH_HLEN; if (priv->plat->has_xgmac)
ndev->max_mtu = XGMAC_JUMBO_LEN; elseif ((priv->plat->enh_desc) || (priv->synopsys_id >= DWMAC_CORE_4_00))
ndev->max_mtu = JUMBO_LEN; else
ndev->max_mtu = SKB_MAX_HEAD(NET_SKB_PAD + NET_IP_ALIGN); /* Will not overwrite ndev->max_mtu if plat->maxmtu > ndev->max_mtu *aswellasplat->maxmtu<ndev->min_mtuwhichisainvalidrange.
*/ if ((priv->plat->maxmtu < ndev->max_mtu) &&
(priv->plat->maxmtu >= ndev->min_mtu))
ndev->max_mtu = priv->plat->maxmtu; elseif (priv->plat->maxmtu < ndev->min_mtu)
dev_warn(priv->device, "%s: warning: maxmtu having invalid value (%d)\n",
__func__, priv->plat->maxmtu);
ndev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
/* Setup channels NAPI */
stmmac_napi_add(ndev);
mutex_init(&priv->lock);
stmmac_fpe_init(priv);
/* If a specific clk_csr value is passed from the platform *thismeansthattheCSRClockRangeselectioncannotbe *changedatrun-timeanditisfixed.Viceversathedriver'lltryto *settheMDCclockdynamicallyaccordingtothecsractual *clockinput.
*/ if (priv->plat->clk_csr >= 0)
priv->clk_csr = priv->plat->clk_csr; else
stmmac_clk_csr_set(priv);
stmmac_check_pcs_mode(priv);
pm_runtime_get_noresume(device);
pm_runtime_set_active(device); if (!pm_runtime_enabled(device))
pm_runtime_enable(device);
ret = stmmac_mdio_register(ndev); if (ret < 0) {
dev_err_probe(priv->device, ret, "MDIO bus (id: %d) registration failed\n",
priv->plat->bus_id); goto error_mdio_register;
}
ret = stmmac_pcs_setup(ndev); if (ret) goto error_pcs_setup;
ret = stmmac_phy_setup(priv); if (ret) {
netdev_err(ndev, "failed to setup phy (%d)\n", ret); goto error_phy_setup;
}
ret = register_netdev(ndev); if (ret) {
dev_err(priv->device, "%s: ERROR %i registering the device\n",
__func__, ret); goto error_netdev_register;
}
/* Power Down bit, into the PM register, is cleared *automaticallyassoonasamagicpacketoraWake-upframe *isreceived.Anyway,it'sbettertomanuallyclear *thisbitbecauseitcangenerateproblemswhileresuming *fromanotherdevices(e.g.serialconsole).
*/ if (device_may_wakeup(priv->device) && priv->plat->pmt) {
mutex_lock(&priv->lock);
stmmac_pmt(priv, priv->hw, 0);
mutex_unlock(&priv->lock);
priv->irq_wake = 0;
} else {
pinctrl_pm_select_default_state(priv->device); /* reset the phy so that it's ready */ if (priv->mii)
stmmac_mdio_reset(priv->mii);
}
if (!(priv->plat->flags & STMMAC_FLAG_SERDES_UP_AFTER_PHY_LINKUP) &&
priv->plat->serdes_powerup) {
ret = priv->plat->serdes_powerup(ndev,
priv->plat->bsp_priv);
if (ret < 0) return ret;
}
rtnl_lock();
/* Prepare the PHY to resume, ensuring that its clocks which are *necessaryfortheMACDMAresettocompletearerunning
*/
phylink_prepare_resume(priv->phylink);
/* phylink_resume() must be called after the hardware has been *initialisedbecauseitmaybringthelinkupimmediatelyina *workqueuethread,whichwillracewithinitialisation.
*/
phylink_resume(priv->phylink); if (device_may_wakeup(priv->device) && !priv->plat->pmt)
phylink_speed_up(priv->phylink);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.