diff options
author | Aurora pro automerger <aurora-pro-automerger@google.com> | 2023-02-17 09:18:51 +0000 |
---|---|---|
committer | Copybara-Service <copybara-worker@google.com> | 2023-03-09 19:56:47 -0800 |
commit | 0ffef44f5b07977a5608985c66e7eeaadc29b426 (patch) | |
tree | b3f76ed4a1152e0d83c4ee3ad43e6a8e448e1f0f /gcip-kernel-driver/drivers | |
parent | b76197f77494d3879a181690558349a4aedb66c6 (diff) | |
download | gs201-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/Makefile | 1 | ||||
-rw-r--r-- | gcip-kernel-driver/drivers/gcip/gcip-firmware.c | 131 | ||||
-rw-r--r-- | gcip-kernel-driver/drivers/gcip/gcip-pm.c | 239 |
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); +} |