1 // SPDX-License-Identifier: (GPL-2.0 OR MIT)
2 /* Google virtual Ethernet (gve) driver
3 *
4 * Copyright (C) 2025 Google LLC
5 */
6
7 #include "gve.h"
8 #include "gve_adminq.h"
9
10 /* Interval to schedule a nic timestamp calibration, 250ms. */
11 #define GVE_NIC_TS_SYNC_INTERVAL_MS 250
12
13 /* Read the nic timestamp from hardware via the admin queue. */
gve_clock_nic_ts_read(struct gve_priv * priv)14 int gve_clock_nic_ts_read(struct gve_priv *priv)
15 {
16 u64 nic_raw;
17 int err;
18
19 err = gve_adminq_report_nic_ts(priv, priv->nic_ts_report_bus);
20 if (err)
21 return err;
22
23 nic_raw = be64_to_cpu(priv->nic_ts_report->nic_timestamp);
24 WRITE_ONCE(priv->last_sync_nic_counter, nic_raw);
25
26 return 0;
27 }
28
gve_ptp_gettimex64(struct ptp_clock_info * info,struct timespec64 * ts,struct ptp_system_timestamp * sts)29 static int gve_ptp_gettimex64(struct ptp_clock_info *info,
30 struct timespec64 *ts,
31 struct ptp_system_timestamp *sts)
32 {
33 return -EOPNOTSUPP;
34 }
35
gve_ptp_settime64(struct ptp_clock_info * info,const struct timespec64 * ts)36 static int gve_ptp_settime64(struct ptp_clock_info *info,
37 const struct timespec64 *ts)
38 {
39 return -EOPNOTSUPP;
40 }
41
gve_ptp_do_aux_work(struct ptp_clock_info * info)42 static long gve_ptp_do_aux_work(struct ptp_clock_info *info)
43 {
44 const struct gve_ptp *ptp = container_of(info, struct gve_ptp, info);
45 struct gve_priv *priv = ptp->priv;
46 int err;
47
48 if (gve_get_reset_in_progress(priv) || !gve_get_admin_queue_ok(priv))
49 goto out;
50
51 err = gve_clock_nic_ts_read(priv);
52 if (err && net_ratelimit())
53 dev_err(&priv->pdev->dev,
54 "%s read err %d\n", __func__, err);
55
56 out:
57 return msecs_to_jiffies(GVE_NIC_TS_SYNC_INTERVAL_MS);
58 }
59
60 static const struct ptp_clock_info gve_ptp_caps = {
61 .owner = THIS_MODULE,
62 .name = "gve clock",
63 .gettimex64 = gve_ptp_gettimex64,
64 .settime64 = gve_ptp_settime64,
65 .do_aux_work = gve_ptp_do_aux_work,
66 };
67
gve_ptp_init(struct gve_priv * priv)68 static int gve_ptp_init(struct gve_priv *priv)
69 {
70 struct gve_ptp *ptp;
71 int err;
72
73 priv->ptp = kzalloc_obj(*priv->ptp);
74 if (!priv->ptp)
75 return -ENOMEM;
76
77 ptp = priv->ptp;
78 ptp->info = gve_ptp_caps;
79 ptp->clock = ptp_clock_register(&ptp->info, &priv->pdev->dev);
80
81 if (IS_ERR(ptp->clock)) {
82 dev_err(&priv->pdev->dev, "PTP clock registration failed\n");
83 err = PTR_ERR(ptp->clock);
84 goto free_ptp;
85 }
86
87 ptp->priv = priv;
88 return 0;
89
90 free_ptp:
91 kfree(ptp);
92 priv->ptp = NULL;
93 return err;
94 }
95
gve_ptp_release(struct gve_priv * priv)96 static void gve_ptp_release(struct gve_priv *priv)
97 {
98 struct gve_ptp *ptp = priv->ptp;
99
100 if (!ptp)
101 return;
102
103 if (ptp->clock)
104 ptp_clock_unregister(ptp->clock);
105
106 kfree(ptp);
107 priv->ptp = NULL;
108 }
109
gve_init_clock(struct gve_priv * priv)110 int gve_init_clock(struct gve_priv *priv)
111 {
112 int err;
113
114 err = gve_ptp_init(priv);
115 if (err)
116 return err;
117
118 priv->nic_ts_report =
119 dma_alloc_coherent(&priv->pdev->dev,
120 sizeof(struct gve_nic_ts_report),
121 &priv->nic_ts_report_bus,
122 GFP_KERNEL);
123 if (!priv->nic_ts_report) {
124 dev_err(&priv->pdev->dev, "%s dma alloc error\n", __func__);
125 err = -ENOMEM;
126 goto release_ptp;
127 }
128 err = gve_clock_nic_ts_read(priv);
129 if (err) {
130 dev_err(&priv->pdev->dev, "failed to read NIC clock %d\n", err);
131 goto release_nic_ts_report;
132 }
133 ptp_schedule_worker(priv->ptp->clock,
134 msecs_to_jiffies(GVE_NIC_TS_SYNC_INTERVAL_MS));
135
136 return 0;
137
138 release_nic_ts_report:
139 dma_free_coherent(&priv->pdev->dev,
140 sizeof(struct gve_nic_ts_report),
141 priv->nic_ts_report, priv->nic_ts_report_bus);
142 priv->nic_ts_report = NULL;
143 release_ptp:
144 gve_ptp_release(priv);
145 return err;
146 }
147
gve_teardown_clock(struct gve_priv * priv)148 void gve_teardown_clock(struct gve_priv *priv)
149 {
150 gve_ptp_release(priv);
151
152 if (priv->nic_ts_report) {
153 dma_free_coherent(&priv->pdev->dev,
154 sizeof(struct gve_nic_ts_report),
155 priv->nic_ts_report, priv->nic_ts_report_bus);
156 priv->nic_ts_report = NULL;
157 }
158 }
159