aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorShawn Bohrer <sbohrer@rgmadvisors.com>2013-12-31 12:39:39 -0500
committerDavid S. Miller <davem@davemloft.net>2014-01-02 03:30:36 -0500
commitad7d4eaed995d76fb24a18e202fdf5072197ff0a (patch)
tree2625d165dd5b6972f582f19e20cc6c955fd9259a
parentdd9b45598a7198f8b12965f2ec453bcb5cb90aec (diff)
mlx4_en: Add PTP hardware clock
This adds a PHC to the mlx4_en driver. We use reader/writer spinlocks to protect the timecounter since every packet received needs to call timecounter_cycle2time() when timestamping is enabled. This can become a performance bottleneck with RSS and multiple receive queues if normal spinlocks are used. This driver has been tested with both Documentation/ptp/testptp and the linuxptp project (http://linuxptp.sourceforge.net/) on a Mellanox ConnectX-3 card. Signed-off-by: Shawn Bohrer <sbohrer@rgmadvisors.com> Acked-By: Hadar Hen Zion <hadarh@mellanox.com> Acked-by: Richard Cochran <richardcochran@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/en_clock.c194
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/en_ethtool.c3
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/en_main.c3
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/mlx4_en.h6
4 files changed, 198 insertions, 8 deletions
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_clock.c b/drivers/net/ethernet/mellanox/mlx4/en_clock.c
index fd6441071319..30712b36f43c 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_clock.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_clock.c
@@ -103,19 +103,191 @@ void mlx4_en_fill_hwtstamps(struct mlx4_en_dev *mdev,
103 struct skb_shared_hwtstamps *hwts, 103 struct skb_shared_hwtstamps *hwts,
104 u64 timestamp) 104 u64 timestamp)
105{ 105{
106 unsigned long flags;
106 u64 nsec; 107 u64 nsec;
107 108
109 read_lock_irqsave(&mdev->clock_lock, flags);
108 nsec = timecounter_cyc2time(&mdev->clock, timestamp); 110 nsec = timecounter_cyc2time(&mdev->clock, timestamp);
111 read_unlock_irqrestore(&mdev->clock_lock, flags);
109 112
110 memset(hwts, 0, sizeof(struct skb_shared_hwtstamps)); 113 memset(hwts, 0, sizeof(struct skb_shared_hwtstamps));
111 hwts->hwtstamp = ns_to_ktime(nsec); 114 hwts->hwtstamp = ns_to_ktime(nsec);
112} 115}
113 116
117/**
118 * mlx4_en_remove_timestamp - disable PTP device
119 * @mdev: board private structure
120 *
121 * Stop the PTP support.
122 **/
123void mlx4_en_remove_timestamp(struct mlx4_en_dev *mdev)
124{
125 if (mdev->ptp_clock) {
126 ptp_clock_unregister(mdev->ptp_clock);
127 mdev->ptp_clock = NULL;
128 mlx4_info(mdev, "removed PHC\n");
129 }
130}
131
132void mlx4_en_ptp_overflow_check(struct mlx4_en_dev *mdev)
133{
134 bool timeout = time_is_before_jiffies(mdev->last_overflow_check +
135 mdev->overflow_period);
136 unsigned long flags;
137
138 if (timeout) {
139 write_lock_irqsave(&mdev->clock_lock, flags);
140 timecounter_read(&mdev->clock);
141 write_unlock_irqrestore(&mdev->clock_lock, flags);
142 mdev->last_overflow_check = jiffies;
143 }
144}
145
146/**
147 * mlx4_en_phc_adjfreq - adjust the frequency of the hardware clock
148 * @ptp: ptp clock structure
149 * @delta: Desired frequency change in parts per billion
150 *
151 * Adjust the frequency of the PHC cycle counter by the indicated delta from
152 * the base frequency.
153 **/
154static int mlx4_en_phc_adjfreq(struct ptp_clock_info *ptp, s32 delta)
155{
156 u64 adj;
157 u32 diff, mult;
158 int neg_adj = 0;
159 unsigned long flags;
160 struct mlx4_en_dev *mdev = container_of(ptp, struct mlx4_en_dev,
161 ptp_clock_info);
162
163 if (delta < 0) {
164 neg_adj = 1;
165 delta = -delta;
166 }
167 mult = mdev->nominal_c_mult;
168 adj = mult;
169 adj *= delta;
170 diff = div_u64(adj, 1000000000ULL);
171
172 write_lock_irqsave(&mdev->clock_lock, flags);
173 timecounter_read(&mdev->clock);
174 mdev->cycles.mult = neg_adj ? mult - diff : mult + diff;
175 write_unlock_irqrestore(&mdev->clock_lock, flags);
176
177 return 0;
178}
179
180/**
181 * mlx4_en_phc_adjtime - Shift the time of the hardware clock
182 * @ptp: ptp clock structure
183 * @delta: Desired change in nanoseconds
184 *
185 * Adjust the timer by resetting the timecounter structure.
186 **/
187static int mlx4_en_phc_adjtime(struct ptp_clock_info *ptp, s64 delta)
188{
189 struct mlx4_en_dev *mdev = container_of(ptp, struct mlx4_en_dev,
190 ptp_clock_info);
191 unsigned long flags;
192 s64 now;
193
194 write_lock_irqsave(&mdev->clock_lock, flags);
195 now = timecounter_read(&mdev->clock);
196 now += delta;
197 timecounter_init(&mdev->clock, &mdev->cycles, now);
198 write_unlock_irqrestore(&mdev->clock_lock, flags);
199
200 return 0;
201}
202
203/**
204 * mlx4_en_phc_gettime - Reads the current time from the hardware clock
205 * @ptp: ptp clock structure
206 * @ts: timespec structure to hold the current time value
207 *
208 * Read the timecounter and return the correct value in ns after converting
209 * it into a struct timespec.
210 **/
211static int mlx4_en_phc_gettime(struct ptp_clock_info *ptp, struct timespec *ts)
212{
213 struct mlx4_en_dev *mdev = container_of(ptp, struct mlx4_en_dev,
214 ptp_clock_info);
215 unsigned long flags;
216 u32 remainder;
217 u64 ns;
218
219 write_lock_irqsave(&mdev->clock_lock, flags);
220 ns = timecounter_read(&mdev->clock);
221 write_unlock_irqrestore(&mdev->clock_lock, flags);
222
223 ts->tv_sec = div_u64_rem(ns, NSEC_PER_SEC, &remainder);
224 ts->tv_nsec = remainder;
225
226 return 0;
227}
228
229/**
230 * mlx4_en_phc_settime - Set the current time on the hardware clock
231 * @ptp: ptp clock structure
232 * @ts: timespec containing the new time for the cycle counter
233 *
234 * Reset the timecounter to use a new base value instead of the kernel
235 * wall timer value.
236 **/
237static int mlx4_en_phc_settime(struct ptp_clock_info *ptp,
238 const struct timespec *ts)
239{
240 struct mlx4_en_dev *mdev = container_of(ptp, struct mlx4_en_dev,
241 ptp_clock_info);
242 u64 ns = timespec_to_ns(ts);
243 unsigned long flags;
244
245 /* reset the timecounter */
246 write_lock_irqsave(&mdev->clock_lock, flags);
247 timecounter_init(&mdev->clock, &mdev->cycles, ns);
248 write_unlock_irqrestore(&mdev->clock_lock, flags);
249
250 return 0;
251}
252
253/**
254 * mlx4_en_phc_enable - enable or disable an ancillary feature
255 * @ptp: ptp clock structure
256 * @request: Desired resource to enable or disable
257 * @on: Caller passes one to enable or zero to disable
258 *
259 * Enable (or disable) ancillary features of the PHC subsystem.
260 * Currently, no ancillary features are supported.
261 **/
262static int mlx4_en_phc_enable(struct ptp_clock_info __always_unused *ptp,
263 struct ptp_clock_request __always_unused *request,
264 int __always_unused on)
265{
266 return -EOPNOTSUPP;
267}
268
269static const struct ptp_clock_info mlx4_en_ptp_clock_info = {
270 .owner = THIS_MODULE,
271 .max_adj = 100000000,
272 .n_alarm = 0,
273 .n_ext_ts = 0,
274 .n_per_out = 0,
275 .pps = 0,
276 .adjfreq = mlx4_en_phc_adjfreq,
277 .adjtime = mlx4_en_phc_adjtime,
278 .gettime = mlx4_en_phc_gettime,
279 .settime = mlx4_en_phc_settime,
280 .enable = mlx4_en_phc_enable,
281};
282
114void mlx4_en_init_timestamp(struct mlx4_en_dev *mdev) 283void mlx4_en_init_timestamp(struct mlx4_en_dev *mdev)
115{ 284{
116 struct mlx4_dev *dev = mdev->dev; 285 struct mlx4_dev *dev = mdev->dev;
286 unsigned long flags;
117 u64 ns; 287 u64 ns;
118 288
289 rwlock_init(&mdev->clock_lock);
290
119 memset(&mdev->cycles, 0, sizeof(mdev->cycles)); 291 memset(&mdev->cycles, 0, sizeof(mdev->cycles));
120 mdev->cycles.read = mlx4_en_read_clock; 292 mdev->cycles.read = mlx4_en_read_clock;
121 mdev->cycles.mask = CLOCKSOURCE_MASK(48); 293 mdev->cycles.mask = CLOCKSOURCE_MASK(48);
@@ -127,9 +299,12 @@ void mlx4_en_init_timestamp(struct mlx4_en_dev *mdev)
127 mdev->cycles.shift = 14; 299 mdev->cycles.shift = 14;
128 mdev->cycles.mult = 300 mdev->cycles.mult =
129 clocksource_khz2mult(1000 * dev->caps.hca_core_clock, mdev->cycles.shift); 301 clocksource_khz2mult(1000 * dev->caps.hca_core_clock, mdev->cycles.shift);
302 mdev->nominal_c_mult = mdev->cycles.mult;
130 303
304 write_lock_irqsave(&mdev->clock_lock, flags);
131 timecounter_init(&mdev->clock, &mdev->cycles, 305 timecounter_init(&mdev->clock, &mdev->cycles,
132 ktime_to_ns(ktime_get_real())); 306 ktime_to_ns(ktime_get_real()));
307 write_unlock_irqrestore(&mdev->clock_lock, flags);
133 308
134 /* Calculate period in seconds to call the overflow watchdog - to make 309 /* Calculate period in seconds to call the overflow watchdog - to make
135 * sure counter is checked at least once every wrap around. 310 * sure counter is checked at least once every wrap around.
@@ -137,15 +312,18 @@ void mlx4_en_init_timestamp(struct mlx4_en_dev *mdev)
137 ns = cyclecounter_cyc2ns(&mdev->cycles, mdev->cycles.mask); 312 ns = cyclecounter_cyc2ns(&mdev->cycles, mdev->cycles.mask);
138 do_div(ns, NSEC_PER_SEC / 2 / HZ); 313 do_div(ns, NSEC_PER_SEC / 2 / HZ);
139 mdev->overflow_period = ns; 314 mdev->overflow_period = ns;
140}
141 315
142void mlx4_en_ptp_overflow_check(struct mlx4_en_dev *mdev) 316 /* Configure the PHC */
143{ 317 mdev->ptp_clock_info = mlx4_en_ptp_clock_info;
144 bool timeout = time_is_before_jiffies(mdev->last_overflow_check + 318 snprintf(mdev->ptp_clock_info.name, 16, "mlx4 ptp");
145 mdev->overflow_period);
146 319
147 if (timeout) { 320 mdev->ptp_clock = ptp_clock_register(&mdev->ptp_clock_info,
148 timecounter_read(&mdev->clock); 321 &mdev->pdev->dev);
149 mdev->last_overflow_check = jiffies; 322 if (IS_ERR(mdev->ptp_clock)) {
323 mdev->ptp_clock = NULL;
324 mlx4_err(mdev, "ptp_clock_register failed\n");
325 } else {
326 mlx4_info(mdev, "registered PHC clock\n");
150 } 327 }
328
151} 329}
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c
index 0596f9f85a0e..3e8d33605fe7 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c
@@ -1193,6 +1193,9 @@ static int mlx4_en_get_ts_info(struct net_device *dev,
1193 info->rx_filters = 1193 info->rx_filters =
1194 (1 << HWTSTAMP_FILTER_NONE) | 1194 (1 << HWTSTAMP_FILTER_NONE) |
1195 (1 << HWTSTAMP_FILTER_ALL); 1195 (1 << HWTSTAMP_FILTER_ALL);
1196
1197 if (mdev->ptp_clock)
1198 info->phc_index = ptp_clock_index(mdev->ptp_clock);
1196 } 1199 }
1197 1200
1198 return ret; 1201 return ret;
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_main.c b/drivers/net/ethernet/mellanox/mlx4/en_main.c
index 725a4e1b5f67..d357bf5a4686 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_main.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_main.c
@@ -199,6 +199,9 @@ static void mlx4_en_remove(struct mlx4_dev *dev, void *endev_ptr)
199 if (mdev->pndev[i]) 199 if (mdev->pndev[i])
200 mlx4_en_destroy_netdev(mdev->pndev[i]); 200 mlx4_en_destroy_netdev(mdev->pndev[i]);
201 201
202 if (mdev->dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_TS)
203 mlx4_en_remove_timestamp(mdev);
204
202 flush_workqueue(mdev->workqueue); 205 flush_workqueue(mdev->workqueue);
203 destroy_workqueue(mdev->workqueue); 206 destroy_workqueue(mdev->workqueue);
204 (void) mlx4_mr_free(dev, &mdev->mr); 207 (void) mlx4_mr_free(dev, &mdev->mr);
diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h
index 766691c66111..2f1e200f2e4c 100644
--- a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h
+++ b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h
@@ -45,6 +45,7 @@
45#include <linux/dcbnl.h> 45#include <linux/dcbnl.h>
46#endif 46#endif
47#include <linux/cpu_rmap.h> 47#include <linux/cpu_rmap.h>
48#include <linux/ptp_clock_kernel.h>
48 49
49#include <linux/mlx4/device.h> 50#include <linux/mlx4/device.h>
50#include <linux/mlx4/qp.h> 51#include <linux/mlx4/qp.h>
@@ -375,10 +376,14 @@ struct mlx4_en_dev {
375 u32 priv_pdn; 376 u32 priv_pdn;
376 spinlock_t uar_lock; 377 spinlock_t uar_lock;
377 u8 mac_removed[MLX4_MAX_PORTS + 1]; 378 u8 mac_removed[MLX4_MAX_PORTS + 1];
379 rwlock_t clock_lock;
380 u32 nominal_c_mult;
378 struct cyclecounter cycles; 381 struct cyclecounter cycles;
379 struct timecounter clock; 382 struct timecounter clock;
380 unsigned long last_overflow_check; 383 unsigned long last_overflow_check;
381 unsigned long overflow_period; 384 unsigned long overflow_period;
385 struct ptp_clock *ptp_clock;
386 struct ptp_clock_info ptp_clock_info;
382}; 387};
383 388
384 389
@@ -791,6 +796,7 @@ void mlx4_en_fill_hwtstamps(struct mlx4_en_dev *mdev,
791 struct skb_shared_hwtstamps *hwts, 796 struct skb_shared_hwtstamps *hwts,
792 u64 timestamp); 797 u64 timestamp);
793void mlx4_en_init_timestamp(struct mlx4_en_dev *mdev); 798void mlx4_en_init_timestamp(struct mlx4_en_dev *mdev);
799void mlx4_en_remove_timestamp(struct mlx4_en_dev *mdev);
794int mlx4_en_timestamp_config(struct net_device *dev, 800int mlx4_en_timestamp_config(struct net_device *dev,
795 int tx_type, 801 int tx_type,
796 int rx_filter); 802 int rx_filter);