I’m trying to get the Linux kernel (and system as a whole) to sync to a monotonically incrementing clock from a FPGA register in memory. The clock is 64-bits wide and divided into two 32-bit sections: a 32-bit seconds register and a 32-bit nanoseconds register which increments in 10s of nanoseconds.
I created an initial implementation based off of the ARM global timer thinking this would be a good starting approach. The driver successful registers (in kernel, not a kernel module), and I can switch the clocksource to the driver, but the timing is off and I get quite a few panics about the scheduler not executing.
My goal is that I can use the typical POSIX APIs to set and get the time from a userspace application without having the userspace involved in reading a register in memory for the time.
Is using the clocksource the right tool for this job? Or do I need something else?
#include <linux/module.h>
#include <linux/init.h>
#include <linux/mod_devicetable.h>
#include <linux/property.h>
#include <linux/platform_device.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/clocksource.h>
#include <linux/slab.h>
#include <linux/sched_clock.h>
/* Meta Information */
MODULE_LICENSE("GPL");
#define FPGA_TIME_DRIVER_NAME "fpga_time"
/* 32-bit seconds register */
#define FPGA_TIME_ADDR_CTRL_TIME_SECONDS 0x00
/* 32-bit subseconds register incrementing in 10s of nanoseconds
*/
#define FPGA_TIME_ADDR_INFO_TIME_SUBSECONDS 0x04
/* Rate at which the time is updated (10ns) */
#define FPGA_TIME_TIMER_TICK_100MHZ (100 * 1000 * 1000)
/* Rating/priority of the clock source compared to other clocks.
* Higher rating means it will be preferred over other clock sources.
*/
#define FPGA_TIME_TIMER_RATING (100)
static u64 FPGA_TIME_clocksource_read(struct clocksource *cs);
/**
* @brief The global timer structure that holds the clocksource info and the
* base register address for reading from memory.
*/
struct clocksource_fpga {
void __iomem *reg;
struct clocksource clksrc;
} g_timerInfo = {
.clksrc = {
.name = FPGA_TIME_DRIVER_NAME,
.rating = FPGA_TIME_TIMER_RATING,
.read = FPGA_TIME_clocksource_read,
.mask = CLOCKSOURCE_MASK(64),
.flags = CLOCK_SOURCE_IS_CONTINUOUS
}
};
static u64 _FPGA_TIME_clocksource_read(void)
{
u64 counter;
u32 seconds, subseconds;
seconds = readl_relaxed(g_timerInfo.reg + FPGA_TIME_ADDR_CTRL_TIME_SECONDS);
subseconds = readl_relaxed(g_timerInfo.reg + FPGA_TIME_ADDR_INFO_TIME_SUBSECONDS);
counter = seconds;
counter <<= 32;
counter |= (subseconds << 1);
return counter;
}
static u64 FPGA_TIME_clocksource_read(struct clocksource *cs)
{
return _FPGA_TIME_clocksource_read();
}
/**
* @brief Linux scheduler clock read.
* @return The current time as a 64-bit integer.
*/
#ifdef CONFIG_CLKSRC_ARM_GLOBAL_TIMER_SCHED_CLOCK
static u64 notrace FPGA_TIME_sched_clock_read(void)
{
return _FPGA_TIME_clocksource_read();
}
#endif
/**
* @brief Registers the timewarp clocksource and if applicable, registers with
* the scheduler.
* @return Returns 0 on success, errno otherwise.
*/
static int timer_clocksource_init(void)
{
int ret;
#ifdef CONFIG_CLKSRC_ARM_GLOBAL_TIMER_SCHED_CLOCK
printk(FPGA_TIME_DRIVER_NAME " - Registering the scheduler...n");
sched_clock_register(FPGA_TIME_sched_clock_read, 64, FPGA_TIME_TIMER_TICK_100MHZ);
#endif
ret = clocksource_register_hz(&g_timerInfo.clksrc, FPGA_TIME_TIMER_TICK_100MHZ);
printk(FPGA_TIME_DRIVER_NAME " - Registering the clocksource returned = %dn", ret);
return ret;
}
/**
* @brief Device driver probed when detected in the device tree.
* This reads in all the property values and initializes the driver.
*
* @param pdev The plaform device which contains the information about the driver.
* @return Returns 0 on success, errno otherwise.
*/
static int __init FPGA_TIME_register(struct device_node *np)
{
printk(FPGA_TIME_DRIVER_NAME " - Loading the driver...n");
/* Get the base address and size. */
g_timerInfo.reg = of_iomap(np, 0);
return timer_clocksource_init();
}
I’m trying to get the Linux kernel and system to use time from an FPGA register in memory using a kernel module. I was expecting that the system time would align properly to the clock, but it is not.
Ryan Sherlock is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.