xref: /freebsd/sys/dev/qat/qat_common/adf_clock.c (revision 8aa51e6d7de0a828020de64560d1385e15955a1c)
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, &timestamp1)) {
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, &timestamp2)) {
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