diff --git a/drivers/gpu/drm/i915/intel_guc_slpc.c b/drivers/gpu/drm/i915/intel_guc_slpc.c
index 974a3c0..bc2c717 100644
--- a/drivers/gpu/drm/i915/intel_guc_slpc.c
+++ b/drivers/gpu/drm/i915/intel_guc_slpc.c
@@ -163,6 +163,211 @@ static void slpc_shared_data_init(struct intel_guc_slpc *slpc)
 	kunmap_atomic(data);
 }
 
+static const char *slpc_status_stringify(int status)
+{
+	const char *str = NULL;
+
+	switch(status) {
+	case SLPC_STATUS_OK:
+		str = "Ok";
+		break;
+	case SLPC_STATUS_ERROR:
+		str = "Error";
+		break;
+	case SLPC_STATUS_ILLEGAL_COMMAND:
+		str = "Illegal command";
+		break;
+	case SLPC_STATUS_INVALID_ARGS:
+		str = "Invalid args";
+		break;
+	case SLPC_STATUS_INVALID_PARAMS:
+		str = "Invalid params";
+		break;
+	case SLPC_STATUS_INVALID_DATA:
+		str = "Invalid data";
+		break;
+	case SLPC_STATUS_OUT_OF_RANGE:
+		str = "Out of range";
+		break;
+	case SLPC_STATUS_NOT_SUPPORTED:
+		str = "Not supported";
+		break;
+	case SLPC_STATUS_NOT_IMPLEMENTED:
+		str = "Not implemented";
+		break;
+	case SLPC_STATUS_NO_DATA:
+		str = "No data";
+		break;
+	case SLPC_STATUS_EVENT_NOT_REGISTERED:
+		str = "Event not registered";
+		break;
+	case SLPC_STATUS_REGISTER_LOCKED:
+		str = "Register locked";
+		break;
+	case SLPC_STATUS_TEMPORARILY_UNAVAILABLE:
+		str = "Temporarily unavailable";
+		break;
+	case SLPC_STATUS_VALUE_ALREADY_SET:
+		str = "Value already set";
+		break;
+	case SLPC_STATUS_VALUE_ALREADY_UNSET:
+		str = "Value already unset";
+		break;
+	case SLPC_STATUS_VALUE_NOT_CHANGED:
+		str = "Value not changed";
+		break;
+	case SLPC_STATUS_MEMIO_ERROR:
+		str = "MMIO error";
+		break;
+	case SLPC_STATUS_EVENT_QUEUED_REQ_DPC:
+		str = "Event queued, DPC requested";
+		break;
+	case SLPC_STATUS_EVENT_QUEUED_NOREQ_DPC:
+		str = "Event queued, DPC not requested";
+		break;
+	case SLPC_STATUS_NO_EVENT_QUEUED:
+		str = "Event not queued";
+		break;
+	case SLPC_STATUS_OUT_OF_SPACE:
+		str = "Out of space";
+		break;
+	case SLPC_STATUS_TIMEOUT:
+		str = "Timeout";
+		break;
+	case SLPC_STATUS_NO_LOCK:
+		str = "No lock";
+		break;
+	}
+
+	return str;
+}
+
+static void slpc_send(struct intel_guc_slpc *slpc,
+		      struct slpc_event_input *input, u32 in_len)
+{
+	struct intel_guc *guc = slpc_to_guc(slpc);
+	struct slpc_event_output output;
+	u32 out_len = 1;
+	u32 *action;
+	int ret;
+
+	action = (u32 *)input;
+	action[0] = INTEL_GUC_ACTION_SLPC_REQUEST;
+
+	ret = intel_guc_send_and_receive(guc, action, in_len,
+					 (u32 *)&output.header, out_len);
+
+	/*
+	 * Currently output data from Host to GuC SLPC actions is populated
+	 * in scratch registers SOFT_SCRATCH(1) to SOFT_SCRATCH(14) based
+	 * on event. Currently only SLPC action status in GuC is meaningful
+	 * as Host can query only overridden parameters and that are fetched
+	 * from Host-GuC SLPC shared data. Other parameters can be read through
+	 * output data but that is available only for debug GuC firmwares.
+	 */
+	if (!ret) {
+		ret = output.header.status;
+		if (ret) {
+			GEM_BUG_ON(ret >= SLPC_STATUS_MAX);
+			DRM_ERROR("event 0x%x status %s\n",
+				  ((output.header.value & 0xFF00) >> 8),
+				  slpc_status_stringify(ret));
+		}
+	}
+}
+
+static void host2guc_slpc_reset(struct intel_guc_slpc *slpc)
+{
+	struct intel_guc *guc = slpc_to_guc(slpc);
+	u32 shared_data_gtt_offset = intel_guc_ggtt_offset(guc, slpc->vma);
+	struct slpc_event_input data = {0};
+
+	data.header.value = SLPC_EVENT(SLPC_EVENT_RESET, 2);
+	data.args[0] = shared_data_gtt_offset;
+	data.args[1] = 0;
+
+	slpc_send(slpc, &data, 4);
+}
+
+static void host2guc_slpc_query_task_state(struct intel_guc_slpc *slpc)
+{
+	struct intel_guc *guc = slpc_to_guc(slpc);
+	u32 shared_data_gtt_offset = intel_guc_ggtt_offset(guc, slpc->vma);
+	struct slpc_event_input data = {0};
+
+	data.header.value = SLPC_EVENT(SLPC_EVENT_QUERY_TASK_STATE, 2);
+	data.args[0] = shared_data_gtt_offset;
+	data.args[1] = 0;
+
+	slpc_send(slpc, &data, 4);
+}
+
+static void slpc_read_shared_data(struct intel_guc_slpc *slpc,
+				  struct slpc_shared_data *data)
+{
+	struct page *page;
+	void *pv = NULL;
+
+	host2guc_slpc_query_task_state(slpc);
+
+	page = i915_vma_first_page(slpc->vma);
+	pv = kmap_atomic(page);
+
+	drm_clflush_virt_range(pv, sizeof(struct slpc_shared_data));
+	memcpy(data, pv, sizeof(struct slpc_shared_data));
+
+	kunmap_atomic(pv);
+}
+
+static const char *slpc_state_stringify(enum slpc_global_state state)
+{
+	const char *str = NULL;
+
+	switch (state) {
+	case SLPC_GLOBAL_STATE_NOT_RUNNING:
+		str = "not running";
+		break;
+	case SLPC_GLOBAL_STATE_INITIALIZING:
+		str = "initializing";
+		break;
+	case SLPC_GLOBAL_STATE_RESETTING:
+		str = "resetting";
+		break;
+	case SLPC_GLOBAL_STATE_RUNNING:
+		str = "running";
+		break;
+	case SLPC_GLOBAL_STATE_SHUTTING_DOWN:
+		str = "shutting down";
+		break;
+	case SLPC_GLOBAL_STATE_ERROR:
+		str = "error";
+		break;
+	default:
+		str = "unknown";
+		break;
+	}
+
+	return str;
+}
+
+static const char *slpc_get_state(struct intel_guc_slpc *slpc)
+{
+	struct slpc_shared_data data;
+
+	slpc_read_shared_data(slpc, &data);
+
+	return slpc_state_stringify(data.global_state);
+}
+
+static bool slpc_running(struct intel_guc_slpc *slpc)
+{
+	struct slpc_shared_data data;
+
+	slpc_read_shared_data(slpc, &data);
+
+	return (data.global_state == SLPC_GLOBAL_STATE_RUNNING);
+}
+
 /**
  * intel_guc_slpc_init() - Initialize the SLPC shared data structure.
  * @slpc: pointer to intel_guc_slpc.
@@ -204,8 +409,42 @@ int intel_guc_slpc_init(struct intel_guc_slpc *slpc)
 	return 0;
 }
 
+/**
+ * intel_guc_slpc_enable() - Start SLPC.
+ * @slpc: pointer to intel_guc_slpc.
+ *
+ * This function will start GuC SLPC by sending Host to GuC action.
+ * SLPC shared data has to be initialized prior to this by
+ * intel_guc_slpc_init().
+ *
+ * Return: 0 on success, non-zero error code on failure.
+ */
 int intel_guc_slpc_enable(struct intel_guc_slpc *slpc)
 {
+	struct slpc_shared_data *data;
+	struct page *page;
+
+	mutex_lock(&slpc->lock);
+
+	page = i915_vma_first_page(slpc->vma);
+	data = kmap_atomic(page);
+	data->global_state = SLPC_GLOBAL_STATE_NOT_RUNNING;
+	kunmap_atomic(data);
+
+	host2guc_slpc_reset(slpc);
+
+	/* Check whether SLPC is running */
+	if (wait_for(slpc_running(slpc), 5)) {
+		DRM_ERROR("SLPC not enabled! State = %s\n",
+			  slpc_get_state(slpc));
+		mutex_unlock(&slpc->lock);
+		return -EIO;
+	}
+
+	DRM_INFO("SLPC state: %s\n", slpc_get_state(slpc));
+
+	mutex_unlock(&slpc->lock);
+
 	return 0;
 }
 
