1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // |
3 | // mcp251xfd - Microchip MCP251xFD Family CAN controller driver |
4 | // |
5 | // Copyright (c) 2021 Pengutronix, |
6 | // Marc Kleine-Budde <kernel@pengutronix.de> |
7 | // |
8 | |
9 | #include <linux/clocksource.h> |
10 | #include <linux/workqueue.h> |
11 | |
12 | #include "mcp251xfd.h" |
13 | |
14 | static u64 mcp251xfd_timestamp_read(const struct cyclecounter *cc) |
15 | { |
16 | const struct mcp251xfd_priv *priv; |
17 | u32 timestamp = 0; |
18 | int err; |
19 | |
20 | priv = container_of(cc, struct mcp251xfd_priv, cc); |
21 | err = mcp251xfd_get_timestamp(priv, timestamp: ×tamp); |
22 | if (err) |
23 | netdev_err(dev: priv->ndev, |
24 | format: "Error %d while reading timestamp. HW timestamps may be inaccurate." , |
25 | err); |
26 | |
27 | return timestamp; |
28 | } |
29 | |
30 | static void mcp251xfd_timestamp_work(struct work_struct *work) |
31 | { |
32 | struct delayed_work *delayed_work = to_delayed_work(work); |
33 | struct mcp251xfd_priv *priv; |
34 | |
35 | priv = container_of(delayed_work, struct mcp251xfd_priv, timestamp); |
36 | timecounter_read(tc: &priv->tc); |
37 | |
38 | schedule_delayed_work(dwork: &priv->timestamp, |
39 | MCP251XFD_TIMESTAMP_WORK_DELAY_SEC * HZ); |
40 | } |
41 | |
42 | void mcp251xfd_skb_set_timestamp(const struct mcp251xfd_priv *priv, |
43 | struct sk_buff *skb, u32 timestamp) |
44 | { |
45 | struct skb_shared_hwtstamps *hwtstamps = skb_hwtstamps(skb); |
46 | u64 ns; |
47 | |
48 | ns = timecounter_cyc2time(tc: &priv->tc, cycle_tstamp: timestamp); |
49 | hwtstamps->hwtstamp = ns_to_ktime(ns); |
50 | } |
51 | |
52 | void mcp251xfd_timestamp_init(struct mcp251xfd_priv *priv) |
53 | { |
54 | struct cyclecounter *cc = &priv->cc; |
55 | |
56 | cc->read = mcp251xfd_timestamp_read; |
57 | cc->mask = CYCLECOUNTER_MASK(32); |
58 | cc->shift = 1; |
59 | cc->mult = clocksource_hz2mult(hz: priv->can.clock.freq, shift_constant: cc->shift); |
60 | |
61 | timecounter_init(tc: &priv->tc, cc: &priv->cc, start_tstamp: ktime_get_real_ns()); |
62 | |
63 | INIT_DELAYED_WORK(&priv->timestamp, mcp251xfd_timestamp_work); |
64 | schedule_delayed_work(dwork: &priv->timestamp, |
65 | MCP251XFD_TIMESTAMP_WORK_DELAY_SEC * HZ); |
66 | } |
67 | |
68 | void mcp251xfd_timestamp_stop(struct mcp251xfd_priv *priv) |
69 | { |
70 | cancel_delayed_work_sync(dwork: &priv->timestamp); |
71 | } |
72 | |