summaryrefslogtreecommitdiff
path: root/gcip-kernel-driver/drivers
diff options
context:
space:
mode:
authorAurora pro automerger <aurora-pro-automerger@google.com>2023-02-17 09:18:51 +0000
committerCopybara-Service <copybara-worker@google.com>2023-03-09 19:56:47 -0800
commit0ffef44f5b07977a5608985c66e7eeaadc29b426 (patch)
treeb3f76ed4a1152e0d83c4ee3ad43e6a8e448e1f0f /gcip-kernel-driver/drivers
parentb76197f77494d3879a181690558349a4aedb66c6 (diff)
downloadgs201-0ffef44f5b07977a5608985c66e7eeaadc29b426.tar.gz
gxp: [Copybara Auto Merge] Merge branch 'gs201-u' into 'android13-gs-pixel-5.10-udc'android-u-beta-2_r0.4android-u-beta-2_r0.3android-u-beta-2_r0.2
gcip: fixup: Use devm_* for gcip-pm Bug: 265870718 gcip: Use devm_* for gcip-pm Bug: 265870718 (repeat) gcip: Remove the start log of async power down Bug: 265870718 (repeat) gcip: Add firmware dynamic tracing support Bug: 262916889 gcip: Add PM support Bug: 265870718 (repeat) GCIP_MAIN_REV_ID: c359c8b4c8e11ff2655dbfd8457605b760db383c gxp: remove redundant domain_attach from vd resume Bug: 269587251 gxp: bump version 1.10 Bug: 269587251 (repeat) gxp: move domain detach to block_unready Bug: 269587251 (repeat) gxp: add mcu_crashed flag to gxp_virtual_device Bug: 269587251 (repeat) gxp: use MCU PSM to handle MCU fw crash Bug: 264621513 gxp: add IDs to VD suspend/resume log gxp: skip suspend/resume in full MCU mode Bug: 269717931 gxp: load core FW only once per image Bug: 267713927 gxp: Adopt GCIP PM Bug: 265870718 (repeat) gxp: Log the clients with a wakelock in gxp_platform_suspend Bug: 265870718 (repeat) gxp: Remove suspended from gxp_wakelock_manager Bug: 265870718 (repeat) gxp: support loading DSP core FW in raw binary format Bug: 259215977 gxp: introduce gxp_vd_invalidate_with_client_id Bug: 263994153 gxp: introduce gxp_vd_generate_debug_dump Bug: 263994153 (repeat) gxp: move vd_invalid_eventfd from client to vd Bug: 263994153 (repeat) gxp: gxp_vd_block_unready needs vd_semaphore write lock Bug: 263994153 (repeat) gxp: bump to version 1.9 Bug: 245751727 Bug: 268449263 gxp: unittest: add a unittest for gxp_mapping_iova_log Bug: 245751727 (repeat) gxp: Add a module flag to enable IOVA-space logging Bug: 245751727 (repeat) gxp: introduce debug_dump_lock per vd Bug: 234172464 gxp: gxp_vd_{run,stop} holds vd_sema for writing Bug: 234172464 (repeat) gxp: pass vd to the static functions of debug dump Bug: 234172464 (repeat) gcip: Add a comment about suspend/resume in gcip-pm Bug: 265870718 (repeat) gcip: Add firmware dynamic tracing header Bug: 262916889 (repeat) gcip: Add PM header Bug: 265870718 (repeat) GCIP_HEADERS_REV_ID: 21105099cc4be7a0fec32f06f34cce5026015601 GitOrigin-RevId: 775c7ff3381a68301beb1d3b790d0954e211244c Change-Id: I9637cc7a2ef6bc3b8b184035834ac1ab13ebafb4
Diffstat (limited to 'gcip-kernel-driver/drivers')
-rw-r--r--gcip-kernel-driver/drivers/gcip/Makefile1
-rw-r--r--gcip-kernel-driver/drivers/gcip/gcip-firmware.c131
-rw-r--r--gcip-kernel-driver/drivers/gcip/gcip-pm.c239
3 files changed, 371 insertions, 0 deletions
diff --git a/gcip-kernel-driver/drivers/gcip/Makefile b/gcip-kernel-driver/drivers/gcip/Makefile
index ab68776..bc370e5 100644
--- a/gcip-kernel-driver/drivers/gcip/Makefile
+++ b/gcip-kernel-driver/drivers/gcip/Makefile
@@ -14,6 +14,7 @@ gcip-objs := gcip-alloc-helper.o \
gcip-kci.o \
gcip-mailbox.o \
gcip-mem-pool.o \
+ gcip-pm.o \
gcip-telemetry.o
CURRENT_DIR=$(dir $(abspath $(lastword $(MAKEFILE_LIST))))
diff --git a/gcip-kernel-driver/drivers/gcip/gcip-firmware.c b/gcip-kernel-driver/drivers/gcip/gcip-firmware.c
index 0b0225c..1d9392c 100644
--- a/gcip-kernel-driver/drivers/gcip/gcip-firmware.c
+++ b/gcip-kernel-driver/drivers/gcip/gcip-firmware.c
@@ -5,7 +5,12 @@
* Copyright (C) 2022 Google LLC
*/
+#include <linux/debugfs.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+
#include <gcip/gcip-firmware.h>
+#include <gcip/gcip-pm.h>
char *gcip_fw_flavor_str(enum gcip_fw_flavor fw_flavor)
{
@@ -23,3 +28,129 @@ char *gcip_fw_flavor_str(enum gcip_fw_flavor fw_flavor)
return "unknown";
}
}
+
+static int gcip_firmware_tracing_active_get(void *data, u64 *val)
+{
+ struct gcip_fw_tracing *fw_tracing = data;
+
+ mutex_lock(&fw_tracing->lock);
+ *val = fw_tracing->active_level;
+ mutex_unlock(&fw_tracing->lock);
+
+ return 0;
+}
+
+DEFINE_DEBUGFS_ATTRIBUTE(fops_gcip_firmware_tracing_active, gcip_firmware_tracing_active_get, NULL,
+ "%llu\n");
+
+static int gcip_firmware_tracing_request_get(void *data, u64 *val)
+{
+ struct gcip_fw_tracing *fw_tracing = data;
+
+ mutex_lock(&fw_tracing->lock);
+ *val = fw_tracing->request_level;
+ mutex_unlock(&fw_tracing->lock);
+
+ return 0;
+}
+
+static int gcip_firmware_tracing_set_level_lock(struct gcip_fw_tracing *fw_tracing)
+{
+ unsigned long active_level;
+ int ret = fw_tracing->set_level(fw_tracing->data, fw_tracing->request_level, &active_level);
+
+ if (ret)
+ dev_warn(fw_tracing->dev, "Failed to set firmware tracing level to %lu: %d",
+ fw_tracing->request_level, ret);
+ else
+ fw_tracing->active_level =
+ (fw_tracing->request_level & GCIP_FW_TRACING_DEFAULT_VOTE) ?
+ GCIP_FW_TRACING_DEFAULT_VOTE :
+ active_level;
+
+ return ret;
+}
+
+static int gcip_firmware_tracing_request_set(void *data, u64 val)
+{
+ struct gcip_fw_tracing *fw_tracing = data;
+ int ret = 0;
+
+ mutex_lock(&fw_tracing->lock);
+
+ fw_tracing->request_level = val;
+ if (!gcip_pm_get_if_powered(fw_tracing->pm, false)) {
+ ret = gcip_firmware_tracing_set_level_lock(fw_tracing);
+ gcip_pm_put(fw_tracing->pm);
+ }
+
+ mutex_unlock(&fw_tracing->lock);
+
+ return ret;
+}
+
+DEFINE_DEBUGFS_ATTRIBUTE(fops_gcip_firmware_tracing_request, gcip_firmware_tracing_request_get,
+ gcip_firmware_tracing_request_set, "%llu\n");
+
+struct gcip_fw_tracing *gcip_firmware_tracing_create(const struct gcip_fw_tracing_args *args)
+{
+ struct gcip_fw_tracing *fw_tracing;
+
+ if (!args->dev || !args->set_level)
+ return ERR_PTR(-EINVAL);
+
+ fw_tracing = kzalloc(sizeof(*fw_tracing), GFP_KERNEL);
+ if (!fw_tracing)
+ return ERR_PTR(-ENOMEM);
+
+ fw_tracing->dev = args->dev;
+ fw_tracing->pm = args->pm;
+ fw_tracing->set_level = args->set_level;
+ fw_tracing->data = args->data;
+ fw_tracing->active_level = GCIP_FW_TRACING_DEFAULT_VOTE;
+ fw_tracing->request_level = GCIP_FW_TRACING_DEFAULT_VOTE;
+ mutex_init(&fw_tracing->lock);
+
+ fw_tracing->dentry = debugfs_create_dir("fw_tracing", args->dentry);
+ if (IS_ERR(fw_tracing->dentry)) {
+ dev_warn(args->dev, "Failed to create debug FS tracing");
+ kfree(fw_tracing);
+
+ return (struct gcip_fw_tracing *)fw_tracing->dentry;
+ }
+
+ debugfs_create_file("active", 0440, fw_tracing->dentry, fw_tracing,
+ &fops_gcip_firmware_tracing_active);
+ debugfs_create_file("request", 0660, fw_tracing->dentry, fw_tracing,
+ &fops_gcip_firmware_tracing_request);
+
+ return fw_tracing;
+}
+
+void gcip_firmware_tracing_destroy(struct gcip_fw_tracing *fw_tracing)
+{
+ if (!fw_tracing)
+ return;
+
+ debugfs_remove_recursive(fw_tracing->dentry);
+ kfree(fw_tracing);
+}
+
+int gcip_firmware_tracing_restore(struct gcip_fw_tracing *fw_tracing)
+{
+ int ret = 0;
+
+ if (!fw_tracing)
+ return 0;
+
+ gcip_pm_lockdep_assert_held(fw_tracing->pm);
+ mutex_lock(&fw_tracing->lock);
+
+ fw_tracing->active_level = GCIP_FW_TRACING_DEFAULT_VOTE;
+ if (!(fw_tracing->request_level & GCIP_FW_TRACING_DEFAULT_VOTE))
+ ret = gcip_firmware_tracing_set_level_lock(fw_tracing);
+
+ mutex_unlock(&fw_tracing->lock);
+
+ return ret;
+}
diff --git a/gcip-kernel-driver/drivers/gcip/gcip-pm.c b/gcip-kernel-driver/drivers/gcip/gcip-pm.c
new file mode 100644
index 0000000..43d9654
--- /dev/null
+++ b/gcip-kernel-driver/drivers/gcip/gcip-pm.c
@@ -0,0 +1,239 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Power management interface for GCIP devices.
+ *
+ * Copyright (C) 2023 Google LLC
+ */
+
+#include <linux/device.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+
+#include <gcip/gcip-pm.h>
+
+#define GCIP_ASYNC_POWER_DOWN_RETRY_DELAY 200 /* ms */
+
+/* Caller must hold @pm->lock. */
+static void gcip_pm_try_power_down(struct gcip_pm *pm)
+{
+ int ret;
+
+ gcip_pm_lockdep_assert_held(pm);
+
+ ret = pm->power_down(pm->data);
+
+ if (ret == -EAGAIN) {
+ dev_warn(pm->dev, "Power down request denied, retrying in %d ms\n",
+ GCIP_ASYNC_POWER_DOWN_RETRY_DELAY);
+ pm->power_down_pending = true;
+ schedule_delayed_work(&pm->power_down_work,
+ msecs_to_jiffies(GCIP_ASYNC_POWER_DOWN_RETRY_DELAY));
+ } else {
+ if (ret)
+ dev_err(pm->dev, "Power down request failed (%d)\n", ret);
+ pm->power_down_pending = false;
+ }
+}
+
+/* Worker for async power down. */
+static void gcip_pm_async_power_down_work(struct work_struct *work)
+{
+ struct delayed_work *dwork = container_of(work, struct delayed_work, work);
+ struct gcip_pm *pm = container_of(dwork, struct gcip_pm, power_down_work);
+
+ mutex_lock(&pm->lock);
+
+ if (pm->power_down_pending)
+ gcip_pm_try_power_down(pm);
+ else
+ dev_info(pm->dev, "Delayed power down cancelled\n");
+
+ mutex_unlock(&pm->lock);
+}
+
+struct gcip_pm *gcip_pm_create(const struct gcip_pm_args *args)
+{
+ struct gcip_pm *pm;
+ int ret;
+
+ if (!args->dev || !args->power_up || !args->power_down)
+ return ERR_PTR(-EINVAL);
+
+ pm = devm_kzalloc(args->dev, sizeof(*pm), GFP_KERNEL);
+ if (!pm)
+ return ERR_PTR(-ENOMEM);
+
+ pm->dev = args->dev;
+ pm->data = args->data;
+ pm->after_create = args->after_create;
+ pm->before_destroy = args->before_destroy;
+ pm->power_up = args->power_up;
+ pm->power_down = args->power_down;
+
+ mutex_init(&pm->lock);
+ INIT_DELAYED_WORK(&pm->power_down_work, gcip_pm_async_power_down_work);
+
+ if (pm->after_create) {
+ ret = pm->after_create(pm->data);
+ if (ret) {
+ devm_kfree(args->dev, pm);
+ return ERR_PTR(ret);
+ }
+ }
+
+ return pm;
+}
+
+void gcip_pm_destroy(struct gcip_pm *pm)
+{
+ if (!pm)
+ return;
+
+ pm->power_down_pending = false;
+ cancel_delayed_work_sync(&pm->power_down_work);
+
+ if (pm->before_destroy)
+ pm->before_destroy(pm->data);
+
+ devm_kfree(pm->dev, pm);
+}
+
+/*
+ * Increases the counter and calls the power_up callback.
+ *
+ * Returns zero on success.
+ *
+ * Caller holds pm->lock.
+ */
+static int gcip_pm_get_locked(struct gcip_pm *pm)
+{
+ int ret = 0;
+
+ gcip_pm_lockdep_assert_held(pm);
+
+ if (!pm->count) {
+ pm->power_down_pending = false;
+ ret = pm->power_up(pm->data);
+ }
+
+ if (!ret)
+ pm->count++;
+
+ dev_dbg(pm->dev, "%s: %d\n", __func__, pm->count);
+
+ return ret;
+}
+
+int gcip_pm_get_if_powered(struct gcip_pm *pm, bool blocking)
+{
+ int ret = -EAGAIN;
+
+ if (!pm)
+ return 0;
+
+ /* Fast fails without holding the lock. */
+ if (!pm->count)
+ return ret;
+
+ if (blocking)
+ mutex_lock(&pm->lock);
+ else if (!mutex_trylock(&pm->lock))
+ return ret;
+
+ if (pm->count)
+ ret = gcip_pm_get_locked(pm);
+
+ mutex_unlock(&pm->lock);
+
+ return ret;
+}
+
+int gcip_pm_get(struct gcip_pm *pm)
+{
+ int ret;
+
+ if (!pm)
+ return 0;
+
+ mutex_lock(&pm->lock);
+ ret = gcip_pm_get_locked(pm);
+ mutex_unlock(&pm->lock);
+
+ return ret;
+}
+
+static void __gcip_pm_put(struct gcip_pm *pm, bool async)
+{
+ if (!pm)
+ return;
+
+ mutex_lock(&pm->lock);
+
+ if (WARN_ON(!pm->count))
+ goto unlock;
+
+ if (!--pm->count) {
+ pm->power_down_pending = true;
+ if (async)
+ schedule_delayed_work(&pm->power_down_work, 0);
+ else
+ gcip_pm_try_power_down(pm);
+ }
+
+ dev_dbg(pm->dev, "%s: %d\n", __func__, pm->count);
+
+unlock:
+ mutex_unlock(&pm->lock);
+}
+
+void gcip_pm_put(struct gcip_pm *pm)
+{
+ __gcip_pm_put(pm, false);
+}
+
+void gcip_pm_put_async(struct gcip_pm *pm)
+{
+ __gcip_pm_put(pm, true);
+}
+
+int gcip_pm_get_count(struct gcip_pm *pm)
+{
+ int count = -EAGAIN;
+
+ if (!pm)
+ return 0;
+
+ if (mutex_trylock(&pm->lock)) {
+ count = pm->count;
+ mutex_unlock(&pm->lock);
+ }
+
+ return count;
+}
+
+bool gcip_pm_is_powered(struct gcip_pm *pm)
+{
+ /* Assumes powered-on in case of no power interface. */
+ return pm ? gcip_pm_get_count(pm) > 0 : true;
+}
+
+void gcip_pm_shutdown(struct gcip_pm *pm, bool force)
+{
+ if (!pm)
+ return;
+
+ mutex_lock(&pm->lock);
+
+ if (pm->count) {
+ if (!force)
+ goto unlock;
+ else
+ dev_warn(pm->dev, "Force shutdown with power up count: %d", pm->count);
+ }
+
+ gcip_pm_try_power_down(pm);
+
+unlock:
+ mutex_unlock(&pm->lock);
+}