diff options
Diffstat (limited to 'drivers/staging/greybus/timesync.c')
-rw-r--r-- | drivers/staging/greybus/timesync.c | 1357 |
1 files changed, 0 insertions, 1357 deletions
diff --git a/drivers/staging/greybus/timesync.c b/drivers/staging/greybus/timesync.c deleted file mode 100644 index 29e6c1c12807..000000000000 --- a/drivers/staging/greybus/timesync.c +++ /dev/null @@ -1,1357 +0,0 @@ -/* - * 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; -} - -/* - * 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); -} - -static void gb_timesync_schedule_svc_timeout(struct gb_timesync_svc - *timesync_svc) -{ - queue_delayed_work(timesync_svc->work_queue, - ×ync_svc->delayed_work, - GB_TIMESYNC_MAX_WAIT_SVC); -} - -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(×ync_svc->wait_queue); - break; - case GB_TIMESYNC_STATE_INACTIVE: - timesync_svc->state = state; - wake_up(×ync_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(×ync_svc->delayed_work); - queue_delayed_work(timesync_svc->work_queue, - ×ync_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(×ync_svc->delayed_work); - queue_delayed_work(timesync_svc->work_queue, - ×ync_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, - ×ync_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(×ync_svc->wait_queue); - } - break; - } - - if (WARN_ON(timesync_svc->state != state)) { - pr_err("Invalid state transition %d=>%d\n", - timesync_svc->state, state); - } -} - -static void gb_timesync_set_state_atomic(struct gb_timesync_svc *timesync_svc, - int state) -{ - unsigned long flags; - - spin_lock_irqsave(×ync_svc->spinlock, flags); - gb_timesync_set_state(timesync_svc, state); - spin_unlock_irqrestore(×ync_svc->spinlock, flags); -} - -static u64 gb_timesync_diff(u64 x, u64 y) -{ - if (x > y) - return x - y; - else - return y - x; -} - -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; - } -} - -/* - * 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; -} - -/* - * 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); - } - } -} - -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, - ×ync_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); -} - -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); - } -} - -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, ×ync_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); -} - -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, - ×ync_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); -} - -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; -} - -/* - * 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(×ync_svc->mutex); - spin_lock_irqsave(×ync_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(×ync_svc->spinlock, flags); - mutex_unlock(×ync_svc->mutex); - return ret; -} - -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, - ×ync_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; -} - -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, - ×ync_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; -} - -/* - * 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, ×ync_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, - ×ync_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, - ×ync_svc->interface_list, list) { - control = timesync_interface->interface->control; - timesync_interface->ping_frame_time = 0; - ping_frame_time = ×ync_interface->ping_frame_time; - ret = gb_control_timesync_get_last_event(control, - ping_frame_time); - if (ret) { - dev_err(×ync_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); -} - -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(×ync_svc->svc->dev, "%s", buf); - kfree(buf); - } -} - -/* - * 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(×ync_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(×ync_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(×ync_svc->mutex); -} - -/* - * 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(×ync_svc->mutex); - if (timesync_svc->state != GB_TIMESYNC_STATE_INVALID) - gb_timesync_set_state_atomic(timesync_svc, state); - else - ret = -ENODEV; - - mutex_unlock(×ync_svc->mutex); - return ret; -} - -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(×ync_svc->mutex); - spin_lock_irqsave(×ync_svc->spinlock, flags); - - ret = __gb_timesync_get_status(timesync_svc); - - spin_unlock_irqrestore(×ync_svc->spinlock, flags); - mutex_unlock(×ync_svc->mutex); - - return ret; -} - -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; -} - -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, ×ync_svc->interface_list, list) { - if (timesync_interface->interface == interface) - return timesync_interface; - } - return NULL; -} - -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; -} -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; -} -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_inode(file)->i_private; - char *buf; - ssize_t ret = 0; - - mutex_lock(&gb_timesync_svc_list_mutex); - mutex_lock(×ync_svc->mutex); - if (list_empty(×ync_svc->interface_list)) - ret = -ENODEV; - timesync_svc->print_ping = false; - mutex_unlock(×ync_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; -} - -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); -} - -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); -} - -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; -} - -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); -} - -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(×ync_svc->interface_list); - INIT_DELAYED_WORK(×ync_svc->delayed_work, gb_timesync_worker); - mutex_init(×ync_svc->mutex); - spin_lock_init(×ync_svc->spinlock); - init_waitqueue_head(×ync_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(×ync_svc->list, &gb_timesync_svc_list); - ret = gb_timesync_hd_add(timesync_svc, svc->hd); - if (ret) { - list_del(×ync_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(×ync_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(×ync_svc->ktime_timer); -done: - mutex_unlock(&gb_timesync_svc_list_mutex); - return ret; -} -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(×ync_svc->delayed_work); - - mutex_lock(×ync_svc->mutex); - - gb_timesync_set_state_atomic(timesync_svc, GB_TIMESYNC_STATE_INVALID); - del_timer_sync(×ync_svc->ktime_timer); - gb_timesync_teardown(timesync_svc); - - gb_timesync_hd_remove(timesync_svc, svc->hd); - list_for_each_entry_safe(timesync_interface, next, - ×ync_svc->interface_list, list) { - list_del(×ync_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(×ync_svc->list); - - mutex_unlock(×ync_svc->mutex); - - kfree(timesync_svc); -done: - mutex_unlock(&gb_timesync_svc_list_mutex); -} -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(×ync_svc->mutex); - timesync_interface->interface = interface; - list_add(×ync_interface->list, ×ync_svc->interface_list); - timesync_svc->strobe_mask |= 1 << interface->interface_id; - mutex_unlock(×ync_svc->mutex); - -done: - mutex_unlock(&gb_timesync_svc_list_mutex); - return ret; -} -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(×ync_svc->mutex); - timesync_svc->strobe_mask &= ~(1 << interface->interface_id); - list_del(×ync_interface->list); - kfree(timesync_interface); - mutex_unlock(×ync_svc->mutex); -done: - mutex_unlock(&gb_timesync_svc_list_mutex); -} -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(×ync_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(×ync_svc->spinlock, flags); - return ret; -} - -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; -} -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; -} -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(×ync_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(×ync_svc->spinlock, flags); - mod_timer(×ync_svc->ktime_timer, - jiffies + GB_TIMESYNC_KTIME_UPDATE); -} - -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; -} -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; -} -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(×ync_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(×ync_svc->spinlock, flags); -} -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; -} - -void gb_timesync_exit(void) -{ - gb_timesync_platform_exit(); -} |