greybus: remove redundant latency-tag sanity checks
[cascardo/linux.git] / drivers / staging / greybus / es2.c
index 8138bc2..5bd348f 100644 (file)
 #include "connection.h"
 #include "greybus_trace.h"
 
+
+/* Fixed CPort numbers */
+#define ES2_CPORT_CDSI0                16
+#define ES2_CPORT_CDSI1                17
+
 /* Memory sizes for the buffers sent to/from the ES2 controller */
 #define ES2_GBUF_MSG_SIZE_MAX  2048
 
@@ -94,6 +99,8 @@ struct es2_ap_dev {
        bool cport_out_urb_cancelled[NUM_CPORT_OUT_URB];
        spinlock_t cport_out_urb_lock;
 
+       bool cdsi1_in_use;
+
        int *cport_to_ep;
 
        struct task_struct *apb_log_task;
@@ -114,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;
@@ -501,6 +531,13 @@ static int cport_reset(struct gb_host_device *hd, u16 cport_id)
        struct usb_device *udev = es2->usb_dev;
        int retval;
 
+       switch (cport_id) {
+       case GB_SVC_CPORT_ID:
+       case ES2_CPORT_CDSI0:
+       case ES2_CPORT_CDSI1:
+               return 0;
+       }
+
        retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
                                 GB_APB_REQUEST_RESET_CPORT,
                                 USB_DIR_OUT | USB_TYPE_VENDOR |
@@ -515,15 +552,66 @@ static int cport_reset(struct gb_host_device *hd, u16 cport_id)
        return 0;
 }
 
+static int es2_cport_allocate(struct gb_host_device *hd, int cport_id,
+                               unsigned long flags)
+{
+       struct es2_ap_dev *es2 = hd_to_es2(hd);
+       struct ida *id_map = &hd->cport_id_map;
+       int ida_start, ida_end;
+
+       switch (cport_id) {
+       case ES2_CPORT_CDSI0:
+       case ES2_CPORT_CDSI1:
+               dev_err(&hd->dev, "cport %d not available\n", cport_id);
+               return -EBUSY;
+       }
+
+       if (flags & GB_CONNECTION_FLAG_OFFLOADED &&
+                       flags & GB_CONNECTION_FLAG_CDSI1) {
+               if (es2->cdsi1_in_use) {
+                       dev_err(&hd->dev, "CDSI1 already in use\n");
+                       return -EBUSY;
+               }
+
+               es2->cdsi1_in_use = true;
+
+               return ES2_CPORT_CDSI1;
+       }
+
+       if (cport_id < 0) {
+               ida_start = 0;
+               ida_end = hd->num_cports;
+       } else if (cport_id < hd->num_cports) {
+               ida_start = cport_id;
+               ida_end = cport_id + 1;
+       } else {
+               dev_err(&hd->dev, "cport %d not available\n", cport_id);
+               return -EINVAL;
+       }
+
+       return ida_simple_get(id_map, ida_start, ida_end, GFP_KERNEL);
+}
+
+static void es2_cport_release(struct gb_host_device *hd, u16 cport_id)
+{
+       struct es2_ap_dev *es2 = hd_to_es2(hd);
+
+       switch (cport_id) {
+       case ES2_CPORT_CDSI1:
+               es2->cdsi1_in_use = false;
+               return;
+       }
+
+       ida_simple_remove(&hd->cport_id_map, cport_id);
+}
+
 static int cport_enable(struct gb_host_device *hd, u16 cport_id)
 {
        int retval;
 
-       if (cport_id != GB_SVC_CPORT_ID) {
-               retval = cport_reset(hd, cport_id);
-               if (retval)
-                       return retval;
-       }
+       retval = cport_reset(hd, cport_id);
+       if (retval)
+               return retval;
 
        return 0;
 }
@@ -534,11 +622,6 @@ static int latency_tag_enable(struct gb_host_device *hd, u16 cport_id)
        struct es2_ap_dev *es2 = hd_to_es2(hd);
        struct usb_device *udev = es2->usb_dev;
 
-       if (!cport_id_valid(hd, cport_id)) {
-               dev_err(&udev->dev, "invalid cport %u\n", cport_id);
-               return -EINVAL;
-       }
-
        retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
                                 GB_APB_REQUEST_LATENCY_TAG_EN,
                                 USB_DIR_OUT | USB_TYPE_VENDOR |
@@ -557,11 +640,6 @@ static int latency_tag_disable(struct gb_host_device *hd, u16 cport_id)
        struct es2_ap_dev *es2 = hd_to_es2(hd);
        struct usb_device *udev = es2->usb_dev;
 
-       if (!cport_id_valid(hd, cport_id)) {
-               dev_err(&udev->dev, "invalid cport %u\n", cport_id);
-               return -EINVAL;
-       }
-
        retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
                                 GB_APB_REQUEST_LATENCY_TAG_DIS,
                                 USB_DIR_OUT | USB_TYPE_VENDOR |
@@ -609,16 +687,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;
+       __le64 *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_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 */
@@ -690,19 +878,6 @@ static void es2_destroy(struct es2_ap_dev *es2)
        usb_put_dev(udev);
 }
 
-static void ap_disconnect(struct usb_interface *interface)
-{
-       struct es2_ap_dev *es2 = usb_get_intfdata(interface);
-       int i;
-
-       gb_hd_del(es2->hd);
-
-       for (i = 0; i < NUM_BULKS; ++i)
-               es2_cport_in_disable(es2, &es2->cport_in[i]);
-
-       es2_destroy(es2);
-}
-
 static void cport_in_callback(struct urb *urb)
 {
        struct gb_host_device *hd = urb->context;
@@ -715,6 +890,11 @@ static void cport_in_callback(struct urb *urb)
        if (status) {
                if ((status == -EAGAIN) || (status == -EPROTO))
                        goto exit;
+
+               /* The urb is being unlinked */
+               if (status == -ENOENT || status == -ESHUTDOWN)
+                       return;
+
                dev_err(dev, "urb cport in error %d (dropped)\n", status);
                return;
        }
@@ -946,10 +1126,9 @@ static int ap_probe(struct usb_interface *interface,
        struct usb_endpoint_descriptor *endpoint;
        int bulk_in = 0;
        int bulk_out = 0;
-       int retval = -ENOMEM;
+       int retval;
        int i;
        int num_cports;
-       int cport_id;
 
        udev = usb_get_dev(interface_to_usbdev(interface));
 
@@ -968,14 +1147,6 @@ static int ap_probe(struct usb_interface *interface,
                return PTR_ERR(hd);
        }
 
-       /*
-        * CPorts 16 and 17 are reserved for CDSI0 and CDSI1, make sure they
-        * won't be allocated dynamically.
-        */
-       do {
-               cport_id = ida_simple_get(&hd->cport_id_map, 16, 18, GFP_KERNEL);
-       } while (cport_id > 0);
-
        es2 = hd_to_es2(hd);
        es2->hd = hd;
        es2->usb_intf = interface;
@@ -984,6 +1155,17 @@ static int ap_probe(struct usb_interface *interface,
        INIT_KFIFO(es2->apb_log_fifo);
        usb_set_intfdata(interface, es2);
 
+       /*
+        * Reserve the CDSI0 and CDSI1 CPorts so they won't be allocated
+        * dynamically.
+        */
+       retval = gb_hd_cport_reserve(hd, ES2_CPORT_CDSI0);
+       if (retval)
+               goto error;
+       retval = gb_hd_cport_reserve(hd, ES2_CPORT_CDSI1);
+       if (retval)
+               goto error;
+
        es2->cport_to_ep = kcalloc(hd->num_cports, sizeof(*es2->cport_to_ep),
                                   GFP_KERNEL);
        if (!es2->cport_to_ep) {
@@ -1010,6 +1192,7 @@ static int ap_probe(struct usb_interface *interface,
        }
        if (bulk_in != NUM_BULKS || bulk_out != NUM_BULKS) {
                dev_err(&udev->dev, "Not enough endpoints found in device, aborting!\n");
+               retval = -ENODEV;
                goto error;
        }
 
@@ -1022,11 +1205,15 @@ static int ap_probe(struct usb_interface *interface,
                        u8 *buffer;
 
                        urb = usb_alloc_urb(0, GFP_KERNEL);
-                       if (!urb)
+                       if (!urb) {
+                               retval = -ENOMEM;
                                goto error;
+                       }
                        buffer = kmalloc(ES2_GBUF_MSG_SIZE_MAX, GFP_KERNEL);
-                       if (!buffer)
+                       if (!buffer) {
+                               retval = -ENOMEM;
                                goto error;
+                       }
 
                        usb_fill_bulk_urb(urb, udev,
                                          usb_rcvbulkpipe(udev,
@@ -1043,8 +1230,10 @@ static int ap_probe(struct usb_interface *interface,
                struct urb *urb;
 
                urb = usb_alloc_urb(0, GFP_KERNEL);
-               if (!urb)
+               if (!urb) {
+                       retval = -ENOMEM;
                        goto error;
+               }
 
                es2->cport_out_urb[i] = urb;
                es2->cport_out_urb_busy[i] = false;     /* just to be anal */
@@ -1078,6 +1267,19 @@ error:
        return retval;
 }
 
+static void ap_disconnect(struct usb_interface *interface)
+{
+       struct es2_ap_dev *es2 = usb_get_intfdata(interface);
+       int i;
+
+       gb_hd_del(es2->hd);
+
+       for (i = 0; i < NUM_BULKS; ++i)
+               es2_cport_in_disable(es2, &es2->cport_in[i]);
+
+       es2_destroy(es2);
+}
+
 static struct usb_driver es2_ap_driver = {
        .name =         "es2_ap_driver",
        .probe =        ap_probe,