15cb8805dSMilena Olech // SPDX-License-Identifier: GPL-2.0-only 25cb8805dSMilena Olech /* Copyright (C) 2024 Intel Corporation */ 35cb8805dSMilena Olech 45cb8805dSMilena Olech #include "idpf.h" 55cb8805dSMilena Olech #include "idpf_ptp.h" 65cb8805dSMilena Olech #include "idpf_virtchnl.h" 75cb8805dSMilena Olech 85cb8805dSMilena Olech /** 95cb8805dSMilena Olech * idpf_ptp_get_caps - Send virtchnl get ptp capabilities message 105cb8805dSMilena Olech * @adapter: Driver specific private structure 115cb8805dSMilena Olech * 125cb8805dSMilena Olech * Send virtchnl get PTP capabilities message. 135cb8805dSMilena Olech * 145cb8805dSMilena Olech * Return: 0 on success, -errno on failure. 155cb8805dSMilena Olech */ 165cb8805dSMilena Olech int idpf_ptp_get_caps(struct idpf_adapter *adapter) 175cb8805dSMilena Olech { 185cb8805dSMilena Olech struct virtchnl2_ptp_get_caps *recv_ptp_caps_msg __free(kfree) = NULL; 195cb8805dSMilena Olech struct virtchnl2_ptp_get_caps send_ptp_caps_msg = { 205cb8805dSMilena Olech .caps = cpu_to_le32(VIRTCHNL2_CAP_PTP_GET_DEVICE_CLK_TIME | 215cb8805dSMilena Olech VIRTCHNL2_CAP_PTP_GET_DEVICE_CLK_TIME_MB | 225cb8805dSMilena Olech VIRTCHNL2_CAP_PTP_GET_CROSS_TIME | 23d5dba8f7SMilena Olech VIRTCHNL2_CAP_PTP_SET_DEVICE_CLK_TIME_MB | 244901e83aSMilena Olech VIRTCHNL2_CAP_PTP_ADJ_DEVICE_CLK_MB | 254901e83aSMilena Olech VIRTCHNL2_CAP_PTP_TX_TSTAMPS_MB) 265cb8805dSMilena Olech }; 275cb8805dSMilena Olech struct idpf_vc_xn_params xn_params = { 285cb8805dSMilena Olech .vc_op = VIRTCHNL2_OP_PTP_GET_CAPS, 295cb8805dSMilena Olech .send_buf.iov_base = &send_ptp_caps_msg, 305cb8805dSMilena Olech .send_buf.iov_len = sizeof(send_ptp_caps_msg), 315cb8805dSMilena Olech .timeout_ms = IDPF_VC_XN_DEFAULT_TIMEOUT_MSEC, 325cb8805dSMilena Olech }; 33d5dba8f7SMilena Olech struct virtchnl2_ptp_clk_adj_reg_offsets clk_adj_offsets; 345cb8805dSMilena Olech struct virtchnl2_ptp_clk_reg_offsets clock_offsets; 355a27503dSMilena Olech struct idpf_ptp_secondary_mbx *scnd_mbx; 365cb8805dSMilena Olech struct idpf_ptp *ptp = adapter->ptp; 375cb8805dSMilena Olech enum idpf_ptp_access access_type; 385cb8805dSMilena Olech u32 temp_offset; 395cb8805dSMilena Olech int reply_sz; 405cb8805dSMilena Olech 415cb8805dSMilena Olech recv_ptp_caps_msg = kzalloc(sizeof(struct virtchnl2_ptp_get_caps), 425cb8805dSMilena Olech GFP_KERNEL); 435cb8805dSMilena Olech if (!recv_ptp_caps_msg) 445cb8805dSMilena Olech return -ENOMEM; 455cb8805dSMilena Olech 465cb8805dSMilena Olech xn_params.recv_buf.iov_base = recv_ptp_caps_msg; 475cb8805dSMilena Olech xn_params.recv_buf.iov_len = sizeof(*recv_ptp_caps_msg); 485cb8805dSMilena Olech 495cb8805dSMilena Olech reply_sz = idpf_vc_xn_exec(adapter, &xn_params); 505cb8805dSMilena Olech if (reply_sz < 0) 515cb8805dSMilena Olech return reply_sz; 525cb8805dSMilena Olech else if (reply_sz != sizeof(*recv_ptp_caps_msg)) 535cb8805dSMilena Olech return -EIO; 545cb8805dSMilena Olech 555cb8805dSMilena Olech ptp->caps = le32_to_cpu(recv_ptp_caps_msg->caps); 56d5dba8f7SMilena Olech ptp->base_incval = le64_to_cpu(recv_ptp_caps_msg->base_incval); 57d5dba8f7SMilena Olech ptp->max_adj = le32_to_cpu(recv_ptp_caps_msg->max_adj); 585cb8805dSMilena Olech 595a27503dSMilena Olech scnd_mbx = &ptp->secondary_mbx; 605a27503dSMilena Olech scnd_mbx->peer_mbx_q_id = le16_to_cpu(recv_ptp_caps_msg->peer_mbx_q_id); 615a27503dSMilena Olech 625a27503dSMilena Olech /* if the ptp_mb_q_id holds invalid value (0xffff), the secondary 635a27503dSMilena Olech * mailbox is not supported. 645a27503dSMilena Olech */ 655a27503dSMilena Olech scnd_mbx->valid = scnd_mbx->peer_mbx_q_id != 0xffff; 665a27503dSMilena Olech if (scnd_mbx->valid) 675a27503dSMilena Olech scnd_mbx->peer_id = recv_ptp_caps_msg->peer_id; 685a27503dSMilena Olech 695cb8805dSMilena Olech /* Determine the access type for the PTP features */ 705cb8805dSMilena Olech idpf_ptp_get_features_access(adapter); 715cb8805dSMilena Olech 725cb8805dSMilena Olech access_type = ptp->get_dev_clk_time_access; 735cb8805dSMilena Olech if (access_type != IDPF_PTP_DIRECT) 74d5dba8f7SMilena Olech goto discipline_clock; 755cb8805dSMilena Olech 765cb8805dSMilena Olech clock_offsets = recv_ptp_caps_msg->clk_offsets; 775cb8805dSMilena Olech 785cb8805dSMilena Olech temp_offset = le32_to_cpu(clock_offsets.dev_clk_ns_l); 795cb8805dSMilena Olech ptp->dev_clk_regs.dev_clk_ns_l = idpf_get_reg_addr(adapter, 805cb8805dSMilena Olech temp_offset); 815cb8805dSMilena Olech temp_offset = le32_to_cpu(clock_offsets.dev_clk_ns_h); 825cb8805dSMilena Olech ptp->dev_clk_regs.dev_clk_ns_h = idpf_get_reg_addr(adapter, 835cb8805dSMilena Olech temp_offset); 845cb8805dSMilena Olech temp_offset = le32_to_cpu(clock_offsets.phy_clk_ns_l); 855cb8805dSMilena Olech ptp->dev_clk_regs.phy_clk_ns_l = idpf_get_reg_addr(adapter, 865cb8805dSMilena Olech temp_offset); 875cb8805dSMilena Olech temp_offset = le32_to_cpu(clock_offsets.phy_clk_ns_h); 885cb8805dSMilena Olech ptp->dev_clk_regs.phy_clk_ns_h = idpf_get_reg_addr(adapter, 895cb8805dSMilena Olech temp_offset); 905cb8805dSMilena Olech temp_offset = le32_to_cpu(clock_offsets.cmd_sync_trigger); 915cb8805dSMilena Olech ptp->dev_clk_regs.cmd_sync = idpf_get_reg_addr(adapter, temp_offset); 925cb8805dSMilena Olech 93d5dba8f7SMilena Olech discipline_clock: 94d5dba8f7SMilena Olech access_type = ptp->adj_dev_clk_time_access; 95d5dba8f7SMilena Olech if (access_type != IDPF_PTP_DIRECT) 96d5dba8f7SMilena Olech return 0; 97d5dba8f7SMilena Olech 98d5dba8f7SMilena Olech clk_adj_offsets = recv_ptp_caps_msg->clk_adj_offsets; 99d5dba8f7SMilena Olech 100d5dba8f7SMilena Olech /* Device clock offsets */ 101d5dba8f7SMilena Olech temp_offset = le32_to_cpu(clk_adj_offsets.dev_clk_cmd_type); 102d5dba8f7SMilena Olech ptp->dev_clk_regs.cmd = idpf_get_reg_addr(adapter, temp_offset); 103d5dba8f7SMilena Olech temp_offset = le32_to_cpu(clk_adj_offsets.dev_clk_incval_l); 104d5dba8f7SMilena Olech ptp->dev_clk_regs.incval_l = idpf_get_reg_addr(adapter, temp_offset); 105d5dba8f7SMilena Olech temp_offset = le32_to_cpu(clk_adj_offsets.dev_clk_incval_h); 106d5dba8f7SMilena Olech ptp->dev_clk_regs.incval_h = idpf_get_reg_addr(adapter, temp_offset); 107d5dba8f7SMilena Olech temp_offset = le32_to_cpu(clk_adj_offsets.dev_clk_shadj_l); 108d5dba8f7SMilena Olech ptp->dev_clk_regs.shadj_l = idpf_get_reg_addr(adapter, temp_offset); 109d5dba8f7SMilena Olech temp_offset = le32_to_cpu(clk_adj_offsets.dev_clk_shadj_h); 110d5dba8f7SMilena Olech ptp->dev_clk_regs.shadj_h = idpf_get_reg_addr(adapter, temp_offset); 111d5dba8f7SMilena Olech 112d5dba8f7SMilena Olech /* PHY clock offsets */ 113d5dba8f7SMilena Olech temp_offset = le32_to_cpu(clk_adj_offsets.phy_clk_cmd_type); 114d5dba8f7SMilena Olech ptp->dev_clk_regs.phy_cmd = idpf_get_reg_addr(adapter, temp_offset); 115d5dba8f7SMilena Olech temp_offset = le32_to_cpu(clk_adj_offsets.phy_clk_incval_l); 116d5dba8f7SMilena Olech ptp->dev_clk_regs.phy_incval_l = idpf_get_reg_addr(adapter, 117d5dba8f7SMilena Olech temp_offset); 118d5dba8f7SMilena Olech temp_offset = le32_to_cpu(clk_adj_offsets.phy_clk_incval_h); 119d5dba8f7SMilena Olech ptp->dev_clk_regs.phy_incval_h = idpf_get_reg_addr(adapter, 120d5dba8f7SMilena Olech temp_offset); 121d5dba8f7SMilena Olech temp_offset = le32_to_cpu(clk_adj_offsets.phy_clk_shadj_l); 122d5dba8f7SMilena Olech ptp->dev_clk_regs.phy_shadj_l = idpf_get_reg_addr(adapter, temp_offset); 123d5dba8f7SMilena Olech temp_offset = le32_to_cpu(clk_adj_offsets.phy_clk_shadj_h); 124d5dba8f7SMilena Olech ptp->dev_clk_regs.phy_shadj_h = idpf_get_reg_addr(adapter, temp_offset); 125d5dba8f7SMilena Olech 1265cb8805dSMilena Olech return 0; 1275cb8805dSMilena Olech } 1285a27503dSMilena Olech 1295a27503dSMilena Olech /** 1305a27503dSMilena Olech * idpf_ptp_get_dev_clk_time - Send virtchnl get device clk time message 1315a27503dSMilena Olech * @adapter: Driver specific private structure 1325a27503dSMilena Olech * @dev_clk_time: Pointer to the device clock structure where the value is set 1335a27503dSMilena Olech * 1345a27503dSMilena Olech * Send virtchnl get time message to get the time of the clock. 1355a27503dSMilena Olech * 1365a27503dSMilena Olech * Return: 0 on success, -errno otherwise. 1375a27503dSMilena Olech */ 1385a27503dSMilena Olech int idpf_ptp_get_dev_clk_time(struct idpf_adapter *adapter, 1395a27503dSMilena Olech struct idpf_ptp_dev_timers *dev_clk_time) 1405a27503dSMilena Olech { 1415a27503dSMilena Olech struct virtchnl2_ptp_get_dev_clk_time get_dev_clk_time_msg; 1425a27503dSMilena Olech struct idpf_vc_xn_params xn_params = { 1435a27503dSMilena Olech .vc_op = VIRTCHNL2_OP_PTP_GET_DEV_CLK_TIME, 1445a27503dSMilena Olech .send_buf.iov_base = &get_dev_clk_time_msg, 1455a27503dSMilena Olech .send_buf.iov_len = sizeof(get_dev_clk_time_msg), 1465a27503dSMilena Olech .recv_buf.iov_base = &get_dev_clk_time_msg, 1475a27503dSMilena Olech .recv_buf.iov_len = sizeof(get_dev_clk_time_msg), 1485a27503dSMilena Olech .timeout_ms = IDPF_VC_XN_DEFAULT_TIMEOUT_MSEC, 1495a27503dSMilena Olech }; 1505a27503dSMilena Olech int reply_sz; 1515a27503dSMilena Olech u64 dev_time; 1525a27503dSMilena Olech 1535a27503dSMilena Olech reply_sz = idpf_vc_xn_exec(adapter, &xn_params); 1545a27503dSMilena Olech if (reply_sz < 0) 1555a27503dSMilena Olech return reply_sz; 1565a27503dSMilena Olech if (reply_sz != sizeof(get_dev_clk_time_msg)) 1575a27503dSMilena Olech return -EIO; 1585a27503dSMilena Olech 1595a27503dSMilena Olech dev_time = le64_to_cpu(get_dev_clk_time_msg.dev_time_ns); 1605a27503dSMilena Olech dev_clk_time->dev_clk_time_ns = dev_time; 1615a27503dSMilena Olech 1625a27503dSMilena Olech return 0; 1635a27503dSMilena Olech } 164d5dba8f7SMilena Olech 165d5dba8f7SMilena Olech /** 166d5dba8f7SMilena Olech * idpf_ptp_set_dev_clk_time - Send virtchnl set device time message 167d5dba8f7SMilena Olech * @adapter: Driver specific private structure 168d5dba8f7SMilena Olech * @time: New time value 169d5dba8f7SMilena Olech * 170d5dba8f7SMilena Olech * Send virtchnl set time message to set the time of the clock. 171d5dba8f7SMilena Olech * 172d5dba8f7SMilena Olech * Return: 0 on success, -errno otherwise. 173d5dba8f7SMilena Olech */ 174d5dba8f7SMilena Olech int idpf_ptp_set_dev_clk_time(struct idpf_adapter *adapter, u64 time) 175d5dba8f7SMilena Olech { 176d5dba8f7SMilena Olech struct virtchnl2_ptp_set_dev_clk_time set_dev_clk_time_msg = { 177d5dba8f7SMilena Olech .dev_time_ns = cpu_to_le64(time), 178d5dba8f7SMilena Olech }; 179d5dba8f7SMilena Olech struct idpf_vc_xn_params xn_params = { 180d5dba8f7SMilena Olech .vc_op = VIRTCHNL2_OP_PTP_SET_DEV_CLK_TIME, 181d5dba8f7SMilena Olech .send_buf.iov_base = &set_dev_clk_time_msg, 182d5dba8f7SMilena Olech .send_buf.iov_len = sizeof(set_dev_clk_time_msg), 183d5dba8f7SMilena Olech .recv_buf.iov_base = &set_dev_clk_time_msg, 184d5dba8f7SMilena Olech .recv_buf.iov_len = sizeof(set_dev_clk_time_msg), 185d5dba8f7SMilena Olech .timeout_ms = IDPF_VC_XN_DEFAULT_TIMEOUT_MSEC, 186d5dba8f7SMilena Olech }; 187d5dba8f7SMilena Olech int reply_sz; 188d5dba8f7SMilena Olech 189d5dba8f7SMilena Olech reply_sz = idpf_vc_xn_exec(adapter, &xn_params); 190d5dba8f7SMilena Olech if (reply_sz < 0) 191d5dba8f7SMilena Olech return reply_sz; 192d5dba8f7SMilena Olech if (reply_sz != sizeof(set_dev_clk_time_msg)) 193d5dba8f7SMilena Olech return -EIO; 194d5dba8f7SMilena Olech 195d5dba8f7SMilena Olech return 0; 196d5dba8f7SMilena Olech } 197d5dba8f7SMilena Olech 198d5dba8f7SMilena Olech /** 199d5dba8f7SMilena Olech * idpf_ptp_adj_dev_clk_time - Send virtchnl adj device clock time message 200d5dba8f7SMilena Olech * @adapter: Driver specific private structure 201d5dba8f7SMilena Olech * @delta: Offset in nanoseconds to adjust the time by 202d5dba8f7SMilena Olech * 203d5dba8f7SMilena Olech * Send virtchnl adj time message to adjust the clock by the indicated delta. 204d5dba8f7SMilena Olech * 205d5dba8f7SMilena Olech * Return: 0 on success, -errno otherwise. 206d5dba8f7SMilena Olech */ 207d5dba8f7SMilena Olech int idpf_ptp_adj_dev_clk_time(struct idpf_adapter *adapter, s64 delta) 208d5dba8f7SMilena Olech { 209d5dba8f7SMilena Olech struct virtchnl2_ptp_adj_dev_clk_time adj_dev_clk_time_msg = { 210d5dba8f7SMilena Olech .delta = cpu_to_le64(delta), 211d5dba8f7SMilena Olech }; 212d5dba8f7SMilena Olech struct idpf_vc_xn_params xn_params = { 213d5dba8f7SMilena Olech .vc_op = VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_TIME, 214d5dba8f7SMilena Olech .send_buf.iov_base = &adj_dev_clk_time_msg, 215d5dba8f7SMilena Olech .send_buf.iov_len = sizeof(adj_dev_clk_time_msg), 216d5dba8f7SMilena Olech .recv_buf.iov_base = &adj_dev_clk_time_msg, 217d5dba8f7SMilena Olech .recv_buf.iov_len = sizeof(adj_dev_clk_time_msg), 218d5dba8f7SMilena Olech .timeout_ms = IDPF_VC_XN_DEFAULT_TIMEOUT_MSEC, 219d5dba8f7SMilena Olech }; 220d5dba8f7SMilena Olech int reply_sz; 221d5dba8f7SMilena Olech 222d5dba8f7SMilena Olech reply_sz = idpf_vc_xn_exec(adapter, &xn_params); 223d5dba8f7SMilena Olech if (reply_sz < 0) 224d5dba8f7SMilena Olech return reply_sz; 225d5dba8f7SMilena Olech if (reply_sz != sizeof(adj_dev_clk_time_msg)) 226d5dba8f7SMilena Olech return -EIO; 227d5dba8f7SMilena Olech 228d5dba8f7SMilena Olech return 0; 229d5dba8f7SMilena Olech } 230d5dba8f7SMilena Olech 231d5dba8f7SMilena Olech /** 232d5dba8f7SMilena Olech * idpf_ptp_adj_dev_clk_fine - Send virtchnl adj time message 233d5dba8f7SMilena Olech * @adapter: Driver specific private structure 234d5dba8f7SMilena Olech * @incval: Source timer increment value per clock cycle 235d5dba8f7SMilena Olech * 236d5dba8f7SMilena Olech * Send virtchnl adj fine message to adjust the frequency of the clock by 237d5dba8f7SMilena Olech * incval. 238d5dba8f7SMilena Olech * 239d5dba8f7SMilena Olech * Return: 0 on success, -errno otherwise. 240d5dba8f7SMilena Olech */ 241d5dba8f7SMilena Olech int idpf_ptp_adj_dev_clk_fine(struct idpf_adapter *adapter, u64 incval) 242d5dba8f7SMilena Olech { 243d5dba8f7SMilena Olech struct virtchnl2_ptp_adj_dev_clk_fine adj_dev_clk_fine_msg = { 244d5dba8f7SMilena Olech .incval = cpu_to_le64(incval), 245d5dba8f7SMilena Olech }; 246d5dba8f7SMilena Olech struct idpf_vc_xn_params xn_params = { 247d5dba8f7SMilena Olech .vc_op = VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_FINE, 248d5dba8f7SMilena Olech .send_buf.iov_base = &adj_dev_clk_fine_msg, 249d5dba8f7SMilena Olech .send_buf.iov_len = sizeof(adj_dev_clk_fine_msg), 250d5dba8f7SMilena Olech .recv_buf.iov_base = &adj_dev_clk_fine_msg, 251d5dba8f7SMilena Olech .recv_buf.iov_len = sizeof(adj_dev_clk_fine_msg), 252d5dba8f7SMilena Olech .timeout_ms = IDPF_VC_XN_DEFAULT_TIMEOUT_MSEC, 253d5dba8f7SMilena Olech }; 254d5dba8f7SMilena Olech int reply_sz; 255d5dba8f7SMilena Olech 256d5dba8f7SMilena Olech reply_sz = idpf_vc_xn_exec(adapter, &xn_params); 257d5dba8f7SMilena Olech if (reply_sz < 0) 258d5dba8f7SMilena Olech return reply_sz; 259d5dba8f7SMilena Olech if (reply_sz != sizeof(adj_dev_clk_fine_msg)) 260d5dba8f7SMilena Olech return -EIO; 261d5dba8f7SMilena Olech 262d5dba8f7SMilena Olech return 0; 263d5dba8f7SMilena Olech } 2644901e83aSMilena Olech 2654901e83aSMilena Olech /** 2664901e83aSMilena Olech * idpf_ptp_get_vport_tstamps_caps - Send virtchnl to get tstamps caps for vport 2674901e83aSMilena Olech * @vport: Virtual port structure 2684901e83aSMilena Olech * 2694901e83aSMilena Olech * Send virtchnl get vport tstamps caps message to receive the set of tstamp 2704901e83aSMilena Olech * capabilities per vport. 2714901e83aSMilena Olech * 2724901e83aSMilena Olech * Return: 0 on success, -errno otherwise. 2734901e83aSMilena Olech */ 2744901e83aSMilena Olech int idpf_ptp_get_vport_tstamps_caps(struct idpf_vport *vport) 2754901e83aSMilena Olech { 2764901e83aSMilena Olech struct virtchnl2_ptp_get_vport_tx_tstamp_caps send_tx_tstamp_caps; 2774901e83aSMilena Olech struct virtchnl2_ptp_get_vport_tx_tstamp_caps *rcv_tx_tstamp_caps; 2784901e83aSMilena Olech struct virtchnl2_ptp_tx_tstamp_latch_caps tx_tstamp_latch_caps; 2794901e83aSMilena Olech struct idpf_ptp_vport_tx_tstamp_caps *tstamp_caps; 2804901e83aSMilena Olech struct idpf_ptp_tx_tstamp *ptp_tx_tstamp, *tmp; 2814901e83aSMilena Olech struct idpf_vc_xn_params xn_params = { 2824901e83aSMilena Olech .vc_op = VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP_CAPS, 2834901e83aSMilena Olech .send_buf.iov_base = &send_tx_tstamp_caps, 2844901e83aSMilena Olech .send_buf.iov_len = sizeof(send_tx_tstamp_caps), 2854901e83aSMilena Olech .recv_buf.iov_len = IDPF_CTLQ_MAX_BUF_LEN, 2864901e83aSMilena Olech .timeout_ms = IDPF_VC_XN_DEFAULT_TIMEOUT_MSEC, 2874901e83aSMilena Olech }; 2884901e83aSMilena Olech enum idpf_ptp_access tstamp_access, get_dev_clk_access; 2894901e83aSMilena Olech struct idpf_ptp *ptp = vport->adapter->ptp; 2904901e83aSMilena Olech struct list_head *head; 2914901e83aSMilena Olech int err = 0, reply_sz; 2924901e83aSMilena Olech u16 num_latches; 2934901e83aSMilena Olech u32 size; 2944901e83aSMilena Olech 2954901e83aSMilena Olech if (!ptp) 2964901e83aSMilena Olech return -EOPNOTSUPP; 2974901e83aSMilena Olech 2984901e83aSMilena Olech tstamp_access = ptp->tx_tstamp_access; 2994901e83aSMilena Olech get_dev_clk_access = ptp->get_dev_clk_time_access; 3004901e83aSMilena Olech if (tstamp_access == IDPF_PTP_NONE || 3014901e83aSMilena Olech get_dev_clk_access == IDPF_PTP_NONE) 3024901e83aSMilena Olech return -EOPNOTSUPP; 3034901e83aSMilena Olech 3044901e83aSMilena Olech rcv_tx_tstamp_caps = kzalloc(IDPF_CTLQ_MAX_BUF_LEN, GFP_KERNEL); 3054901e83aSMilena Olech if (!rcv_tx_tstamp_caps) 3064901e83aSMilena Olech return -ENOMEM; 3074901e83aSMilena Olech 3084901e83aSMilena Olech send_tx_tstamp_caps.vport_id = cpu_to_le32(vport->vport_id); 3094901e83aSMilena Olech xn_params.recv_buf.iov_base = rcv_tx_tstamp_caps; 3104901e83aSMilena Olech 3114901e83aSMilena Olech reply_sz = idpf_vc_xn_exec(vport->adapter, &xn_params); 3124901e83aSMilena Olech if (reply_sz < 0) { 3134901e83aSMilena Olech err = reply_sz; 3144901e83aSMilena Olech goto get_tstamp_caps_out; 3154901e83aSMilena Olech } 3164901e83aSMilena Olech 3174901e83aSMilena Olech num_latches = le16_to_cpu(rcv_tx_tstamp_caps->num_latches); 3184901e83aSMilena Olech size = struct_size(rcv_tx_tstamp_caps, tstamp_latches, num_latches); 3194901e83aSMilena Olech if (reply_sz != size) { 3204901e83aSMilena Olech err = -EIO; 3214901e83aSMilena Olech goto get_tstamp_caps_out; 3224901e83aSMilena Olech } 3234901e83aSMilena Olech 3244901e83aSMilena Olech size = struct_size(tstamp_caps, tx_tstamp_status, num_latches); 3254901e83aSMilena Olech tstamp_caps = kzalloc(size, GFP_KERNEL); 3264901e83aSMilena Olech if (!tstamp_caps) { 3274901e83aSMilena Olech err = -ENOMEM; 3284901e83aSMilena Olech goto get_tstamp_caps_out; 3294901e83aSMilena Olech } 3304901e83aSMilena Olech 331*1a49cf81SMilena Olech tstamp_caps->access = true; 3324901e83aSMilena Olech tstamp_caps->num_entries = num_latches; 333*1a49cf81SMilena Olech 3344901e83aSMilena Olech INIT_LIST_HEAD(&tstamp_caps->latches_in_use); 3354901e83aSMilena Olech INIT_LIST_HEAD(&tstamp_caps->latches_free); 3364901e83aSMilena Olech 3374901e83aSMilena Olech spin_lock_init(&tstamp_caps->latches_lock); 3384901e83aSMilena Olech spin_lock_init(&tstamp_caps->status_lock); 3394901e83aSMilena Olech 3404901e83aSMilena Olech tstamp_caps->tstamp_ns_lo_bit = rcv_tx_tstamp_caps->tstamp_ns_lo_bit; 3414901e83aSMilena Olech 3424901e83aSMilena Olech for (u16 i = 0; i < tstamp_caps->num_entries; i++) { 3434901e83aSMilena Olech __le32 offset_l, offset_h; 3444901e83aSMilena Olech 3454901e83aSMilena Olech ptp_tx_tstamp = kzalloc(sizeof(*ptp_tx_tstamp), GFP_KERNEL); 3464901e83aSMilena Olech if (!ptp_tx_tstamp) { 3474901e83aSMilena Olech err = -ENOMEM; 3484901e83aSMilena Olech goto err_free_ptp_tx_stamp_list; 3494901e83aSMilena Olech } 3504901e83aSMilena Olech 3514901e83aSMilena Olech tx_tstamp_latch_caps = rcv_tx_tstamp_caps->tstamp_latches[i]; 3524901e83aSMilena Olech 3534901e83aSMilena Olech if (tstamp_access != IDPF_PTP_DIRECT) 3544901e83aSMilena Olech goto skip_offsets; 3554901e83aSMilena Olech 3564901e83aSMilena Olech offset_l = tx_tstamp_latch_caps.tx_latch_reg_offset_l; 3574901e83aSMilena Olech offset_h = tx_tstamp_latch_caps.tx_latch_reg_offset_h; 3584901e83aSMilena Olech ptp_tx_tstamp->tx_latch_reg_offset_l = le32_to_cpu(offset_l); 3594901e83aSMilena Olech ptp_tx_tstamp->tx_latch_reg_offset_h = le32_to_cpu(offset_h); 3604901e83aSMilena Olech 3614901e83aSMilena Olech skip_offsets: 3624901e83aSMilena Olech ptp_tx_tstamp->idx = tx_tstamp_latch_caps.index; 3634901e83aSMilena Olech 3644901e83aSMilena Olech list_add(&ptp_tx_tstamp->list_member, 3654901e83aSMilena Olech &tstamp_caps->latches_free); 3664901e83aSMilena Olech 3674901e83aSMilena Olech tstamp_caps->tx_tstamp_status[i].state = IDPF_PTP_FREE; 3684901e83aSMilena Olech } 3694901e83aSMilena Olech 3704901e83aSMilena Olech vport->tx_tstamp_caps = tstamp_caps; 3714901e83aSMilena Olech kfree(rcv_tx_tstamp_caps); 3724901e83aSMilena Olech 3734901e83aSMilena Olech return 0; 3744901e83aSMilena Olech 3754901e83aSMilena Olech err_free_ptp_tx_stamp_list: 3764901e83aSMilena Olech head = &tstamp_caps->latches_free; 3774901e83aSMilena Olech list_for_each_entry_safe(ptp_tx_tstamp, tmp, head, list_member) { 3784901e83aSMilena Olech list_del(&ptp_tx_tstamp->list_member); 3794901e83aSMilena Olech kfree(ptp_tx_tstamp); 3804901e83aSMilena Olech } 3814901e83aSMilena Olech 3824901e83aSMilena Olech kfree(tstamp_caps); 3834901e83aSMilena Olech get_tstamp_caps_out: 3844901e83aSMilena Olech kfree(rcv_tx_tstamp_caps); 3854901e83aSMilena Olech 3864901e83aSMilena Olech return err; 3874901e83aSMilena Olech } 388*1a49cf81SMilena Olech 389*1a49cf81SMilena Olech /** 390*1a49cf81SMilena Olech * idpf_ptp_update_tstamp_tracker - Update the Tx timestamp tracker based on 391*1a49cf81SMilena Olech * the skb compatibility. 392*1a49cf81SMilena Olech * @caps: Tx timestamp capabilities that monitor the latch status 393*1a49cf81SMilena Olech * @skb: skb for which the tstamp value is returned through virtchnl message 394*1a49cf81SMilena Olech * @current_state: Current state of the Tx timestamp latch 395*1a49cf81SMilena Olech * @expected_state: Expected state of the Tx timestamp latch 396*1a49cf81SMilena Olech * 397*1a49cf81SMilena Olech * Find a proper skb tracker for which the Tx timestamp is received and change 398*1a49cf81SMilena Olech * the state to expected value. 399*1a49cf81SMilena Olech * 400*1a49cf81SMilena Olech * Return: true if the tracker has been found and updated, false otherwise. 401*1a49cf81SMilena Olech */ 402*1a49cf81SMilena Olech static bool 403*1a49cf81SMilena Olech idpf_ptp_update_tstamp_tracker(struct idpf_ptp_vport_tx_tstamp_caps *caps, 404*1a49cf81SMilena Olech struct sk_buff *skb, 405*1a49cf81SMilena Olech enum idpf_ptp_tx_tstamp_state current_state, 406*1a49cf81SMilena Olech enum idpf_ptp_tx_tstamp_state expected_state) 407*1a49cf81SMilena Olech { 408*1a49cf81SMilena Olech bool updated = false; 409*1a49cf81SMilena Olech 410*1a49cf81SMilena Olech spin_lock(&caps->status_lock); 411*1a49cf81SMilena Olech for (u16 i = 0; i < caps->num_entries; i++) { 412*1a49cf81SMilena Olech struct idpf_ptp_tx_tstamp_status *status; 413*1a49cf81SMilena Olech 414*1a49cf81SMilena Olech status = &caps->tx_tstamp_status[i]; 415*1a49cf81SMilena Olech 416*1a49cf81SMilena Olech if (skb == status->skb && status->state == current_state) { 417*1a49cf81SMilena Olech status->state = expected_state; 418*1a49cf81SMilena Olech updated = true; 419*1a49cf81SMilena Olech break; 420*1a49cf81SMilena Olech } 421*1a49cf81SMilena Olech } 422*1a49cf81SMilena Olech spin_unlock(&caps->status_lock); 423*1a49cf81SMilena Olech 424*1a49cf81SMilena Olech return updated; 425*1a49cf81SMilena Olech } 426*1a49cf81SMilena Olech 427*1a49cf81SMilena Olech /** 428*1a49cf81SMilena Olech * idpf_ptp_get_tstamp_value - Get the Tx timestamp value and provide it 429*1a49cf81SMilena Olech * back to the skb. 430*1a49cf81SMilena Olech * @vport: Virtual port structure 431*1a49cf81SMilena Olech * @tstamp_latch: Tx timestamp latch structure fulfilled by the Control Plane 432*1a49cf81SMilena Olech * @ptp_tx_tstamp: Tx timestamp latch to add to the free list 433*1a49cf81SMilena Olech * 434*1a49cf81SMilena Olech * Read the value of the Tx timestamp for a given latch received from the 435*1a49cf81SMilena Olech * Control Plane, extend it to 64 bit and provide back to the skb. 436*1a49cf81SMilena Olech * 437*1a49cf81SMilena Olech * Return: 0 on success, -errno otherwise. 438*1a49cf81SMilena Olech */ 439*1a49cf81SMilena Olech static int 440*1a49cf81SMilena Olech idpf_ptp_get_tstamp_value(struct idpf_vport *vport, 441*1a49cf81SMilena Olech struct virtchnl2_ptp_tx_tstamp_latch *tstamp_latch, 442*1a49cf81SMilena Olech struct idpf_ptp_tx_tstamp *ptp_tx_tstamp) 443*1a49cf81SMilena Olech { 444*1a49cf81SMilena Olech struct idpf_ptp_vport_tx_tstamp_caps *tx_tstamp_caps; 445*1a49cf81SMilena Olech struct skb_shared_hwtstamps shhwtstamps; 446*1a49cf81SMilena Olech bool state_upd = false; 447*1a49cf81SMilena Olech u8 tstamp_ns_lo_bit; 448*1a49cf81SMilena Olech u64 tstamp; 449*1a49cf81SMilena Olech 450*1a49cf81SMilena Olech tx_tstamp_caps = vport->tx_tstamp_caps; 451*1a49cf81SMilena Olech tstamp_ns_lo_bit = tx_tstamp_caps->tstamp_ns_lo_bit; 452*1a49cf81SMilena Olech 453*1a49cf81SMilena Olech ptp_tx_tstamp->tstamp = le64_to_cpu(tstamp_latch->tstamp); 454*1a49cf81SMilena Olech ptp_tx_tstamp->tstamp >>= tstamp_ns_lo_bit; 455*1a49cf81SMilena Olech 456*1a49cf81SMilena Olech state_upd = idpf_ptp_update_tstamp_tracker(tx_tstamp_caps, 457*1a49cf81SMilena Olech ptp_tx_tstamp->skb, 458*1a49cf81SMilena Olech IDPF_PTP_READ_VALUE, 459*1a49cf81SMilena Olech IDPF_PTP_FREE); 460*1a49cf81SMilena Olech if (!state_upd) 461*1a49cf81SMilena Olech return -EINVAL; 462*1a49cf81SMilena Olech 463*1a49cf81SMilena Olech tstamp = idpf_ptp_extend_ts(vport, ptp_tx_tstamp->tstamp); 464*1a49cf81SMilena Olech shhwtstamps.hwtstamp = ns_to_ktime(tstamp); 465*1a49cf81SMilena Olech skb_tstamp_tx(ptp_tx_tstamp->skb, &shhwtstamps); 466*1a49cf81SMilena Olech consume_skb(ptp_tx_tstamp->skb); 467*1a49cf81SMilena Olech 468*1a49cf81SMilena Olech list_add(&ptp_tx_tstamp->list_member, 469*1a49cf81SMilena Olech &tx_tstamp_caps->latches_free); 470*1a49cf81SMilena Olech 471*1a49cf81SMilena Olech return 0; 472*1a49cf81SMilena Olech } 473*1a49cf81SMilena Olech 474*1a49cf81SMilena Olech /** 475*1a49cf81SMilena Olech * idpf_ptp_get_tx_tstamp_async_handler - Async callback for getting Tx tstamps 476*1a49cf81SMilena Olech * @adapter: Driver specific private structure 477*1a49cf81SMilena Olech * @xn: transaction for message 478*1a49cf81SMilena Olech * @ctlq_msg: received message 479*1a49cf81SMilena Olech * 480*1a49cf81SMilena Olech * Read the tstamps Tx tstamp values from a received message and put them 481*1a49cf81SMilena Olech * directly to the skb. The number of timestamps to read is specified by 482*1a49cf81SMilena Olech * the virtchnl message. 483*1a49cf81SMilena Olech * 484*1a49cf81SMilena Olech * Return: 0 on success, -errno otherwise. 485*1a49cf81SMilena Olech */ 486*1a49cf81SMilena Olech static int 487*1a49cf81SMilena Olech idpf_ptp_get_tx_tstamp_async_handler(struct idpf_adapter *adapter, 488*1a49cf81SMilena Olech struct idpf_vc_xn *xn, 489*1a49cf81SMilena Olech const struct idpf_ctlq_msg *ctlq_msg) 490*1a49cf81SMilena Olech { 491*1a49cf81SMilena Olech struct virtchnl2_ptp_get_vport_tx_tstamp_latches *recv_tx_tstamp_msg; 492*1a49cf81SMilena Olech struct idpf_ptp_vport_tx_tstamp_caps *tx_tstamp_caps; 493*1a49cf81SMilena Olech struct virtchnl2_ptp_tx_tstamp_latch tstamp_latch; 494*1a49cf81SMilena Olech struct idpf_ptp_tx_tstamp *tx_tstamp, *tmp; 495*1a49cf81SMilena Olech struct idpf_vport *tstamp_vport = NULL; 496*1a49cf81SMilena Olech struct list_head *head; 497*1a49cf81SMilena Olech u16 num_latches; 498*1a49cf81SMilena Olech u32 vport_id; 499*1a49cf81SMilena Olech int err = 0; 500*1a49cf81SMilena Olech 501*1a49cf81SMilena Olech recv_tx_tstamp_msg = ctlq_msg->ctx.indirect.payload->va; 502*1a49cf81SMilena Olech vport_id = le32_to_cpu(recv_tx_tstamp_msg->vport_id); 503*1a49cf81SMilena Olech 504*1a49cf81SMilena Olech idpf_for_each_vport(adapter, vport) { 505*1a49cf81SMilena Olech if (!vport) 506*1a49cf81SMilena Olech continue; 507*1a49cf81SMilena Olech 508*1a49cf81SMilena Olech if (vport->vport_id == vport_id) { 509*1a49cf81SMilena Olech tstamp_vport = vport; 510*1a49cf81SMilena Olech break; 511*1a49cf81SMilena Olech } 512*1a49cf81SMilena Olech } 513*1a49cf81SMilena Olech 514*1a49cf81SMilena Olech if (!tstamp_vport || !tstamp_vport->tx_tstamp_caps) 515*1a49cf81SMilena Olech return -EINVAL; 516*1a49cf81SMilena Olech 517*1a49cf81SMilena Olech tx_tstamp_caps = tstamp_vport->tx_tstamp_caps; 518*1a49cf81SMilena Olech num_latches = le16_to_cpu(recv_tx_tstamp_msg->num_latches); 519*1a49cf81SMilena Olech 520*1a49cf81SMilena Olech spin_lock_bh(&tx_tstamp_caps->latches_lock); 521*1a49cf81SMilena Olech head = &tx_tstamp_caps->latches_in_use; 522*1a49cf81SMilena Olech 523*1a49cf81SMilena Olech for (u16 i = 0; i < num_latches; i++) { 524*1a49cf81SMilena Olech tstamp_latch = recv_tx_tstamp_msg->tstamp_latches[i]; 525*1a49cf81SMilena Olech 526*1a49cf81SMilena Olech if (!tstamp_latch.valid) 527*1a49cf81SMilena Olech continue; 528*1a49cf81SMilena Olech 529*1a49cf81SMilena Olech if (list_empty(head)) { 530*1a49cf81SMilena Olech err = -ENOBUFS; 531*1a49cf81SMilena Olech goto unlock; 532*1a49cf81SMilena Olech } 533*1a49cf81SMilena Olech 534*1a49cf81SMilena Olech list_for_each_entry_safe(tx_tstamp, tmp, head, list_member) { 535*1a49cf81SMilena Olech if (tstamp_latch.index == tx_tstamp->idx) { 536*1a49cf81SMilena Olech list_del(&tx_tstamp->list_member); 537*1a49cf81SMilena Olech err = idpf_ptp_get_tstamp_value(tstamp_vport, 538*1a49cf81SMilena Olech &tstamp_latch, 539*1a49cf81SMilena Olech tx_tstamp); 540*1a49cf81SMilena Olech if (err) 541*1a49cf81SMilena Olech goto unlock; 542*1a49cf81SMilena Olech 543*1a49cf81SMilena Olech break; 544*1a49cf81SMilena Olech } 545*1a49cf81SMilena Olech } 546*1a49cf81SMilena Olech } 547*1a49cf81SMilena Olech 548*1a49cf81SMilena Olech unlock: 549*1a49cf81SMilena Olech spin_unlock_bh(&tx_tstamp_caps->latches_lock); 550*1a49cf81SMilena Olech 551*1a49cf81SMilena Olech return err; 552*1a49cf81SMilena Olech } 553*1a49cf81SMilena Olech 554*1a49cf81SMilena Olech /** 555*1a49cf81SMilena Olech * idpf_ptp_get_tx_tstamp - Send virtchnl get Tx timestamp latches message 556*1a49cf81SMilena Olech * @vport: Virtual port structure 557*1a49cf81SMilena Olech * 558*1a49cf81SMilena Olech * Send virtchnl get Tx tstamp message to read the value of the HW timestamp. 559*1a49cf81SMilena Olech * The message contains a list of indexes set in the Tx descriptors. 560*1a49cf81SMilena Olech * 561*1a49cf81SMilena Olech * Return: 0 on success, -errno otherwise. 562*1a49cf81SMilena Olech */ 563*1a49cf81SMilena Olech int idpf_ptp_get_tx_tstamp(struct idpf_vport *vport) 564*1a49cf81SMilena Olech { 565*1a49cf81SMilena Olech struct virtchnl2_ptp_get_vport_tx_tstamp_latches *send_tx_tstamp_msg; 566*1a49cf81SMilena Olech struct idpf_ptp_vport_tx_tstamp_caps *tx_tstamp_caps; 567*1a49cf81SMilena Olech struct idpf_vc_xn_params xn_params = { 568*1a49cf81SMilena Olech .vc_op = VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP, 569*1a49cf81SMilena Olech .timeout_ms = IDPF_VC_XN_DEFAULT_TIMEOUT_MSEC, 570*1a49cf81SMilena Olech .async = true, 571*1a49cf81SMilena Olech .async_handler = idpf_ptp_get_tx_tstamp_async_handler, 572*1a49cf81SMilena Olech }; 573*1a49cf81SMilena Olech struct idpf_ptp_tx_tstamp *ptp_tx_tstamp; 574*1a49cf81SMilena Olech int reply_sz, size, msg_size; 575*1a49cf81SMilena Olech struct list_head *head; 576*1a49cf81SMilena Olech bool state_upd; 577*1a49cf81SMilena Olech u16 id = 0; 578*1a49cf81SMilena Olech 579*1a49cf81SMilena Olech tx_tstamp_caps = vport->tx_tstamp_caps; 580*1a49cf81SMilena Olech head = &tx_tstamp_caps->latches_in_use; 581*1a49cf81SMilena Olech 582*1a49cf81SMilena Olech size = struct_size(send_tx_tstamp_msg, tstamp_latches, 583*1a49cf81SMilena Olech tx_tstamp_caps->num_entries); 584*1a49cf81SMilena Olech send_tx_tstamp_msg = kzalloc(size, GFP_KERNEL); 585*1a49cf81SMilena Olech if (!send_tx_tstamp_msg) 586*1a49cf81SMilena Olech return -ENOMEM; 587*1a49cf81SMilena Olech 588*1a49cf81SMilena Olech spin_lock_bh(&tx_tstamp_caps->latches_lock); 589*1a49cf81SMilena Olech list_for_each_entry(ptp_tx_tstamp, head, list_member) { 590*1a49cf81SMilena Olech u8 idx; 591*1a49cf81SMilena Olech 592*1a49cf81SMilena Olech state_upd = idpf_ptp_update_tstamp_tracker(tx_tstamp_caps, 593*1a49cf81SMilena Olech ptp_tx_tstamp->skb, 594*1a49cf81SMilena Olech IDPF_PTP_REQUEST, 595*1a49cf81SMilena Olech IDPF_PTP_READ_VALUE); 596*1a49cf81SMilena Olech if (!state_upd) 597*1a49cf81SMilena Olech continue; 598*1a49cf81SMilena Olech 599*1a49cf81SMilena Olech idx = ptp_tx_tstamp->idx; 600*1a49cf81SMilena Olech send_tx_tstamp_msg->tstamp_latches[id].index = idx; 601*1a49cf81SMilena Olech id++; 602*1a49cf81SMilena Olech } 603*1a49cf81SMilena Olech spin_unlock_bh(&tx_tstamp_caps->latches_lock); 604*1a49cf81SMilena Olech 605*1a49cf81SMilena Olech msg_size = struct_size(send_tx_tstamp_msg, tstamp_latches, id); 606*1a49cf81SMilena Olech send_tx_tstamp_msg->vport_id = cpu_to_le32(vport->vport_id); 607*1a49cf81SMilena Olech send_tx_tstamp_msg->num_latches = cpu_to_le16(id); 608*1a49cf81SMilena Olech xn_params.send_buf.iov_base = send_tx_tstamp_msg; 609*1a49cf81SMilena Olech xn_params.send_buf.iov_len = msg_size; 610*1a49cf81SMilena Olech 611*1a49cf81SMilena Olech reply_sz = idpf_vc_xn_exec(vport->adapter, &xn_params); 612*1a49cf81SMilena Olech kfree(send_tx_tstamp_msg); 613*1a49cf81SMilena Olech 614*1a49cf81SMilena Olech return min(reply_sz, 0); 615*1a49cf81SMilena Olech } 616