diff options
Diffstat (limited to 'drivers/edgetpu/edgetpu-firmware.c')
-rw-r--r-- | drivers/edgetpu/edgetpu-firmware.c | 159 |
1 files changed, 156 insertions, 3 deletions
diff --git a/drivers/edgetpu/edgetpu-firmware.c b/drivers/edgetpu/edgetpu-firmware.c index 1ef1354..ad27ec9 100644 --- a/drivers/edgetpu/edgetpu-firmware.c +++ b/drivers/edgetpu/edgetpu-firmware.c @@ -5,6 +5,7 @@ * Copyright (C) 2019-2020 Google, Inc. */ +#include <linux/debugfs.h> #include <linux/delay.h> #include <linux/device.h> #include <linux/firmware.h> @@ -29,6 +30,30 @@ static char *firmware_name; module_param(firmware_name, charp, 0660); +/* + * Any tracing level vote with the following bit set will be considered as a default vote. + */ +#define EDGETPU_FW_TRACING_DEFAULT_VOTE BIT(8) + +struct edgetpu_fw_tracing { + struct device *dev; + struct dentry *dentry; + + /* + * Lock to protect the struct members listed below. + * + * Note that since the request of tracing level adjusting might happen during power state + * transitions (i.e., another thread calling edgetpu_firmware_tracing_restore_on_powering() + * with pm lock held), one must either use the non-blocking edgetpu_pm_trylock() or make + * sure there won't be any new power transition after holding this lock to prevent deadlock. + */ + struct mutex lock; + /* Actual firmware tracing level. */ + unsigned long active_level; + /* Requested firmware tracing level. */ + unsigned long request_level; +}; + struct edgetpu_firmware_private { const struct edgetpu_firmware_chip_data *chip_fw; void *data; /* for edgetpu_firmware_(set/get)_data */ @@ -38,6 +63,7 @@ struct edgetpu_firmware_private { struct edgetpu_firmware_desc bl1_fw_desc; enum edgetpu_firmware_status status; struct edgetpu_fw_info fw_info; + struct edgetpu_fw_tracing fw_tracing; }; void edgetpu_firmware_set_data(struct edgetpu_firmware *et_fw, void *data) @@ -134,6 +160,124 @@ static char *fw_flavor_str(enum edgetpu_fw_flavor fw_flavor) return "?"; } +static int edgetpu_firmware_tracing_active_get(void *data, u64 *val) +{ + struct edgetpu_firmware *et_fw = data; + struct edgetpu_fw_tracing *fw_tracing = &et_fw->p->fw_tracing; + + mutex_lock(&fw_tracing->lock); + *val = fw_tracing->active_level; + mutex_unlock(&fw_tracing->lock); + + return 0; +} + +DEFINE_DEBUGFS_ATTRIBUTE(fops_edgetpu_firmware_tracing_active, edgetpu_firmware_tracing_active_get, + NULL, "%llu\n"); + +static int edgetpu_firmware_tracing_request_get(void *data, u64 *val) +{ + struct edgetpu_firmware *et_fw = data; + struct edgetpu_fw_tracing *fw_tracing = &et_fw->p->fw_tracing; + + mutex_lock(&fw_tracing->lock); + *val = fw_tracing->request_level; + mutex_unlock(&fw_tracing->lock); + + return 0; +} + +/* + * fw_tracing->lock may optionally be held if the caller wants the new level to be set as a + * critical section. If not held the caller is syncing current tracing level but not as a critical + * section with the calling code. Firmware tracing levels are not expected to change frequently or + * via concurrent requests. Only the code that restore the tracing level at power up requires + * consistency with the state managed by the calling code. Since this code is called as part of + * power up processing, in order to avoid deadlocks, most callers set a requested state and then + * sync the current state to firmware (if powered on) without holding the lock across the powered-on + * check, with no harm done if the requested state changed again using a concurrent request. + */ +static int edgetpu_firmware_tracing_set_level(struct edgetpu_firmware *et_fw) +{ + unsigned long active_level; + struct edgetpu_dev *etdev = et_fw->etdev; + struct edgetpu_fw_tracing *fw_tracing = &et_fw->p->fw_tracing; + int ret = edgetpu_kci_firmware_tracing_level(etdev, fw_tracing->request_level, + &active_level); + + if (ret) + etdev_warn(et_fw->etdev, "Failed to set firmware tracing level to %lu: %d", + fw_tracing->request_level, ret); + else + fw_tracing->active_level = + (fw_tracing->request_level & EDGETPU_FW_TRACING_DEFAULT_VOTE) ? + EDGETPU_FW_TRACING_DEFAULT_VOTE : active_level; + + return ret; +} + +static int edgetpu_firmware_tracing_request_set(void *data, u64 val) +{ + struct edgetpu_firmware *et_fw = data; + struct edgetpu_dev *etdev = et_fw->etdev; + struct edgetpu_fw_tracing *fw_tracing = &et_fw->p->fw_tracing; + int ret = 0; + + mutex_lock(&fw_tracing->lock); + fw_tracing->request_level = val; + mutex_unlock(&fw_tracing->lock); + + if (edgetpu_pm_get_if_powered(etdev->pm)) { + ret = edgetpu_firmware_tracing_set_level(et_fw); + edgetpu_pm_put(etdev->pm); + } + + return ret; +} + +DEFINE_DEBUGFS_ATTRIBUTE(fops_edgetpu_firmware_tracing_request, + edgetpu_firmware_tracing_request_get, edgetpu_firmware_tracing_request_set, + "%llu\n"); + +static void edgetpu_firmware_tracing_init(struct edgetpu_firmware *et_fw) +{ + struct edgetpu_dev *etdev = et_fw->etdev; + struct edgetpu_fw_tracing *fw_tracing = &et_fw->p->fw_tracing; + + fw_tracing->active_level = EDGETPU_FW_TRACING_DEFAULT_VOTE; + fw_tracing->request_level = EDGETPU_FW_TRACING_DEFAULT_VOTE; + mutex_init(&fw_tracing->lock); + + fw_tracing->dentry = debugfs_create_dir("fw_tracing", etdev->d_entry); + if (IS_ERR(fw_tracing->dentry)) { + etdev_warn(etdev, "Failed to create fw tracing debugfs interface"); + return; + } + + debugfs_create_file("active", 0440, fw_tracing->dentry, et_fw, + &fops_edgetpu_firmware_tracing_active); + debugfs_create_file("request", 0660, fw_tracing->dentry, et_fw, + &fops_edgetpu_firmware_tracing_request); +} + +static void edgetpu_firmware_tracing_destroy(struct edgetpu_firmware *et_fw) +{ + debugfs_remove_recursive(et_fw->p->fw_tracing.dentry); +} + +static int edgetpu_firmware_tracing_restore_on_powering(struct edgetpu_firmware *et_fw) +{ + int ret = 0; + struct edgetpu_fw_tracing *fw_tracing = &et_fw->p->fw_tracing; + + mutex_lock(&fw_tracing->lock); + fw_tracing->active_level = EDGETPU_FW_TRACING_DEFAULT_VOTE; + if (!(fw_tracing->request_level & EDGETPU_FW_TRACING_DEFAULT_VOTE)) + ret = edgetpu_firmware_tracing_set_level(et_fw); + mutex_unlock(&fw_tracing->lock); + return ret; +} + static int edgetpu_firmware_handshake(struct edgetpu_firmware *et_fw) { struct edgetpu_dev *etdev = et_fw->etdev; @@ -172,6 +316,9 @@ static int edgetpu_firmware_handshake(struct edgetpu_firmware *et_fw) if (ret) etdev_warn(etdev, "telemetry KCI error: %d", ret); + ret = edgetpu_firmware_tracing_restore_on_powering(et_fw); + if (ret) + etdev_warn_ratelimited(etdev, "firmware tracing restore error: %d", ret); /* Set debug dump buffer in FW */ edgetpu_get_debug_dump(etdev, 0); } @@ -331,20 +478,21 @@ int edgetpu_firmware_run_locked(struct edgetpu_firmware *et_fw, enum edgetpu_firmware_flags flags) { const struct edgetpu_firmware_chip_data *chip_fw = et_fw->p->chip_fw; + struct edgetpu_dev *etdev = et_fw->etdev; struct edgetpu_firmware_desc new_fw_desc; int ret; bool is_bl1_run = (flags & FW_BL1); edgetpu_firmware_set_loading(et_fw); if (!is_bl1_run) - edgetpu_sw_wdt_stop(et_fw->etdev); + edgetpu_sw_wdt_stop(etdev); memset(&new_fw_desc, 0, sizeof(new_fw_desc)); ret = edgetpu_firmware_load_locked(et_fw, &new_fw_desc, name, flags); if (ret) goto out_failed; - etdev_dbg(et_fw->etdev, "run fw %s flags=%#x", name, flags); + etdev_dbg(etdev, "run fw %s flags=%#x", name, flags); if (chip_fw->prepare_run) { /* Note this may recursively call us to run BL1 */ ret = chip_fw->prepare_run(et_fw, &new_fw_desc.buf); @@ -369,13 +517,16 @@ int edgetpu_firmware_run_locked(struct edgetpu_firmware *et_fw, /* Don't start wdt if loaded firmware is second stage bootloader. */ if (!ret && !is_bl1_run && et_fw->p->fw_info.fw_flavor != FW_FLAVOR_BL1) - edgetpu_sw_wdt_start(et_fw->etdev); + edgetpu_sw_wdt_start(etdev); if (!ret && !is_bl1_run && chip_fw->launch_complete) chip_fw->launch_complete(et_fw); else if (ret && chip_fw->launch_failed) chip_fw->launch_failed(et_fw, ret); edgetpu_firmware_set_state(et_fw, ret); + /* If previous firmware was metrics v1-only reset that flag and probe this again. */ + if (etdev->usage_stats) + etdev->usage_stats->use_metrics_v1 = false; return ret; out_unload_new_fw: @@ -687,6 +838,7 @@ int edgetpu_firmware_create(struct edgetpu_dev *etdev, else edgetpu_sw_wdt_set_handler( etdev, edgetpu_firmware_wdt_timeout_action, etdev); + edgetpu_firmware_tracing_init(et_fw); return 0; out_device_remove_group: @@ -724,6 +876,7 @@ void edgetpu_firmware_destroy(struct edgetpu_dev *etdev) edgetpu_firmware_unload_locked(et_fw, &et_fw->p->fw_desc); edgetpu_firmware_unload_locked(et_fw, &et_fw->p->bl1_fw_desc); mutex_unlock(&et_fw->p->fw_desc_lock); + edgetpu_firmware_tracing_destroy(et_fw); } etdev->firmware = NULL; |