diff options
Diffstat (limited to 'drivers/staging/fsl-dpaa2/ethernet')
-rw-r--r-- | drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth.c | 281 | ||||
-rw-r--r-- | drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth.h | 54 | ||||
-rw-r--r-- | drivers/staging/fsl-dpaa2/ethernet/dpaa2-ethtool.c | 14 | ||||
-rw-r--r-- | drivers/staging/fsl-dpaa2/ethernet/dpni-cmd.h | 5 | ||||
-rw-r--r-- | drivers/staging/fsl-dpaa2/ethernet/dpni.c | 32 | ||||
-rw-r--r-- | drivers/staging/fsl-dpaa2/ethernet/dpni.h | 5 |
6 files changed, 260 insertions, 131 deletions
diff --git a/drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth.c b/drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth.c index 26017fe9df93..0d8ed002adcb 100644 --- a/drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth.c +++ b/drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth.c @@ -104,9 +104,11 @@ static void free_rx_fd(struct dpaa2_eth_priv *priv, /* We don't support any other format */ return; - /* For S/G frames, we first need to free all SG entries */ + /* For S/G frames, we first need to free all SG entries + * except the first one, which was taken care of already + */ sgt = vaddr + dpaa2_fd_get_offset(fd); - for (i = 0; i < DPAA2_ETH_MAX_SG_ENTRIES; i++) { + for (i = 1; i < DPAA2_ETH_MAX_SG_ENTRIES; i++) { addr = dpaa2_sg_get_addr(&sgt[i]); sg_vaddr = dpaa2_iova_to_virt(priv->iommu_domain, addr); dma_unmap_single(dev, addr, DPAA2_ETH_RX_BUF_SIZE, @@ -131,16 +133,15 @@ static struct sk_buff *build_linear_skb(struct dpaa2_eth_priv *priv, u16 fd_offset = dpaa2_fd_get_offset(fd); u32 fd_length = dpaa2_fd_get_len(fd); - skb = build_skb(fd_vaddr, DPAA2_ETH_RX_BUF_SIZE + - SKB_DATA_ALIGN(sizeof(struct skb_shared_info))); + ch->buf_count--; + + skb = build_skb(fd_vaddr, DPAA2_ETH_SKB_SIZE); if (unlikely(!skb)) return NULL; skb_reserve(skb, fd_offset); skb_put(skb, fd_length); - ch->buf_count--; - return skb; } @@ -176,10 +177,21 @@ static struct sk_buff *build_frag_skb(struct dpaa2_eth_priv *priv, if (i == 0) { /* We build the skb around the first data buffer */ - skb = build_skb(sg_vaddr, DPAA2_ETH_RX_BUF_SIZE + - SKB_DATA_ALIGN(sizeof(struct skb_shared_info))); - if (unlikely(!skb)) - return NULL; + skb = build_skb(sg_vaddr, DPAA2_ETH_SKB_SIZE); + if (unlikely(!skb)) { + /* Free the first SG entry now, since we already + * unmapped it and obtained the virtual address + */ + skb_free_frag(sg_vaddr); + + /* We still need to subtract the buffers used + * by this FD from our software counter + */ + while (!dpaa2_sg_is_final(&sgt[i]) && + i < DPAA2_ETH_MAX_SG_ENTRIES) + i++; + break; + } sg_offset = dpaa2_sg_get_offset(sge); skb_reserve(skb, sg_offset); @@ -206,6 +218,8 @@ static struct sk_buff *build_frag_skb(struct dpaa2_eth_priv *priv, break; } + WARN_ONCE(i == DPAA2_ETH_MAX_SG_ENTRIES, "Final bit not set in SGT"); + /* Count all data buffers + SG table buffer */ ch->buf_count -= i + 2; @@ -557,10 +571,10 @@ static netdev_tx_t dpaa2_eth_tx(struct sk_buff *skb, struct net_device *net_dev) percpu_stats = this_cpu_ptr(priv->percpu_stats); percpu_extras = this_cpu_ptr(priv->percpu_extras); - if (unlikely(skb_headroom(skb) < DPAA2_ETH_NEEDED_HEADROOM(priv))) { + if (unlikely(skb_headroom(skb) < dpaa2_eth_needed_headroom(priv))) { struct sk_buff *ns; - ns = skb_realloc_headroom(skb, DPAA2_ETH_NEEDED_HEADROOM(priv)); + ns = skb_realloc_headroom(skb, dpaa2_eth_needed_headroom(priv)); if (unlikely(!ns)) { percpu_stats->tx_dropped++; goto err_alloc_headroom; @@ -718,6 +732,23 @@ static int set_tx_csum(struct dpaa2_eth_priv *priv, bool enable) return 0; } +/* Free buffers acquired from the buffer pool or which were meant to + * be released in the pool + */ +static void free_bufs(struct dpaa2_eth_priv *priv, u64 *buf_array, int count) +{ + struct device *dev = priv->net_dev->dev.parent; + void *vaddr; + int i; + + for (i = 0; i < count; i++) { + vaddr = dpaa2_iova_to_virt(priv->iommu_domain, buf_array[i]); + dma_unmap_single(dev, buf_array[i], DPAA2_ETH_RX_BUF_SIZE, + DMA_BIDIRECTIONAL); + skb_free_frag(vaddr); + } +} + /* Perform a single release command to add buffers * to the specified buffer pool */ @@ -727,17 +758,17 @@ static int add_bufs(struct dpaa2_eth_priv *priv, u16 bpid) u64 buf_array[DPAA2_ETH_BUFS_PER_CMD]; void *buf; dma_addr_t addr; - int i; + int i, err; for (i = 0; i < DPAA2_ETH_BUFS_PER_CMD; i++) { /* Allocate buffer visible to WRIOP + skb shared info + * alignment padding */ - buf = napi_alloc_frag(DPAA2_ETH_BUF_RAW_SIZE); + buf = napi_alloc_frag(dpaa2_eth_buf_raw_size(priv)); if (unlikely(!buf)) goto err_alloc; - buf = PTR_ALIGN(buf, DPAA2_ETH_RX_BUF_ALIGN); + buf = PTR_ALIGN(buf, priv->rx_buf_align); addr = dma_map_single(dev, buf, DPAA2_ETH_RX_BUF_SIZE, DMA_FROM_DEVICE); @@ -748,28 +779,33 @@ static int add_bufs(struct dpaa2_eth_priv *priv, u16 bpid) /* tracing point */ trace_dpaa2_eth_buf_seed(priv->net_dev, - buf, DPAA2_ETH_BUF_RAW_SIZE, + buf, dpaa2_eth_buf_raw_size(priv), addr, DPAA2_ETH_RX_BUF_SIZE, bpid); } release_bufs: - /* In case the portal is busy, retry until successful. - * The buffer release function would only fail if the QBMan portal - * was busy, which implies portal contention (i.e. more CPUs than - * portals, i.e. GPPs w/o affine DPIOs). For all practical purposes, - * there is little we can realistically do, short of giving up - - * in which case we'd risk depleting the buffer pool and never again - * receiving the Rx interrupt which would kick-start the refill logic. - * So just keep retrying, at the risk of being moved to ksoftirqd. - */ - while (dpaa2_io_service_release(NULL, bpid, buf_array, i)) + /* In case the portal is busy, retry until successful */ + while ((err = dpaa2_io_service_release(NULL, bpid, + buf_array, i)) == -EBUSY) cpu_relax(); + + /* If release command failed, clean up and bail out; + * not much else we can do about it + */ + if (err) { + free_bufs(priv, buf_array, i); + return 0; + } + return i; err_map: skb_free_frag(buf); err_alloc: + /* If we managed to allocate at least some buffers, + * release them to hardware + */ if (i) goto release_bufs; @@ -811,10 +847,8 @@ static int seed_pool(struct dpaa2_eth_priv *priv, u16 bpid) */ static void drain_bufs(struct dpaa2_eth_priv *priv, int count) { - struct device *dev = priv->net_dev->dev.parent; u64 buf_array[DPAA2_ETH_BUFS_PER_CMD]; - void *vaddr; - int ret, i; + int ret; do { ret = dpaa2_io_service_acquire(NULL, priv->bpid, @@ -823,15 +857,7 @@ static void drain_bufs(struct dpaa2_eth_priv *priv, int count) netdev_err(priv->net_dev, "dpaa2_io_service_acquire() failed\n"); return; } - for (i = 0; i < ret; i++) { - /* Same logic as on regular Rx path */ - vaddr = dpaa2_iova_to_virt(priv->iommu_domain, - buf_array[i]); - dma_unmap_single(dev, buf_array[i], - DPAA2_ETH_RX_BUF_SIZE, - DMA_FROM_DEVICE); - skb_free_frag(vaddr); - } + free_bufs(priv, buf_array, ret); } while (ret); } @@ -927,13 +953,14 @@ static int dpaa2_eth_poll(struct napi_struct *napi, int budget) break; } - if (cleaned < budget) { - napi_complete_done(napi, cleaned); + if (cleaned < budget && napi_complete_done(napi, cleaned)) { /* Re-enable data available notifications */ do { err = dpaa2_io_service_rearm(NULL, &ch->nctx); cpu_relax(); } while (err == -EBUSY); + WARN_ONCE(err, "CDAN notifications rearm failed on core %d", + ch->nctx.desired_cpu); } ch->stats.frames += cleaned; @@ -1415,34 +1442,32 @@ static struct fsl_mc_device *setup_dpcon(struct dpaa2_eth_priv *priv) err = dpcon_open(priv->mc_io, 0, dpcon->obj_desc.id, &dpcon->mc_handle); if (err) { dev_err(dev, "dpcon_open() failed\n"); - goto err_open; + goto free; } err = dpcon_reset(priv->mc_io, 0, dpcon->mc_handle); if (err) { dev_err(dev, "dpcon_reset() failed\n"); - goto err_reset; + goto close; } err = dpcon_get_attributes(priv->mc_io, 0, dpcon->mc_handle, &attrs); if (err) { dev_err(dev, "dpcon_get_attributes() failed\n"); - goto err_get_attr; + goto close; } err = dpcon_enable(priv->mc_io, 0, dpcon->mc_handle); if (err) { dev_err(dev, "dpcon_enable() failed\n"); - goto err_enable; + goto close; } return dpcon; -err_enable: -err_get_attr: -err_reset: +close: dpcon_close(priv->mc_io, 0, dpcon->mc_handle); -err_open: +free: fsl_mc_object_free(dpcon); return NULL; @@ -1749,66 +1774,32 @@ static void free_dpbp(struct dpaa2_eth_priv *priv) fsl_mc_object_free(priv->dpbp_dev); } -/* Configure the DPNI object this interface is associated with */ -static int setup_dpni(struct fsl_mc_device *ls_dev) +static int set_buffer_layout(struct dpaa2_eth_priv *priv) { - struct device *dev = &ls_dev->dev; - struct dpaa2_eth_priv *priv; - struct net_device *net_dev; + struct device *dev = priv->net_dev->dev.parent; struct dpni_buffer_layout buf_layout = {0}; int err; - net_dev = dev_get_drvdata(dev); - priv = netdev_priv(net_dev); - - /* get a handle for the DPNI object */ - err = dpni_open(priv->mc_io, 0, ls_dev->obj_desc.id, &priv->mc_token); - if (err) { - dev_err(dev, "dpni_open() failed\n"); - goto err_open; - } - - ls_dev->mc_io = priv->mc_io; - ls_dev->mc_handle = priv->mc_token; - - err = dpni_reset(priv->mc_io, 0, priv->mc_token); - if (err) { - dev_err(dev, "dpni_reset() failed\n"); - goto err_reset; - } - - err = dpni_get_attributes(priv->mc_io, 0, priv->mc_token, - &priv->dpni_attrs); - if (err) { - dev_err(dev, "dpni_get_attributes() failed (err=%d)\n", err); - goto err_get_attr; - } + /* We need to check for WRIOP version 1.0.0, but depending on the MC + * version, this number is not always provided correctly on rev1. + * We need to check for both alternatives in this situation. + */ + if (priv->dpni_attrs.wriop_version == DPAA2_WRIOP_VERSION(0, 0, 0) || + priv->dpni_attrs.wriop_version == DPAA2_WRIOP_VERSION(1, 0, 0)) + priv->rx_buf_align = DPAA2_ETH_RX_BUF_ALIGN_REV1; + else + priv->rx_buf_align = DPAA2_ETH_RX_BUF_ALIGN; - /* Configure buffer layouts */ - /* rx buffer */ - buf_layout.pass_parser_result = true; + /* tx buffer */ buf_layout.pass_frame_status = true; buf_layout.private_data_size = DPAA2_ETH_SWA_SIZE; - buf_layout.data_align = DPAA2_ETH_RX_BUF_ALIGN; - buf_layout.options = DPNI_BUF_LAYOUT_OPT_PARSER_RESULT | - DPNI_BUF_LAYOUT_OPT_FRAME_STATUS | - DPNI_BUF_LAYOUT_OPT_PRIVATE_DATA_SIZE | - DPNI_BUF_LAYOUT_OPT_DATA_ALIGN; - err = dpni_set_buffer_layout(priv->mc_io, 0, priv->mc_token, - DPNI_QUEUE_RX, &buf_layout); - if (err) { - dev_err(dev, "dpni_set_buffer_layout(RX) failed\n"); - goto err_buf_layout; - } - - /* tx buffer */ buf_layout.options = DPNI_BUF_LAYOUT_OPT_FRAME_STATUS | DPNI_BUF_LAYOUT_OPT_PRIVATE_DATA_SIZE; err = dpni_set_buffer_layout(priv->mc_io, 0, priv->mc_token, DPNI_QUEUE_TX, &buf_layout); if (err) { dev_err(dev, "dpni_set_buffer_layout(TX) failed\n"); - goto err_buf_layout; + return err; } /* tx-confirm buffer */ @@ -1817,7 +1808,7 @@ static int setup_dpni(struct fsl_mc_device *ls_dev) DPNI_QUEUE_TX_CONFIRM, &buf_layout); if (err) { dev_err(dev, "dpni_set_buffer_layout(TX_CONF) failed\n"); - goto err_buf_layout; + return err; } /* Now that we've set our tx buffer layout, retrieve the minimum @@ -1827,24 +1818,76 @@ static int setup_dpni(struct fsl_mc_device *ls_dev) &priv->tx_data_offset); if (err) { dev_err(dev, "dpni_get_tx_data_offset() failed\n"); - goto err_data_offset; + return err; } if ((priv->tx_data_offset % 64) != 0) dev_warn(dev, "Tx data offset (%d) not a multiple of 64B\n", priv->tx_data_offset); - /* Accommodate software annotation space (SWA) */ - priv->tx_data_offset += DPAA2_ETH_SWA_SIZE; + /* rx buffer */ + buf_layout.pass_parser_result = true; + buf_layout.data_align = priv->rx_buf_align; + buf_layout.data_head_room = dpaa2_eth_rx_head_room(priv); + buf_layout.private_data_size = 0; + buf_layout.options = DPNI_BUF_LAYOUT_OPT_PARSER_RESULT | + DPNI_BUF_LAYOUT_OPT_FRAME_STATUS | + DPNI_BUF_LAYOUT_OPT_DATA_ALIGN | + DPNI_BUF_LAYOUT_OPT_DATA_HEAD_ROOM; + err = dpni_set_buffer_layout(priv->mc_io, 0, priv->mc_token, + DPNI_QUEUE_RX, &buf_layout); + if (err) { + dev_err(dev, "dpni_set_buffer_layout(RX) failed\n"); + return err; + } return 0; +} -err_data_offset: -err_buf_layout: -err_get_attr: -err_reset: +/* Configure the DPNI object this interface is associated with */ +static int setup_dpni(struct fsl_mc_device *ls_dev) +{ + struct device *dev = &ls_dev->dev; + struct dpaa2_eth_priv *priv; + struct net_device *net_dev; + int err; + + net_dev = dev_get_drvdata(dev); + priv = netdev_priv(net_dev); + + /* get a handle for the DPNI object */ + err = dpni_open(priv->mc_io, 0, ls_dev->obj_desc.id, &priv->mc_token); + if (err) { + dev_err(dev, "dpni_open() failed\n"); + return err; + } + + ls_dev->mc_io = priv->mc_io; + ls_dev->mc_handle = priv->mc_token; + + err = dpni_reset(priv->mc_io, 0, priv->mc_token); + if (err) { + dev_err(dev, "dpni_reset() failed\n"); + goto close; + } + + err = dpni_get_attributes(priv->mc_io, 0, priv->mc_token, + &priv->dpni_attrs); + if (err) { + dev_err(dev, "dpni_get_attributes() failed (err=%d)\n", err); + goto close; + } + + err = set_buffer_layout(priv); + if (err) + goto close; + + + return 0; + +close: dpni_close(priv->mc_io, 0, priv->mc_token); -err_open: + return err; } @@ -2085,7 +2128,7 @@ static int bind_dpni(struct dpaa2_eth_priv *priv) */ err = dpaa2_eth_set_hash(net_dev, DPAA2_RXH_SUPPORTED); if (err) - netdev_err(net_dev, "Failed to configure hashing\n"); + dev_err(dev, "Failed to configure hashing\n"); /* Configure handling of error frames */ err_cfg.errors = DPAA2_FAS_RX_ERR_MASK; @@ -2230,6 +2273,7 @@ static int netdev_init(struct net_device *net_dev) { struct device *dev = net_dev->dev.parent; struct dpaa2_eth_priv *priv = netdev_priv(net_dev); + u16 rx_headroom, req_headroom; u8 bcast_addr[ETH_ALEN]; u8 num_queues; int err; @@ -2251,7 +2295,20 @@ static int netdev_init(struct net_device *net_dev) /* Reserve enough space to align buffer as per hardware requirement; * NOTE: priv->tx_data_offset MUST be initialized at this point. */ - net_dev->needed_headroom = DPAA2_ETH_NEEDED_HEADROOM(priv); + net_dev->needed_headroom = dpaa2_eth_needed_headroom(priv); + + /* If headroom guaranteed by hardware in the Rx frame buffer is + * smaller than the Tx headroom required by the stack, issue a + * one time warning. This will most likely mean skbs forwarded to + * another DPAA2 network interface will get reallocated, with a + * significant performance impact. + */ + req_headroom = LL_RESERVED_SPACE(net_dev) - ETH_HLEN; + rx_headroom = ALIGN(DPAA2_ETH_RX_HWA_SIZE + + dpaa2_eth_rx_head_room(priv), priv->rx_buf_align); + if (req_headroom > rx_headroom) + dev_info_once(dev, "Required headroom (%d) greater than available (%d)\n", + req_headroom, rx_headroom); /* Set MTU limits */ net_dev->min_mtu = 68; @@ -2303,7 +2360,7 @@ static irqreturn_t dpni_irq0_handler(int irq_num, void *arg) static irqreturn_t dpni_irq0_handler_thread(int irq_num, void *arg) { - u32 status = 0, clear = 0; + u32 status = ~0; struct device *dev = (struct device *)arg; struct fsl_mc_device *dpni_dev = to_fsl_mc_device(dev); struct net_device *net_dev = dev_get_drvdata(dev); @@ -2313,18 +2370,12 @@ static irqreturn_t dpni_irq0_handler_thread(int irq_num, void *arg) DPNI_IRQ_INDEX, &status); if (unlikely(err)) { netdev_err(net_dev, "Can't get irq status (err %d)\n", err); - clear = 0xffffffff; - goto out; + return IRQ_HANDLED; } - if (status & DPNI_IRQ_EVENT_LINK_CHANGED) { - clear |= DPNI_IRQ_EVENT_LINK_CHANGED; + if (status & DPNI_IRQ_EVENT_LINK_CHANGED) link_state_update(netdev_priv(net_dev)); - } -out: - dpni_clear_irq_status(dpni_dev->mc_io, 0, dpni_dev->mc_handle, - DPNI_IRQ_INDEX, clear); return IRQ_HANDLED; } diff --git a/drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth.h b/drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth.h index bfbabae1aad8..5b3ab9f62d5e 100644 --- a/drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth.h +++ b/drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth.h @@ -45,6 +45,8 @@ #include "dpaa2-eth-trace.h" +#define DPAA2_WRIOP_VERSION(x, y, z) ((x) << 10 | (y) << 5 | (z) << 0) + #define DPAA2_ETH_STORE_SIZE 16 /* Maximum number of scatter-gather entries in an ingress frame, @@ -80,23 +82,21 @@ */ #define DPAA2_ETH_BUFS_PER_CMD 7 -/* Hardware requires alignment for ingress/egress buffer addresses - * and ingress buffer lengths. - */ -#define DPAA2_ETH_RX_BUF_SIZE 2048 +/* Hardware requires alignment for ingress/egress buffer addresses */ #define DPAA2_ETH_TX_BUF_ALIGN 64 -#define DPAA2_ETH_RX_BUF_ALIGN 256 -#define DPAA2_ETH_NEEDED_HEADROOM(p_priv) \ - ((p_priv)->tx_data_offset + DPAA2_ETH_TX_BUF_ALIGN) -/* Hardware only sees DPAA2_ETH_RX_BUF_SIZE, but we need to allocate ingress - * buffers large enough to allow building an skb around them and also account - * for alignment restrictions +#define DPAA2_ETH_RX_BUF_SIZE 2048 +#define DPAA2_ETH_SKB_SIZE \ + (DPAA2_ETH_RX_BUF_SIZE + SKB_DATA_ALIGN(sizeof(struct skb_shared_info))) + +/* Hardware annotation area in RX buffers */ +#define DPAA2_ETH_RX_HWA_SIZE 64 + +/* Due to a limitation in WRIOP 1.0.0, the RX buffer data must be aligned + * to 256B. For newer revisions, the requirement is only for 64B alignment */ -#define DPAA2_ETH_BUF_RAW_SIZE \ - (DPAA2_ETH_RX_BUF_SIZE + \ - SKB_DATA_ALIGN(sizeof(struct skb_shared_info)) + \ - DPAA2_ETH_RX_BUF_ALIGN) +#define DPAA2_ETH_RX_BUF_ALIGN_REV1 256 +#define DPAA2_ETH_RX_BUF_ALIGN 64 /* We are accommodating a skb backpointer and some S/G info * in the frame's software annotation. The hardware @@ -134,7 +134,7 @@ struct dpaa2_eth_swa { DPAA2_FD_CTRL_FAERR) /* Annotation bits in FD CTRL */ -#define DPAA2_FD_CTRL_ASAL 0x00020000 /* ASAL = 128 */ +#define DPAA2_FD_CTRL_ASAL 0x00010000 /* ASAL = 64 */ #define DPAA2_FD_CTRL_PTA 0x00800000 #define DPAA2_FD_CTRL_PTV1 0x00400000 @@ -318,6 +318,7 @@ struct dpaa2_eth_priv { struct iommu_domain *iommu_domain; u16 tx_qdid; + u16 rx_buf_align; struct fsl_mc_io *mc_io; /* Cores which have an affine DPIO/DPCON. * This is the cpu set on which Rx and Tx conf frames are processed @@ -353,6 +354,29 @@ struct dpaa2_eth_priv { extern const struct ethtool_ops dpaa2_ethtool_ops; extern const char dpaa2_eth_drv_version[]; +/* Hardware only sees DPAA2_ETH_RX_BUF_SIZE, but the skb built around + * the buffer also needs space for its shared info struct, and we need + * to allocate enough to accommodate hardware alignment restrictions + */ +static inline unsigned int dpaa2_eth_buf_raw_size(struct dpaa2_eth_priv *priv) +{ + return DPAA2_ETH_SKB_SIZE + priv->rx_buf_align; +} + +static inline +unsigned int dpaa2_eth_needed_headroom(struct dpaa2_eth_priv *priv) +{ + return priv->tx_data_offset + DPAA2_ETH_TX_BUF_ALIGN - HH_DATA_MOD; +} + +/* Extra headroom space requested to hardware, in order to make sure there's + * no realloc'ing in forwarding scenarios + */ +static inline unsigned int dpaa2_eth_rx_head_room(struct dpaa2_eth_priv *priv) +{ + return dpaa2_eth_needed_headroom(priv) - DPAA2_ETH_RX_HWA_SIZE; +} + static int dpaa2_eth_queue_count(struct dpaa2_eth_priv *priv) { return priv->dpni_attrs.num_queues; diff --git a/drivers/staging/fsl-dpaa2/ethernet/dpaa2-ethtool.c b/drivers/staging/fsl-dpaa2/ethernet/dpaa2-ethtool.c index 031179ab3a22..ebe8fd6ccf2c 100644 --- a/drivers/staging/fsl-dpaa2/ethernet/dpaa2-ethtool.c +++ b/drivers/staging/fsl-dpaa2/ethernet/dpaa2-ethtool.c @@ -76,10 +76,22 @@ static char dpaa2_ethtool_extras[][ETH_GSTRING_LEN] = { static void dpaa2_eth_get_drvinfo(struct net_device *net_dev, struct ethtool_drvinfo *drvinfo) { + struct dpaa2_eth_priv *priv = netdev_priv(net_dev); + u16 fw_major, fw_minor; + int err; + strlcpy(drvinfo->driver, KBUILD_MODNAME, sizeof(drvinfo->driver)); strlcpy(drvinfo->version, dpaa2_eth_drv_version, sizeof(drvinfo->version)); - strlcpy(drvinfo->fw_version, "N/A", sizeof(drvinfo->fw_version)); + + err = dpni_get_api_version(priv->mc_io, 0, &fw_major, &fw_minor); + if (!err) + snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version), + "%u.%u", fw_major, fw_minor); + else + strlcpy(drvinfo->fw_version, "N/A", + sizeof(drvinfo->fw_version)); + strlcpy(drvinfo->bus_info, dev_name(net_dev->dev.parent->parent), sizeof(drvinfo->bus_info)); } diff --git a/drivers/staging/fsl-dpaa2/ethernet/dpni-cmd.h b/drivers/staging/fsl-dpaa2/ethernet/dpni-cmd.h index 57df22292233..3120e22496d0 100644 --- a/drivers/staging/fsl-dpaa2/ethernet/dpni-cmd.h +++ b/drivers/staging/fsl-dpaa2/ethernet/dpni-cmd.h @@ -538,4 +538,9 @@ struct dpni_rsp_get_taildrop { __le32 threshold; }; +struct dpni_rsp_get_api_version { + u16 major; + u16 minor; +}; + #endif /* _FSL_DPNI_CMD_H */ diff --git a/drivers/staging/fsl-dpaa2/ethernet/dpni.c b/drivers/staging/fsl-dpaa2/ethernet/dpni.c index 04a5b14bc1c5..e8be76181c36 100644 --- a/drivers/staging/fsl-dpaa2/ethernet/dpni.c +++ b/drivers/staging/fsl-dpaa2/ethernet/dpni.c @@ -1594,3 +1594,35 @@ int dpni_get_taildrop(struct fsl_mc_io *mc_io, return 0; } + +/** + * dpni_get_api_version() - Get Data Path Network Interface API version + * @mc_io: Pointer to MC portal's I/O object + * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' + * @major_ver: Major version of data path network interface API + * @minor_ver: Minor version of data path network interface API + * + * Return: '0' on Success; Error code otherwise. + */ +int dpni_get_api_version(struct fsl_mc_io *mc_io, + u32 cmd_flags, + u16 *major_ver, + u16 *minor_ver) +{ + struct dpni_rsp_get_api_version *rsp_params; + struct mc_command cmd = { 0 }; + int err; + + cmd.header = mc_encode_cmd_header(DPNI_CMDID_GET_API_VERSION, + cmd_flags, 0); + + err = mc_send_command(mc_io, &cmd); + if (err) + return err; + + rsp_params = (struct dpni_rsp_get_api_version *)cmd.params; + *major_ver = le16_to_cpu(rsp_params->major); + *minor_ver = le16_to_cpu(rsp_params->minor); + + return 0; +} diff --git a/drivers/staging/fsl-dpaa2/ethernet/dpni.h b/drivers/staging/fsl-dpaa2/ethernet/dpni.h index 282e5e85ffa7..ce86a816af45 100644 --- a/drivers/staging/fsl-dpaa2/ethernet/dpni.h +++ b/drivers/staging/fsl-dpaa2/ethernet/dpni.h @@ -829,4 +829,9 @@ struct dpni_rule_cfg { u8 key_size; }; +int dpni_get_api_version(struct fsl_mc_io *mc_io, + u32 cmd_flags, + u16 *major_ver, + u16 *minor_ver); + #endif /* __FSL_DPNI_H */ |