1a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck/* Intel Ethernet Switch Host Interface Driver
2a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck * Copyright(c) 2013 - 2014 Intel Corporation.
3a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck *
4a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck * This program is free software; you can redistribute it and/or modify it
5a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck * under the terms and conditions of the GNU General Public License,
6a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck * version 2, as published by the Free Software Foundation.
7a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck *
8a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck * This program is distributed in the hope it will be useful, but WITHOUT
9a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
11a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck * more details.
12a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck *
13a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck * The full GNU General Public License is included in this distribution in
14a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck * the file called "COPYING".
15a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck *
16a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck * Contact Information:
17a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck * e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
18a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
19a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck */
20a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
21a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck#include <linux/ptp_classify.h>
22a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck#include <linux/ptp_clock_kernel.h>
23a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
24a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck#include "fm10k.h"
25a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
26a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck#define FM10K_TS_TX_TIMEOUT		(HZ * 15)
27a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
28a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyckvoid fm10k_systime_to_hwtstamp(struct fm10k_intfc *interface,
29a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck			       struct skb_shared_hwtstamps *hwtstamp,
30a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck			       u64 systime)
31a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck{
32a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	unsigned long flags;
33a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
34a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	read_lock_irqsave(&interface->systime_lock, flags);
35a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	systime += interface->ptp_adjust;
36a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	read_unlock_irqrestore(&interface->systime_lock, flags);
37a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
38a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	hwtstamp->hwtstamp = ns_to_ktime(systime);
39a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck}
40a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
41a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyckstatic struct sk_buff *fm10k_ts_tx_skb(struct fm10k_intfc *interface,
42a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck				       __le16 dglort)
43a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck{
44a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	struct sk_buff_head *list = &interface->ts_tx_skb_queue;
45a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	struct sk_buff *skb;
46a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
47a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	skb_queue_walk(list, skb) {
48a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck		if (FM10K_CB(skb)->fi.w.dglort == dglort)
49a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck			return skb;
50a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	}
51a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
52a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	return NULL;
53a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck}
54a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
55a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyckvoid fm10k_ts_tx_enqueue(struct fm10k_intfc *interface, struct sk_buff *skb)
56a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck{
57a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	struct sk_buff_head *list = &interface->ts_tx_skb_queue;
58a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	struct sk_buff *clone;
59a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	unsigned long flags;
60a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	__le16 dglort;
61a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
62a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	/* create clone for us to return on the Tx path */
63a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	clone = skb_clone_sk(skb);
64a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	if (!clone)
65a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck		return;
66a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
67a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	FM10K_CB(clone)->ts_tx_timeout = jiffies + FM10K_TS_TX_TIMEOUT;
68a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	dglort = FM10K_CB(clone)->fi.w.dglort;
69a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
70a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	spin_lock_irqsave(&list->lock, flags);
71a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
72a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	/* attempt to locate any buffers with the same dglort,
73a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	 * if none are present then insert skb in tail of list
74a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	 */
75a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	skb = fm10k_ts_tx_skb(interface, FM10K_CB(clone)->fi.w.dglort);
76a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	if (!skb)
77a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck		__skb_queue_tail(list, clone);
78a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
79a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	spin_unlock_irqrestore(&list->lock, flags);
80a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
81a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	/* if list is already has one then we just free the clone */
82a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	if (skb)
83a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck		kfree_skb(skb);
84a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	else
85a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck		skb_shinfo(clone)->tx_flags |= SKBTX_IN_PROGRESS;
86a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck}
87a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
88a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyckvoid fm10k_ts_tx_hwtstamp(struct fm10k_intfc *interface, __le16 dglort,
89a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck			  u64 systime)
90a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck{
91a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	struct skb_shared_hwtstamps shhwtstamps;
92a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	struct sk_buff_head *list = &interface->ts_tx_skb_queue;
93a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	struct sk_buff *skb;
94a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	unsigned long flags;
95a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
96a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	spin_lock_irqsave(&list->lock, flags);
97a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
98a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	/* attempt to locate and pull the sk_buff out of the list */
99a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	skb = fm10k_ts_tx_skb(interface, dglort);
100a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	if (skb)
101a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck		__skb_unlink(skb, list);
102a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
103a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	spin_unlock_irqrestore(&list->lock, flags);
104a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
105a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	/* if not found do nothing */
106a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	if (!skb)
107a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck		return;
108a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
109a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	/* timestamp the sk_buff and return it to the socket */
110a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	fm10k_systime_to_hwtstamp(interface, &shhwtstamps, systime);
111a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	skb_complete_tx_timestamp(skb, &shhwtstamps);
112a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck}
113a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
114a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyckvoid fm10k_ts_tx_subtask(struct fm10k_intfc *interface)
115a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck{
116a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	struct sk_buff_head *list = &interface->ts_tx_skb_queue;
117a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	struct sk_buff *skb, *tmp;
118a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	unsigned long flags;
119a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
120a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	/* If we're down or resetting, just bail */
121a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	if (test_bit(__FM10K_DOWN, &interface->state) ||
122a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	    test_bit(__FM10K_RESETTING, &interface->state))
123a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck		return;
124a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
125a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	spin_lock_irqsave(&list->lock, flags);
126a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
127a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	/* walk though the list and flush any expired timestamp packets */
128a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	skb_queue_walk_safe(list, skb, tmp) {
129a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck		if (!time_is_after_jiffies(FM10K_CB(skb)->ts_tx_timeout))
130a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck			continue;
131a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck		__skb_unlink(skb, list);
132a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck		kfree_skb(skb);
133a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck		interface->tx_hwtstamp_timeouts++;
134a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	}
135a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
136a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	spin_unlock_irqrestore(&list->lock, flags);
137a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck}
138a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
139a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyckstatic u64 fm10k_systime_read(struct fm10k_intfc *interface)
140a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck{
141a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	struct fm10k_hw *hw = &interface->hw;
142a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
143a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	return hw->mac.ops.read_systime(hw);
144a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck}
145a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
146a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyckvoid fm10k_ts_reset(struct fm10k_intfc *interface)
147a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck{
148a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	s64 ns = ktime_to_ns(ktime_get_real());
149a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	unsigned long flags;
150a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
151a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	/* reinitialize the clock */
152a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	write_lock_irqsave(&interface->systime_lock, flags);
153a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	interface->ptp_adjust = fm10k_systime_read(interface) - ns;
154a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	write_unlock_irqrestore(&interface->systime_lock, flags);
155a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck}
156a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
157a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyckvoid fm10k_ts_init(struct fm10k_intfc *interface)
158a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck{
159a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	/* Initialize lock protecting systime access */
160a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	rwlock_init(&interface->systime_lock);
161a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
162a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	/* Initialize skb queue for pending timestamp requests */
163a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	skb_queue_head_init(&interface->ts_tx_skb_queue);
164a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
165a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	/* reset the clock to current kernel time */
166a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	fm10k_ts_reset(interface);
167a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck}
168a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
169a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck/**
170a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck * fm10k_get_ts_config - get current hardware timestamping configuration
171a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck * @netdev: network interface device structure
172a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck * @ifreq: ioctl data
173a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck *
174a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck * This function returns the current timestamping settings. Rather than
175a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck * attempt to deconstruct registers to fill in the values, simply keep a copy
176a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck * of the old settings around, and return a copy when requested.
177a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck */
178a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyckint fm10k_get_ts_config(struct net_device *netdev, struct ifreq *ifr)
179a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck{
180a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	struct fm10k_intfc *interface = netdev_priv(netdev);
181a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	struct hwtstamp_config *config = &interface->ts_config;
182a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
183a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	return copy_to_user(ifr->ifr_data, config, sizeof(*config)) ?
184a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck		-EFAULT : 0;
185a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck}
186a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
187a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck/**
188a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck * fm10k_set_ts_config - control hardware time stamping
189a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck * @netdev: network interface device structure
190a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck * @ifreq: ioctl data
191a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck *
192a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck * Outgoing time stamping can be enabled and disabled. Play nice and
193a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck * disable it when requested, although it shouldn't cause any overhead
194a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck * when no packet needs it. At most one packet in the queue may be
195a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck * marked for time stamping, otherwise it would be impossible to tell
196a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck * for sure to which packet the hardware time stamp belongs.
197a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck *
198a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck * Incoming time stamping has to be configured via the hardware
199a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck * filters. Not all combinations are supported, in particular event
200a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck * type has to be specified. Matching the kind of event packet is
201a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck * not supported, with the exception of "all V2 events regardless of
202a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck * level 2 or 4".
203a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck *
204a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck * Since hardware always timestamps Path delay packets when timestamping V2
205a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck * packets, regardless of the type specified in the register, only use V2
206a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck * Event mode. This more accurately tells the user what the hardware is going
207a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck * to do anyways.
208a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck */
209a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyckint fm10k_set_ts_config(struct net_device *netdev, struct ifreq *ifr)
210a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck{
211a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	struct fm10k_intfc *interface = netdev_priv(netdev);
212a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	struct hwtstamp_config ts_config;
213a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
214a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	if (copy_from_user(&ts_config, ifr->ifr_data, sizeof(ts_config)))
215a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck		return -EFAULT;
216a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
217a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	/* reserved for future extensions */
218a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	if (ts_config.flags)
219a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck		return -EINVAL;
220a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
221a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	switch (ts_config.tx_type) {
222a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	case HWTSTAMP_TX_OFF:
223a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck		break;
224a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	case HWTSTAMP_TX_ON:
225a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck		/* we likely need some check here to see if this is supported */
226a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck		break;
227a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	default:
228a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck		return -ERANGE;
229a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	}
230a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
231a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	switch (ts_config.rx_filter) {
232a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	case HWTSTAMP_FILTER_NONE:
233a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck		interface->flags &= ~FM10K_FLAG_RX_TS_ENABLED;
234a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck		break;
235a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
236a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
237a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
238a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
239a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
240a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
241a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
242a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
243a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:
244a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	case HWTSTAMP_FILTER_PTP_V2_EVENT:
245a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	case HWTSTAMP_FILTER_PTP_V2_SYNC:
246a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
247a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	case HWTSTAMP_FILTER_ALL:
248a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck		interface->flags |= FM10K_FLAG_RX_TS_ENABLED;
249a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck		ts_config.rx_filter = HWTSTAMP_FILTER_ALL;
250a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck		break;
251a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	default:
252a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck		return -ERANGE;
253a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	}
254a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
255a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	/* save these settings for future reference */
256a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	interface->ts_config = ts_config;
257a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
258a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	return copy_to_user(ifr->ifr_data, &ts_config, sizeof(ts_config)) ?
259a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck		-EFAULT : 0;
260a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck}
261a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
262a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyckstatic int fm10k_ptp_adjfreq(struct ptp_clock_info *ptp, s32 ppb)
263a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck{
264a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	struct fm10k_intfc *interface;
265a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	struct fm10k_hw *hw;
266a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	int err;
267a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
268a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	interface = container_of(ptp, struct fm10k_intfc, ptp_caps);
269a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	hw = &interface->hw;
270a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
271a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	err = hw->mac.ops.adjust_systime(hw, ppb);
272a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
273a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	/* the only error we should see is if the value is out of range */
274a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	return (err == FM10K_ERR_PARAM) ? -ERANGE : err;
275a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck}
276a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
277a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyckstatic int fm10k_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
278a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck{
279a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	struct fm10k_intfc *interface;
280a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	unsigned long flags;
281a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
282a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	interface = container_of(ptp, struct fm10k_intfc, ptp_caps);
283a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
284a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	write_lock_irqsave(&interface->systime_lock, flags);
285a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	interface->ptp_adjust += delta;
286a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	write_unlock_irqrestore(&interface->systime_lock, flags);
287a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
288a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	return 0;
289a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck}
290a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
291a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyckstatic int fm10k_ptp_gettime(struct ptp_clock_info *ptp, struct timespec *ts)
292a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck{
293a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	struct fm10k_intfc *interface;
294a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	unsigned long flags;
295a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	u64 now;
296a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
297a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	interface = container_of(ptp, struct fm10k_intfc, ptp_caps);
298a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
299a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	read_lock_irqsave(&interface->systime_lock, flags);
300a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	now = fm10k_systime_read(interface) + interface->ptp_adjust;
301a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	read_unlock_irqrestore(&interface->systime_lock, flags);
302a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
303a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	*ts = ns_to_timespec(now);
304a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
305a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	return 0;
306a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck}
307a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
308a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyckstatic int fm10k_ptp_settime(struct ptp_clock_info *ptp,
309a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck			     const struct timespec *ts)
310a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck{
311a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	struct fm10k_intfc *interface;
312a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	unsigned long flags;
313a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	u64 ns = timespec_to_ns(ts);
314a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
315a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	interface = container_of(ptp, struct fm10k_intfc, ptp_caps);
316a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
317a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	write_lock_irqsave(&interface->systime_lock, flags);
318a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	interface->ptp_adjust = fm10k_systime_read(interface) - ns;
319a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	write_unlock_irqrestore(&interface->systime_lock, flags);
320a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
321a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	return 0;
322a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck}
323a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
324a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyckstatic int fm10k_ptp_enable(struct ptp_clock_info *ptp,
325a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck			    struct ptp_clock_request *rq, int on)
326a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck{
327a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	struct ptp_clock_time *t = &rq->perout.period;
328a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	struct fm10k_intfc *interface;
329a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	struct fm10k_hw *hw;
330a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	u64 period;
331a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	u32 step;
332a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
333a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	/* we can only support periodic output */
334a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	if (rq->type != PTP_CLK_REQ_PEROUT)
335a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck		return -EINVAL;
336a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
337a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	/* verify the requested channel is there */
338a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	if (rq->perout.index >= ptp->n_per_out)
339a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck		return -EINVAL;
340a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
341a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	/* we cannot enforce start time as there is no
342a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	 * mechanism for that in the hardware, we can only control
343a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	 * the period.
344a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	 */
345a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
346a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	/* we cannot support periods greater than 4 seconds due to reg limit */
347a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	if (t->sec > 4 || t->sec < 0)
348a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck		return -ERANGE;
349a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
350a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	interface = container_of(ptp, struct fm10k_intfc, ptp_caps);
351a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	hw = &interface->hw;
352a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
353a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	/* we simply cannot support the operation if we don't have BAR4 */
354a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	if (!hw->sw_addr)
355a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck		return -ENOTSUPP;
356a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
357a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	/* convert to unsigned 64b ns, verify we can put it in a 32b register */
358a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	period = t->sec * 1000000000LL + t->nsec;
359a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
360a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	/* determine the minimum size for period */
361a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	step = 2 * (fm10k_read_reg(hw, FM10K_SYSTIME_CFG) &
362a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck		    FM10K_SYSTIME_CFG_STEP_MASK);
363a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
364a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	/* verify the value is in range supported by hardware */
365a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	if ((period && (period < step)) || (period > U32_MAX))
366a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck		return -ERANGE;
367a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
368a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	/* notify hardware of request to being sending pulses */
369a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	fm10k_write_sw_reg(hw, FM10K_SW_SYSTIME_PULSE(rq->perout.index),
370a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck			   (u32)period);
371a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
372a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	return 0;
373a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck}
374a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
375a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyckstatic struct ptp_pin_desc fm10k_ptp_pd[2] = {
376a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	{
377a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck		.name = "IEEE1588_PULSE0",
378a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck		.index = 0,
379a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck		.func = PTP_PF_PEROUT,
380a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck		.chan = 0
381a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	},
382a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	{
383a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck		.name = "IEEE1588_PULSE1",
384a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck		.index = 1,
385a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck		.func = PTP_PF_PEROUT,
386a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck		.chan = 1
387a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	}
388a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck};
389a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
390a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyckstatic int fm10k_ptp_verify(struct ptp_clock_info *ptp, unsigned int pin,
391a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck			    enum ptp_pin_function func, unsigned int chan)
392a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck{
393a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	/* verify the requested pin is there */
394a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	if (pin >= ptp->n_pins || !ptp->pin_config)
395a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck		return -EINVAL;
396a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
397a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	/* enforce locked channels, no changing them */
398a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	if (chan != ptp->pin_config[pin].chan)
399a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck		return -EINVAL;
400a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
401a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	/* we want to keep the functions locked as well */
402a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	if (func != ptp->pin_config[pin].func)
403a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck		return -EINVAL;
404a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
405a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	return 0;
406a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck}
407a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
408a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyckvoid fm10k_ptp_register(struct fm10k_intfc *interface)
409a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck{
410a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	struct ptp_clock_info *ptp_caps = &interface->ptp_caps;
411a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	struct device *dev = &interface->pdev->dev;
412a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	struct ptp_clock *ptp_clock;
413a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
414a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	snprintf(ptp_caps->name, sizeof(ptp_caps->name),
415a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck		 "%s", interface->netdev->name);
416a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	ptp_caps->owner		= THIS_MODULE;
417a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	/* This math is simply the inverse of the math in
418a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	 * fm10k_adjust_systime_pf applied to an adjustment value
419a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	 * of 2^30 - 1 which is the maximum value of the register:
420a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	 * 	max_ppb == ((2^30 - 1) * 5^9) / 2^31
421a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	 */
422a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	ptp_caps->max_adj	= 976562;
423a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	ptp_caps->adjfreq	= fm10k_ptp_adjfreq;
424a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	ptp_caps->adjtime	= fm10k_ptp_adjtime;
425a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	ptp_caps->gettime	= fm10k_ptp_gettime;
426a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	ptp_caps->settime	= fm10k_ptp_settime;
427a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
428a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	/* provide pins if BAR4 is accessible */
429a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	if (interface->sw_addr) {
430a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck		/* enable periodic outputs */
431a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck		ptp_caps->n_per_out = 2;
432a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck		ptp_caps->enable	= fm10k_ptp_enable;
433a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
434a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck		/* enable clock pins */
435a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck		ptp_caps->verify	= fm10k_ptp_verify;
436a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck		ptp_caps->n_pins = 2;
437a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck		ptp_caps->pin_config = fm10k_ptp_pd;
438a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	}
439a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
440a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	ptp_clock = ptp_clock_register(ptp_caps, dev);
441a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	if (IS_ERR(ptp_clock)) {
442a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck		ptp_clock = NULL;
443a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck		dev_err(dev, "ptp_clock_register failed\n");
444a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	} else {
445a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck		dev_info(dev, "registered PHC device %s\n", ptp_caps->name);
446a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	}
447a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
448a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	interface->ptp_clock = ptp_clock;
449a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck}
450a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
451a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyckvoid fm10k_ptp_unregister(struct fm10k_intfc *interface)
452a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck{
453a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	struct ptp_clock *ptp_clock = interface->ptp_clock;
454a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	struct device *dev = &interface->pdev->dev;
455a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
456a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	if (!ptp_clock)
457a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck		return;
458a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
459a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	interface->ptp_clock = NULL;
460a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck
461a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	ptp_clock_unregister(ptp_clock);
462a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck	dev_info(dev, "removed PHC %s\n", interface->ptp_caps.name);
463a211e0136c9a3653acba13ec3b9a2f49c3c44f5eAlexander Duyck}
464