diff options
-rw-r--r-- | drivers/net/ethernet/intel/fm10k/Makefile | 2 | ||||
-rw-r--r-- | drivers/net/ethernet/intel/fm10k/fm10k.h | 37 | ||||
-rw-r--r-- | drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c | 30 | ||||
-rw-r--r-- | drivers/net/ethernet/intel/fm10k/fm10k_main.c | 20 | ||||
-rw-r--r-- | drivers/net/ethernet/intel/fm10k/fm10k_netdev.c | 20 | ||||
-rw-r--r-- | drivers/net/ethernet/intel/fm10k/fm10k_pci.c | 112 | ||||
-rw-r--r-- | drivers/net/ethernet/intel/fm10k/fm10k_ptp.c | 463 |
7 files changed, 683 insertions, 1 deletions
diff --git a/drivers/net/ethernet/intel/fm10k/Makefile b/drivers/net/ethernet/intel/fm10k/Makefile index fbc0e092077c..08859dd220a8 100644 --- a/drivers/net/ethernet/intel/fm10k/Makefile +++ b/drivers/net/ethernet/intel/fm10k/Makefile | |||
@@ -30,4 +30,4 @@ obj-$(CONFIG_FM10K) += fm10k.o | |||
30 | fm10k-objs := fm10k_main.o fm10k_common.o fm10k_pci.o \ | 30 | fm10k-objs := fm10k_main.o fm10k_common.o fm10k_pci.o \ |
31 | fm10k_netdev.o fm10k_ethtool.o fm10k_pf.o fm10k_vf.o \ | 31 | fm10k_netdev.o fm10k_ethtool.o fm10k_pf.o fm10k_vf.o \ |
32 | fm10k_mbx.o fm10k_iov.o fm10k_tlv.o \ | 32 | fm10k_mbx.o fm10k_iov.o fm10k_tlv.o \ |
33 | fm10k_debugfs.o fm10k_dcbnl.o | 33 | fm10k_debugfs.o fm10k_ptp.o fm10k_dcbnl.o |
diff --git a/drivers/net/ethernet/intel/fm10k/fm10k.h b/drivers/net/ethernet/intel/fm10k/fm10k.h index 10454834176a..05658275ba17 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k.h +++ b/drivers/net/ethernet/intel/fm10k/fm10k.h | |||
@@ -26,6 +26,9 @@ | |||
26 | #include <linux/rtnetlink.h> | 26 | #include <linux/rtnetlink.h> |
27 | #include <linux/if_vlan.h> | 27 | #include <linux/if_vlan.h> |
28 | #include <linux/pci.h> | 28 | #include <linux/pci.h> |
29 | #include <linux/net_tstamp.h> | ||
30 | #include <linux/clocksource.h> | ||
31 | #include <linux/ptp_clock_kernel.h> | ||
29 | 32 | ||
30 | #include "fm10k_pf.h" | 33 | #include "fm10k_pf.h" |
31 | #include "fm10k_vf.h" | 34 | #include "fm10k_vf.h" |
@@ -293,6 +296,7 @@ struct fm10k_intfc { | |||
293 | struct fm10k_hw_stats stats; | 296 | struct fm10k_hw_stats stats; |
294 | struct fm10k_hw hw; | 297 | struct fm10k_hw hw; |
295 | u32 __iomem *uc_addr; | 298 | u32 __iomem *uc_addr; |
299 | u32 __iomem *sw_addr; | ||
296 | u16 msg_enable; | 300 | u16 msg_enable; |
297 | u16 tx_ring_count; | 301 | u16 tx_ring_count; |
298 | u16 rx_ring_count; | 302 | u16 rx_ring_count; |
@@ -314,6 +318,20 @@ struct fm10k_intfc { | |||
314 | struct dentry *dbg_intfc; | 318 | struct dentry *dbg_intfc; |
315 | 319 | ||
316 | #endif /* CONFIG_DEBUG_FS */ | 320 | #endif /* CONFIG_DEBUG_FS */ |
321 | struct ptp_clock_info ptp_caps; | ||
322 | struct ptp_clock *ptp_clock; | ||
323 | |||
324 | struct sk_buff_head ts_tx_skb_queue; | ||
325 | u32 tx_hwtstamp_timeouts; | ||
326 | |||
327 | struct hwtstamp_config ts_config; | ||
328 | /* We are unable to actually adjust the clock beyond the frequency | ||
329 | * value. Once the clock is started there is no resetting it. As | ||
330 | * such we maintain a separate offset from the actual hardware clock | ||
331 | * to allow for offset adjustment. | ||
332 | */ | ||
333 | s64 ptp_adjust; | ||
334 | rwlock_t systime_lock; | ||
317 | #ifdef CONFIG_DCB | 335 | #ifdef CONFIG_DCB |
318 | u8 pfc_en; | 336 | u8 pfc_en; |
319 | #endif | 337 | #endif |
@@ -411,6 +429,10 @@ union fm10k_ftag_info { | |||
411 | }; | 429 | }; |
412 | 430 | ||
413 | struct fm10k_cb { | 431 | struct fm10k_cb { |
432 | union { | ||
433 | __le64 tstamp; | ||
434 | unsigned long ts_tx_timeout; | ||
435 | }; | ||
414 | union fm10k_ftag_info fi; | 436 | union fm10k_ftag_info fi; |
415 | }; | 437 | }; |
416 | 438 | ||
@@ -492,6 +514,21 @@ static inline void fm10k_dbg_init(void) {} | |||
492 | static inline void fm10k_dbg_exit(void) {} | 514 | static inline void fm10k_dbg_exit(void) {} |
493 | #endif /* CONFIG_DEBUG_FS */ | 515 | #endif /* CONFIG_DEBUG_FS */ |
494 | 516 | ||
517 | /* Time Stamping */ | ||
518 | void fm10k_systime_to_hwtstamp(struct fm10k_intfc *interface, | ||
519 | struct skb_shared_hwtstamps *hwtstamp, | ||
520 | u64 systime); | ||
521 | void fm10k_ts_tx_enqueue(struct fm10k_intfc *interface, struct sk_buff *skb); | ||
522 | void fm10k_ts_tx_hwtstamp(struct fm10k_intfc *interface, __le16 dglort, | ||
523 | u64 systime); | ||
524 | void fm10k_ts_reset(struct fm10k_intfc *interface); | ||
525 | void fm10k_ts_init(struct fm10k_intfc *interface); | ||
526 | void fm10k_ts_tx_subtask(struct fm10k_intfc *interface); | ||
527 | void fm10k_ptp_register(struct fm10k_intfc *interface); | ||
528 | void fm10k_ptp_unregister(struct fm10k_intfc *interface); | ||
529 | int fm10k_get_ts_config(struct net_device *netdev, struct ifreq *ifr); | ||
530 | int fm10k_set_ts_config(struct net_device *netdev, struct ifreq *ifr); | ||
531 | |||
495 | /* DCB */ | 532 | /* DCB */ |
496 | void fm10k_dcbnl_set_ops(struct net_device *dev); | 533 | void fm10k_dcbnl_set_ops(struct net_device *dev); |
497 | #endif /* _FM10K_H_ */ | 534 | #endif /* _FM10K_H_ */ |
diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c b/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c index 42beb89ae15d..a9bbe60347d8 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c | |||
@@ -91,6 +91,8 @@ static const struct fm10k_stats fm10k_gstrings_stats[] = { | |||
91 | FM10K_STAT("mbx_rx_messages", hw.mbx.rx_messages), | 91 | FM10K_STAT("mbx_rx_messages", hw.mbx.rx_messages), |
92 | FM10K_STAT("mbx_rx_dwords", hw.mbx.rx_dwords), | 92 | FM10K_STAT("mbx_rx_dwords", hw.mbx.rx_dwords), |
93 | FM10K_STAT("mbx_rx_parse_err", hw.mbx.rx_parse_err), | 93 | FM10K_STAT("mbx_rx_parse_err", hw.mbx.rx_parse_err), |
94 | |||
95 | FM10K_STAT("tx_hwtstamp_timeouts", tx_hwtstamp_timeouts), | ||
94 | }; | 96 | }; |
95 | 97 | ||
96 | #define FM10K_GLOBAL_STATS_LEN ARRAY_SIZE(fm10k_gstrings_stats) | 98 | #define FM10K_GLOBAL_STATS_LEN ARRAY_SIZE(fm10k_gstrings_stats) |
@@ -1006,6 +1008,33 @@ static int fm10k_set_channels(struct net_device *dev, | |||
1006 | return fm10k_setup_tc(dev, netdev_get_num_tc(dev)); | 1008 | return fm10k_setup_tc(dev, netdev_get_num_tc(dev)); |
1007 | } | 1009 | } |
1008 | 1010 | ||
1011 | static int fm10k_get_ts_info(struct net_device *dev, | ||
1012 | struct ethtool_ts_info *info) | ||
1013 | { | ||
1014 | struct fm10k_intfc *interface = netdev_priv(dev); | ||
1015 | |||
1016 | info->so_timestamping = | ||
1017 | SOF_TIMESTAMPING_TX_SOFTWARE | | ||
1018 | SOF_TIMESTAMPING_RX_SOFTWARE | | ||
1019 | SOF_TIMESTAMPING_SOFTWARE | | ||
1020 | SOF_TIMESTAMPING_TX_HARDWARE | | ||
1021 | SOF_TIMESTAMPING_RX_HARDWARE | | ||
1022 | SOF_TIMESTAMPING_RAW_HARDWARE; | ||
1023 | |||
1024 | if (interface->ptp_clock) | ||
1025 | info->phc_index = ptp_clock_index(interface->ptp_clock); | ||
1026 | else | ||
1027 | info->phc_index = -1; | ||
1028 | |||
1029 | info->tx_types = (1 << HWTSTAMP_TX_OFF) | | ||
1030 | (1 << HWTSTAMP_TX_ON); | ||
1031 | |||
1032 | info->rx_filters = (1 << HWTSTAMP_FILTER_NONE) | | ||
1033 | (1 << HWTSTAMP_FILTER_ALL); | ||
1034 | |||
1035 | return 0; | ||
1036 | } | ||
1037 | |||
1009 | static const struct ethtool_ops fm10k_ethtool_ops = { | 1038 | static const struct ethtool_ops fm10k_ethtool_ops = { |
1010 | .get_strings = fm10k_get_strings, | 1039 | .get_strings = fm10k_get_strings, |
1011 | .get_sset_count = fm10k_get_sset_count, | 1040 | .get_sset_count = fm10k_get_sset_count, |
@@ -1031,6 +1060,7 @@ static const struct ethtool_ops fm10k_ethtool_ops = { | |||
1031 | .set_rxfh = fm10k_set_rssh, | 1060 | .set_rxfh = fm10k_set_rssh, |
1032 | .get_channels = fm10k_get_channels, | 1061 | .get_channels = fm10k_get_channels, |
1033 | .set_channels = fm10k_set_channels, | 1062 | .set_channels = fm10k_set_channels, |
1063 | .get_ts_info = fm10k_get_ts_info, | ||
1034 | }; | 1064 | }; |
1035 | 1065 | ||
1036 | void fm10k_set_ethtool_ops(struct net_device *dev) | 1066 | void fm10k_set_ethtool_ops(struct net_device *dev) |
diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_main.c b/drivers/net/ethernet/intel/fm10k/fm10k_main.c index d9987331387e..6c800a330d66 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_main.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_main.c | |||
@@ -399,6 +399,19 @@ static inline void fm10k_rx_hash(struct fm10k_ring *ring, | |||
399 | PKT_HASH_TYPE_L4 : PKT_HASH_TYPE_L3); | 399 | PKT_HASH_TYPE_L4 : PKT_HASH_TYPE_L3); |
400 | } | 400 | } |
401 | 401 | ||
402 | static void fm10k_rx_hwtstamp(struct fm10k_ring *rx_ring, | ||
403 | union fm10k_rx_desc *rx_desc, | ||
404 | struct sk_buff *skb) | ||
405 | { | ||
406 | struct fm10k_intfc *interface = rx_ring->q_vector->interface; | ||
407 | |||
408 | FM10K_CB(skb)->tstamp = rx_desc->q.timestamp; | ||
409 | |||
410 | if (unlikely(interface->flags & FM10K_FLAG_RX_TS_ENABLED)) | ||
411 | fm10k_systime_to_hwtstamp(interface, skb_hwtstamps(skb), | ||
412 | le64_to_cpu(rx_desc->q.timestamp)); | ||
413 | } | ||
414 | |||
402 | static void fm10k_type_trans(struct fm10k_ring *rx_ring, | 415 | static void fm10k_type_trans(struct fm10k_ring *rx_ring, |
403 | union fm10k_rx_desc *rx_desc, | 416 | union fm10k_rx_desc *rx_desc, |
404 | struct sk_buff *skb) | 417 | struct sk_buff *skb) |
@@ -448,6 +461,8 @@ static unsigned int fm10k_process_skb_fields(struct fm10k_ring *rx_ring, | |||
448 | 461 | ||
449 | fm10k_rx_checksum(rx_ring, rx_desc, skb); | 462 | fm10k_rx_checksum(rx_ring, rx_desc, skb); |
450 | 463 | ||
464 | fm10k_rx_hwtstamp(rx_ring, rx_desc, skb); | ||
465 | |||
451 | FM10K_CB(skb)->fi.w.vlan = rx_desc->w.vlan; | 466 | FM10K_CB(skb)->fi.w.vlan = rx_desc->w.vlan; |
452 | 467 | ||
453 | skb_record_rx_queue(skb, rx_ring->queue_index); | 468 | skb_record_rx_queue(skb, rx_ring->queue_index); |
@@ -886,6 +901,11 @@ static u8 fm10k_tx_desc_flags(struct sk_buff *skb, u32 tx_flags) | |||
886 | /* set type for advanced descriptor with frame checksum insertion */ | 901 | /* set type for advanced descriptor with frame checksum insertion */ |
887 | u32 desc_flags = 0; | 902 | u32 desc_flags = 0; |
888 | 903 | ||
904 | /* set timestamping bits */ | ||
905 | if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) && | ||
906 | likely(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS)) | ||
907 | desc_flags |= FM10K_TXD_FLAG_TIME; | ||
908 | |||
889 | /* set checksum offload bits */ | 909 | /* set checksum offload bits */ |
890 | desc_flags |= FM10K_SET_FLAG(tx_flags, FM10K_TX_FLAGS_CSUM, | 910 | desc_flags |= FM10K_SET_FLAG(tx_flags, FM10K_TX_FLAGS_CSUM, |
891 | FM10K_TXD_FLAG_CSUM); | 911 | FM10K_TXD_FLAG_CSUM); |
diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c b/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c index 991abb25451e..dcec000bdb68 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c | |||
@@ -243,6 +243,9 @@ void fm10k_clean_all_tx_rings(struct fm10k_intfc *interface) | |||
243 | 243 | ||
244 | for (i = 0; i < interface->num_tx_queues; i++) | 244 | for (i = 0; i < interface->num_tx_queues; i++) |
245 | fm10k_clean_tx_ring(interface->tx_ring[i]); | 245 | fm10k_clean_tx_ring(interface->tx_ring[i]); |
246 | |||
247 | /* remove any stale timestamp buffers and free them */ | ||
248 | skb_queue_purge(&interface->ts_tx_skb_queue); | ||
246 | } | 249 | } |
247 | 250 | ||
248 | /** | 251 | /** |
@@ -651,6 +654,10 @@ static netdev_tx_t fm10k_xmit_frame(struct sk_buff *skb, struct net_device *dev) | |||
651 | __skb_put(skb, pad_len); | 654 | __skb_put(skb, pad_len); |
652 | } | 655 | } |
653 | 656 | ||
657 | /* prepare packet for hardware time stamping */ | ||
658 | if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)) | ||
659 | fm10k_ts_tx_enqueue(interface, skb); | ||
660 | |||
654 | if (r_idx >= interface->num_tx_queues) | 661 | if (r_idx >= interface->num_tx_queues) |
655 | r_idx %= interface->num_tx_queues; | 662 | r_idx %= interface->num_tx_queues; |
656 | 663 | ||
@@ -1177,6 +1184,18 @@ int fm10k_setup_tc(struct net_device *dev, u8 tc) | |||
1177 | return 0; | 1184 | return 0; |
1178 | } | 1185 | } |
1179 | 1186 | ||
1187 | static int fm10k_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) | ||
1188 | { | ||
1189 | switch (cmd) { | ||
1190 | case SIOCGHWTSTAMP: | ||
1191 | return fm10k_get_ts_config(netdev, ifr); | ||
1192 | case SIOCSHWTSTAMP: | ||
1193 | return fm10k_set_ts_config(netdev, ifr); | ||
1194 | default: | ||
1195 | return -EOPNOTSUPP; | ||
1196 | } | ||
1197 | } | ||
1198 | |||
1180 | static void fm10k_assign_l2_accel(struct fm10k_intfc *interface, | 1199 | static void fm10k_assign_l2_accel(struct fm10k_intfc *interface, |
1181 | struct fm10k_l2_accel *l2_accel) | 1200 | struct fm10k_l2_accel *l2_accel) |
1182 | { | 1201 | { |
@@ -1345,6 +1364,7 @@ static const struct net_device_ops fm10k_netdev_ops = { | |||
1345 | .ndo_get_vf_config = fm10k_ndo_get_vf_config, | 1364 | .ndo_get_vf_config = fm10k_ndo_get_vf_config, |
1346 | .ndo_add_vxlan_port = fm10k_add_vxlan_port, | 1365 | .ndo_add_vxlan_port = fm10k_add_vxlan_port, |
1347 | .ndo_del_vxlan_port = fm10k_del_vxlan_port, | 1366 | .ndo_del_vxlan_port = fm10k_del_vxlan_port, |
1367 | .ndo_do_ioctl = fm10k_ioctl, | ||
1348 | .ndo_dfwd_add_station = fm10k_dfwd_add_station, | 1368 | .ndo_dfwd_add_station = fm10k_dfwd_add_station, |
1349 | .ndo_dfwd_del_station = fm10k_dfwd_del_station, | 1369 | .ndo_dfwd_del_station = fm10k_dfwd_del_station, |
1350 | }; | 1370 | }; |
diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_pci.c b/drivers/net/ethernet/intel/fm10k/fm10k_pci.c index 74d7d473d113..e02036c427b9 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_pci.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_pci.c | |||
@@ -170,6 +170,9 @@ static void fm10k_reinit(struct fm10k_intfc *interface) | |||
170 | /* reassociate interrupts */ | 170 | /* reassociate interrupts */ |
171 | fm10k_mbx_request_irq(interface); | 171 | fm10k_mbx_request_irq(interface); |
172 | 172 | ||
173 | /* reset clock */ | ||
174 | fm10k_ts_reset(interface); | ||
175 | |||
173 | if (netif_running(netdev)) | 176 | if (netif_running(netdev)) |
174 | fm10k_open(netdev); | 177 | fm10k_open(netdev); |
175 | 178 | ||
@@ -490,6 +493,7 @@ static void fm10k_service_task(struct work_struct *work) | |||
490 | /* tasks only run when interface is up */ | 493 | /* tasks only run when interface is up */ |
491 | fm10k_watchdog_subtask(interface); | 494 | fm10k_watchdog_subtask(interface); |
492 | fm10k_check_hang_subtask(interface); | 495 | fm10k_check_hang_subtask(interface); |
496 | fm10k_ts_tx_subtask(interface); | ||
493 | 497 | ||
494 | /* release lock on service events to allow scheduling next event */ | 498 | /* release lock on service events to allow scheduling next event */ |
495 | fm10k_service_event_complete(interface); | 499 | fm10k_service_event_complete(interface); |
@@ -1064,6 +1068,25 @@ static s32 fm10k_mbx_mac_addr(struct fm10k_hw *hw, u32 **results, | |||
1064 | return 0; | 1068 | return 0; |
1065 | } | 1069 | } |
1066 | 1070 | ||
1071 | static s32 fm10k_1588_msg_vf(struct fm10k_hw *hw, u32 **results, | ||
1072 | struct fm10k_mbx_info *mbx) | ||
1073 | { | ||
1074 | struct fm10k_intfc *interface; | ||
1075 | u64 timestamp; | ||
1076 | s32 err; | ||
1077 | |||
1078 | err = fm10k_tlv_attr_get_u64(results[FM10K_1588_MSG_TIMESTAMP], | ||
1079 | ×tamp); | ||
1080 | if (err) | ||
1081 | return err; | ||
1082 | |||
1083 | interface = container_of(hw, struct fm10k_intfc, hw); | ||
1084 | |||
1085 | fm10k_ts_tx_hwtstamp(interface, 0, timestamp); | ||
1086 | |||
1087 | return 0; | ||
1088 | } | ||
1089 | |||
1067 | /* generic error handler for mailbox issues */ | 1090 | /* generic error handler for mailbox issues */ |
1068 | static s32 fm10k_mbx_error(struct fm10k_hw *hw, u32 **results, | 1091 | static s32 fm10k_mbx_error(struct fm10k_hw *hw, u32 **results, |
1069 | struct fm10k_mbx_info *mbx) | 1092 | struct fm10k_mbx_info *mbx) |
@@ -1084,6 +1107,7 @@ static const struct fm10k_msg_data vf_mbx_data[] = { | |||
1084 | FM10K_TLV_MSG_TEST_HANDLER(fm10k_tlv_msg_test), | 1107 | FM10K_TLV_MSG_TEST_HANDLER(fm10k_tlv_msg_test), |
1085 | FM10K_VF_MSG_MAC_VLAN_HANDLER(fm10k_mbx_mac_addr), | 1108 | FM10K_VF_MSG_MAC_VLAN_HANDLER(fm10k_mbx_mac_addr), |
1086 | FM10K_VF_MSG_LPORT_STATE_HANDLER(fm10k_msg_lport_state_vf), | 1109 | FM10K_VF_MSG_LPORT_STATE_HANDLER(fm10k_msg_lport_state_vf), |
1110 | FM10K_VF_MSG_1588_HANDLER(fm10k_1588_msg_vf), | ||
1087 | FM10K_TLV_MSG_ERROR_HANDLER(fm10k_mbx_error), | 1111 | FM10K_TLV_MSG_ERROR_HANDLER(fm10k_mbx_error), |
1088 | }; | 1112 | }; |
1089 | 1113 | ||
@@ -1181,6 +1205,68 @@ static s32 fm10k_update_pvid(struct fm10k_hw *hw, u32 **results, | |||
1181 | return 0; | 1205 | return 0; |
1182 | } | 1206 | } |
1183 | 1207 | ||
1208 | static s32 fm10k_1588_msg_pf(struct fm10k_hw *hw, u32 **results, | ||
1209 | struct fm10k_mbx_info *mbx) | ||
1210 | { | ||
1211 | struct fm10k_swapi_1588_timestamp timestamp; | ||
1212 | struct fm10k_iov_data *iov_data; | ||
1213 | struct fm10k_intfc *interface; | ||
1214 | u16 sglort, vf_idx; | ||
1215 | s32 err; | ||
1216 | |||
1217 | err = fm10k_tlv_attr_get_le_struct( | ||
1218 | results[FM10K_PF_ATTR_ID_1588_TIMESTAMP], | ||
1219 | ×tamp, sizeof(timestamp)); | ||
1220 | if (err) | ||
1221 | return err; | ||
1222 | |||
1223 | interface = container_of(hw, struct fm10k_intfc, hw); | ||
1224 | |||
1225 | if (timestamp.dglort) { | ||
1226 | fm10k_ts_tx_hwtstamp(interface, timestamp.dglort, | ||
1227 | le64_to_cpu(timestamp.egress)); | ||
1228 | return 0; | ||
1229 | } | ||
1230 | |||
1231 | /* either dglort or sglort must be set */ | ||
1232 | if (!timestamp.sglort) | ||
1233 | return FM10K_ERR_PARAM; | ||
1234 | |||
1235 | /* verify GLORT is at least one of the ones we own */ | ||
1236 | sglort = le16_to_cpu(timestamp.sglort); | ||
1237 | if (!fm10k_glort_valid_pf(hw, sglort)) | ||
1238 | return FM10K_ERR_PARAM; | ||
1239 | |||
1240 | if (sglort == interface->glort) { | ||
1241 | fm10k_ts_tx_hwtstamp(interface, 0, | ||
1242 | le64_to_cpu(timestamp.ingress)); | ||
1243 | return 0; | ||
1244 | } | ||
1245 | |||
1246 | /* if there is no iov_data then there is no mailboxes to process */ | ||
1247 | if (!ACCESS_ONCE(interface->iov_data)) | ||
1248 | return FM10K_ERR_PARAM; | ||
1249 | |||
1250 | rcu_read_lock(); | ||
1251 | |||
1252 | /* notify VF if this timestamp belongs to it */ | ||
1253 | iov_data = interface->iov_data; | ||
1254 | vf_idx = (hw->mac.dglort_map & FM10K_DGLORTMAP_NONE) - sglort; | ||
1255 | |||
1256 | if (!iov_data || vf_idx >= iov_data->num_vfs) { | ||
1257 | err = FM10K_ERR_PARAM; | ||
1258 | goto err_unlock; | ||
1259 | } | ||
1260 | |||
1261 | err = hw->iov.ops.report_timestamp(hw, &iov_data->vf_info[vf_idx], | ||
1262 | le64_to_cpu(timestamp.ingress)); | ||
1263 | |||
1264 | err_unlock: | ||
1265 | rcu_read_unlock(); | ||
1266 | |||
1267 | return err; | ||
1268 | } | ||
1269 | |||
1184 | static const struct fm10k_msg_data pf_mbx_data[] = { | 1270 | static const struct fm10k_msg_data pf_mbx_data[] = { |
1185 | FM10K_PF_MSG_ERR_HANDLER(XCAST_MODES, fm10k_msg_err_pf), | 1271 | FM10K_PF_MSG_ERR_HANDLER(XCAST_MODES, fm10k_msg_err_pf), |
1186 | FM10K_PF_MSG_ERR_HANDLER(UPDATE_MAC_FWD_RULE, fm10k_msg_err_pf), | 1272 | FM10K_PF_MSG_ERR_HANDLER(UPDATE_MAC_FWD_RULE, fm10k_msg_err_pf), |
@@ -1188,6 +1274,7 @@ static const struct fm10k_msg_data pf_mbx_data[] = { | |||
1188 | FM10K_PF_MSG_ERR_HANDLER(LPORT_CREATE, fm10k_msg_err_pf), | 1274 | FM10K_PF_MSG_ERR_HANDLER(LPORT_CREATE, fm10k_msg_err_pf), |
1189 | FM10K_PF_MSG_ERR_HANDLER(LPORT_DELETE, fm10k_msg_err_pf), | 1275 | FM10K_PF_MSG_ERR_HANDLER(LPORT_DELETE, fm10k_msg_err_pf), |
1190 | FM10K_PF_MSG_UPDATE_PVID_HANDLER(fm10k_update_pvid), | 1276 | FM10K_PF_MSG_UPDATE_PVID_HANDLER(fm10k_update_pvid), |
1277 | FM10K_PF_MSG_1588_TIMESTAMP_HANDLER(fm10k_1588_msg_pf), | ||
1191 | FM10K_TLV_MSG_ERROR_HANDLER(fm10k_mbx_error), | 1278 | FM10K_TLV_MSG_ERROR_HANDLER(fm10k_mbx_error), |
1192 | }; | 1279 | }; |
1193 | 1280 | ||
@@ -1549,6 +1636,12 @@ static int fm10k_sw_init(struct fm10k_intfc *interface, | |||
1549 | return -EIO; | 1636 | return -EIO; |
1550 | } | 1637 | } |
1551 | 1638 | ||
1639 | /* assign BAR 4 resources for use with PTP */ | ||
1640 | if (fm10k_read_reg(hw, FM10K_CTRL) & FM10K_CTRL_BAR4_ALLOWED) | ||
1641 | interface->sw_addr = ioremap(pci_resource_start(pdev, 4), | ||
1642 | pci_resource_len(pdev, 4)); | ||
1643 | hw->sw_addr = interface->sw_addr; | ||
1644 | |||
1552 | /* Only the PF can support VXLAN and NVGRE offloads */ | 1645 | /* Only the PF can support VXLAN and NVGRE offloads */ |
1553 | if (hw->mac.type != fm10k_mac_pf) { | 1646 | if (hw->mac.type != fm10k_mac_pf) { |
1554 | netdev->hw_enc_features = 0; | 1647 | netdev->hw_enc_features = 0; |
@@ -1565,6 +1658,9 @@ static int fm10k_sw_init(struct fm10k_intfc *interface, | |||
1565 | (unsigned long)interface); | 1658 | (unsigned long)interface); |
1566 | INIT_WORK(&interface->service_task, fm10k_service_task); | 1659 | INIT_WORK(&interface->service_task, fm10k_service_task); |
1567 | 1660 | ||
1661 | /* Intitialize timestamp data */ | ||
1662 | fm10k_ts_init(interface); | ||
1663 | |||
1568 | /* set default ring sizes */ | 1664 | /* set default ring sizes */ |
1569 | interface->tx_ring_count = FM10K_DEFAULT_TXD; | 1665 | interface->tx_ring_count = FM10K_DEFAULT_TXD; |
1570 | interface->rx_ring_count = FM10K_DEFAULT_RXD; | 1666 | interface->rx_ring_count = FM10K_DEFAULT_RXD; |
@@ -1716,6 +1812,9 @@ static int fm10k_probe(struct pci_dev *pdev, | |||
1716 | /* stop all the transmit queues from transmitting until link is up */ | 1812 | /* stop all the transmit queues from transmitting until link is up */ |
1717 | netif_tx_stop_all_queues(netdev); | 1813 | netif_tx_stop_all_queues(netdev); |
1718 | 1814 | ||
1815 | /* Register PTP interface */ | ||
1816 | fm10k_ptp_register(interface); | ||
1817 | |||
1719 | /* print bus type/speed/width info */ | 1818 | /* print bus type/speed/width info */ |
1720 | dev_info(&pdev->dev, "(PCI Express:%s Width: %s Payload: %s)\n", | 1819 | dev_info(&pdev->dev, "(PCI Express:%s Width: %s Payload: %s)\n", |
1721 | (hw->bus.speed == fm10k_bus_speed_8000 ? "8.0GT/s" : | 1820 | (hw->bus.speed == fm10k_bus_speed_8000 ? "8.0GT/s" : |
@@ -1747,6 +1846,8 @@ err_register: | |||
1747 | err_mbx_interrupt: | 1846 | err_mbx_interrupt: |
1748 | fm10k_clear_queueing_scheme(interface); | 1847 | fm10k_clear_queueing_scheme(interface); |
1749 | err_sw_init: | 1848 | err_sw_init: |
1849 | if (interface->sw_addr) | ||
1850 | iounmap(interface->sw_addr); | ||
1750 | iounmap(interface->uc_addr); | 1851 | iounmap(interface->uc_addr); |
1751 | err_ioremap: | 1852 | err_ioremap: |
1752 | free_netdev(netdev); | 1853 | free_netdev(netdev); |
@@ -1780,6 +1881,9 @@ static void fm10k_remove(struct pci_dev *pdev) | |||
1780 | if (netdev->reg_state == NETREG_REGISTERED) | 1881 | if (netdev->reg_state == NETREG_REGISTERED) |
1781 | unregister_netdev(netdev); | 1882 | unregister_netdev(netdev); |
1782 | 1883 | ||
1884 | /* cleanup timestamp handling */ | ||
1885 | fm10k_ptp_unregister(interface); | ||
1886 | |||
1783 | /* release VFs */ | 1887 | /* release VFs */ |
1784 | fm10k_iov_disable(pdev); | 1888 | fm10k_iov_disable(pdev); |
1785 | 1889 | ||
@@ -1792,6 +1896,8 @@ static void fm10k_remove(struct pci_dev *pdev) | |||
1792 | /* remove any debugfs interfaces */ | 1896 | /* remove any debugfs interfaces */ |
1793 | fm10k_dbg_intfc_exit(interface); | 1897 | fm10k_dbg_intfc_exit(interface); |
1794 | 1898 | ||
1899 | if (interface->sw_addr) | ||
1900 | iounmap(interface->sw_addr); | ||
1795 | iounmap(interface->uc_addr); | 1901 | iounmap(interface->uc_addr); |
1796 | 1902 | ||
1797 | free_netdev(netdev); | 1903 | free_netdev(netdev); |
@@ -1848,6 +1954,9 @@ static int fm10k_resume(struct pci_dev *pdev) | |||
1848 | /* reset statistics starting values */ | 1954 | /* reset statistics starting values */ |
1849 | hw->mac.ops.rebind_hw_stats(hw, &interface->stats); | 1955 | hw->mac.ops.rebind_hw_stats(hw, &interface->stats); |
1850 | 1956 | ||
1957 | /* reset clock */ | ||
1958 | fm10k_ts_reset(interface); | ||
1959 | |||
1851 | rtnl_lock(); | 1960 | rtnl_lock(); |
1852 | 1961 | ||
1853 | err = fm10k_init_queueing_scheme(interface); | 1962 | err = fm10k_init_queueing_scheme(interface); |
@@ -2004,6 +2113,9 @@ static void fm10k_io_resume(struct pci_dev *pdev) | |||
2004 | /* reassociate interrupts */ | 2113 | /* reassociate interrupts */ |
2005 | fm10k_mbx_request_irq(interface); | 2114 | fm10k_mbx_request_irq(interface); |
2006 | 2115 | ||
2116 | /* reset clock */ | ||
2117 | fm10k_ts_reset(interface); | ||
2118 | |||
2007 | if (netif_running(netdev)) | 2119 | if (netif_running(netdev)) |
2008 | err = fm10k_open(netdev); | 2120 | err = fm10k_open(netdev); |
2009 | 2121 | ||
diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_ptp.c b/drivers/net/ethernet/intel/fm10k/fm10k_ptp.c new file mode 100644 index 000000000000..7822809436a3 --- /dev/null +++ b/drivers/net/ethernet/intel/fm10k/fm10k_ptp.c | |||
@@ -0,0 +1,463 @@ | |||
1 | /* Intel Ethernet Switch Host Interface Driver | ||
2 | * Copyright(c) 2013 - 2014 Intel Corporation. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify it | ||
5 | * under the terms and conditions of the GNU General Public License, | ||
6 | * version 2, as published by the Free Software Foundation. | ||
7 | * | ||
8 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
11 | * more details. | ||
12 | * | ||
13 | * The full GNU General Public License is included in this distribution in | ||
14 | * the file called "COPYING". | ||
15 | * | ||
16 | * Contact Information: | ||
17 | * e1000-devel Mailing List <e1000-devel@lists.sourceforge.net> | ||
18 | * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 | ||
19 | */ | ||
20 | |||
21 | #include <linux/ptp_classify.h> | ||
22 | #include <linux/ptp_clock_kernel.h> | ||
23 | |||
24 | #include "fm10k.h" | ||
25 | |||
26 | #define FM10K_TS_TX_TIMEOUT (HZ * 15) | ||
27 | |||
28 | void fm10k_systime_to_hwtstamp(struct fm10k_intfc *interface, | ||
29 | struct skb_shared_hwtstamps *hwtstamp, | ||
30 | u64 systime) | ||
31 | { | ||
32 | unsigned long flags; | ||
33 | |||
34 | read_lock_irqsave(&interface->systime_lock, flags); | ||
35 | systime += interface->ptp_adjust; | ||
36 | read_unlock_irqrestore(&interface->systime_lock, flags); | ||
37 | |||
38 | hwtstamp->hwtstamp = ns_to_ktime(systime); | ||
39 | } | ||
40 | |||
41 | static struct sk_buff *fm10k_ts_tx_skb(struct fm10k_intfc *interface, | ||
42 | __le16 dglort) | ||
43 | { | ||
44 | struct sk_buff_head *list = &interface->ts_tx_skb_queue; | ||
45 | struct sk_buff *skb; | ||
46 | |||
47 | skb_queue_walk(list, skb) { | ||
48 | if (FM10K_CB(skb)->fi.w.dglort == dglort) | ||
49 | return skb; | ||
50 | } | ||
51 | |||
52 | return NULL; | ||
53 | } | ||
54 | |||
55 | void fm10k_ts_tx_enqueue(struct fm10k_intfc *interface, struct sk_buff *skb) | ||
56 | { | ||
57 | struct sk_buff_head *list = &interface->ts_tx_skb_queue; | ||
58 | struct sk_buff *clone; | ||
59 | unsigned long flags; | ||
60 | __le16 dglort; | ||
61 | |||
62 | /* create clone for us to return on the Tx path */ | ||
63 | clone = skb_clone_sk(skb); | ||
64 | if (!clone) | ||
65 | return; | ||
66 | |||
67 | FM10K_CB(clone)->ts_tx_timeout = jiffies + FM10K_TS_TX_TIMEOUT; | ||
68 | dglort = FM10K_CB(clone)->fi.w.dglort; | ||
69 | |||
70 | spin_lock_irqsave(&list->lock, flags); | ||
71 | |||
72 | /* attempt to locate any buffers with the same dglort, | ||
73 | * if none are present then insert skb in tail of list | ||
74 | */ | ||
75 | skb = fm10k_ts_tx_skb(interface, FM10K_CB(clone)->fi.w.dglort); | ||
76 | if (!skb) | ||
77 | __skb_queue_tail(list, clone); | ||
78 | |||
79 | spin_unlock_irqrestore(&list->lock, flags); | ||
80 | |||
81 | /* if list is already has one then we just free the clone */ | ||
82 | if (skb) | ||
83 | kfree_skb(skb); | ||
84 | else | ||
85 | skb_shinfo(clone)->tx_flags |= SKBTX_IN_PROGRESS; | ||
86 | } | ||
87 | |||
88 | void fm10k_ts_tx_hwtstamp(struct fm10k_intfc *interface, __le16 dglort, | ||
89 | u64 systime) | ||
90 | { | ||
91 | struct skb_shared_hwtstamps shhwtstamps; | ||
92 | struct sk_buff_head *list = &interface->ts_tx_skb_queue; | ||
93 | struct sk_buff *skb; | ||
94 | unsigned long flags; | ||
95 | |||
96 | spin_lock_irqsave(&list->lock, flags); | ||
97 | |||
98 | /* attempt to locate and pull the sk_buff out of the list */ | ||
99 | skb = fm10k_ts_tx_skb(interface, dglort); | ||
100 | if (skb) | ||
101 | __skb_unlink(skb, list); | ||
102 | |||
103 | spin_unlock_irqrestore(&list->lock, flags); | ||
104 | |||
105 | /* if not found do nothing */ | ||
106 | if (!skb) | ||
107 | return; | ||
108 | |||
109 | /* timestamp the sk_buff and return it to the socket */ | ||
110 | fm10k_systime_to_hwtstamp(interface, &shhwtstamps, systime); | ||
111 | skb_complete_tx_timestamp(skb, &shhwtstamps); | ||
112 | } | ||
113 | |||
114 | void fm10k_ts_tx_subtask(struct fm10k_intfc *interface) | ||
115 | { | ||
116 | struct sk_buff_head *list = &interface->ts_tx_skb_queue; | ||
117 | struct sk_buff *skb, *tmp; | ||
118 | unsigned long flags; | ||
119 | |||
120 | /* If we're down or resetting, just bail */ | ||
121 | if (test_bit(__FM10K_DOWN, &interface->state) || | ||
122 | test_bit(__FM10K_RESETTING, &interface->state)) | ||
123 | return; | ||
124 | |||
125 | spin_lock_irqsave(&list->lock, flags); | ||
126 | |||
127 | /* walk though the list and flush any expired timestamp packets */ | ||
128 | skb_queue_walk_safe(list, skb, tmp) { | ||
129 | if (!time_is_after_jiffies(FM10K_CB(skb)->ts_tx_timeout)) | ||
130 | continue; | ||
131 | __skb_unlink(skb, list); | ||
132 | kfree_skb(skb); | ||
133 | interface->tx_hwtstamp_timeouts++; | ||
134 | } | ||
135 | |||
136 | spin_unlock_irqrestore(&list->lock, flags); | ||
137 | } | ||
138 | |||
139 | static u64 fm10k_systime_read(struct fm10k_intfc *interface) | ||
140 | { | ||
141 | struct fm10k_hw *hw = &interface->hw; | ||
142 | |||
143 | return hw->mac.ops.read_systime(hw); | ||
144 | } | ||
145 | |||
146 | void fm10k_ts_reset(struct fm10k_intfc *interface) | ||
147 | { | ||
148 | s64 ns = ktime_to_ns(ktime_get_real()); | ||
149 | unsigned long flags; | ||
150 | |||
151 | /* reinitialize the clock */ | ||
152 | write_lock_irqsave(&interface->systime_lock, flags); | ||
153 | interface->ptp_adjust = fm10k_systime_read(interface) - ns; | ||
154 | write_unlock_irqrestore(&interface->systime_lock, flags); | ||
155 | } | ||
156 | |||
157 | void fm10k_ts_init(struct fm10k_intfc *interface) | ||
158 | { | ||
159 | /* Initialize lock protecting systime access */ | ||
160 | rwlock_init(&interface->systime_lock); | ||
161 | |||
162 | /* Initialize skb queue for pending timestamp requests */ | ||
163 | skb_queue_head_init(&interface->ts_tx_skb_queue); | ||
164 | |||
165 | /* reset the clock to current kernel time */ | ||
166 | fm10k_ts_reset(interface); | ||
167 | } | ||
168 | |||
169 | /** | ||
170 | * fm10k_get_ts_config - get current hardware timestamping configuration | ||
171 | * @netdev: network interface device structure | ||
172 | * @ifreq: ioctl data | ||
173 | * | ||
174 | * This function returns the current timestamping settings. Rather than | ||
175 | * attempt to deconstruct registers to fill in the values, simply keep a copy | ||
176 | * of the old settings around, and return a copy when requested. | ||
177 | */ | ||
178 | int fm10k_get_ts_config(struct net_device *netdev, struct ifreq *ifr) | ||
179 | { | ||
180 | struct fm10k_intfc *interface = netdev_priv(netdev); | ||
181 | struct hwtstamp_config *config = &interface->ts_config; | ||
182 | |||
183 | return copy_to_user(ifr->ifr_data, config, sizeof(*config)) ? | ||
184 | -EFAULT : 0; | ||
185 | } | ||
186 | |||
187 | /** | ||
188 | * fm10k_set_ts_config - control hardware time stamping | ||
189 | * @netdev: network interface device structure | ||
190 | * @ifreq: ioctl data | ||
191 | * | ||
192 | * Outgoing time stamping can be enabled and disabled. Play nice and | ||
193 | * disable it when requested, although it shouldn't cause any overhead | ||
194 | * when no packet needs it. At most one packet in the queue may be | ||
195 | * marked for time stamping, otherwise it would be impossible to tell | ||
196 | * for sure to which packet the hardware time stamp belongs. | ||
197 | * | ||
198 | * Incoming time stamping has to be configured via the hardware | ||
199 | * filters. Not all combinations are supported, in particular event | ||
200 | * type has to be specified. Matching the kind of event packet is | ||
201 | * not supported, with the exception of "all V2 events regardless of | ||
202 | * level 2 or 4". | ||
203 | * | ||
204 | * Since hardware always timestamps Path delay packets when timestamping V2 | ||
205 | * packets, regardless of the type specified in the register, only use V2 | ||
206 | * Event mode. This more accurately tells the user what the hardware is going | ||
207 | * to do anyways. | ||
208 | */ | ||
209 | int fm10k_set_ts_config(struct net_device *netdev, struct ifreq *ifr) | ||
210 | { | ||
211 | struct fm10k_intfc *interface = netdev_priv(netdev); | ||
212 | struct hwtstamp_config ts_config; | ||
213 | |||
214 | if (copy_from_user(&ts_config, ifr->ifr_data, sizeof(ts_config))) | ||
215 | return -EFAULT; | ||
216 | |||
217 | /* reserved for future extensions */ | ||
218 | if (ts_config.flags) | ||
219 | return -EINVAL; | ||
220 | |||
221 | switch (ts_config.tx_type) { | ||
222 | case HWTSTAMP_TX_OFF: | ||
223 | break; | ||
224 | case HWTSTAMP_TX_ON: | ||
225 | /* we likely need some check here to see if this is supported */ | ||
226 | break; | ||
227 | default: | ||
228 | return -ERANGE; | ||
229 | } | ||
230 | |||
231 | switch (ts_config.rx_filter) { | ||
232 | case HWTSTAMP_FILTER_NONE: | ||
233 | interface->flags &= ~FM10K_FLAG_RX_TS_ENABLED; | ||
234 | break; | ||
235 | case HWTSTAMP_FILTER_PTP_V1_L4_EVENT: | ||
236 | case HWTSTAMP_FILTER_PTP_V1_L4_SYNC: | ||
237 | case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ: | ||
238 | case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: | ||
239 | case HWTSTAMP_FILTER_PTP_V2_L4_SYNC: | ||
240 | case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ: | ||
241 | case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: | ||
242 | case HWTSTAMP_FILTER_PTP_V2_L2_SYNC: | ||
243 | case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ: | ||
244 | case HWTSTAMP_FILTER_PTP_V2_EVENT: | ||
245 | case HWTSTAMP_FILTER_PTP_V2_SYNC: | ||
246 | case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: | ||
247 | case HWTSTAMP_FILTER_ALL: | ||
248 | interface->flags |= FM10K_FLAG_RX_TS_ENABLED; | ||
249 | ts_config.rx_filter = HWTSTAMP_FILTER_ALL; | ||
250 | break; | ||
251 | default: | ||
252 | return -ERANGE; | ||
253 | } | ||
254 | |||
255 | /* save these settings for future reference */ | ||
256 | interface->ts_config = ts_config; | ||
257 | |||
258 | return copy_to_user(ifr->ifr_data, &ts_config, sizeof(ts_config)) ? | ||
259 | -EFAULT : 0; | ||
260 | } | ||
261 | |||
262 | static int fm10k_ptp_adjfreq(struct ptp_clock_info *ptp, s32 ppb) | ||
263 | { | ||
264 | struct fm10k_intfc *interface; | ||
265 | struct fm10k_hw *hw; | ||
266 | int err; | ||
267 | |||
268 | interface = container_of(ptp, struct fm10k_intfc, ptp_caps); | ||
269 | hw = &interface->hw; | ||
270 | |||
271 | err = hw->mac.ops.adjust_systime(hw, ppb); | ||
272 | |||
273 | /* the only error we should see is if the value is out of range */ | ||
274 | return (err == FM10K_ERR_PARAM) ? -ERANGE : err; | ||
275 | } | ||
276 | |||
277 | static int fm10k_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) | ||
278 | { | ||
279 | struct fm10k_intfc *interface; | ||
280 | unsigned long flags; | ||
281 | |||
282 | interface = container_of(ptp, struct fm10k_intfc, ptp_caps); | ||
283 | |||
284 | write_lock_irqsave(&interface->systime_lock, flags); | ||
285 | interface->ptp_adjust += delta; | ||
286 | write_unlock_irqrestore(&interface->systime_lock, flags); | ||
287 | |||
288 | return 0; | ||
289 | } | ||
290 | |||
291 | static int fm10k_ptp_gettime(struct ptp_clock_info *ptp, struct timespec *ts) | ||
292 | { | ||
293 | struct fm10k_intfc *interface; | ||
294 | unsigned long flags; | ||
295 | u64 now; | ||
296 | |||
297 | interface = container_of(ptp, struct fm10k_intfc, ptp_caps); | ||
298 | |||
299 | read_lock_irqsave(&interface->systime_lock, flags); | ||
300 | now = fm10k_systime_read(interface) + interface->ptp_adjust; | ||
301 | read_unlock_irqrestore(&interface->systime_lock, flags); | ||
302 | |||
303 | *ts = ns_to_timespec(now); | ||
304 | |||
305 | return 0; | ||
306 | } | ||
307 | |||
308 | static int fm10k_ptp_settime(struct ptp_clock_info *ptp, | ||
309 | const struct timespec *ts) | ||
310 | { | ||
311 | struct fm10k_intfc *interface; | ||
312 | unsigned long flags; | ||
313 | u64 ns = timespec_to_ns(ts); | ||
314 | |||
315 | interface = container_of(ptp, struct fm10k_intfc, ptp_caps); | ||
316 | |||
317 | write_lock_irqsave(&interface->systime_lock, flags); | ||
318 | interface->ptp_adjust = fm10k_systime_read(interface) - ns; | ||
319 | write_unlock_irqrestore(&interface->systime_lock, flags); | ||
320 | |||
321 | return 0; | ||
322 | } | ||
323 | |||
324 | static int fm10k_ptp_enable(struct ptp_clock_info *ptp, | ||
325 | struct ptp_clock_request *rq, int on) | ||
326 | { | ||
327 | struct ptp_clock_time *t = &rq->perout.period; | ||
328 | struct fm10k_intfc *interface; | ||
329 | struct fm10k_hw *hw; | ||
330 | u64 period; | ||
331 | u32 step; | ||
332 | |||
333 | /* we can only support periodic output */ | ||
334 | if (rq->type != PTP_CLK_REQ_PEROUT) | ||
335 | return -EINVAL; | ||
336 | |||
337 | /* verify the requested channel is there */ | ||
338 | if (rq->perout.index >= ptp->n_per_out) | ||
339 | return -EINVAL; | ||
340 | |||
341 | /* we cannot enforce start time as there is no | ||
342 | * mechanism for that in the hardware, we can only control | ||
343 | * the period. | ||
344 | */ | ||
345 | |||
346 | /* we cannot support periods greater than 4 seconds due to reg limit */ | ||
347 | if (t->sec > 4 || t->sec < 0) | ||
348 | return -ERANGE; | ||
349 | |||
350 | interface = container_of(ptp, struct fm10k_intfc, ptp_caps); | ||
351 | hw = &interface->hw; | ||
352 | |||
353 | /* we simply cannot support the operation if we don't have BAR4 */ | ||
354 | if (!hw->sw_addr) | ||
355 | return -ENOTSUPP; | ||
356 | |||
357 | /* convert to unsigned 64b ns, verify we can put it in a 32b register */ | ||
358 | period = t->sec * 1000000000LL + t->nsec; | ||
359 | |||
360 | /* determine the minimum size for period */ | ||
361 | step = 2 * (fm10k_read_reg(hw, FM10K_SYSTIME_CFG) & | ||
362 | FM10K_SYSTIME_CFG_STEP_MASK); | ||
363 | |||
364 | /* verify the value is in range supported by hardware */ | ||
365 | if ((period && (period < step)) || (period > U32_MAX)) | ||
366 | return -ERANGE; | ||
367 | |||
368 | /* notify hardware of request to being sending pulses */ | ||
369 | fm10k_write_sw_reg(hw, FM10K_SW_SYSTIME_PULSE(rq->perout.index), | ||
370 | (u32)period); | ||
371 | |||
372 | return 0; | ||
373 | } | ||
374 | |||
375 | static struct ptp_pin_desc fm10k_ptp_pd[2] = { | ||
376 | { | ||
377 | .name = "IEEE1588_PULSE0", | ||
378 | .index = 0, | ||
379 | .func = PTP_PF_PEROUT, | ||
380 | .chan = 0 | ||
381 | }, | ||
382 | { | ||
383 | .name = "IEEE1588_PULSE1", | ||
384 | .index = 1, | ||
385 | .func = PTP_PF_PEROUT, | ||
386 | .chan = 1 | ||
387 | } | ||
388 | }; | ||
389 | |||
390 | static int fm10k_ptp_verify(struct ptp_clock_info *ptp, unsigned int pin, | ||
391 | enum ptp_pin_function func, unsigned int chan) | ||
392 | { | ||
393 | /* verify the requested pin is there */ | ||
394 | if (pin >= ptp->n_pins || !ptp->pin_config) | ||
395 | return -EINVAL; | ||
396 | |||
397 | /* enforce locked channels, no changing them */ | ||
398 | if (chan != ptp->pin_config[pin].chan) | ||
399 | return -EINVAL; | ||
400 | |||
401 | /* we want to keep the functions locked as well */ | ||
402 | if (func != ptp->pin_config[pin].func) | ||
403 | return -EINVAL; | ||
404 | |||
405 | return 0; | ||
406 | } | ||
407 | |||
408 | void fm10k_ptp_register(struct fm10k_intfc *interface) | ||
409 | { | ||
410 | struct ptp_clock_info *ptp_caps = &interface->ptp_caps; | ||
411 | struct device *dev = &interface->pdev->dev; | ||
412 | struct ptp_clock *ptp_clock; | ||
413 | |||
414 | snprintf(ptp_caps->name, sizeof(ptp_caps->name), | ||
415 | "%s", interface->netdev->name); | ||
416 | ptp_caps->owner = THIS_MODULE; | ||
417 | /* This math is simply the inverse of the math in | ||
418 | * fm10k_adjust_systime_pf applied to an adjustment value | ||
419 | * of 2^30 - 1 which is the maximum value of the register: | ||
420 | * max_ppb == ((2^30 - 1) * 5^9) / 2^31 | ||
421 | */ | ||
422 | ptp_caps->max_adj = 976562; | ||
423 | ptp_caps->adjfreq = fm10k_ptp_adjfreq; | ||
424 | ptp_caps->adjtime = fm10k_ptp_adjtime; | ||
425 | ptp_caps->gettime = fm10k_ptp_gettime; | ||
426 | ptp_caps->settime = fm10k_ptp_settime; | ||
427 | |||
428 | /* provide pins if BAR4 is accessible */ | ||
429 | if (interface->sw_addr) { | ||
430 | /* enable periodic outputs */ | ||
431 | ptp_caps->n_per_out = 2; | ||
432 | ptp_caps->enable = fm10k_ptp_enable; | ||
433 | |||
434 | /* enable clock pins */ | ||
435 | ptp_caps->verify = fm10k_ptp_verify; | ||
436 | ptp_caps->n_pins = 2; | ||
437 | ptp_caps->pin_config = fm10k_ptp_pd; | ||
438 | } | ||
439 | |||
440 | ptp_clock = ptp_clock_register(ptp_caps, dev); | ||
441 | if (IS_ERR(ptp_clock)) { | ||
442 | ptp_clock = NULL; | ||
443 | dev_err(dev, "ptp_clock_register failed\n"); | ||
444 | } else { | ||
445 | dev_info(dev, "registered PHC device %s\n", ptp_caps->name); | ||
446 | } | ||
447 | |||
448 | interface->ptp_clock = ptp_clock; | ||
449 | } | ||
450 | |||
451 | void fm10k_ptp_unregister(struct fm10k_intfc *interface) | ||
452 | { | ||
453 | struct ptp_clock *ptp_clock = interface->ptp_clock; | ||
454 | struct device *dev = &interface->pdev->dev; | ||
455 | |||
456 | if (!ptp_clock) | ||
457 | return; | ||
458 | |||
459 | interface->ptp_clock = NULL; | ||
460 | |||
461 | ptp_clock_unregister(ptp_clock); | ||
462 | dev_info(dev, "removed PHC %s\n", interface->ptp_caps.name); | ||
463 | } | ||