summaryrefslogtreecommitdiff
path: root/chip/ish/system_state_subsys.c
diff options
context:
space:
mode:
Diffstat (limited to 'chip/ish/system_state_subsys.c')
-rw-r--r--chip/ish/system_state_subsys.c149
1 files changed, 149 insertions, 0 deletions
diff --git a/chip/ish/system_state_subsys.c b/chip/ish/system_state_subsys.c
new file mode 100644
index 0000000000..bb5b62dce3
--- /dev/null
+++ b/chip/ish/system_state_subsys.c
@@ -0,0 +1,149 @@
+/* Copyright 2018 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "heci_client.h"
+#include "system_state.h"
+#include "console.h"
+
+#ifdef SS_SUBSYSTEM_DEBUG
+#define CPUTS(outstr) cputs(CC_LPC, outstr)
+#define CPRINTS(format, args...) cprints(CC_LPC, format, ## args)
+#define CPRINTF(format, args...) cprintf(CC_LPC, format, ## args)
+#else
+#define CPUTS(outstr)
+#define CPRINTS(format, args...)
+#define CPRINTF(format, args...)
+#endif
+
+
+/* the following "define"s and structures are from host driver
+ * and they are slightly modified for look&feel purpose.
+ */
+#define SYSTEM_STATE_SUBSCRIBE 0x1
+#define SYSTEM_STATE_STATUS 0x2
+#define SYSTEM_STATE_QUERY_SUBSCRIBERS 0x3
+#define SYSTEM_STATE_STATE_CHANGE_REQ 0x4
+
+#define SUSPEND_STATE_BIT (1<<1) /* suspend/resume */
+
+struct ss_header {
+ uint32_t cmd;
+ uint32_t cmd_status;
+} __packed;
+
+struct ss_query_subscribers {
+ struct ss_header hdr;
+} __packed;
+
+struct ss_subscribe {
+ struct ss_header hdr;
+ uint32_t states;
+} __packed;
+
+struct ss_status {
+ struct ss_header hdr;
+ uint32_t supported_states;
+ uint32_t states_status;
+} __packed;
+
+/* change request from device but host doesn't support it */
+struct ss_state_change_req {
+ struct ss_header hdr;
+ uint32_t requested_states;
+ uint32_t states_status;
+} __packed;
+
+/*
+ * TODO: For now, every HECI client with valid .suspend or .resume callback is
+ * automatically registered as client of system state subsystem.
+ * so MAX_SS_CLIENTS should be HECI_MAX_NUM_OF_CLIENTS.
+ * if an object wants to get system state event then it can embeds
+ * "struct ss_subsys_device" in it and calls ss_subsys_register_client() like
+ * HECI client.
+ */
+#define MAX_SS_CLIENTS HECI_MAX_NUM_OF_CLIENTS
+
+struct ss_subsystem_context {
+ uint32_t registered_state;
+
+ int num_of_ss_client;
+ struct ss_subsys_device *clients[MAX_SS_CLIENTS];
+};
+
+static struct ss_subsystem_context ss_subsys_ctx;
+
+int ss_subsys_register_client(struct ss_subsys_device *ss_device)
+{
+ int handle;
+
+ if (ss_subsys_ctx.num_of_ss_client == MAX_SS_CLIENTS)
+ return -1;
+
+ if (ss_device->cbs->resume || ss_device->cbs->suspend) {
+ handle = ss_subsys_ctx.num_of_ss_client++;
+ ss_subsys_ctx.registered_state |= SUSPEND_STATE_BIT;
+ ss_subsys_ctx.clients[handle] = ss_device;
+ } else {
+ return -1;
+ }
+
+ return handle;
+}
+
+static int ss_subsys_suspend(void)
+{
+ int i;
+
+ for (i = ss_subsys_ctx.num_of_ss_client - 1; i >= 0; i--) {
+ if (ss_subsys_ctx.clients[i]->cbs->suspend)
+ ss_subsys_ctx.clients[i]->cbs->suspend(
+ ss_subsys_ctx.clients[i]);
+ }
+
+ return EC_SUCCESS;
+}
+
+static int ss_subsys_resume(void)
+{
+ int i;
+
+ for (i = 0; i < ss_subsys_ctx.num_of_ss_client; i++) {
+ if (ss_subsys_ctx.clients[i]->cbs->resume)
+ ss_subsys_ctx.clients[i]->cbs->resume(
+ ss_subsys_ctx.clients[i]);
+ }
+
+ return EC_SUCCESS;
+}
+
+void heci_handle_system_state_msg(uint8_t *msg, const size_t length)
+{
+ struct ss_header *hdr = (struct ss_header *)msg;
+ struct ss_subscribe subscribe;
+ struct ss_status *status;
+
+ switch (hdr->cmd) {
+ case SYSTEM_STATE_QUERY_SUBSCRIBERS:
+ subscribe.hdr.cmd = SYSTEM_STATE_SUBSCRIBE;
+ subscribe.hdr.cmd_status = 0;
+ subscribe.states = ss_subsys_ctx.registered_state;
+
+ heci_send_fixed_client_msg(HECI_FIXED_SYSTEM_STATE_ADDR,
+ (uint8_t *)&subscribe,
+ sizeof(subscribe));
+
+ break;
+ case SYSTEM_STATE_STATUS:
+ status = (struct ss_status *)msg;
+ if (status->supported_states & SUSPEND_STATE_BIT) {
+ if (status->states_status & SUSPEND_STATE_BIT)
+ ss_subsys_suspend();
+ else
+ ss_subsys_resume();
+ }
+
+ break;
+ }
+}