greybus: es2: implement the fct flow control requests
[linux-2.6-microblaze.git] / drivers / staging / greybus / es2.c
index 7b07ab7..e920563 100644 (file)
@@ -13,7 +13,6 @@
 #include <linux/debugfs.h>
 #include <asm/unaligned.h>
 
-#include "es2.h"
 #include "greybus.h"
 #include "kernel_ver.h"
 #include "connection.h"
@@ -45,25 +44,6 @@ MODULE_DEVICE_TABLE(usb, id_table);
  */
 #define NUM_CPORT_OUT_URB      (8 * NUM_BULKS)
 
-/* vendor request APB1 log */
-#define REQUEST_LOG            0x02
-
-/* vendor request to map a cport to bulk in and bulk out endpoints */
-#define REQUEST_EP_MAPPING     0x03
-
-/* vendor request to get the number of cports available */
-#define REQUEST_CPORT_COUNT    0x04
-
-/* vendor request to reset a cport state */
-#define REQUEST_RESET_CPORT    0x05
-
-/* vendor request to time the latency of messages on a given cport */
-#define REQUEST_LATENCY_TAG_EN 0x06
-#define REQUEST_LATENCY_TAG_DIS        0x07
-
-/* vendor request to control the CSI transmitter */
-#define REQUEST_CSI_TX_CONTROL 0x08
-
 /*
  * @endpoint: bulk in endpoint for CPort data
  * @urb: array of urbs for the CPort in messages
@@ -134,14 +114,6 @@ struct cport_to_ep {
        __u8 endpoint_out;
 };
 
-struct es2_ap_csi_config_request {
-       u8 csi_id;
-       u8 clock_mode;
-       u8 num_lanes;
-       u8 padding;
-       __le32 bus_freq;
-} __attribute__((__packed__));
-
 static inline struct es2_ap_dev *hd_to_es2(struct gb_host_device *hd)
 {
        return (struct es2_ap_dev *)&hd->hd_priv;
@@ -200,7 +172,7 @@ static int map_cport_to_ep(struct es2_ap_dev *es2,
 
        retval = usb_control_msg(es2->usb_dev,
                                 usb_sndctrlpipe(es2->usb_dev, 0),
-                                REQUEST_EP_MAPPING,
+                                GB_APB_REQUEST_EP_MAPPING,
                                 USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
                                 0x00, 0x00,
                                 (char *)cport_to_ep,
@@ -220,38 +192,87 @@ static int unmap_cport(struct es2_ap_dev *es2, u16 cport_id)
 }
 #endif
 
-int es2_ap_csi_setup(struct gb_host_device *hd, bool start,
-                    struct es2_ap_csi_config *cfg)
+static int output_sync(struct es2_ap_dev *es2, void *req, u16 size, u8 cmd)
 {
-       struct es2_ap_csi_config_request *cfg_req;
-       struct es2_ap_dev *es2 = hd_to_es2(hd);
        struct usb_device *udev = es2->usb_dev;
+       u8 *data;
        int retval;
 
-       cfg_req = kzalloc(sizeof(*cfg_req), GFP_KERNEL);
-       if (!cfg_req)
+       data = kmalloc(size, GFP_KERNEL);
+       if (!data)
                return -ENOMEM;
-
-       cfg_req->csi_id = cfg->csi_id;
-
-       if (start) {
-               cfg_req->clock_mode = cfg->clock_mode;
-               cfg_req->num_lanes = cfg->num_lanes;
-               cfg_req->bus_freq = cpu_to_le32(cfg->bus_freq);
-       }
+       memcpy(data, req, size);
 
        retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
-                                REQUEST_CSI_TX_CONTROL,
+                                cmd,
                                 USB_DIR_OUT | USB_TYPE_VENDOR |
-                                USB_RECIP_INTERFACE, 0, 0, cfg_req,
-                                sizeof(*cfg_req), ES2_TIMEOUT);
+                                USB_RECIP_INTERFACE,
+                                0, 0, data, size, ES2_TIMEOUT);
        if (retval < 0)
-               dev_err(&udev->dev, "failed to setup csi: %d\n", retval);
+               dev_err(&udev->dev, "%s: return error %d\n", __func__, retval);
+       else
+               retval = 0;
 
-       kfree(cfg_req);
+       kfree(data);
+       return retval;
+}
+
+static void ap_urb_complete(struct urb *urb)
+{
+       struct usb_ctrlrequest *dr = urb->context;
+
+       kfree(dr);
+       usb_free_urb(urb);
+}
+
+static int output_async(struct es2_ap_dev *es2, void *req, u16 size, u8 cmd)
+{
+       struct usb_device *udev = es2->usb_dev;
+       struct urb *urb;
+       struct usb_ctrlrequest *dr;
+       u8 *buf;
+       int retval;
+
+       urb = usb_alloc_urb(0, GFP_ATOMIC);
+       if (!urb)
+               return -ENOMEM;
+
+       dr = kmalloc(sizeof(*dr) + size, GFP_ATOMIC);
+       if (!dr) {
+               usb_free_urb(urb);
+               return -ENOMEM;
+       }
+
+       buf = (u8 *)dr + sizeof(*dr);
+       memcpy(buf, req, size);
+
+       dr->bRequest = cmd;
+       dr->bRequestType = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE;
+       dr->wValue = 0;
+       dr->wIndex = 0;
+       dr->wLength = cpu_to_le16(size);
+
+       usb_fill_control_urb(urb, udev, usb_sndctrlpipe(udev, 0),
+                            (unsigned char *)dr, buf, size,
+                            ap_urb_complete, dr);
+       retval = usb_submit_urb(urb, GFP_ATOMIC);
+       if (retval) {
+               usb_free_urb(urb);
+               kfree(dr);
+       }
        return retval;
 }
-EXPORT_SYMBOL_GPL(es2_ap_csi_setup);
+
+static int output(struct gb_host_device *hd, void *req, u16 size, u8 cmd,
+                    bool async)
+{
+       struct es2_ap_dev *es2 = hd_to_es2(hd);
+
+       if (async)
+               return output_async(es2, req, size, cmd);
+
+       return output_sync(es2, req, size, cmd);
+}
 
 static int es2_cport_in_enable(struct es2_ap_dev *es2,
                                struct es2_cport_in *cport_in)
@@ -481,7 +502,7 @@ static int cport_reset(struct gb_host_device *hd, u16 cport_id)
        int retval;
 
        retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
-                                REQUEST_RESET_CPORT,
+                                GB_APB_REQUEST_RESET_CPORT,
                                 USB_DIR_OUT | USB_TYPE_VENDOR |
                                 USB_RECIP_INTERFACE, cport_id, 0,
                                 NULL, 0, ES2_TIMEOUT);
@@ -519,7 +540,7 @@ static int latency_tag_enable(struct gb_host_device *hd, u16 cport_id)
        }
 
        retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
-                                REQUEST_LATENCY_TAG_EN,
+                                GB_APB_REQUEST_LATENCY_TAG_EN,
                                 USB_DIR_OUT | USB_TYPE_VENDOR |
                                 USB_RECIP_INTERFACE, cport_id, 0, NULL,
                                 0, ES2_TIMEOUT);
@@ -542,7 +563,7 @@ static int latency_tag_disable(struct gb_host_device *hd, u16 cport_id)
        }
 
        retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
-                                REQUEST_LATENCY_TAG_DIS,
+                                GB_APB_REQUEST_LATENCY_TAG_DIS,
                                 USB_DIR_OUT | USB_TYPE_VENDOR |
                                 USB_RECIP_INTERFACE, cport_id, 0, NULL,
                                 0, ES2_TIMEOUT);
@@ -553,6 +574,41 @@ static int latency_tag_disable(struct gb_host_device *hd, u16 cport_id)
        return retval;
 }
 
+static int fct_flow_enable(struct gb_host_device *hd, u16 cport_id)
+{
+       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),
+                                GB_APB_REQUEST_FCT_FLOW_EN,
+                                USB_DIR_OUT | USB_TYPE_VENDOR |
+                                USB_RECIP_INTERFACE, cport_id, 0, NULL,
+                                0, ES2_TIMEOUT);
+       if (retval < 0)
+               dev_err(&udev->dev, "Cannot enable FCT flow for cport %u: %d\n",
+                       cport_id, retval);
+       return retval;
+}
+
+static int fct_flow_disable(struct gb_host_device *hd, u16 cport_id)
+{
+       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),
+                                GB_APB_REQUEST_FCT_FLOW_DIS,
+                                USB_DIR_OUT | USB_TYPE_VENDOR |
+                                USB_RECIP_INTERFACE, cport_id, 0, NULL,
+                                0, ES2_TIMEOUT);
+       if (retval < 0)
+               dev_err(&udev->dev,
+                       "Cannot disable FCT flow for cport %u: %d\n",
+                       cport_id, retval);
+       return retval;
+}
+
 static struct gb_hd_driver es2_driver = {
        .hd_priv_size           = sizeof(struct es2_ap_dev),
        .message_send           = message_send,
@@ -560,6 +616,9 @@ static struct gb_hd_driver es2_driver = {
        .cport_enable           = cport_enable,
        .latency_tag_enable     = latency_tag_enable,
        .latency_tag_disable    = latency_tag_disable,
+       .output                 = output,
+       .fct_flow_enable        = fct_flow_enable,
+       .fct_flow_disable       = fct_flow_disable,
 };
 
 /* Common function to report consistent warnings based on URB status */
@@ -715,7 +774,7 @@ static void apb_log_get(struct es2_ap_dev *es2, char *buf)
        do {
                retval = usb_control_msg(es2->usb_dev,
                                        usb_rcvctrlpipe(es2->usb_dev, 0),
-                                       REQUEST_LOG,
+                                       GB_APB_REQUEST_LOG,
                                        USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
                                        0x00, 0x00,
                                        buf,
@@ -839,18 +898,22 @@ static int apb_get_cport_count(struct usb_device *udev)
        int retval;
        __le16 *cport_count;
 
-       cport_count = kmalloc(sizeof(*cport_count), GFP_KERNEL);
+       cport_count = kzalloc(sizeof(*cport_count), GFP_KERNEL);
        if (!cport_count)
                return -ENOMEM;
 
        retval = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
-                                REQUEST_CPORT_COUNT,
+                                GB_APB_REQUEST_CPORT_COUNT,
                                 USB_DIR_IN | USB_TYPE_VENDOR |
                                 USB_RECIP_INTERFACE, 0, 0, cport_count,
                                 sizeof(*cport_count), ES2_TIMEOUT);
-       if (retval < 0) {
+       if (retval != sizeof(*cport_count)) {
                dev_err(&udev->dev, "Cannot retrieve CPort count: %d\n",
                        retval);
+
+               if (retval >= 0)
+                       retval = -EIO;
+
                goto out;
        }