liquidio VF timestamp
[sfrench/cifs-2.6.git] / drivers / net / ethernet / cavium / liquidio / lio_vf_main.c
index 97e9b6b5b0821a0848399318bbce3944b94461c9..dc0e1f6c743e3fd70baaf0e61be81f251f278938 100644 (file)
@@ -42,6 +42,7 @@ MODULE_PARM_DESC(debug, "NETIF_MSG debug bits");
 #define   LIO_IFSTATE_DROQ_OPS             0x01
 #define   LIO_IFSTATE_REGISTERED           0x02
 #define   LIO_IFSTATE_RUNNING              0x04
+#define   LIO_IFSTATE_RX_TIMESTAMP_ENABLED 0x08
 
 struct liquidio_if_cfg_context {
        int octeon_id;
@@ -65,6 +66,12 @@ struct liquidio_rx_ctl_context {
        int cond;
 };
 
+struct oct_timestamp_resp {
+       u64 rh;
+       u64 timestamp;
+       u64 status;
+};
+
 union tx_info {
        u64 u64;
        struct {
@@ -1894,6 +1901,169 @@ static int liquidio_change_mtu(struct net_device *netdev, int new_mtu)
        return 0;
 }
 
+/**
+ * \brief Handler for SIOCSHWTSTAMP ioctl
+ * @param netdev network device
+ * @param ifr interface request
+ * @param cmd command
+ */
+static int hwtstamp_ioctl(struct net_device *netdev, struct ifreq *ifr)
+{
+       struct lio *lio = GET_LIO(netdev);
+       struct hwtstamp_config conf;
+
+       if (copy_from_user(&conf, ifr->ifr_data, sizeof(conf)))
+               return -EFAULT;
+
+       if (conf.flags)
+               return -EINVAL;
+
+       switch (conf.tx_type) {
+       case HWTSTAMP_TX_ON:
+       case HWTSTAMP_TX_OFF:
+               break;
+       default:
+               return -ERANGE;
+       }
+
+       switch (conf.rx_filter) {
+       case HWTSTAMP_FILTER_NONE:
+               break;
+       case HWTSTAMP_FILTER_ALL:
+       case HWTSTAMP_FILTER_SOME:
+       case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
+       case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
+       case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
+       case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
+       case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
+       case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
+       case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
+       case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
+       case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:
+       case HWTSTAMP_FILTER_PTP_V2_EVENT:
+       case HWTSTAMP_FILTER_PTP_V2_SYNC:
+       case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
+               conf.rx_filter = HWTSTAMP_FILTER_ALL;
+               break;
+       default:
+               return -ERANGE;
+       }
+
+       if (conf.rx_filter == HWTSTAMP_FILTER_ALL)
+               ifstate_set(lio, LIO_IFSTATE_RX_TIMESTAMP_ENABLED);
+
+       else
+               ifstate_reset(lio, LIO_IFSTATE_RX_TIMESTAMP_ENABLED);
+
+       return copy_to_user(ifr->ifr_data, &conf, sizeof(conf)) ? -EFAULT : 0;
+}
+
+/**
+ * \brief ioctl handler
+ * @param netdev network device
+ * @param ifr interface request
+ * @param cmd command
+ */
+static int liquidio_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
+{
+       switch (cmd) {
+       case SIOCSHWTSTAMP:
+               return hwtstamp_ioctl(netdev, ifr);
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
+static void handle_timestamp(struct octeon_device *oct, u32 status, void *buf)
+{
+       struct sk_buff *skb = (struct sk_buff *)buf;
+       struct octnet_buf_free_info *finfo;
+       struct oct_timestamp_resp *resp;
+       struct octeon_soft_command *sc;
+       struct lio *lio;
+
+       finfo = (struct octnet_buf_free_info *)skb->cb;
+       lio = finfo->lio;
+       sc = finfo->sc;
+       oct = lio->oct_dev;
+       resp = (struct oct_timestamp_resp *)sc->virtrptr;
+
+       if (status != OCTEON_REQUEST_DONE) {
+               dev_err(&oct->pci_dev->dev, "Tx timestamp instruction failed. Status: %llx\n",
+                       CVM_CAST64(status));
+               resp->timestamp = 0;
+       }
+
+       octeon_swap_8B_data(&resp->timestamp, 1);
+
+       if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS)) {
+               struct skb_shared_hwtstamps ts;
+               u64 ns = resp->timestamp;
+
+               netif_info(lio, tx_done, lio->netdev,
+                          "Got resulting SKBTX_HW_TSTAMP skb=%p ns=%016llu\n",
+                          skb, (unsigned long long)ns);
+               ts.hwtstamp = ns_to_ktime(ns + lio->ptp_adjust);
+               skb_tstamp_tx(skb, &ts);
+       }
+
+       octeon_free_soft_command(oct, sc);
+       tx_buffer_free(skb);
+}
+
+/* \brief Send a data packet that will be timestamped
+ * @param oct octeon device
+ * @param ndata pointer to network data
+ * @param finfo pointer to private network data
+ */
+static int send_nic_timestamp_pkt(struct octeon_device *oct,
+                                 struct octnic_data_pkt *ndata,
+                                 struct octnet_buf_free_info *finfo)
+{
+       struct octeon_soft_command *sc;
+       int ring_doorbell;
+       struct lio *lio;
+       int retval;
+       u32 len;
+
+       lio = finfo->lio;
+
+       sc = octeon_alloc_soft_command_resp(oct, &ndata->cmd,
+                                           sizeof(struct oct_timestamp_resp));
+       finfo->sc = sc;
+
+       if (!sc) {
+               dev_err(&oct->pci_dev->dev, "No memory for timestamped data packet\n");
+               return IQ_SEND_FAILED;
+       }
+
+       if (ndata->reqtype == REQTYPE_NORESP_NET)
+               ndata->reqtype = REQTYPE_RESP_NET;
+       else if (ndata->reqtype == REQTYPE_NORESP_NET_SG)
+               ndata->reqtype = REQTYPE_RESP_NET_SG;
+
+       sc->callback = handle_timestamp;
+       sc->callback_arg = finfo->skb;
+       sc->iq_no = ndata->q_no;
+
+       len = (u32)((struct octeon_instr_ih3 *)(&sc->cmd.cmd3.ih3))->dlengsz;
+
+       ring_doorbell = 1;
+
+       retval = octeon_send_command(oct, sc->iq_no, ring_doorbell, &sc->cmd,
+                                    sc, len, ndata->reqtype);
+
+       if (retval == IQ_SEND_FAILED) {
+               dev_err(&oct->pci_dev->dev, "timestamp data packet failed status: %x\n",
+                       retval);
+               octeon_free_soft_command(oct, sc);
+       } else {
+               netif_info(lio, tx_queued, lio->netdev, "Queued timestamp packet\n");
+       }
+
+       return retval;
+}
+
 /** \brief Transmit networks packets to the Octeon interface
  * @param skbuff   skbuff struct to be passed to network layer.
  * @param netdev   pointer to network device
@@ -1986,6 +2156,10 @@ static int liquidio_xmit(struct sk_buff *skb, struct net_device *netdev)
                        cmdsetup.s.transport_csum = 1;
                }
        }
+       if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)) {
+               skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
+               cmdsetup.s.timestamp = 1;
+       }
 
        if (!skb_shinfo(skb)->nr_frags) {
                cmdsetup.s.u.datasize = skb->len;
@@ -2110,7 +2284,10 @@ static int liquidio_xmit(struct sk_buff *skb, struct net_device *netdev)
                irh->vlan = skb_vlan_tag_get(skb) & VLAN_VID_MASK;
        }
 
-       status = octnet_send_nic_data_pkt(oct, &ndata);
+       if (unlikely(cmdsetup.s.timestamp))
+               status = send_nic_timestamp_pkt(oct, &ndata, finfo);
+       else
+               status = octnet_send_nic_data_pkt(oct, &ndata);
        if (status == IQ_SEND_FAILED)
                goto lio_xmit_failed;
 
@@ -2382,6 +2559,7 @@ static const struct net_device_ops lionetdevops = {
        .ndo_vlan_rx_add_vid    = liquidio_vlan_rx_add_vid,
        .ndo_vlan_rx_kill_vid   = liquidio_vlan_rx_kill_vid,
        .ndo_change_mtu         = liquidio_change_mtu,
+       .ndo_do_ioctl           = liquidio_ioctl,
        .ndo_fix_features       = liquidio_fix_features,
        .ndo_set_features       = liquidio_set_features,
        .ndo_udp_tunnel_add     = liquidio_add_vxlan_port,