greybus: hd: Add TimeSync APBridge commands
authorBryan O'Donoghue <bryan.odonoghue@linaro.org>
Sun, 15 May 2016 18:37:48 +0000 (19:37 +0100)
committerGreg Kroah-Hartman <gregkh@google.com>
Sun, 15 May 2016 18:39:57 +0000 (11:39 -0700)
This patch adds a number of USB Vendor commands to es2.c to enable TimeSync
in the bridge.

Adds:
- es2.c::timesync_enable(u8 count, u64 frame_time, u32 strobe_delay,
 u32 refclk);
  Commands APBx to enable timers and clocks to track a pulse-train of
  incoming TIME_SYNC strobes with strobe_delay microseconds between each.
  Provides the reference clock the AP is using to track FrameTime. It is
  the responsibility of APBx to adequately track the FrameTime based on
  the indicated AP refclk. Once this command has succeeded APBx may not
  transition to a low-power state were FrameTime counters stop.

  This function is initiated from the timesync worker thread logic when
  re-synchronizing frame-time throughout the system.

  TimeSync is at this time enabled for all APBx active in the system i.e.
  currently APB2 will not receive TimeSync commands until it becomes a
  registered host-device in Greybus.

- es2.c::timesync_disable(void)
  Commands APBx to discontinue tracking of FrameTime. After this operation
  completes APBx may transition to a low-power state where timer-clocks
  stop operating.

- es2.c::timesync_authoritative(u64 *frame_time)
  Provides an authoritative time for each TIME_SYNC strobe to APBx.
  APBx must align its local FrameTime to the authoritative clock.

- es2.c::timesync_get_last_event(u64 *frame_time)
  Returns the FrameTime at the last SVC_TIMESYNC_PING to the AP Module.

Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
drivers/staging/greybus/es2.c
drivers/staging/greybus/greybus_protocols.h
drivers/staging/greybus/hd.h

index 998b41e..d6abfda 100644 (file)
@@ -121,6 +121,29 @@ struct cport_to_ep {
        __u8 endpoint_out;
 };
 
+/**
+ * timesync_enable_request - Enable timesync in an APBridge
+ * @count: number of TimeSync Pulses to expect
+ * @frame_time: the initial FrameTime at the first TimeSync Pulse
+ * @strobe_delay: the expected delay in microseconds between each TimeSync Pulse
+ * @refclk: The AP mandated reference clock to run FrameTime at
+ */
+struct timesync_enable_request {
+       __u8    count;
+       __le64  frame_time;
+       __le32  strobe_delay;
+       __le32  refclk;
+} __packed;
+
+/**
+ * timesync_authoritative_request - Transmit authoritative FrameTime to APBridge
+ * @frame_time: An array of authoritative FrameTimes provided by the SVC
+ *              and relayed to the APBridge by the AP
+ */
+struct timesync_authoritative_request {
+       __le64  frame_time[GB_TIMESYNC_MAX_STROBES];
+} __packed;
+
 static inline struct es2_ap_dev *hd_to_es2(struct gb_host_device *hd)
 {
        return (struct es2_ap_dev *)&hd->hd_priv;
@@ -674,18 +697,126 @@ static int cport_features_disable(struct gb_host_device *hd, u16 cport_id)
        return retval;
 }
 
+static int timesync_enable(struct gb_host_device *hd, u8 count,
+                          u64 frame_time, u32 strobe_delay, u32 refclk)
+{
+       int retval;
+       struct es2_ap_dev *es2 = hd_to_es2(hd);
+       struct usb_device *udev = es2->usb_dev;
+       struct gb_control_timesync_enable_request *request;
+
+       request = kzalloc(sizeof(*request), GFP_KERNEL);
+       if (!request)
+               return -ENOMEM;
+
+       request->count = count;
+       request->frame_time = cpu_to_le64(frame_time);
+       request->strobe_delay = cpu_to_le32(strobe_delay);
+       request->refclk = cpu_to_le32(refclk);
+       retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+                                REQUEST_TIMESYNC_ENABLE,
+                                USB_DIR_OUT | USB_TYPE_VENDOR |
+                                USB_RECIP_INTERFACE, 0, 0, request,
+                                sizeof(*request), ES2_TIMEOUT);
+       if (retval < 0)
+               dev_err(&udev->dev, "Cannot enable timesync %d\n", retval);
+
+       kfree(request);
+       return retval;
+}
+
+static int timesync_disable(struct gb_host_device *hd)
+{
+       int retval;
+       struct es2_ap_dev *es2 = hd_to_es2(hd);
+       struct usb_device *udev = es2->usb_dev;
+
+       retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+                                REQUEST_TIMESYNC_DISABLE,
+                                USB_DIR_OUT | USB_TYPE_VENDOR |
+                                USB_RECIP_INTERFACE, 0, 0, NULL,
+                                0, ES2_TIMEOUT);
+       if (retval < 0)
+               dev_err(&udev->dev, "Cannot disable timesync %d\n", retval);
+
+       return retval;
+}
+
+static int timesync_authoritative(struct gb_host_device *hd, u64 *frame_time)
+{
+       int retval, i;
+       struct es2_ap_dev *es2 = hd_to_es2(hd);
+       struct usb_device *udev = es2->usb_dev;
+       struct timesync_authoritative_request *request;
+
+       request = kzalloc(sizeof(*request), GFP_KERNEL);
+       if (!request)
+               return -ENOMEM;
+
+       for (i = 0; i < GB_TIMESYNC_MAX_STROBES; i++)
+               request->frame_time[i] = cpu_to_le64(frame_time[i]);
+
+       retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+                                REQUEST_TIMESYNC_AUTHORITATIVE,
+                                USB_DIR_OUT | USB_TYPE_VENDOR |
+                                USB_RECIP_INTERFACE, 0, 0, request,
+                                sizeof(*request), ES2_TIMEOUT);
+       if (retval < 0)
+               dev_err(&udev->dev, "Cannot timesync authoritative out %d\n", retval);
+
+       kfree(request);
+       return retval;
+}
+
+static int timesync_get_last_event(struct gb_host_device *hd, u64 *frame_time)
+{
+       int retval;
+       struct es2_ap_dev *es2 = hd_to_es2(hd);
+       struct usb_device *udev = es2->usb_dev;
+       u64 *response_frame_time;
+
+       response_frame_time = kzalloc(sizeof(*response_frame_time), GFP_KERNEL);
+       if (!response_frame_time)
+               return -ENOMEM;
+
+       retval = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+                                REQUEST_TIMESYNC_GET_LAST_EVENT,
+                                USB_DIR_IN | USB_TYPE_VENDOR |
+                                USB_RECIP_INTERFACE, 0, 0, response_frame_time,
+                                sizeof(*response_frame_time), ES2_TIMEOUT);
+
+       if (retval != sizeof(*response_frame_time)) {
+               dev_err(&udev->dev, "Cannot get last TimeSync event: %d\n",
+                       retval);
+
+               if (retval >= 0)
+                       retval = -EIO;
+
+               goto out;
+       }
+       *frame_time = le64_to_cpu(*response_frame_time);
+       retval = 0;
+out:
+       kfree(response_frame_time);
+       return retval;
+}
+
 static struct gb_hd_driver es2_driver = {
-       .hd_priv_size           = sizeof(struct es2_ap_dev),
-       .message_send           = message_send,
-       .message_cancel         = message_cancel,
-       .cport_allocate         = es2_cport_allocate,
-       .cport_release          = es2_cport_release,
-       .cport_enable           = cport_enable,
-       .latency_tag_enable     = latency_tag_enable,
-       .latency_tag_disable    = latency_tag_disable,
-       .output                 = output,
-       .cport_features_enable  = cport_features_enable,
-       .cport_features_disable = cport_features_disable,
+       .hd_priv_size                   = sizeof(struct es2_ap_dev),
+       .message_send                   = message_send,
+       .message_cancel                 = message_cancel,
+       .cport_allocate                 = es2_cport_allocate,
+       .cport_release                  = es2_cport_release,
+       .cport_enable                   = cport_enable,
+       .latency_tag_enable             = latency_tag_enable,
+       .latency_tag_disable            = latency_tag_disable,
+       .output                         = output,
+       .cport_features_enable          = cport_features_enable,
+       .cport_features_disable         = cport_features_disable,
+       .timesync_enable                = timesync_enable,
+       .timesync_disable               = timesync_disable,
+       .timesync_authoritative         = timesync_authoritative,
+       .timesync_get_last_event        = timesync_get_last_event,
 };
 
 /* Common function to report consistent warnings based on URB status */
index e3ad5d7..d379fe3 100644 (file)
@@ -188,30 +188,36 @@ struct gb_control_timesync_get_last_event_response {
 /* APBridge protocol */
 
 /* request APB1 log */
-#define GB_APB_REQUEST_LOG             0x02
+#define GB_APB_REQUEST_LOG                     0x02
 
 /* request to map a cport to bulk in and bulk out endpoints */
-#define GB_APB_REQUEST_EP_MAPPING      0x03
+#define GB_APB_REQUEST_EP_MAPPING              0x03
 
 /* request to get the number of cports available */
-#define GB_APB_REQUEST_CPORT_COUNT     0x04
+#define GB_APB_REQUEST_CPORT_COUNT             0x04
 
 /* request to reset a cport state */
-#define GB_APB_REQUEST_RESET_CPORT     0x05
+#define GB_APB_REQUEST_RESET_CPORT             0x05
 
 /* request to time the latency of messages on a given cport */
-#define GB_APB_REQUEST_LATENCY_TAG_EN  0x06
-#define GB_APB_REQUEST_LATENCY_TAG_DIS 0x07
+#define GB_APB_REQUEST_LATENCY_TAG_EN          0x06
+#define GB_APB_REQUEST_LATENCY_TAG_DIS         0x07
 
 /* request to control the CSI transmitter */
-#define GB_APB_REQUEST_CSI_TX_CONTROL  0x08
+#define GB_APB_REQUEST_CSI_TX_CONTROL          0x08
 
 /* request to control the CSI transmitter */
-#define GB_APB_REQUEST_AUDIO_CONTROL   0x09
+#define GB_APB_REQUEST_AUDIO_CONTROL           0x09
 
 /* vendor requests to enable/disable CPort features */
-#define GB_APB_REQUEST_CPORT_FEAT_EN   0x0b
-#define GB_APB_REQUEST_CPORT_FEAT_DIS  0x0c
+#define GB_APB_REQUEST_CPORT_FEAT_EN           0x0b
+#define GB_APB_REQUEST_CPORT_FEAT_DIS          0x0c
+
+/* TimeSync commands */
+#define REQUEST_TIMESYNC_ENABLE                        0x0d
+#define REQUEST_TIMESYNC_DISABLE               0x0e
+#define REQUEST_TIMESYNC_AUTHORITATIVE         0x0f
+#define REQUEST_TIMESYNC_GET_LAST_EVENT                0x10
 
 /* Firmware Download Protocol */
 
index 80573ae..8510816 100644 (file)
@@ -30,6 +30,13 @@ struct gb_hd_driver {
                      bool async);
        int (*cport_features_enable)(struct gb_host_device *hd, u16 cport_id);
        int (*cport_features_disable)(struct gb_host_device *hd, u16 cport_id);
+       int (*timesync_enable)(struct gb_host_device *hd, u8 count,
+                              u64 frame_time, u32 strobe_delay, u32 refclk);
+       int (*timesync_disable)(struct gb_host_device *hd);
+       int (*timesync_authoritative)(struct gb_host_device *hd,
+                                     u64 *frame_time);
+       int (*timesync_get_last_event)(struct gb_host_device *hd,
+                                      u64 *frame_time);
 };
 
 struct gb_host_device {