1 /* SPDX-License-Identifier: BSD-3-Clause */
2 /* Copyright(c) 2007-2025 Intel Corporation */
3 #include "adf_accel_devices.h"
4 #include "adf_common_drv.h"
5
6 #include <linux/delay.h>
7 #include <sys/priv.h>
8
9 #define MEASURE_CLOCK_RETRIES 10
10 #define MEASURE_CLOCK_DELTA_THRESHOLD 100
11 #define MEASURE_CLOCK_DELAY 10000
12 #define ME_CLK_DIVIDER 16
13
14 #define CLK_DBGFS_FILE "frequency"
15 #define HB_SYSCTL_ERR(RC) \
16 do { \
17 if (!RC) { \
18 device_printf(GET_DEV(accel_dev), \
19 "Memory allocation failed in \
20 adf_heartbeat_dbg_add\n"); \
21 return ENOMEM; \
22 } \
23 } while (0)
24
adf_clock_read_frequency(SYSCTL_HANDLER_ARGS)25 static int adf_clock_read_frequency(SYSCTL_HANDLER_ARGS)
26 {
27 struct adf_accel_dev *accel_dev = arg1;
28 struct adf_hw_device_data *hw_data;
29 int error = EFAULT;
30
31 if (priv_check(curthread, PRIV_DRIVER) != 0)
32 return EPERM;
33
34 if (accel_dev == NULL)
35 return EINVAL;
36
37 hw_data = accel_dev->hw_device;
38
39 error = sysctl_handle_int(oidp, &hw_data->clock_frequency, 0, req);
40 if (error || !req->newptr)
41 return error;
42
43 return (0);
44 }
45
46 int
adf_clock_debugfs_add(struct adf_accel_dev * accel_dev)47 adf_clock_debugfs_add(struct adf_accel_dev *accel_dev)
48 {
49 struct sysctl_ctx_list *qat_sysctl_ctx;
50 struct sysctl_oid *qat_sysctl_tree;
51 struct sysctl_oid *rc = 0;
52
53 qat_sysctl_ctx =
54 device_get_sysctl_ctx(accel_dev->accel_pci_dev.pci_dev);
55 qat_sysctl_tree =
56 device_get_sysctl_tree(accel_dev->accel_pci_dev.pci_dev);
57
58 rc = SYSCTL_ADD_PROC(qat_sysctl_ctx,
59 SYSCTL_CHILDREN(qat_sysctl_tree),
60 OID_AUTO,
61 CLK_DBGFS_FILE,
62 CTLTYPE_INT | CTLFLAG_RD,
63 accel_dev,
64 0,
65 adf_clock_read_frequency,
66 "IU",
67 "clock frequency");
68 HB_SYSCTL_ERR(rc);
69 return 0;
70 }
71
72 /**
73 * adf_dev_measure_clock() -- Measure the CPM clock frequency
74 * @accel_dev: Pointer to acceleration device.
75 * @frequency: Pointer to returned frequency in Hz.
76 *
77 * Return: 0 on success, error code otherwise.
78 */
79 static int
measure_clock(struct adf_accel_dev * accel_dev,u32 * frequency)80 measure_clock(struct adf_accel_dev *accel_dev, u32 *frequency)
81 {
82 struct timespec ts1;
83 struct timespec ts2;
84 struct timespec ts3;
85 struct timespec ts4;
86 struct timespec delta;
87 u64 delta_us = 0;
88 u64 timestamp1 = 0;
89 u64 timestamp2 = 0;
90 u64 temp = 0;
91 int tries = 0;
92
93 if (!accel_dev || !frequency)
94 return EIO;
95 do {
96 nanotime(&ts1);
97 if (adf_get_fw_timestamp(accel_dev, ×tamp1)) {
98 device_printf(GET_DEV(accel_dev),
99 "Failed to get fw timestamp\n");
100 return EIO;
101 }
102 nanotime(&ts2);
103
104 delta = timespec_sub(ts2, ts1);
105 temp = delta.tv_nsec;
106 do_div(temp, NSEC_PER_USEC);
107
108 delta_us = delta.tv_sec * USEC_PER_SEC + temp;
109 } while (delta_us > MEASURE_CLOCK_DELTA_THRESHOLD &&
110 ++tries < MEASURE_CLOCK_RETRIES);
111
112 if (tries >= MEASURE_CLOCK_RETRIES) {
113 device_printf(GET_DEV(accel_dev),
114 "Excessive clock measure delay\n");
115 return EIO;
116 }
117
118 usleep_range(MEASURE_CLOCK_DELAY, MEASURE_CLOCK_DELAY * 2);
119 tries = 0;
120 do {
121 nanotime(&ts3);
122 if (adf_get_fw_timestamp(accel_dev, ×tamp2)) {
123 device_printf(GET_DEV(accel_dev),
124 "Failed to get fw timestamp\n");
125 return EIO;
126 }
127 nanotime(&ts4);
128
129 delta = timespec_sub(ts4, ts3);
130 temp = delta.tv_nsec;
131 do_div(temp, NSEC_PER_USEC);
132
133 delta_us = delta.tv_sec * USEC_PER_SEC + temp;
134 } while (delta_us > MEASURE_CLOCK_DELTA_THRESHOLD &&
135 ++tries < MEASURE_CLOCK_RETRIES);
136
137 if (tries >= MEASURE_CLOCK_RETRIES) {
138 device_printf(GET_DEV(accel_dev),
139 "Excessive clock measure delay\n");
140 return EIO;
141 }
142
143 delta = timespec_sub(ts3, ts1);
144 temp =
145 delta.tv_sec * NSEC_PER_SEC + delta.tv_nsec + (NSEC_PER_USEC / 2);
146 do_div(temp, NSEC_PER_USEC);
147 delta_us = temp;
148 /* Don't pretend that this gives better than 100KHz resolution */
149 temp = (timestamp2 - timestamp1) * ME_CLK_DIVIDER * 10 + (delta_us / 2);
150 do_div(temp, delta_us);
151 *frequency = temp * 100000;
152
153 return 0;
154 }
155
156 /**
157 * adf_dev_measure_clock() -- Measure the CPM clock frequency
158 * @accel_dev: Pointer to acceleration device.
159 * @frequency: Pointer to returned frequency in Hz.
160 * @min: Minimum expected frequency
161 * @max: Maximum expected frequency
162 *
163 * Return: 0 on success, error code otherwise.
164 */
165 int
adf_dev_measure_clock(struct adf_accel_dev * accel_dev,u32 * frequency,u32 min,u32 max)166 adf_dev_measure_clock(struct adf_accel_dev *accel_dev,
167 u32 *frequency,
168 u32 min,
169 u32 max)
170 {
171 int ret;
172 u32 freq;
173
174 ret = measure_clock(accel_dev, &freq);
175 if (ret)
176 return ret;
177
178 if (freq < min) {
179 device_printf(GET_DEV(accel_dev),
180 "Slow clock %d MHz measured, assuming %d\n",
181 freq,
182 min);
183 freq = min;
184 } else if (freq > max) {
185 device_printf(GET_DEV(accel_dev),
186 "Fast clock %d MHz measured, assuming %d\n",
187 freq,
188 max);
189 freq = max;
190 }
191 *frequency = freq;
192 return 0;
193 }
194
195 static inline u64
timespec_to_ms(const struct timespec * ts)196 timespec_to_ms(const struct timespec *ts)
197 {
198 return (uint64_t)(ts->tv_sec * (1000)) + (ts->tv_nsec / NSEC_PER_MSEC);
199 }
200
201 u64
adf_clock_get_current_time(void)202 adf_clock_get_current_time(void)
203 {
204 struct timespec ts;
205
206 getnanotime(&ts);
207 return timespec_to_ms(&ts);
208 }
209