Commit 970dc85b authored by Bryan O'Donoghue's avatar Bryan O'Donoghue Committed by Greg Kroah-Hartman

greybus: timesync: Add timesync core driver

This patch adds the core timesync functionality.

0. arche-platform.c/arche-apb-ctrl.c
   Modifies the platform layer to hook the incoming TIME_SYNC signal up to
   the timesync strobe IRQ handler. If the arche-platform driver can't
   satisfy the request for the wake-detect line, it will return -EAGAIN and
   the calling work-queue must reschedule the attempt to get exclusive
   access to the wake-detect pin logic. A private data field is added to
   the arche-platform driver to enable passing of a timesync pointer to the
   ISR responsible for synchronizing time.

1. timesync.c
   A new file added which contains all of the logic associated with sending
   greybus commands to SVC, APBx or Interfaces to enable, disable and
   disseminate timing information.

2. timesync_platform.c
   Any platform/arch specific code goes into timesync_platform.c.
   Originally the idea was to keep the x86 and ARM arch dependencies in a
   timesync_platform_arch.c file - however with further refinement that's
   currently not necessary however just-in-case it becomes necessary to
   resuscitate arch or platform specific methods for accessing timer
   resources that access shouldn't be part of the core timesync.c logic and
   so for the moment we access these timer resources through a thin access
   layer in timesync_platform.c. Expect this to go away long term ideally.
Signed-off-by: default avatarBryan O'Donoghue <bryan.odonoghue@linaro.org>
Acked-by: default avatarAlex Elder <elder@linaro.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@google.com>
parent 698282f6
......@@ -10,7 +10,9 @@ greybus-y := core.o \
svc.o \
svc_watchdog.o \
bootrom.o \
operation.o
operation.o \
timesync.o \
timesync_platform.o
gb-gbphy-y := gbphy.o
......
......@@ -90,7 +90,7 @@ static int coldboot_seq(struct platform_device *pdev)
}
}
gpio_set_value(apb->boot_ret_gpio, 0);
apb_bootret_deassert(dev);
/* On DB3 clock was not mandatory */
if (gpio_is_valid(apb->clk_en_gpio))
......@@ -184,6 +184,20 @@ static void poweroff_seq(struct platform_device *pdev)
/* TODO: May have to send an event to SVC about this exit */
}
void apb_bootret_assert(struct device *dev)
{
struct arche_apb_ctrl_drvdata *apb = dev_get_drvdata(dev);
gpio_set_value(apb->boot_ret_gpio, 1);
}
void apb_bootret_deassert(struct device *dev)
{
struct arche_apb_ctrl_drvdata *apb = dev_get_drvdata(dev);
gpio_set_value(apb->boot_ret_gpio, 0);
}
int apb_ctrl_coldboot(struct device *dev)
{
return coldboot_seq(to_platform_device(dev));
......
......@@ -22,6 +22,7 @@
#include <linux/suspend.h>
#include <linux/time.h>
#include "arche_platform.h"
#include "greybus.h"
#include <linux/usb/usb3613.h>
......@@ -34,6 +35,7 @@ enum svc_wakedetect_state {
WD_STATE_STANDBYBOOT_TRIG, /* As of now not used ?? */
WD_STATE_COLDBOOT_START, /* Cold boot process started */
WD_STATE_STANDBYBOOT_START, /* Not used */
WD_STATE_TIMESYNC,
};
struct arche_platform_drvdata {
......@@ -57,13 +59,27 @@ struct arche_platform_drvdata {
int wake_detect_irq;
spinlock_t wake_lock; /* Protect wake_detect_state */
struct mutex platform_state_mutex; /* Protect state */
wait_queue_head_t wq; /* WQ for arche_pdata->state */
unsigned long wake_detect_start;
struct notifier_block pm_notifier;
struct device *dev;
struct gb_timesync_svc *timesync_svc_pdata;
};
/* Requires calling context to hold arche_pdata->spinlock */
static int arche_apb_bootret_assert(struct device *dev, void *data)
{
apb_bootret_assert(dev);
return 0;
}
static int arche_apb_bootret_deassert(struct device *dev, void *data)
{
apb_bootret_deassert(dev);
return 0;
}
/* Requires calling context to hold arche_pdata->platform_state_mutex */
static void arche_platform_set_state(struct arche_platform_drvdata *arche_pdata,
enum arche_platform_state state)
{
......@@ -94,7 +110,8 @@ static void arche_platform_set_state(struct arche_platform_drvdata *arche_pdata,
* satisfy the requested state-transition or -EINVAL for all other
* state-transition requests.
*/
int arche_platform_change_state(enum arche_platform_state state)
int arche_platform_change_state(enum arche_platform_state state,
struct gb_timesync_svc *timesync_svc_pdata)
{
struct arche_platform_drvdata *arche_pdata;
struct platform_device *pdev;
......@@ -118,11 +135,6 @@ int arche_platform_change_state(enum arche_platform_state state)
mutex_lock(&arche_pdata->platform_state_mutex);
spin_lock_irqsave(&arche_pdata->wake_lock, flags);
if (arche_pdata->wake_detect_state != WD_STATE_IDLE) {
dev_err(arche_pdata->dev,
"driver busy with wake/detect line ops\n");
goto exit;
}
if (arche_pdata->state == state) {
ret = 0;
......@@ -131,10 +143,27 @@ int arche_platform_change_state(enum arche_platform_state state)
switch (state) {
case ARCHE_PLATFORM_STATE_TIME_SYNC:
disable_irq(arche_pdata->wake_detect_irq);
if (arche_pdata->state != ARCHE_PLATFORM_STATE_ACTIVE) {
ret = -EINVAL;
goto exit;
}
if (arche_pdata->wake_detect_state != WD_STATE_IDLE) {
dev_err(arche_pdata->dev,
"driver busy with wake/detect line ops\n");
goto exit;
}
device_for_each_child(arche_pdata->dev, NULL,
arche_apb_bootret_assert);
arche_pdata->wake_detect_state = WD_STATE_TIMESYNC;
break;
case ARCHE_PLATFORM_STATE_ACTIVE:
enable_irq(arche_pdata->wake_detect_irq);
if (arche_pdata->state != ARCHE_PLATFORM_STATE_TIME_SYNC) {
ret = -EINVAL;
goto exit;
}
device_for_each_child(arche_pdata->dev, NULL,
arche_apb_bootret_deassert);
arche_pdata->wake_detect_state = WD_STATE_IDLE;
break;
case ARCHE_PLATFORM_STATE_OFF:
case ARCHE_PLATFORM_STATE_STANDBY:
......@@ -147,7 +176,11 @@ int arche_platform_change_state(enum arche_platform_state state)
"invalid state transition request\n");
goto exit;
}
arche_pdata->timesync_svc_pdata = timesync_svc_pdata;
arche_platform_set_state(arche_pdata, state);
if (state == ARCHE_PLATFORM_STATE_ACTIVE)
wake_up(&arche_pdata->wq);
ret = 0;
exit:
spin_unlock_irqrestore(&arche_pdata->wake_lock, flags);
......@@ -252,6 +285,11 @@ static irqreturn_t arche_platform_wd_irq(int irq, void *devid)
spin_lock_irqsave(&arche_pdata->wake_lock, flags);
if (arche_pdata->wake_detect_state == WD_STATE_TIMESYNC) {
gb_timesync_irq(arche_pdata->timesync_svc_pdata);
goto exit;
}
if (gpio_get_value(arche_pdata->wake_detect_gpio)) {
/* wake/detect rising */
......@@ -293,6 +331,7 @@ static irqreturn_t arche_platform_wd_irq(int irq, void *devid)
}
}
exit:
spin_unlock_irqrestore(&arche_pdata->wake_lock, flags);
return IRQ_HANDLED;
......@@ -402,7 +441,17 @@ static ssize_t state_store(struct device *dev,
struct arche_platform_drvdata *arche_pdata = platform_get_drvdata(pdev);
int ret = 0;
retry:
mutex_lock(&arche_pdata->platform_state_mutex);
if (arche_pdata->state == ARCHE_PLATFORM_STATE_TIME_SYNC) {
mutex_unlock(&arche_pdata->platform_state_mutex);
ret = wait_event_interruptible(
arche_pdata->wq,
arche_pdata->state != ARCHE_PLATFORM_STATE_TIME_SYNC);
if (ret)
return ret;
goto retry;
}
if (sysfs_streq(buf, "off")) {
if (arche_pdata->state == ARCHE_PLATFORM_STATE_OFF)
......@@ -610,6 +659,7 @@ static int arche_platform_probe(struct platform_device *pdev)
spin_lock_init(&arche_pdata->wake_lock);
mutex_init(&arche_pdata->platform_state_mutex);
init_waitqueue_head(&arche_pdata->wq);
arche_pdata->wake_detect_irq =
gpio_to_irq(arche_pdata->wake_detect_gpio);
......@@ -653,6 +703,9 @@ static int arche_platform_probe(struct platform_device *pdev)
goto err_populate;
}
/* Register callback pointer */
arche_platform_change_state_cb = arche_platform_change_state;
dev_info(dev, "Device registered successfully\n");
return 0;
......
......@@ -10,6 +10,8 @@
#ifndef __ARCHE_PLATFORM_H
#define __ARCHE_PLATFORM_H
#include "timesync.h"
enum arche_platform_state {
ARCHE_PLATFORM_STATE_OFF,
ARCHE_PLATFORM_STATE_ACTIVE,
......@@ -18,8 +20,11 @@ enum arche_platform_state {
ARCHE_PLATFORM_STATE_TIME_SYNC,
};
int arche_platform_change_state(enum arche_platform_state state);
int arche_platform_change_state(enum arche_platform_state state,
struct gb_timesync_svc *pdata);
extern int (*arche_platform_change_state_cb)(enum arche_platform_state state,
struct gb_timesync_svc *pdata);
int __init arche_apb_init(void);
void __exit arche_apb_exit(void);
......@@ -28,5 +33,7 @@ int apb_ctrl_coldboot(struct device *dev);
int apb_ctrl_fw_flashing(struct device *dev);
int apb_ctrl_standby_boot(struct device *dev);
void apb_ctrl_poweroff(struct device *dev);
void apb_bootret_assert(struct device *dev);
void apb_bootret_deassert(struct device *dev);
#endif /* __ARCHE_PLATFORM_H */
......@@ -33,7 +33,7 @@
#include "bundle.h"
#include "connection.h"
#include "operation.h"
#include "timesync.h"
/* Matches up with the Greybus Protocol specification document */
#define GREYBUS_VERSION_MAJOR 0x00
......
......@@ -865,6 +865,61 @@ void gb_interface_disable(struct gb_interface *intf)
intf->enabled = false;
}
/*
* Enable TimeSync on an Interface control connection.
*
* Locking: Takes and releases the interface mutex.
*/
int gb_interface_timesync_enable(struct gb_interface *intf, u8 count,
u64 frame_time, u32 strobe_delay, u32 refclk)
{
int ret = -ENODEV;
mutex_lock(&intf->mutex);
if (intf->enabled) {
ret = gb_control_timesync_enable(intf->control, count,
frame_time, strobe_delay,
refclk);
}
mutex_unlock(&intf->mutex);
return ret;
}
/*
* Disable TimeSync on an Interface control connection.
*
* Locking: Takes and releases the interface mutex.
*/
int gb_interface_timesync_disable(struct gb_interface *intf)
{
int ret = -ENODEV;
mutex_lock(&intf->mutex);
if (intf->enabled)
ret = gb_control_timesync_disable(intf->control);
mutex_unlock(&intf->mutex);
return ret;
}
/*
* Transmit the Authoritative FrameTime via an Interface control connection.
*
* Locking: Takes and releases the interface mutex.
*/
int gb_interface_timesync_authoritative(struct gb_interface *intf,
u64 *frame_time)
{
int ret = -ENODEV;
mutex_lock(&intf->mutex);
if (intf->enabled) {
ret = gb_control_timesync_authoritative(intf->control,
frame_time);
}
mutex_unlock(&intf->mutex);
return ret;
}
/* Register an interface. */
int gb_interface_add(struct gb_interface *intf)
{
......
......@@ -58,6 +58,11 @@ int gb_interface_activate(struct gb_interface *intf);
void gb_interface_deactivate(struct gb_interface *intf);
int gb_interface_enable(struct gb_interface *intf);
void gb_interface_disable(struct gb_interface *intf);
int gb_interface_timesync_enable(struct gb_interface *intf, u8 count,
u64 frame_time, u32 strobe_delay, u32 refclk);
int gb_interface_timesync_authoritative(struct gb_interface *intf,
u64 *frame_time);
int gb_interface_timesync_disable(struct gb_interface *intf);
int gb_interface_add(struct gb_interface *intf);
void gb_interface_del(struct gb_interface *intf);
void gb_interface_put(struct gb_interface *intf);
......
This diff is collapsed.
/*
* TimeSync API driver.
*
* Copyright 2016 Google Inc.
* Copyright 2016 Linaro Ltd.
*
* Released under the GPLv2 only.
*/
#ifndef __TIMESYNC_H
#define __TIMESYNC_H
struct gb_svc;
struct gb_interface;
struct gb_timesync_svc;
/* Platform */
u64 gb_timesync_platform_get_counter(void);
u32 gb_timesync_platform_get_clock_rate(void);
int gb_timesync_platform_lock_bus(struct gb_timesync_svc *pdata);
void gb_timesync_platform_unlock_bus(void);
int gb_timesync_platform_init(void);
void gb_timesync_platform_exit(void);
/* Core API */
int gb_timesync_interface_add(struct gb_interface *interface);
void gb_timesync_interface_remove(struct gb_interface *interface);
int gb_timesync_svc_add(struct gb_svc *svc);
void gb_timesync_svc_remove(struct gb_svc *svc);
u64 gb_timesync_get_frame_time_by_interface(struct gb_interface *interface);
u64 gb_timesync_get_frame_time_by_svc(struct gb_svc *svc);
int gb_timesync_schedule_synchronous(struct gb_interface *intf);
void gb_timesync_schedule_asynchronous(struct gb_interface *intf);
void gb_timesync_irq(struct gb_timesync_svc *timesync_svc);
int gb_timesync_init(void);
void gb_timesync_exit(void);
#endif /* __TIMESYNC_H */
/*
* TimeSync API driver.
*
* Copyright 2016 Google Inc.
* Copyright 2016 Linaro Ltd.
*
* Released under the GPLv2 only.
*
* This code reads directly from an ARMv7 memory-mapped timer that lives in
* MMIO space. Since this counter lives inside of MMIO space its shared between
* cores and that means we don't have to worry about issues like TSC on x86
* where each time-stamp-counter (TSC) is local to a particular core.
*
* Register-level access code is based on
* drivers/clocksource/arm_arch_timer.c
*/
#include <linux/cpufreq.h>
#include <linux/of_platform.h>
#include "greybus.h"
#include "arche_platform.h"
static u32 gb_timesync_clock_frequency;
int (*arche_platform_change_state_cb)(enum arche_platform_state state,
struct gb_timesync_svc *pdata);
EXPORT_SYMBOL_GPL(arche_platform_change_state_cb);
u64 gb_timesync_platform_get_counter(void)
{
return (u64)get_cycles();
}
u32 gb_timesync_platform_get_clock_rate(void)
{
if (unlikely(!gb_timesync_clock_frequency))
return cpufreq_get(0);
return gb_timesync_clock_frequency;
}
int gb_timesync_platform_lock_bus(struct gb_timesync_svc *pdata)
{
return arche_platform_change_state_cb(ARCHE_PLATFORM_STATE_TIME_SYNC,
pdata);
}
void gb_timesync_platform_unlock_bus(void)
{
arche_platform_change_state_cb(ARCHE_PLATFORM_STATE_ACTIVE, NULL);
}
static const struct of_device_id arch_timer_of_match[] = {
{ .compatible = "google,greybus-frame-time-counter", },
{},
};
int __init gb_timesync_platform_init(void)
{
struct device_node *np;
np = of_find_matching_node(NULL, arch_timer_of_match);
if (!np) {
/* Tolerate not finding to allow BBB etc to continue */
pr_warn("Unable to find a compatible ARMv7 timer\n");
return 0;
}
if (of_property_read_u32(np, "clock-frequency",
&gb_timesync_clock_frequency)) {
pr_err("Unable to find timer clock-frequency\n");
return -ENODEV;
}
return 0;
}
void gb_timesync_platform_exit(void) {}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment