Merge branch 'remotes/lorenzo/pci/xgene'
[linux-2.6-microblaze.git] / drivers / net / ethernet / broadcom / bnxt / bnxt.c
index aef3fcc..e373ae0 100644 (file)
@@ -49,6 +49,8 @@
 #include <linux/log2.h>
 #include <linux/aer.h>
 #include <linux/bitmap.h>
+#include <linux/ptp_clock_kernel.h>
+#include <linux/timecounter.h>
 #include <linux/cpu_rmap.h>
 #include <linux/cpumask.h>
 #include <net/pkt_cls.h>
@@ -63,6 +65,7 @@
 #include "bnxt_ethtool.h"
 #include "bnxt_dcb.h"
 #include "bnxt_xdp.h"
+#include "bnxt_ptp.h"
 #include "bnxt_vfr.h"
 #include "bnxt_tc.h"
 #include "bnxt_devlink.h"
@@ -418,12 +421,25 @@ static netdev_tx_t bnxt_start_xmit(struct sk_buff *skb, struct net_device *dev)
                        vlan_tag_flags |= 1 << TX_BD_CFA_META_TPID_SHIFT;
        }
 
-       if (unlikely(skb->no_fcs)) {
-               lflags |= cpu_to_le32(TX_BD_FLAGS_NO_CRC);
-               goto normal_tx;
+       if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)) {
+               struct bnxt_ptp_cfg *ptp = bp->ptp_cfg;
+
+               if (ptp && ptp->tx_tstamp_en && !skb_is_gso(skb) &&
+                   atomic_dec_if_positive(&ptp->tx_avail) >= 0) {
+                       if (!bnxt_ptp_parse(skb, &ptp->tx_seqid)) {
+                               lflags |= cpu_to_le32(TX_BD_FLAGS_STAMP);
+                               skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
+                       } else {
+                               atomic_inc(&bp->ptp_cfg->tx_avail);
+                       }
+               }
        }
 
-       if (free_size == bp->tx_ring_size && length <= bp->tx_push_thresh) {
+       if (unlikely(skb->no_fcs))
+               lflags |= cpu_to_le32(TX_BD_FLAGS_NO_CRC);
+
+       if (free_size == bp->tx_ring_size && length <= bp->tx_push_thresh &&
+           !lflags) {
                struct tx_push_buffer *tx_push_buf = txr->tx_push;
                struct tx_push_bd *tx_push = &tx_push_buf->push_bd;
                struct tx_bd_ext *tx_push1 = &tx_push->txbd2;
@@ -590,6 +606,8 @@ normal_tx:
 
        netdev_tx_sent_queue(txq, skb->len);
 
+       skb_tx_timestamp(skb);
+
        /* Sync BD data before updating doorbell */
        wmb();
 
@@ -619,6 +637,9 @@ tx_done:
        return NETDEV_TX_OK;
 
 tx_dma_error:
+       if (BNXT_TX_PTP_IS_SET(lflags))
+               atomic_inc(&bp->ptp_cfg->tx_avail);
+
        last_frag = i;
 
        /* start back at beginning and unmap skb */
@@ -653,6 +674,7 @@ static void bnxt_tx_int(struct bnxt *bp, struct bnxt_napi *bnapi, int nr_pkts)
 
        for (i = 0; i < nr_pkts; i++) {
                struct bnxt_sw_tx_bd *tx_buf;
+               bool compl_deferred = false;
                struct sk_buff *skb;
                int j, last;
 
@@ -679,12 +701,21 @@ static void bnxt_tx_int(struct bnxt *bp, struct bnxt_napi *bnapi, int nr_pkts)
                                skb_frag_size(&skb_shinfo(skb)->frags[j]),
                                PCI_DMA_TODEVICE);
                }
+               if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS)) {
+                       if (bp->flags & BNXT_FLAG_CHIP_P5) {
+                               if (!bnxt_get_tx_ts_p5(bp, skb))
+                                       compl_deferred = true;
+                               else
+                                       atomic_inc(&bp->ptp_cfg->tx_avail);
+                       }
+               }
 
 next_tx_int:
                cons = NEXT_TX(cons);
 
                tx_bytes += skb->len;
-               dev_kfree_skb_any(skb);
+               if (!compl_deferred)
+                       dev_kfree_skb_any(skb);
        }
 
        netdev_tx_completed_queue(txq, nr_pkts, tx_bytes);
@@ -1706,9 +1737,9 @@ static int bnxt_rx_pkt(struct bnxt *bp, struct bnxt_cp_ring_info *cpr,
        u8 *data_ptr, agg_bufs, cmp_type;
        dma_addr_t dma_addr;
        struct sk_buff *skb;
+       u32 flags, misc;
        void *data;
        int rc = 0;
-       u32 misc;
 
        rxcmp = (struct rx_cmp *)
                        &cpr->cp_desc_ring[CP_RING(cp_cons)][CP_IDX(cp_cons)];
@@ -1806,7 +1837,8 @@ static int bnxt_rx_pkt(struct bnxt *bp, struct bnxt_cp_ring_info *cpr,
                goto next_rx_no_len;
        }
 
-       len = le32_to_cpu(rxcmp->rx_cmp_len_flags_type) >> RX_CMP_LEN_SHIFT;
+       flags = le32_to_cpu(rxcmp->rx_cmp_len_flags_type);
+       len = flags >> RX_CMP_LEN_SHIFT;
        dma_addr = rx_buf->mapping;
 
        if (bnxt_rx_xdp(bp, rxr, cons, data, &data_ptr, &len, event)) {
@@ -1883,6 +1915,24 @@ static int bnxt_rx_pkt(struct bnxt *bp, struct bnxt_cp_ring_info *cpr,
                }
        }
 
+       if (unlikely((flags & RX_CMP_FLAGS_ITYPES_MASK) ==
+                    RX_CMP_FLAGS_ITYPE_PTP_W_TS)) {
+               if (bp->flags & BNXT_FLAG_CHIP_P5) {
+                       u32 cmpl_ts = le32_to_cpu(rxcmp1->rx_cmp_timestamp);
+                       u64 ns, ts;
+
+                       if (!bnxt_get_rx_ts_p5(bp, &ts, cmpl_ts)) {
+                               struct bnxt_ptp_cfg *ptp = bp->ptp_cfg;
+
+                               spin_lock_bh(&ptp->ptp_lock);
+                               ns = timecounter_cyc2time(&ptp->tc, ts);
+                               spin_unlock_bh(&ptp->ptp_lock);
+                               memset(skb_hwtstamps(skb), 0,
+                                      sizeof(*skb_hwtstamps(skb)));
+                               skb_hwtstamps(skb)->hwtstamp = ns_to_ktime(ns);
+                       }
+               }
+       }
        bnxt_deliver_skb(bp, bnapi, skb);
        rc = 1;
 
@@ -2184,6 +2234,7 @@ static int bnxt_hwrm_handler(struct bnxt *bp, struct tx_cmp *txcmp)
        case CMPL_BASE_TYPE_HWRM_ASYNC_EVENT:
                bnxt_async_event_process(bp,
                                         (struct hwrm_async_event_cmpl *)txcmp);
+               break;
 
        default:
                break;
@@ -7391,6 +7442,56 @@ hwrm_func_resc_qcaps_exit:
        return rc;
 }
 
+/* bp->hwrm_cmd_lock already held. */
+static int __bnxt_hwrm_ptp_qcfg(struct bnxt *bp)
+{
+       struct hwrm_port_mac_ptp_qcfg_output *resp = bp->hwrm_cmd_resp_addr;
+       struct hwrm_port_mac_ptp_qcfg_input req = {0};
+       struct bnxt_ptp_cfg *ptp = bp->ptp_cfg;
+       u8 flags;
+       int rc;
+
+       if (bp->hwrm_spec_code < 0x10801) {
+               rc = -ENODEV;
+               goto no_ptp;
+       }
+
+       req.port_id = cpu_to_le16(bp->pf.port_id);
+       bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_PORT_MAC_PTP_QCFG, -1, -1);
+       rc = _hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT);
+       if (rc)
+               goto no_ptp;
+
+       flags = resp->flags;
+       if (!(flags & PORT_MAC_PTP_QCFG_RESP_FLAGS_HWRM_ACCESS)) {
+               rc = -ENODEV;
+               goto no_ptp;
+       }
+       if (!ptp) {
+               ptp = kzalloc(sizeof(*ptp), GFP_KERNEL);
+               if (!ptp)
+                       return -ENOMEM;
+               ptp->bp = bp;
+               bp->ptp_cfg = ptp;
+       }
+       if (flags & PORT_MAC_PTP_QCFG_RESP_FLAGS_PARTIAL_DIRECT_ACCESS_REF_CLOCK) {
+               ptp->refclk_regs[0] = le32_to_cpu(resp->ts_ref_clock_reg_lower);
+               ptp->refclk_regs[1] = le32_to_cpu(resp->ts_ref_clock_reg_upper);
+       } else if (bp->flags & BNXT_FLAG_CHIP_P5) {
+               ptp->refclk_regs[0] = BNXT_TS_REG_TIMESYNC_TS0_LOWER;
+               ptp->refclk_regs[1] = BNXT_TS_REG_TIMESYNC_TS0_UPPER;
+       } else {
+               rc = -ENODEV;
+               goto no_ptp;
+       }
+       return 0;
+
+no_ptp:
+       kfree(ptp);
+       bp->ptp_cfg = NULL;
+       return rc;
+}
+
 static int __bnxt_hwrm_func_qcaps(struct bnxt *bp)
 {
        int rc = 0;
@@ -7462,6 +7563,8 @@ static int __bnxt_hwrm_func_qcaps(struct bnxt *bp)
                bp->flags &= ~BNXT_FLAG_WOL_CAP;
                if (flags & FUNC_QCAPS_RESP_FLAGS_WOL_MAGICPKT_SUPPORTED)
                        bp->flags |= BNXT_FLAG_WOL_CAP;
+               if (flags & FUNC_QCAPS_RESP_FLAGS_PTP_SUPPORTED)
+                       __bnxt_hwrm_ptp_qcfg(bp);
        } else {
 #ifdef CONFIG_BNXT_SRIOV
                struct bnxt_vf_info *vf = &bp->vf;
@@ -10020,6 +10123,7 @@ static int __bnxt_open_nic(struct bnxt *bp, bool irq_re_init, bool link_re_init)
                }
        }
 
+       bnxt_ptp_start(bp);
        rc = bnxt_init_nic(bp, irq_re_init);
        if (rc) {
                netdev_err(bp->dev, "bnxt_init_nic err: %x\n", rc);
@@ -10335,6 +10439,12 @@ static int bnxt_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
                return bnxt_hwrm_port_phy_write(bp, mdio->phy_id, mdio->reg_num,
                                                mdio->val_in);
 
+       case SIOCSHWTSTAMP:
+               return bnxt_hwtstamp_set(dev, ifr);
+
+       case SIOCGHWTSTAMP:
+               return bnxt_hwtstamp_get(dev, ifr);
+
        default:
                /* do nothing */
                break;
@@ -12551,6 +12661,8 @@ static void bnxt_remove_one(struct pci_dev *pdev)
 
        if (BNXT_PF(bp))
                devlink_port_type_clear(&bp->dl_port);
+
+       bnxt_ptp_clear(bp);
        pci_disable_pcie_error_reporting(pdev);
        unregister_netdev(dev);
        clear_bit(BNXT_STATE_IN_FW_RESET, &bp->state);
@@ -12571,6 +12683,8 @@ static void bnxt_remove_one(struct pci_dev *pdev)
        bnxt_dcb_free(bp);
        kfree(bp->edev);
        bp->edev = NULL;
+       kfree(bp->ptp_cfg);
+       bp->ptp_cfg = NULL;
        kfree(bp->fw_health);
        bp->fw_health = NULL;
        bnxt_cleanup_pci(bp);
@@ -12863,60 +12977,35 @@ static int bnxt_init_mac_addr(struct bnxt *bp)
        return rc;
 }
 
-#define BNXT_VPD_LEN   512
 static void bnxt_vpd_read_info(struct bnxt *bp)
 {
        struct pci_dev *pdev = bp->pdev;
-       int i, len, pos, ro_size, size;
-       ssize_t vpd_size;
+       unsigned int vpd_size, kw_len;
+       int pos, size;
        u8 *vpd_data;
 
-       vpd_data = kmalloc(BNXT_VPD_LEN, GFP_KERNEL);
-       if (!vpd_data)
+       vpd_data = pci_vpd_alloc(pdev, &vpd_size);
+       if (IS_ERR(vpd_data)) {
+               pci_warn(pdev, "Unable to read VPD\n");
                return;
-
-       vpd_size = pci_read_vpd(pdev, 0, BNXT_VPD_LEN, vpd_data);
-       if (vpd_size <= 0) {
-               netdev_err(bp->dev, "Unable to read VPD\n");
-               goto exit;
-       }
-
-       i = pci_vpd_find_tag(vpd_data, vpd_size, PCI_VPD_LRDT_RO_DATA);
-       if (i < 0) {
-               netdev_err(bp->dev, "VPD READ-Only not found\n");
-               goto exit;
        }
 
-       ro_size = pci_vpd_lrdt_size(&vpd_data[i]);
-       i += PCI_VPD_LRDT_TAG_SIZE;
-       if (i + ro_size > vpd_size)
-               goto exit;
-
-       pos = pci_vpd_find_info_keyword(vpd_data, i, ro_size,
-                                       PCI_VPD_RO_KEYWORD_PARTNO);
+       pos = pci_vpd_find_ro_info_keyword(vpd_data, vpd_size,
+                                          PCI_VPD_RO_KEYWORD_PARTNO, &kw_len);
        if (pos < 0)
                goto read_sn;
 
-       len = pci_vpd_info_field_size(&vpd_data[pos]);
-       pos += PCI_VPD_INFO_FLD_HDR_SIZE;
-       if (len + pos > vpd_size)
-               goto read_sn;
-
-       size = min(len, BNXT_VPD_FLD_LEN - 1);
+       size = min_t(int, kw_len, BNXT_VPD_FLD_LEN - 1);
        memcpy(bp->board_partno, &vpd_data[pos], size);
 
 read_sn:
-       pos = pci_vpd_find_info_keyword(vpd_data, i, ro_size,
-                                       PCI_VPD_RO_KEYWORD_SERIALNO);
+       pos = pci_vpd_find_ro_info_keyword(vpd_data, vpd_size,
+                                          PCI_VPD_RO_KEYWORD_SERIALNO,
+                                          &kw_len);
        if (pos < 0)
                goto exit;
 
-       len = pci_vpd_info_field_size(&vpd_data[pos]);
-       pos += PCI_VPD_INFO_FLD_HDR_SIZE;
-       if (len + pos > vpd_size)
-               goto exit;
-
-       size = min(len, BNXT_VPD_FLD_LEN - 1);
+       size = min_t(int, kw_len, BNXT_VPD_FLD_LEN - 1);
        memcpy(bp->board_serialno, &vpd_data[pos], size);
 exit:
        kfree(vpd_data);
@@ -13132,6 +13221,11 @@ static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
                                   rc);
        }
 
+       if (bnxt_ptp_init(bp)) {
+               netdev_warn(dev, "PTP initialization failed.\n");
+               kfree(bp->ptp_cfg);
+               bp->ptp_cfg = NULL;
+       }
        bnxt_inv_fw_health_reg(bp);
        bnxt_dl_register(bp);
 
@@ -13161,6 +13255,8 @@ init_err_pci_clean:
        bnxt_free_hwrm_short_cmd_req(bp);
        bnxt_free_hwrm_resources(bp);
        bnxt_ethtool_free(bp);
+       kfree(bp->ptp_cfg);
+       bp->ptp_cfg = NULL;
        kfree(bp->fw_health);
        bp->fw_health = NULL;
        bnxt_cleanup_pci(bp);