These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / drivers / misc / mei / hbm.c
index 58da925..e7b7aad 100644 (file)
@@ -52,6 +52,7 @@ static const char *mei_cl_conn_status_str(enum mei_cl_connect_status status)
        MEI_CL_CS(ALREADY_STARTED);
        MEI_CL_CS(OUT_OF_RESOURCES);
        MEI_CL_CS(MESSAGE_SMALL);
+       MEI_CL_CS(NOT_ALLOWED);
        default: return "unknown";
        }
 #undef MEI_CL_CCS
@@ -89,6 +90,7 @@ static int mei_cl_conn_status_to_errno(enum mei_cl_connect_status status)
        case MEI_CL_CONN_ALREADY_STARTED:  return -EBUSY;
        case MEI_CL_CONN_OUT_OF_RESOURCES: return -EBUSY;
        case MEI_CL_CONN_MESSAGE_SMALL:    return -EINVAL;
+       case MEI_CL_CONN_NOT_ALLOWED:      return -EBUSY;
        default:                           return -EINVAL;
        }
 }
@@ -150,8 +152,8 @@ void mei_hbm_cl_hdr(struct mei_cl *cl, u8 hbm_cmd, void *buf, size_t len)
        memset(cmd, 0, len);
 
        cmd->hbm_cmd = hbm_cmd;
-       cmd->host_addr = cl->host_client_id;
-       cmd->me_addr = cl->me_client_id;
+       cmd->host_addr = mei_cl_host_addr(cl);
+       cmd->me_addr = mei_cl_me_id(cl);
 }
 
 /**
@@ -188,8 +190,8 @@ int mei_hbm_cl_write(struct mei_device *dev,
 static inline
 bool mei_hbm_cl_addr_equal(struct mei_cl *cl, struct mei_hbm_cl_cmd *cmd)
 {
-       return cl->host_client_id == cmd->host_addr &&
-               cl->me_client_id == cmd->me_addr;
+       return  mei_cl_host_addr(cl) == cmd->host_addr &&
+               mei_cl_me_id(cl) == cmd->me_addr;
 }
 
 /**
@@ -279,7 +281,7 @@ int mei_hbm_start_req(struct mei_device *dev)
        return 0;
 }
 
-/*
+/**
  * mei_hbm_enum_clients_req - sends enumeration client request message.
  *
  * @dev: the device structure
@@ -299,6 +301,7 @@ static int mei_hbm_enum_clients_req(struct mei_device *dev)
        enum_req = (struct hbm_host_enum_request *)dev->wr_msg.data;
        memset(enum_req, 0, len);
        enum_req->hbm_cmd = HOST_ENUM_REQ_CMD;
+       enum_req->allow_add = dev->hbm_f_dc_supported;
 
        ret = mei_write_message(dev, mei_hdr, dev->wr_msg.data);
        if (ret) {
@@ -311,7 +314,7 @@ static int mei_hbm_enum_clients_req(struct mei_device *dev)
        return 0;
 }
 
-/*
+/**
  * mei_hbm_me_cl_add - add new me client to the list
  *
  * @dev: the device structure
@@ -343,6 +346,180 @@ static int mei_hbm_me_cl_add(struct mei_device *dev,
        return 0;
 }
 
+/**
+ * mei_hbm_add_cl_resp - send response to fw on client add request
+ *
+ * @dev: the device structure
+ * @addr: me address
+ * @status: response status
+ *
+ * Return: 0 on success and < 0 on failure
+ */
+static int mei_hbm_add_cl_resp(struct mei_device *dev, u8 addr, u8 status)
+{
+       struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr;
+       struct hbm_add_client_response *resp;
+       const size_t len = sizeof(struct hbm_add_client_response);
+       int ret;
+
+       dev_dbg(dev->dev, "adding client response\n");
+
+       resp = (struct hbm_add_client_response *)dev->wr_msg.data;
+
+       mei_hbm_hdr(mei_hdr, len);
+       memset(resp, 0, sizeof(struct hbm_add_client_response));
+
+       resp->hbm_cmd = MEI_HBM_ADD_CLIENT_RES_CMD;
+       resp->me_addr = addr;
+       resp->status  = status;
+
+       ret = mei_write_message(dev, mei_hdr, dev->wr_msg.data);
+       if (ret)
+               dev_err(dev->dev, "add client response write failed: ret = %d\n",
+                       ret);
+       return ret;
+}
+
+/**
+ * mei_hbm_fw_add_cl_req - request from the fw to add a client
+ *
+ * @dev: the device structure
+ * @req: add client request
+ *
+ * Return: 0 on success and < 0 on failure
+ */
+static int mei_hbm_fw_add_cl_req(struct mei_device *dev,
+                             struct hbm_add_client_request *req)
+{
+       int ret;
+       u8 status = MEI_HBMS_SUCCESS;
+
+       BUILD_BUG_ON(sizeof(struct hbm_add_client_request) !=
+                       sizeof(struct hbm_props_response));
+
+       ret = mei_hbm_me_cl_add(dev, (struct hbm_props_response *)req);
+       if (ret)
+               status = !MEI_HBMS_SUCCESS;
+
+       return mei_hbm_add_cl_resp(dev, req->me_addr, status);
+}
+
+/**
+ * mei_hbm_cl_notify_req - send notification request
+ *
+ * @dev: the device structure
+ * @cl: a client to disconnect from
+ * @start: true for start false for stop
+ *
+ * Return: 0 on success and -EIO on write failure
+ */
+int mei_hbm_cl_notify_req(struct mei_device *dev,
+                         struct mei_cl *cl, u8 start)
+{
+
+       struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr;
+       struct hbm_notification_request *req;
+       const size_t len = sizeof(struct hbm_notification_request);
+       int ret;
+
+       mei_hbm_hdr(mei_hdr, len);
+       mei_hbm_cl_hdr(cl, MEI_HBM_NOTIFY_REQ_CMD, dev->wr_msg.data, len);
+
+       req = (struct hbm_notification_request *)dev->wr_msg.data;
+       req->start = start;
+
+       ret = mei_write_message(dev, mei_hdr, dev->wr_msg.data);
+       if (ret)
+               dev_err(dev->dev, "notify request failed: ret = %d\n", ret);
+
+       return ret;
+}
+
+/**
+ *  notify_res_to_fop - convert notification response to the proper
+ *      notification FOP
+ *
+ * @cmd: client notification start response command
+ *
+ * Return:  MEI_FOP_NOTIFY_START or MEI_FOP_NOTIFY_STOP;
+ */
+static inline enum mei_cb_file_ops notify_res_to_fop(struct mei_hbm_cl_cmd *cmd)
+{
+       struct hbm_notification_response *rs =
+               (struct hbm_notification_response *)cmd;
+
+       return mei_cl_notify_req2fop(rs->start);
+}
+
+/**
+ * mei_hbm_cl_notify_start_res - update the client state according
+ *       notify start response
+ *
+ * @dev: the device structure
+ * @cl: mei host client
+ * @cmd: client notification start response command
+ */
+static void mei_hbm_cl_notify_start_res(struct mei_device *dev,
+                                       struct mei_cl *cl,
+                                       struct mei_hbm_cl_cmd *cmd)
+{
+       struct hbm_notification_response *rs =
+               (struct hbm_notification_response *)cmd;
+
+       cl_dbg(dev, cl, "hbm: notify start response status=%d\n", rs->status);
+
+       if (rs->status == MEI_HBMS_SUCCESS ||
+           rs->status == MEI_HBMS_ALREADY_STARTED) {
+               cl->notify_en = true;
+               cl->status = 0;
+       } else {
+               cl->status = -EINVAL;
+       }
+}
+
+/**
+ * mei_hbm_cl_notify_stop_res - update the client state according
+ *       notify stop response
+ *
+ * @dev: the device structure
+ * @cl: mei host client
+ * @cmd: client notification stop response command
+ */
+static void mei_hbm_cl_notify_stop_res(struct mei_device *dev,
+                                      struct mei_cl *cl,
+                                      struct mei_hbm_cl_cmd *cmd)
+{
+       struct hbm_notification_response *rs =
+               (struct hbm_notification_response *)cmd;
+
+       cl_dbg(dev, cl, "hbm: notify stop response status=%d\n", rs->status);
+
+       if (rs->status == MEI_HBMS_SUCCESS ||
+           rs->status == MEI_HBMS_NOT_STARTED) {
+               cl->notify_en = false;
+               cl->status = 0;
+       } else {
+               /* TODO: spec is not clear yet about other possible issues */
+               cl->status = -EINVAL;
+       }
+}
+
+/**
+ * mei_hbm_cl_notify - signal notification event
+ *
+ * @dev: the device structure
+ * @cmd: notification client message
+ */
+static void mei_hbm_cl_notify(struct mei_device *dev,
+                             struct mei_hbm_cl_cmd *cmd)
+{
+       struct mei_cl *cl;
+
+       cl = mei_hbm_cl_find_by_cmd(dev, cmd);
+       if (cl)
+               mei_cl_notify(cl);
+}
+
 /**
  * mei_hbm_prop_req - request property for a single client
  *
@@ -392,7 +569,7 @@ static int mei_hbm_prop_req(struct mei_device *dev)
        return 0;
 }
 
-/*
+/**
  * mei_hbm_pg - sends pg command
  *
  * @dev: the device structure
@@ -572,7 +749,7 @@ static void mei_hbm_cl_disconnect_res(struct mei_device *dev, struct mei_cl *cl,
        cl_dbg(dev, cl, "hbm: disconnect response status=%d\n", rs->status);
 
        if (rs->status == MEI_CL_DISCONN_SUCCESS)
-               cl->state = MEI_FILE_DISCONNECTED;
+               cl->state = MEI_FILE_DISCONNECT_REPLY;
        cl->status = 0;
 }
 
@@ -610,8 +787,11 @@ static void mei_hbm_cl_connect_res(struct mei_device *dev, struct mei_cl *cl,
 
        if (rs->status == MEI_CL_CONN_SUCCESS)
                cl->state = MEI_FILE_CONNECTED;
-       else
-               cl->state = MEI_FILE_DISCONNECTED;
+       else {
+               cl->state = MEI_FILE_DISCONNECT_REPLY;
+               if (rs->status == MEI_CL_CONN_NOT_FOUND)
+                       mei_me_cl_del(dev, cl->me_cl);
+       }
        cl->status = mei_cl_conn_status_to_errno(rs->status);
 }
 
@@ -654,6 +834,12 @@ static void mei_hbm_cl_res(struct mei_device *dev,
        case MEI_FOP_DISCONNECT:
                mei_hbm_cl_disconnect_res(dev, cl, rs);
                break;
+       case MEI_FOP_NOTIFY_START:
+               mei_hbm_cl_notify_start_res(dev, cl, rs);
+               break;
+       case MEI_FOP_NOTIFY_STOP:
+               mei_hbm_cl_notify_stop_res(dev, cl, rs);
+               break;
        default:
                return;
        }
@@ -680,8 +866,8 @@ static int mei_hbm_fw_disconnect_req(struct mei_device *dev,
 
        cl = mei_hbm_cl_find_by_cmd(dev, disconnect_req);
        if (cl) {
-               cl_dbg(dev, cl, "disconnect request received\n");
-               cl->state = MEI_FILE_DISCONNECTED;
+               cl_dbg(dev, cl, "fw disconnect request received\n");
+               cl->state = MEI_FILE_DISCONNECTING;
                cl->timer_count = 0;
 
                cb = mei_io_cb_init(cl, MEI_FOP_DISCONNECT_RSP, NULL);
@@ -693,6 +879,79 @@ static int mei_hbm_fw_disconnect_req(struct mei_device *dev,
        return 0;
 }
 
+/**
+ * mei_hbm_pg_enter_res - PG enter response received
+ *
+ * @dev: the device structure.
+ *
+ * Return: 0 on success, -EPROTO on state mismatch
+ */
+static int mei_hbm_pg_enter_res(struct mei_device *dev)
+{
+       if (mei_pg_state(dev) != MEI_PG_OFF ||
+           dev->pg_event != MEI_PG_EVENT_WAIT) {
+               dev_err(dev->dev, "hbm: pg entry response: state mismatch [%s, %d]\n",
+                       mei_pg_state_str(mei_pg_state(dev)), dev->pg_event);
+               return -EPROTO;
+       }
+
+       dev->pg_event = MEI_PG_EVENT_RECEIVED;
+       wake_up(&dev->wait_pg);
+
+       return 0;
+}
+
+/**
+ * mei_hbm_pg_resume - process with PG resume
+ *
+ * @dev: the device structure.
+ */
+void mei_hbm_pg_resume(struct mei_device *dev)
+{
+       pm_request_resume(dev->dev);
+}
+EXPORT_SYMBOL_GPL(mei_hbm_pg_resume);
+
+/**
+ * mei_hbm_pg_exit_res - PG exit response received
+ *
+ * @dev: the device structure.
+ *
+ * Return: 0 on success, -EPROTO on state mismatch
+ */
+static int mei_hbm_pg_exit_res(struct mei_device *dev)
+{
+       if (mei_pg_state(dev) != MEI_PG_ON ||
+           (dev->pg_event != MEI_PG_EVENT_WAIT &&
+            dev->pg_event != MEI_PG_EVENT_IDLE)) {
+               dev_err(dev->dev, "hbm: pg exit response: state mismatch [%s, %d]\n",
+                       mei_pg_state_str(mei_pg_state(dev)), dev->pg_event);
+               return -EPROTO;
+       }
+
+       switch (dev->pg_event) {
+       case MEI_PG_EVENT_WAIT:
+               dev->pg_event = MEI_PG_EVENT_RECEIVED;
+               wake_up(&dev->wait_pg);
+               break;
+       case MEI_PG_EVENT_IDLE:
+               /*
+               * If the driver is not waiting on this then
+               * this is HW initiated exit from PG.
+               * Start runtime pm resume sequence to exit from PG.
+               */
+               dev->pg_event = MEI_PG_EVENT_RECEIVED;
+               mei_hbm_pg_resume(dev);
+               break;
+       default:
+               WARN(1, "hbm: pg exit response: unexpected pg event = %d\n",
+                    dev->pg_event);
+               return -EPROTO;
+       }
+
+       return 0;
+}
+
 /**
  * mei_hbm_config_features - check what hbm features and commands
  *        are supported by the fw
@@ -709,6 +968,17 @@ static void mei_hbm_config_features(struct mei_device *dev)
        if (dev->version.major_version == HBM_MAJOR_VERSION_PGI &&
            dev->version.minor_version >= HBM_MINOR_VERSION_PGI)
                dev->hbm_f_pg_supported = 1;
+
+       if (dev->version.major_version >= HBM_MAJOR_VERSION_DC)
+               dev->hbm_f_dc_supported = 1;
+
+       /* disconnect on connect timeout instead of link reset */
+       if (dev->version.major_version >= HBM_MAJOR_VERSION_DOT)
+               dev->hbm_f_dot_supported = 1;
+
+       /* Notification Event Support */
+       if (dev->version.major_version >= HBM_MAJOR_VERSION_EV)
+               dev->hbm_f_ev_supported = 1;
 }
 
 /**
@@ -740,6 +1010,8 @@ int mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr)
        struct hbm_host_version_response *version_res;
        struct hbm_props_response *props_res;
        struct hbm_host_enum_response *enum_res;
+       struct hbm_add_client_request *add_cl_req;
+       int ret;
 
        struct mei_hbm_cl_cmd *cl_cmd;
        struct hbm_client_connect_request *disconnect_req;
@@ -828,24 +1100,17 @@ int mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr)
                break;
 
        case MEI_PG_ISOLATION_ENTRY_RES_CMD:
-               dev_dbg(dev->dev, "power gate isolation entry response received\n");
-               dev->pg_event = MEI_PG_EVENT_RECEIVED;
-               if (waitqueue_active(&dev->wait_pg))
-                       wake_up(&dev->wait_pg);
+               dev_dbg(dev->dev, "hbm: power gate isolation entry response received\n");
+               ret = mei_hbm_pg_enter_res(dev);
+               if (ret)
+                       return ret;
                break;
 
        case MEI_PG_ISOLATION_EXIT_REQ_CMD:
-               dev_dbg(dev->dev, "power gate isolation exit request received\n");
-               dev->pg_event = MEI_PG_EVENT_RECEIVED;
-               if (waitqueue_active(&dev->wait_pg))
-                       wake_up(&dev->wait_pg);
-               else
-                       /*
-                       * If the driver is not waiting on this then
-                       * this is HW initiated exit from PG.
-                       * Start runtime pm resume sequence to exit from PG.
-                       */
-                       pm_request_resume(dev->dev);
+               dev_dbg(dev->dev, "hbm: power gate isolation exit request received\n");
+               ret = mei_hbm_pg_exit_res(dev);
+               if (ret)
+                       return ret;
                break;
 
        case HOST_CLIENT_PROPERTIES_RES_CMD:
@@ -937,6 +1202,39 @@ int mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr)
                        return -EIO;
                }
                break;
+
+       case MEI_HBM_ADD_CLIENT_REQ_CMD:
+               dev_dbg(dev->dev, "hbm: add client request received\n");
+               /*
+                * after the host receives the enum_resp
+                * message clients may be added or removed
+                */
+               if (dev->hbm_state <= MEI_HBM_ENUM_CLIENTS ||
+                   dev->hbm_state >= MEI_HBM_STOPPED) {
+                       dev_err(dev->dev, "hbm: add client: state mismatch, [%d, %d]\n",
+                               dev->dev_state, dev->hbm_state);
+                       return -EPROTO;
+               }
+               add_cl_req = (struct hbm_add_client_request *)mei_msg;
+               ret = mei_hbm_fw_add_cl_req(dev, add_cl_req);
+               if (ret) {
+                       dev_err(dev->dev, "hbm: add client: failed to send response %d\n",
+                               ret);
+                       return -EIO;
+               }
+               dev_dbg(dev->dev, "hbm: add client request processed\n");
+               break;
+
+       case MEI_HBM_NOTIFY_RES_CMD:
+               dev_dbg(dev->dev, "hbm: notify response received\n");
+               mei_hbm_cl_res(dev, cl_cmd, notify_res_to_fop(cl_cmd));
+               break;
+
+       case MEI_HBM_NOTIFICATION_CMD:
+               dev_dbg(dev->dev, "hbm: notification\n");
+               mei_hbm_cl_notify(dev, cl_cmd);
+               break;
+
        default:
                BUG();
                break;