#include "adf_accel_devices.h"
#include "adf_common_drv.h"
#include <linux/delay.h>
#include <sys/priv.h>
#define MEASURE_CLOCK_RETRIES 10
#define MEASURE_CLOCK_DELTA_THRESHOLD 100
#define MEASURE_CLOCK_DELAY 10000
#define ME_CLK_DIVIDER 16
#define CLK_DBGFS_FILE "frequency"
#define HB_SYSCTL_ERR(RC) \
do { \
if (!RC) { \
device_printf(GET_DEV(accel_dev), \
"Memory allocation failed in \
adf_heartbeat_dbg_add\n"); \
return ENOMEM; \
} \
} while (0)
static int adf_clock_read_frequency(SYSCTL_HANDLER_ARGS)
{
struct adf_accel_dev *accel_dev = arg1;
struct adf_hw_device_data *hw_data;
int error = EFAULT;
if (priv_check(curthread, PRIV_DRIVER) != 0)
return EPERM;
if (accel_dev == NULL)
return EINVAL;
hw_data = accel_dev->hw_device;
error = sysctl_handle_int(oidp, &hw_data->clock_frequency, 0, req);
if (error || !req->newptr)
return error;
return (0);
}
int
adf_clock_debugfs_add(struct adf_accel_dev *accel_dev)
{
struct sysctl_ctx_list *qat_sysctl_ctx;
struct sysctl_oid *qat_sysctl_tree;
struct sysctl_oid *rc = 0;
qat_sysctl_ctx =
device_get_sysctl_ctx(accel_dev->accel_pci_dev.pci_dev);
qat_sysctl_tree =
device_get_sysctl_tree(accel_dev->accel_pci_dev.pci_dev);
rc = SYSCTL_ADD_PROC(qat_sysctl_ctx,
SYSCTL_CHILDREN(qat_sysctl_tree),
OID_AUTO,
CLK_DBGFS_FILE,
CTLTYPE_INT | CTLFLAG_RD,
accel_dev,
0,
adf_clock_read_frequency,
"IU",
"clock frequency");
HB_SYSCTL_ERR(rc);
return 0;
}
static int
measure_clock(struct adf_accel_dev *accel_dev, u32 *frequency)
{
struct timespec ts1;
struct timespec ts2;
struct timespec ts3;
struct timespec ts4;
struct timespec delta;
u64 delta_us = 0;
u64 timestamp1 = 0;
u64 timestamp2 = 0;
u64 temp = 0;
int tries = 0;
if (!accel_dev || !frequency)
return EIO;
do {
nanotime(&ts1);
if (adf_get_fw_timestamp(accel_dev, ×tamp1)) {
device_printf(GET_DEV(accel_dev),
"Failed to get fw timestamp\n");
return EIO;
}
nanotime(&ts2);
delta = timespec_sub(ts2, ts1);
temp = delta.tv_nsec;
do_div(temp, NSEC_PER_USEC);
delta_us = delta.tv_sec * USEC_PER_SEC + temp;
} while (delta_us > MEASURE_CLOCK_DELTA_THRESHOLD &&
++tries < MEASURE_CLOCK_RETRIES);
if (tries >= MEASURE_CLOCK_RETRIES) {
device_printf(GET_DEV(accel_dev),
"Excessive clock measure delay\n");
return EIO;
}
usleep_range(MEASURE_CLOCK_DELAY, MEASURE_CLOCK_DELAY * 2);
tries = 0;
do {
nanotime(&ts3);
if (adf_get_fw_timestamp(accel_dev, ×tamp2)) {
device_printf(GET_DEV(accel_dev),
"Failed to get fw timestamp\n");
return EIO;
}
nanotime(&ts4);
delta = timespec_sub(ts4, ts3);
temp = delta.tv_nsec;
do_div(temp, NSEC_PER_USEC);
delta_us = delta.tv_sec * USEC_PER_SEC + temp;
} while (delta_us > MEASURE_CLOCK_DELTA_THRESHOLD &&
++tries < MEASURE_CLOCK_RETRIES);
if (tries >= MEASURE_CLOCK_RETRIES) {
device_printf(GET_DEV(accel_dev),
"Excessive clock measure delay\n");
return EIO;
}
delta = timespec_sub(ts3, ts1);
temp =
delta.tv_sec * NSEC_PER_SEC + delta.tv_nsec + (NSEC_PER_USEC / 2);
do_div(temp, NSEC_PER_USEC);
delta_us = temp;
temp = (timestamp2 - timestamp1) * ME_CLK_DIVIDER * 10 + (delta_us / 2);
do_div(temp, delta_us);
*frequency = temp * 100000;
return 0;
}
int
adf_dev_measure_clock(struct adf_accel_dev *accel_dev,
u32 *frequency,
u32 min,
u32 max)
{
int ret;
u32 freq;
ret = measure_clock(accel_dev, &freq);
if (ret)
return ret;
if (freq < min) {
device_printf(GET_DEV(accel_dev),
"Slow clock %d MHz measured, assuming %d\n",
freq,
min);
freq = min;
} else if (freq > max) {
device_printf(GET_DEV(accel_dev),
"Fast clock %d MHz measured, assuming %d\n",
freq,
max);
freq = max;
}
*frequency = freq;
return 0;
}
static inline u64
timespec_to_ms(const struct timespec *ts)
{
return (uint64_t)(ts->tv_sec * (1000)) + (ts->tv_nsec / NSEC_PER_MSEC);
}
u64
adf_clock_get_current_time(void)
{
struct timespec ts;
getnanotime(&ts);
return timespec_to_ms(&ts);
}