cregit-Linux how code gets into the kernel

Release 4.9 drivers/staging/greybus/timesync.c

/*
 * TimeSync API driver.
 *
 * Copyright 2016 Google Inc.
 * Copyright 2016 Linaro Ltd.
 *
 * Released under the GPLv2 only.
 */
#include <linux/debugfs.h>
#include <linux/hrtimer.h>
#include "greybus.h"
#include "timesync.h"
#include "greybus_trace.h"

/*
 * Minimum inter-strobe value of one millisecond is chosen because it
 * just-about fits the common definition of a jiffy.
 *
 * Maximum value OTOH is constrained by the number of bits the SVC can fit
 * into a 16 bit up-counter. The SVC configures the timer in microseconds
 * so the maximum allowable value is 65535 microseconds. We clip that value
 * to 10000 microseconds for the sake of using nice round base 10 numbers
 * and since right-now there's no imaginable use-case requiring anything
 * other than a one millisecond inter-strobe time, let alone something
 * higher than ten milliseconds.
 */

#define GB_TIMESYNC_STROBE_DELAY_US		1000

#define GB_TIMESYNC_DEFAULT_OFFSET_US		1000

/* Work queue timers long, short and SVC strobe timeout */

#define GB_TIMESYNC_DELAYED_WORK_LONG		msecs_to_jiffies(10)

#define GB_TIMESYNC_DELAYED_WORK_SHORT		msecs_to_jiffies(1)

#define GB_TIMESYNC_MAX_WAIT_SVC		msecs_to_jiffies(5000)

#define GB_TIMESYNC_KTIME_UPDATE		msecs_to_jiffies(1000)

#define GB_TIMESYNC_MAX_KTIME_CONVERSION	15

/* Maximum number of times we'll retry a failed synchronous sync */

#define GB_TIMESYNC_MAX_RETRIES			5

/* Reported nanoseconds/femtoseconds per clock */

static u64 gb_timesync_ns_per_clock;

static u64 gb_timesync_fs_per_clock;

/* Maximum difference we will accept converting FrameTime to ktime */

static u32 gb_timesync_max_ktime_diff;

/* Reported clock rate */

static unsigned long gb_timesync_clock_rate;

/* Workqueue */
static void gb_timesync_worker(struct work_struct *work);

/* List of SVCs with one FrameTime per SVC */
static LIST_HEAD(gb_timesync_svc_list);

/* Synchronize parallel contexts accessing a valid timesync_svc pointer */
static DEFINE_MUTEX(gb_timesync_svc_list_mutex);

/* Structure to convert from FrameTime to timespec/ktime */

struct gb_timesync_frame_time_data {
	
u64 frame_time;
	
struct timespec ts;
};


struct gb_timesync_svc {
	
struct list_head list;
	
struct list_head interface_list;
	
struct gb_svc *svc;
	
struct gb_timesync_host_device *timesync_hd;

	
spinlock_t spinlock;	/* Per SVC spinlock to sync with ISR */
	
struct mutex mutex;	/* Per SVC mutex for regular synchronization */

	
struct dentry *frame_time_dentry;
	
struct dentry *frame_ktime_dentry;
	
struct workqueue_struct *work_queue;
	
wait_queue_head_t wait_queue;
	
struct delayed_work delayed_work;
	
struct timer_list ktime_timer;

	/* The current local FrameTime */
	
u64 frame_time_offset;
	
struct gb_timesync_frame_time_data strobe_data[GB_TIMESYNC_MAX_STROBES];
	
struct gb_timesync_frame_time_data ktime_data;

	/* The SVC FrameTime and relative AP FrameTime @ last TIMESYNC_PING */
	
u64 svc_ping_frame_time;
	
u64 ap_ping_frame_time;

	/* Transitory settings */
	
u32 strobe_mask;
	
bool offset_down;
	
bool print_ping;
	
bool capture_ping;
	
int strobe;

	/* Current state */
	
int state;
};


struct gb_timesync_host_device {
	
struct list_head list;
	
struct gb_host_device *hd;
	
u64 ping_frame_time;
};


struct gb_timesync_interface {
	
struct list_head list;
	
struct gb_interface *interface;
	
u64 ping_frame_time;
};


enum gb_timesync_state {
	
GB_TIMESYNC_STATE_INVALID		= 0,
	
GB_TIMESYNC_STATE_INACTIVE		= 1,
	
GB_TIMESYNC_STATE_INIT			= 2,
	
GB_TIMESYNC_STATE_WAIT_SVC		= 3,
	
GB_TIMESYNC_STATE_AUTHORITATIVE		= 4,
	
GB_TIMESYNC_STATE_PING			= 5,
	
GB_TIMESYNC_STATE_ACTIVE		= 6,
};

static void gb_timesync_ktime_timer_fn(unsigned long data);


static u64 gb_timesync_adjust_count(struct gb_timesync_svc *timesync_svc, u64 counts) { if (timesync_svc->offset_down) return counts - timesync_svc->frame_time_offset; else return counts + timesync_svc->frame_time_offset; }

Contributors

PersonTokensPropCommitsCommitProp
bryan o'donoghuebryan o'donoghue35100.00%1100.00%
Total35100.00%1100.00%

/* * This function provides the authoritative FrameTime to a calling function. It * is designed to be lockless and should remain that way the caller is assumed * to be state-aware. */
static u64 __gb_timesync_get_frame_time(struct gb_timesync_svc *timesync_svc) { u64 clocks = gb_timesync_platform_get_counter(); return gb_timesync_adjust_count(timesync_svc, clocks); }

Contributors

PersonTokensPropCommitsCommitProp
bryan o'donoghuebryan o'donoghue25100.00%1100.00%
Total25100.00%1100.00%


static void gb_timesync_schedule_svc_timeout(struct gb_timesync_svc *timesync_svc) { queue_delayed_work(timesync_svc->work_queue, &timesync_svc->delayed_work, GB_TIMESYNC_MAX_WAIT_SVC); }

Contributors

PersonTokensPropCommitsCommitProp
bryan o'donoghuebryan o'donoghue25100.00%1100.00%
Total25100.00%1100.00%


static void gb_timesync_set_state(struct gb_timesync_svc *timesync_svc, int state) { switch (state) { case GB_TIMESYNC_STATE_INVALID: timesync_svc->state = state; wake_up(&timesync_svc->wait_queue); break; case GB_TIMESYNC_STATE_INACTIVE: timesync_svc->state = state; wake_up(&timesync_svc->wait_queue); break; case GB_TIMESYNC_STATE_INIT: if (timesync_svc->state != GB_TIMESYNC_STATE_INVALID) { timesync_svc->strobe = 0; timesync_svc->frame_time_offset = 0; timesync_svc->state = state; cancel_delayed_work(&timesync_svc->delayed_work); queue_delayed_work(timesync_svc->work_queue, &timesync_svc->delayed_work, GB_TIMESYNC_DELAYED_WORK_LONG); } break; case GB_TIMESYNC_STATE_WAIT_SVC: if (timesync_svc->state == GB_TIMESYNC_STATE_INIT) timesync_svc->state = state; break; case GB_TIMESYNC_STATE_AUTHORITATIVE: if (timesync_svc->state == GB_TIMESYNC_STATE_WAIT_SVC) { timesync_svc->state = state; cancel_delayed_work(&timesync_svc->delayed_work); queue_delayed_work(timesync_svc->work_queue, &timesync_svc->delayed_work, 0); } break; case GB_TIMESYNC_STATE_PING: if (timesync_svc->state == GB_TIMESYNC_STATE_ACTIVE) { timesync_svc->state = state; queue_delayed_work(timesync_svc->work_queue, &timesync_svc->delayed_work, GB_TIMESYNC_DELAYED_WORK_SHORT); } break; case GB_TIMESYNC_STATE_ACTIVE: if (timesync_svc->state == GB_TIMESYNC_STATE_AUTHORITATIVE || timesync_svc->state == GB_TIMESYNC_STATE_PING) { timesync_svc->state = state; wake_up(&timesync_svc->wait_queue); } break; } if (WARN_ON(timesync_svc->state != state)) { pr_err("Invalid state transition %d=>%d\n", timesync_svc->state, state); } }

Contributors

PersonTokensPropCommitsCommitProp
bryan o'donoghuebryan o'donoghue262100.00%1100.00%
Total262100.00%1100.00%


static void gb_timesync_set_state_atomic(struct gb_timesync_svc *timesync_svc, int state) { unsigned long flags; spin_lock_irqsave(&timesync_svc->spinlock, flags); gb_timesync_set_state(timesync_svc, state); spin_unlock_irqrestore(&timesync_svc->spinlock, flags); }

Contributors

PersonTokensPropCommitsCommitProp
bryan o'donoghuebryan o'donoghue45100.00%1100.00%
Total45100.00%1100.00%


static u64 gb_timesync_diff(u64 x, u64 y) { if (x > y) return x - y; else return y - x; }

Contributors

PersonTokensPropCommitsCommitProp
bryan o'donoghuebryan o'donoghue29100.00%1100.00%
Total29100.00%1100.00%


static void gb_timesync_adjust_to_svc(struct gb_timesync_svc *svc, u64 svc_frame_time, u64 ap_frame_time) { if (svc_frame_time > ap_frame_time) { svc->frame_time_offset = svc_frame_time - ap_frame_time; svc->offset_down = false; } else { svc->frame_time_offset = ap_frame_time - svc_frame_time; svc->offset_down = true; } }

Contributors

PersonTokensPropCommitsCommitProp
bryan o'donoghuebryan o'donoghue56100.00%1100.00%
Total56100.00%1100.00%

/* * Associate a FrameTime with a ktime timestamp represented as struct timespec * Requires the calling context to hold timesync_svc->mutex */
static void gb_timesync_store_ktime(struct gb_timesync_svc *timesync_svc, struct timespec ts, u64 frame_time) { timesync_svc->ktime_data.ts = ts; timesync_svc->ktime_data.frame_time = frame_time; }

Contributors

PersonTokensPropCommitsCommitProp
bryan o'donoghuebryan o'donoghue34100.00%1100.00%
Total34100.00%1100.00%

/* * Find the two pulses that best-match our expected inter-strobe gap and * then calculate the difference between the SVC time at the second pulse * to the local time at the second pulse. */
static void gb_timesync_collate_frame_time(struct gb_timesync_svc *timesync_svc, u64 *frame_time) { int i = 0; u64 delta, ap_frame_time; u64 strobe_delay_ns = GB_TIMESYNC_STROBE_DELAY_US * NSEC_PER_USEC; u64 least = 0; for (i = 1; i < GB_TIMESYNC_MAX_STROBES; i++) { delta = timesync_svc->strobe_data[i].frame_time - timesync_svc->strobe_data[i - 1].frame_time; delta *= gb_timesync_ns_per_clock; delta = gb_timesync_diff(delta, strobe_delay_ns); if (!least || delta < least) { least = delta; gb_timesync_adjust_to_svc(timesync_svc, frame_time[i], timesync_svc->strobe_data[i].frame_time); ap_frame_time = timesync_svc->strobe_data[i].frame_time; ap_frame_time = gb_timesync_adjust_count(timesync_svc, ap_frame_time); gb_timesync_store_ktime(timesync_svc, timesync_svc->strobe_data[i].ts, ap_frame_time); pr_debug("adjust %s local %llu svc %llu delta %llu\n", timesync_svc->offset_down ? "down" : "up", timesync_svc->strobe_data[i].frame_time, frame_time[i], delta); } } }

Contributors

PersonTokensPropCommitsCommitProp
bryan o'donoghuebryan o'donoghue186100.00%2100.00%
Total186100.00%2100.00%


static void gb_timesync_teardown(struct gb_timesync_svc *timesync_svc) { struct gb_timesync_interface *timesync_interface; struct gb_svc *svc = timesync_svc->svc; struct gb_interface *interface; struct gb_host_device *hd; int ret; list_for_each_entry(timesync_interface, &timesync_svc->interface_list, list) { interface = timesync_interface->interface; ret = gb_interface_timesync_disable(interface); if (ret) { dev_err(&interface->dev, "interface timesync_disable %d\n", ret); } } hd = timesync_svc->timesync_hd->hd; ret = hd->driver->timesync_disable(hd); if (ret < 0) { dev_err(&hd->dev, "host timesync_disable %d\n", ret); } gb_svc_timesync_wake_pins_release(svc); gb_svc_timesync_disable(svc); gb_timesync_platform_unlock_bus(); gb_timesync_set_state_atomic(timesync_svc, GB_TIMESYNC_STATE_INACTIVE); }

Contributors

PersonTokensPropCommitsCommitProp
bryan o'donoghuebryan o'donoghue138100.00%1100.00%
Total138100.00%1100.00%


static void gb_timesync_platform_lock_bus_fail(struct gb_timesync_svc *timesync_svc, int ret) { if (ret == -EAGAIN) { gb_timesync_set_state(timesync_svc, timesync_svc->state); } else { pr_err("Failed to lock timesync bus %d\n", ret); gb_timesync_set_state(timesync_svc, GB_TIMESYNC_STATE_INACTIVE); } }

Contributors

PersonTokensPropCommitsCommitProp
bryan o'donoghuebryan o'donoghue49100.00%1100.00%
Total49100.00%1100.00%


static void gb_timesync_enable(struct gb_timesync_svc *timesync_svc) { struct gb_svc *svc = timesync_svc->svc; struct gb_host_device *hd; struct gb_timesync_interface *timesync_interface; struct gb_interface *interface; u64 init_frame_time; unsigned long clock_rate = gb_timesync_clock_rate; int ret; /* * Get access to the wake pins in the AP and SVC * Release these pins either in gb_timesync_teardown() or in * gb_timesync_authoritative() */ ret = gb_timesync_platform_lock_bus(timesync_svc); if (ret < 0) { gb_timesync_platform_lock_bus_fail(timesync_svc, ret); return; } ret = gb_svc_timesync_wake_pins_acquire(svc, timesync_svc->strobe_mask); if (ret) { dev_err(&svc->dev, "gb_svc_timesync_wake_pins_acquire %d\n", ret); gb_timesync_teardown(timesync_svc); return; } /* Choose an initial time in the future */ init_frame_time = __gb_timesync_get_frame_time(timesync_svc) + 100000UL; /* Send enable command to all relevant participants */ list_for_each_entry(timesync_interface, &timesync_svc->interface_list, list) { interface = timesync_interface->interface; ret = gb_interface_timesync_enable(interface, GB_TIMESYNC_MAX_STROBES, init_frame_time, GB_TIMESYNC_STROBE_DELAY_US, clock_rate); if (ret) { dev_err(&interface->dev, "interface timesync_enable %d\n", ret); } } hd = timesync_svc->timesync_hd->hd; ret = hd->driver->timesync_enable(hd, GB_TIMESYNC_MAX_STROBES, init_frame_time, GB_TIMESYNC_STROBE_DELAY_US, clock_rate); if (ret < 0) { dev_err(&hd->dev, "host timesync_enable %d\n", ret); } gb_timesync_set_state_atomic(timesync_svc, GB_TIMESYNC_STATE_WAIT_SVC); ret = gb_svc_timesync_enable(svc, GB_TIMESYNC_MAX_STROBES, init_frame_time, GB_TIMESYNC_STROBE_DELAY_US, clock_rate); if (ret) { dev_err(&svc->dev, "gb_svc_timesync_enable %d\n", ret); gb_timesync_teardown(timesync_svc); return; } /* Schedule a timeout waiting for SVC to complete strobing */ gb_timesync_schedule_svc_timeout(timesync_svc); }

Contributors

PersonTokensPropCommitsCommitProp
bryan o'donoghuebryan o'donoghue265100.00%1100.00%
Total265100.00%1100.00%


static void gb_timesync_authoritative(struct gb_timesync_svc *timesync_svc) { struct gb_svc *svc = timesync_svc->svc; struct gb_host_device *hd; struct gb_timesync_interface *timesync_interface; struct gb_interface *interface; u64 svc_frame_time[GB_TIMESYNC_MAX_STROBES]; int ret; /* Get authoritative time from SVC and adjust local clock */ ret = gb_svc_timesync_authoritative(svc, svc_frame_time); if (ret) { dev_err(&svc->dev, "gb_svc_timesync_authoritative %d\n", ret); gb_timesync_teardown(timesync_svc); return; } gb_timesync_collate_frame_time(timesync_svc, svc_frame_time); /* Transmit authoritative time to downstream slaves */ hd = timesync_svc->timesync_hd->hd; ret = hd->driver->timesync_authoritative(hd, svc_frame_time); if (ret < 0) dev_err(&hd->dev, "host timesync_authoritative %d\n", ret); list_for_each_entry(timesync_interface, &timesync_svc->interface_list, list) { interface = timesync_interface->interface; ret = gb_interface_timesync_authoritative( interface, svc_frame_time); if (ret) { dev_err(&interface->dev, "interface timesync_authoritative %d\n", ret); } } /* Release wake pins */ gb_svc_timesync_wake_pins_release(svc); gb_timesync_platform_unlock_bus(); /* Transition to state ACTIVE */ gb_timesync_set_state_atomic(timesync_svc, GB_TIMESYNC_STATE_ACTIVE); /* Schedule a ping to verify the synchronized system time */ timesync_svc->print_ping = true; gb_timesync_set_state_atomic(timesync_svc, GB_TIMESYNC_STATE_PING); }

Contributors

PersonTokensPropCommitsCommitProp
bryan o'donoghuebryan o'donoghue199100.00%1100.00%
Total199100.00%1100.00%


static int __gb_timesync_get_status(struct gb_timesync_svc *timesync_svc) { int ret = -EINVAL; switch (timesync_svc->state) { case GB_TIMESYNC_STATE_INVALID: case GB_TIMESYNC_STATE_INACTIVE: ret = -ENODEV; break; case GB_TIMESYNC_STATE_INIT: case GB_TIMESYNC_STATE_WAIT_SVC: case GB_TIMESYNC_STATE_AUTHORITATIVE: ret = -EAGAIN; break; case GB_TIMESYNC_STATE_PING: case GB_TIMESYNC_STATE_ACTIVE: ret = 0; break; } return ret; }

Contributors

PersonTokensPropCommitsCommitProp
bryan o'donoghuebryan o'donoghue66100.00%3100.00%
Total66100.00%3100.00%

/* * This routine takes a FrameTime and derives the difference with-respect * to a reference FrameTime/ktime pair. It then returns the calculated * ktime based on the difference between the supplied FrameTime and * the reference FrameTime. * * The time difference is calculated to six decimal places. Taking 19.2MHz * as an example this means we have 52.083333~ nanoseconds per clock or * 52083333~ femtoseconds per clock. * * Naively taking the count difference and converting to * seconds/nanoseconds would quickly see the 0.0833 component produce * noticeable errors. For example a time difference of one second would * loose 19200000 * 0.08333x nanoseconds or 1.59 seconds. * * In contrast calculating in femtoseconds the same example of 19200000 * * 0.000000083333x nanoseconds per count of error is just 1.59 nanoseconds! * * Continuing the example of 19.2 MHz we cap the maximum error difference * at a worst-case 0.3 microseconds over a potential calculation window of * abount 15 seconds, meaning you can convert a FrameTime that is <= 15 * seconds older/younger than the reference time with a maximum error of * 0.2385 useconds. Note 19.2MHz is an example frequency not a requirement. */
static int gb_timesync_to_timespec(struct gb_timesync_svc *timesync_svc, u64 frame_time, struct timespec *ts) { unsigned long flags; u64 delta_fs, counts, sec, nsec; bool add; int ret = 0; memset(ts, 0x00, sizeof(*ts)); mutex_lock(&timesync_svc->mutex); spin_lock_irqsave(&timesync_svc->spinlock, flags); ret = __gb_timesync_get_status(timesync_svc); if (ret) goto done; /* Support calculating ktime upwards or downwards from the reference */ if (frame_time < timesync_svc->ktime_data.frame_time) { add = false; counts = timesync_svc->ktime_data.frame_time - frame_time; } else { add = true; counts = frame_time - timesync_svc->ktime_data.frame_time; } /* Enforce the .23 of a usecond boundary @ 19.2MHz */ if (counts > gb_timesync_max_ktime_diff) { ret = -EINVAL; goto done; } /* Determine the time difference in femtoseconds */ delta_fs = counts * gb_timesync_fs_per_clock; /* Convert to seconds */ sec = delta_fs; do_div(sec, NSEC_PER_SEC); do_div(sec, 1000000UL); /* Get the nanosecond remainder */ nsec = do_div(delta_fs, sec); do_div(nsec, 1000000UL); if (add) { /* Add the calculated offset - overflow nanoseconds upwards */ ts->tv_sec = timesync_svc->ktime_data.ts.tv_sec + sec; ts->tv_nsec = timesync_svc->ktime_data.ts.tv_nsec + nsec; if (ts->tv_nsec >= NSEC_PER_SEC) { ts->tv_sec++; ts->tv_nsec -= NSEC_PER_SEC; } } else { /* Subtract the difference over/underflow as necessary */ if (nsec > timesync_svc->ktime_data.ts.tv_nsec) { sec++; nsec = nsec + timesync_svc->ktime_data.ts.tv_nsec; nsec = do_div(nsec, NSEC_PER_SEC); } else { nsec = timesync_svc->ktime_data.ts.tv_nsec - nsec; } /* Cannot return a negative second value */ if (sec > timesync_svc->ktime_data.ts.tv_sec) { ret = -EINVAL; goto done; } ts->tv_sec = timesync_svc->ktime_data.ts.tv_sec - sec; ts->tv_nsec = nsec; } done: spin_unlock_irqrestore(&timesync_svc->spinlock, flags); mutex_unlock(&timesync_svc->mutex); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
bryan o'donoghuebryan o'donoghue368100.00%4100.00%
Total368100.00%4100.00%


static size_t gb_timesync_log_frame_time(struct gb_timesync_svc *timesync_svc, char *buf, size_t buflen) { struct gb_svc *svc = timesync_svc->svc; struct gb_host_device *hd; struct gb_timesync_interface *timesync_interface; struct gb_interface *interface; unsigned int len; size_t off; /* AP/SVC */ off = snprintf(buf, buflen, "%s frametime: ap=%llu %s=%llu ", greybus_bus_type.name, timesync_svc->ap_ping_frame_time, dev_name(&svc->dev), timesync_svc->svc_ping_frame_time); len = buflen - off; /* APB/GPB */ if (len < buflen) { hd = timesync_svc->timesync_hd->hd; off += snprintf(&buf[off], len, "%s=%llu ", dev_name(&hd->dev), timesync_svc->timesync_hd->ping_frame_time); len = buflen - off; } list_for_each_entry(timesync_interface, &timesync_svc->interface_list, list) { if (len < buflen) { interface = timesync_interface->interface; off += snprintf(&buf[off], len, "%s=%llu ", dev_name(&interface->dev), timesync_interface->ping_frame_time); len = buflen - off; } } if (len < buflen) off += snprintf(&buf[off], len, "\n"); return off; }

Contributors

PersonTokensPropCommitsCommitProp
bryan o'donoghuebryan o'donoghue220100.00%3100.00%
Total220100.00%3100.00%


static size_t gb_timesync_log_frame_ktime(struct gb_timesync_svc *timesync_svc, char *buf, size_t buflen) { struct gb_svc *svc = timesync_svc->svc; struct gb_host_device *hd; struct gb_timesync_interface *timesync_interface; struct gb_interface *interface; struct timespec ts; unsigned int len; size_t off; /* AP */ gb_timesync_to_timespec(timesync_svc, timesync_svc->ap_ping_frame_time, &ts); off = snprintf(buf, buflen, "%s frametime: ap=%lu.%lu ", greybus_bus_type.name, ts.tv_sec, ts.tv_nsec); len = buflen - off; if (len >= buflen) goto done; /* SVC */ gb_timesync_to_timespec(timesync_svc, timesync_svc->svc_ping_frame_time, &ts); off += snprintf(&buf[off], len, "%s=%lu.%lu ", dev_name(&svc->dev), ts.tv_sec, ts.tv_nsec); len = buflen - off; if (len >= buflen) goto done; /* APB/GPB */ hd = timesync_svc->timesync_hd->hd; gb_timesync_to_timespec(timesync_svc, timesync_svc->timesync_hd->ping_frame_time, &ts); off += snprintf(&buf[off], len, "%s=%lu.%lu ", dev_name(&hd->dev), ts.tv_sec, ts.tv_nsec); len = buflen - off; if (len >= buflen) goto done; list_for_each_entry(timesync_interface, &timesync_svc->interface_list, list) { interface = timesync_interface->interface; gb_timesync_to_timespec(timesync_svc, timesync_interface->ping_frame_time, &ts); off += snprintf(&buf[off], len, "%s=%lu.%lu ", dev_name(&interface->dev), ts.tv_sec, ts.tv_nsec); len = buflen - off; if (len >= buflen) goto done; } off += snprintf(&buf[off], len, "\n"); done: return off; }

Contributors

PersonTokensPropCommitsCommitProp
bryan o'donoghuebryan o'donoghue326100.00%2100.00%
Total326100.00%2100.00%

/* * Send an SVC initiated wake 'ping' to each TimeSync participant. * Get the FrameTime from each participant associated with the wake * ping. */
static void gb_timesync_ping(struct gb_timesync_svc *timesync_svc) { struct gb_svc *svc = timesync_svc->svc; struct gb_host_device *hd; struct gb_timesync_interface *timesync_interface; struct gb_control *control; u64 *ping_frame_time; int ret; /* Get access to the wake pins in the AP and SVC */ ret = gb_timesync_platform_lock_bus(timesync_svc); if (ret < 0) { gb_timesync_platform_lock_bus_fail(timesync_svc, ret); return; } ret = gb_svc_timesync_wake_pins_acquire(svc, timesync_svc->strobe_mask); if (ret) { dev_err(&svc->dev, "gb_svc_timesync_wake_pins_acquire %d\n", ret); gb_timesync_teardown(timesync_svc); return; } /* Have SVC generate a timesync ping */ timesync_svc->capture_ping = true; timesync_svc->svc_ping_frame_time = 0; ret = gb_svc_timesync_ping(svc, &timesync_svc->svc_ping_frame_time); timesync_svc->capture_ping = false; if (ret) { dev_err(&svc->dev, "gb_svc_timesync_ping %d\n", ret); gb_timesync_teardown(timesync_svc); return; } /* Get the ping FrameTime from each APB/GPB */ hd = timesync_svc->timesync_hd->hd; timesync_svc->timesync_hd->ping_frame_time = 0; ret = hd->driver->timesync_get_last_event(hd, &timesync_svc->timesync_hd->ping_frame_time); if (ret) dev_err(&hd->dev, "host timesync_get_last_event %d\n", ret); list_for_each_entry(timesync_interface, &timesync_svc->interface_list, list) { control = timesync_interface->interface->control; timesync_interface->ping_frame_time = 0; ping_frame_time = &timesync_interface->ping_frame_time; ret = gb_control_timesync_get_last_event(control, ping_frame_time); if (ret) { dev_err(&timesync_interface->interface->dev, "gb_control_timesync_get_last_event %d\n", ret); } } /* Ping success - move to timesync active */ gb_svc_timesync_wake_pins_release(svc); gb_timesync_platform_unlock_bus(); gb_timesync_set_state_atomic(timesync_svc, GB_TIMESYNC_STATE_ACTIVE); }

Contributors

PersonTokensPropCommitsCommitProp
bryan o'donoghuebryan o'donoghue283100.00%2100.00%
Total283100.00%2100.00%


static void gb_timesync_log_ping_time(struct gb_timesync_svc *timesync_svc) { char *buf; if (!timesync_svc->print_ping) return; buf = kzalloc(PAGE_SIZE, GFP_KERNEL); if (buf) { gb_timesync_log_frame_time(timesync_svc, buf, PAGE_SIZE); dev_dbg(&timesync_svc->svc->dev, "%s", buf); kfree(buf); } }

Contributors

PersonTokensPropCommitsCommitProp
bryan o'donoghuebryan o'donoghue5887.88%150.00%
david lindavid lin812.12%150.00%
Total66100.00%2100.00%

/* * Perform the actual work of scheduled TimeSync logic. */
static void gb_timesync_worker(struct work_struct *work) { struct delayed_work *delayed_work = to_delayed_work(work); struct gb_timesync_svc *timesync_svc = container_of(delayed_work, struct gb_timesync_svc, delayed_work); mutex_lock(&timesync_svc->mutex); switch (timesync_svc->state) { case GB_TIMESYNC_STATE_INIT: gb_timesync_enable(timesync_svc); break; case GB_TIMESYNC_STATE_WAIT_SVC: dev_err(&timesync_svc->svc->dev, "timeout SVC strobe completion %d/%d\n", timesync_svc->strobe, GB_TIMESYNC_MAX_STROBES); gb_timesync_teardown(timesync_svc); break; case GB_TIMESYNC_STATE_AUTHORITATIVE: gb_timesync_authoritative(timesync_svc); break; case GB_TIMESYNC_STATE_PING: gb_timesync_ping(timesync_svc); gb_timesync_log_ping_time(timesync_svc); break; default: pr_err("Invalid state %d for delayed work\n", timesync_svc->state); break; } mutex_unlock(&timesync_svc->mutex); }

Contributors

PersonTokensPropCommitsCommitProp
bryan o'donoghuebryan o'donoghue130100.00%2100.00%
Total130100.00%2100.00%

/* * Schedule a new TimeSync INIT or PING operation serialized w/r to * gb_timesync_worker(). */
static int gb_timesync_schedule(struct gb_timesync_svc *timesync_svc, int state) { int ret = 0; if (state != GB_TIMESYNC_STATE_INIT && state != GB_TIMESYNC_STATE_PING) return -EINVAL; mutex_lock(&timesync_svc->mutex); if (timesync_svc->state != GB_TIMESYNC_STATE_INVALID) { gb_timesync_set_state_atomic(timesync_svc, state); } else { ret = -ENODEV; } mutex_unlock(&timesync_svc->mutex); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
bryan o'donoghuebryan o'donoghue77100.00%2100.00%
Total77100.00%2100.00%


static int __gb_timesync_schedule_synchronous( struct gb_timesync_svc *timesync_svc, int state) { unsigned long flags; int ret; ret = gb_timesync_schedule(timesync_svc, state); if (ret) return ret; ret = wait_event_interruptible(timesync_svc->wait_queue, (timesync_svc->state == GB_TIMESYNC_STATE_ACTIVE || timesync_svc->state == GB_TIMESYNC_STATE_INACTIVE || timesync_svc->state == GB_TIMESYNC_STATE_INVALID)); if (ret) return ret; mutex_lock(&timesync_svc->mutex); spin_lock_irqsave(&timesync_svc->spinlock, flags); ret = __gb_timesync_get_status(timesync_svc); spin_unlock_irqrestore(&timesync_svc->spinlock, flags); mutex_unlock(&timesync_svc->mutex); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
bryan o'donoghuebryan o'donoghue119100.00%2100.00%
Total119100.00%2100.00%


static struct gb_timesync_svc *gb_timesync_find_timesync_svc( struct gb_host_device *hd) { struct gb_timesync_svc *timesync_svc; list_for_each_entry(timesync_svc, &gb_timesync_svc_list, list) { if (timesync_svc->svc == hd->svc) return timesync_svc; } return NULL; }

Contributors

PersonTokensPropCommitsCommitProp
bryan o'donoghuebryan o'donoghue44100.00%1100.00%
Total44100.00%1100.00%


static struct gb_timesync_interface *gb_timesync_find_timesync_interface( struct gb_timesync_svc *timesync_svc, struct gb_interface *interface) { struct gb_timesync_interface *timesync_interface; list_for_each_entry(timesync_interface, &timesync_svc->interface_list, list) { if (timesync_interface->interface == interface) return timesync_interface; } return NULL; }

Contributors

PersonTokensPropCommitsCommitProp
bryan o'donoghuebryan o'donoghue47100.00%1100.00%
Total47100.00%1100.00%


int gb_timesync_schedule_synchronous(struct gb_interface *interface) { int ret; struct gb_timesync_svc *timesync_svc; int retries; if (!(interface->features & GREYBUS_INTERFACE_FEATURE_TIMESYNC)) return 0; mutex_lock(&gb_timesync_svc_list_mutex); for (retries = 0; retries < GB_TIMESYNC_MAX_RETRIES; retries++) { timesync_svc = gb_timesync_find_timesync_svc(interface->hd); if (!timesync_svc) { ret = -ENODEV; goto done; } ret = __gb_timesync_schedule_synchronous(timesync_svc, GB_TIMESYNC_STATE_INIT); if (!ret) break; } if (ret && retries == GB_TIMESYNC_MAX_RETRIES) ret = -ETIMEDOUT; done: mutex_unlock(&gb_timesync_svc_list_mutex); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
bryan o'donoghuebryan o'donoghue119100.00%2100.00%
Total119100.00%2100.00%

EXPORT_SYMBOL_GPL(gb_timesync_schedule_synchronous);
void gb_timesync_schedule_asynchronous(struct gb_interface *interface) { struct gb_timesync_svc *timesync_svc; if (!(interface->features & GREYBUS_INTERFACE_FEATURE_TIMESYNC)) return; mutex_lock(&gb_timesync_svc_list_mutex); timesync_svc = gb_timesync_find_timesync_svc(interface->hd); if (!timesync_svc) goto done; gb_timesync_schedule(timesync_svc, GB_TIMESYNC_STATE_INIT); done: mutex_unlock(&gb_timesync_svc_list_mutex); return; }

Contributors

PersonTokensPropCommitsCommitProp
bryan o'donoghuebryan o'donoghue66100.00%1100.00%
Total66100.00%1100.00%

EXPORT_SYMBOL_GPL(gb_timesync_schedule_asynchronous);
static ssize_t gb_timesync_ping_read(struct file *file, char __user *ubuf, size_t len, loff_t *offset, bool ktime) { struct gb_timesync_svc *timesync_svc = file->f_inode->i_private; char *buf; ssize_t ret = 0; mutex_lock(&gb_timesync_svc_list_mutex); mutex_lock(&timesync_svc->mutex); if (list_empty(&timesync_svc->interface_list)) ret = -ENODEV; timesync_svc->print_ping = false; mutex_unlock(&timesync_svc->mutex); if (ret) goto done; ret = __gb_timesync_schedule_synchronous(timesync_svc, GB_TIMESYNC_STATE_PING); if (ret) goto done; buf = kzalloc(PAGE_SIZE, GFP_KERNEL); if (!buf) { ret = -ENOMEM; goto done; } if (ktime) ret = gb_timesync_log_frame_ktime(timesync_svc, buf, PAGE_SIZE); else ret = gb_timesync_log_frame_time(timesync_svc, buf, PAGE_SIZE); if (ret > 0) ret = simple_read_from_buffer(ubuf, len, offset, buf, ret); kfree(buf); done: mutex_unlock(&gb_timesync_svc_list_mutex); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
bryan o'donoghuebryan o'donoghue200100.00%2100.00%
Total200100.00%2100.00%


static ssize_t gb_timesync_ping_read_frame_time(struct file *file, char __user *buf, size_t len, loff_t *offset) { return gb_timesync_ping_read(file, buf, len, offset, false); }

Contributors

PersonTokensPropCommitsCommitProp
bryan o'donoghuebryan o'donoghue37100.00%1100.00%
Total37100.00%1100.00%


static ssize_t gb_timesync_ping_read_frame_ktime(struct file *file, char __user *buf, size_t len, loff_t *offset) { return gb_timesync_ping_read(file, buf, len, offset, true); }

Contributors

PersonTokensPropCommitsCommitProp
bryan o'donoghuebryan o'donoghue37100.00%1100.00%
Total37100.00%1100.00%

static const struct file_operations gb_timesync_debugfs_frame_time_ops = { .read = gb_timesync_ping_read_frame_time, }; static const struct file_operations gb_timesync_debugfs_frame_ktime_ops = { .read = gb_timesync_ping_read_frame_ktime, };
static int gb_timesync_hd_add(struct gb_timesync_svc *timesync_svc, struct gb_host_device *hd) { struct gb_timesync_host_device *timesync_hd; timesync_hd = kzalloc(sizeof(*timesync_hd), GFP_KERNEL); if (!timesync_hd) return -ENOMEM; WARN_ON(timesync_svc->timesync_hd); timesync_hd->hd = hd; timesync_svc->timesync_hd = timesync_hd; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
bryan o'donoghuebryan o'donoghue65100.00%1100.00%
Total65100.00%1100.00%


static void gb_timesync_hd_remove(struct gb_timesync_svc *timesync_svc, struct gb_host_device *hd) { if (timesync_svc->timesync_hd->hd == hd) { kfree(timesync_svc->timesync_hd); timesync_svc->timesync_hd = NULL; return; } WARN_ON(1); }

Contributors

PersonTokensPropCommitsCommitProp
bryan o'donoghuebryan o'donoghue47100.00%1100.00%
Total47100.00%1100.00%


int gb_timesync_svc_add(struct gb_svc *svc) { struct gb_timesync_svc *timesync_svc; int ret; timesync_svc = kzalloc(sizeof(*timesync_svc), GFP_KERNEL); if (!timesync_svc) return -ENOMEM; timesync_svc->work_queue = create_singlethread_workqueue("gb-timesync-work_queue"); if (!timesync_svc->work_queue) { kfree(timesync_svc); return -ENOMEM; } mutex_lock(&gb_timesync_svc_list_mutex); INIT_LIST_HEAD(&timesync_svc->interface_list); INIT_DELAYED_WORK(&timesync_svc->delayed_work, gb_timesync_worker); mutex_init(&timesync_svc->mutex); spin_lock_init(&timesync_svc->spinlock); init_waitqueue_head(&timesync_svc->wait_queue); timesync_svc->svc = svc; timesync_svc->frame_time_offset = 0; timesync_svc->capture_ping = false; gb_timesync_set_state_atomic(timesync_svc, GB_TIMESYNC_STATE_INACTIVE); timesync_svc->frame_time_dentry = debugfs_create_file("frame-time", S_IRUGO, svc->debugfs_dentry, timesync_svc, &gb_timesync_debugfs_frame_time_ops); timesync_svc->frame_ktime_dentry = debugfs_create_file("frame-ktime", S_IRUGO, svc->debugfs_dentry, timesync_svc, &gb_timesync_debugfs_frame_ktime_ops); list_add(&timesync_svc->list, &gb_timesync_svc_list); ret = gb_timesync_hd_add(timesync_svc, svc->hd); if (ret) { list_del(&timesync_svc->list); debugfs_remove(timesync_svc->frame_ktime_dentry); debugfs_remove(timesync_svc->frame_time_dentry); destroy_workqueue(timesync_svc->work_queue); kfree(timesync_svc); goto done; } init_timer(&timesync_svc->ktime_timer); timesync_svc->ktime_timer.function = gb_timesync_ktime_timer_fn; timesync_svc->ktime_timer.expires = jiffies + GB_TIMESYNC_KTIME_UPDATE; timesync_svc->ktime_timer.data = (unsigned long)timesync_svc; add_timer(&timesync_svc->ktime_timer); done: mutex_unlock(&gb_timesync_svc_list_mutex); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
bryan o'donoghuebryan o'donoghue302100.00%3100.00%
Total302100.00%3100.00%

EXPORT_SYMBOL_GPL(gb_timesync_svc_add);
void gb_timesync_svc_remove(struct gb_svc *svc) { struct gb_timesync_svc *timesync_svc; struct gb_timesync_interface *timesync_interface; struct gb_timesync_interface *next; mutex_lock(&gb_timesync_svc_list_mutex); timesync_svc = gb_timesync_find_timesync_svc(svc->hd); if (!timesync_svc) goto done; cancel_delayed_work_sync(&timesync_svc->delayed_work); mutex_lock(&timesync_svc->mutex); gb_timesync_set_state_atomic(timesync_svc, GB_TIMESYNC_STATE_INVALID); del_timer_sync(&timesync_svc->ktime_timer); gb_timesync_teardown(timesync_svc); gb_timesync_hd_remove(timesync_svc, svc->hd); list_for_each_entry_safe(timesync_interface, next, &timesync_svc->interface_list, list) { list_del(&timesync_interface->list); kfree(timesync_interface); } debugfs_remove(timesync_svc->frame_ktime_dentry); debugfs_remove(timesync_svc->frame_time_dentry); destroy_workqueue(timesync_svc->work_queue); list_del(&timesync_svc->list); mutex_unlock(&timesync_svc->mutex); kfree(timesync_svc); done: mutex_unlock(&gb_timesync_svc_list_mutex); }

Contributors

PersonTokensPropCommitsCommitProp
bryan o'donoghuebryan o'donoghue168100.00%5100.00%
Total168100.00%5100.00%

EXPORT_SYMBOL_GPL(gb_timesync_svc_remove); /* * Add a Greybus Interface to the set of TimeSync Interfaces. */
int gb_timesync_interface_add(struct gb_interface *interface) { struct gb_timesync_svc *timesync_svc; struct gb_timesync_interface *timesync_interface; int ret = 0; if (!(interface->features & GREYBUS_INTERFACE_FEATURE_TIMESYNC)) return 0; mutex_lock(&gb_timesync_svc_list_mutex); timesync_svc = gb_timesync_find_timesync_svc(interface->hd); if (!timesync_svc) { ret = -ENODEV; goto done; } timesync_interface = kzalloc(sizeof(*timesync_interface), GFP_KERNEL); if (!timesync_interface) { ret = -ENOMEM; goto done; } mutex_lock(&timesync_svc->mutex); timesync_interface->interface = interface; list_add(&timesync_interface->list, &timesync_svc->interface_list); timesync_svc->strobe_mask |= 1 << interface->interface_id; mutex_unlock(&timesync_svc->mutex); done: mutex_unlock(&gb_timesync_svc_list_mutex); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
bryan o'donoghuebryan o'donoghue153100.00%2100.00%
Total153100.00%2100.00%

EXPORT_SYMBOL_GPL(gb_timesync_interface_add); /* * Remove a Greybus Interface from the set of TimeSync Interfaces. */
void gb_timesync_interface_remove(struct gb_interface *interface) { struct gb_timesync_svc *timesync_svc; struct gb_timesync_interface *timesync_interface; if (!(interface->features & GREYBUS_INTERFACE_FEATURE_TIMESYNC)) return; mutex_lock(&gb_timesync_svc_list_mutex); timesync_svc = gb_timesync_find_timesync_svc(interface->hd); if (!timesync_svc) goto done; timesync_interface = gb_timesync_find_timesync_interface(timesync_svc, interface); if (!timesync_interface) goto done; mutex_lock(&timesync_svc->mutex); timesync_svc->strobe_mask &= ~(1 << interface->interface_id); list_del(&timesync_interface->list); kfree(timesync_interface); mutex_unlock(&timesync_svc->mutex); done: mutex_unlock(&gb_timesync_svc_list_mutex); }

Contributors

PersonTokensPropCommitsCommitProp
bryan o'donoghuebryan o'donoghue122100.00%2100.00%
Total122100.00%2100.00%

EXPORT_SYMBOL_GPL(gb_timesync_interface_remove); /* * Give the authoritative FrameTime to the calling function. Returns zero if we * are not in GB_TIMESYNC_STATE_ACTIVE. */
static u64 gb_timesync_get_frame_time(struct gb_timesync_svc *timesync_svc) { unsigned long flags; u64 ret; spin_lock_irqsave(&timesync_svc->spinlock, flags); if (timesync_svc->state == GB_TIMESYNC_STATE_ACTIVE) ret = __gb_timesync_get_frame_time(timesync_svc); else ret = 0; spin_unlock_irqrestore(&timesync_svc->spinlock, flags); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
bryan o'donoghuebryan o'donoghue61100.00%2100.00%
Total61100.00%2100.00%


u64 gb_timesync_get_frame_time_by_interface(struct gb_interface *interface) { struct gb_timesync_svc *timesync_svc; u64 ret = 0; mutex_lock(&gb_timesync_svc_list_mutex); timesync_svc = gb_timesync_find_timesync_svc(interface->hd); if (!timesync_svc) goto done; ret = gb_timesync_get_frame_time(timesync_svc); done: mutex_unlock(&gb_timesync_svc_list_mutex); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
bryan o'donoghuebryan o'donoghue61100.00%2100.00%
Total61100.00%2100.00%

EXPORT_SYMBOL_GPL(gb_timesync_get_frame_time_by_interface);
u64 gb_timesync_get_frame_time_by_svc(struct gb_svc *svc) { struct gb_timesync_svc *timesync_svc; u64 ret = 0; mutex_lock(&gb_timesync_svc_list_mutex); timesync_svc = gb_timesync_find_timesync_svc(svc->hd); if (!timesync_svc) goto done; ret = gb_timesync_get_frame_time(timesync_svc); done: mutex_unlock(&gb_timesync_svc_list_mutex); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
bryan o'donoghuebryan o'donoghue61100.00%2100.00%
Total61100.00%2100.00%

EXPORT_SYMBOL_GPL(gb_timesync_get_frame_time_by_svc); /* Incrementally updates the conversion base from FrameTime to ktime */
static void gb_timesync_ktime_timer_fn(unsigned long data) { struct gb_timesync_svc *timesync_svc = (struct gb_timesync_svc *)data; unsigned long flags; u64 frame_time; struct timespec ts; spin_lock_irqsave(&timesync_svc->spinlock, flags); if (timesync_svc->state != GB_TIMESYNC_STATE_ACTIVE) goto done; ktime_get_ts(&ts); frame_time = __gb_timesync_get_frame_time(timesync_svc); gb_timesync_store_ktime(timesync_svc, ts, frame_time); done: spin_unlock_irqrestore(&timesync_svc->spinlock, flags); mod_timer(&timesync_svc->ktime_timer, jiffies + GB_TIMESYNC_KTIME_UPDATE); }

Contributors

PersonTokensPropCommitsCommitProp
bryan o'donoghuebryan o'donoghue100100.00%2100.00%
Total100100.00%2100.00%


int gb_timesync_to_timespec_by_svc(struct gb_svc *svc, u64 frame_time, struct timespec *ts) { struct gb_timesync_svc *timesync_svc; int ret = 0; mutex_lock(&gb_timesync_svc_list_mutex); timesync_svc = gb_timesync_find_timesync_svc(svc->hd); if (!timesync_svc) { ret = -ENODEV; goto done; } ret = gb_timesync_to_timespec(timesync_svc, frame_time, ts); done: mutex_unlock(&gb_timesync_svc_list_mutex); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
bryan o'donoghuebryan o'donoghue80100.00%2100.00%
Total80100.00%2100.00%

EXPORT_SYMBOL_GPL(gb_timesync_to_timespec_by_svc);
int gb_timesync_to_timespec_by_interface(struct gb_interface *interface, u64 frame_time, struct timespec *ts) { struct gb_timesync_svc *timesync_svc; int ret = 0; mutex_lock(&gb_timesync_svc_list_mutex); timesync_svc = gb_timesync_find_timesync_svc(interface->hd); if (!timesync_svc) { ret = -ENODEV; goto done; } ret = gb_timesync_to_timespec(timesync_svc, frame_time, ts); done: mutex_unlock(&gb_timesync_svc_list_mutex); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
bryan o'donoghuebryan o'donoghue80100.00%2100.00%
Total80100.00%2100.00%

EXPORT_SYMBOL_GPL(gb_timesync_to_timespec_by_interface);
void gb_timesync_irq(struct gb_timesync_svc *timesync_svc) { unsigned long flags; u64 strobe_time; bool strobe_is_ping = true; struct timespec ts; ktime_get_ts(&ts); strobe_time = __gb_timesync_get_frame_time(timesync_svc); spin_lock_irqsave(&timesync_svc->spinlock, flags); if (timesync_svc->state == GB_TIMESYNC_STATE_PING) { if (!timesync_svc->capture_ping) goto done_nolog; timesync_svc->ap_ping_frame_time = strobe_time; goto done_log; } else if (timesync_svc->state != GB_TIMESYNC_STATE_WAIT_SVC) { goto done_nolog; } timesync_svc->strobe_data[timesync_svc->strobe].frame_time = strobe_time; timesync_svc->strobe_data[timesync_svc->strobe].ts = ts; if (++timesync_svc->strobe == GB_TIMESYNC_MAX_STROBES) { gb_timesync_set_state(timesync_svc, GB_TIMESYNC_STATE_AUTHORITATIVE); } strobe_is_ping = false; done_log: trace_gb_timesync_irq(strobe_is_ping, timesync_svc->strobe, GB_TIMESYNC_MAX_STROBES, strobe_time); done_nolog: spin_unlock_irqrestore(&timesync_svc->spinlock, flags); }

Contributors

PersonTokensPropCommitsCommitProp
bryan o'donoghuebryan o'donoghue167100.00%3100.00%
Total167100.00%3100.00%

EXPORT_SYMBOL(gb_timesync_irq);
int __init gb_timesync_init(void) { int ret = 0; ret = gb_timesync_platform_init(); if (ret) { pr_err("timesync platform init fail!\n"); return ret; } gb_timesync_clock_rate = gb_timesync_platform_get_clock_rate(); /* Calculate nanoseconds and femtoseconds per clock */ gb_timesync_fs_per_clock = FSEC_PER_SEC; do_div(gb_timesync_fs_per_clock, gb_timesync_clock_rate); gb_timesync_ns_per_clock = NSEC_PER_SEC; do_div(gb_timesync_ns_per_clock, gb_timesync_clock_rate); /* Calculate the maximum number of clocks we will convert to ktime */ gb_timesync_max_ktime_diff = GB_TIMESYNC_MAX_KTIME_CONVERSION * gb_timesync_clock_rate; pr_info("Time-Sync @ %lu Hz max ktime conversion +/- %d seconds\n", gb_timesync_clock_rate, GB_TIMESYNC_MAX_KTIME_CONVERSION); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
bryan o'donoghuebryan o'donoghue79100.00%3100.00%
Total79100.00%3100.00%


void gb_timesync_exit(void) { gb_timesync_platform_exit(); }

Contributors

PersonTokensPropCommitsCommitProp
bryan o'donoghuebryan o'donoghue10100.00%1100.00%
Total10100.00%1100.00%


Overall Contributors

PersonTokensPropCommitsCommitProp
bryan o'donoghuebryan o'donoghue547799.85%1493.33%
david lindavid lin80.15%16.67%
Total5485100.00%15100.00%