// SPDX-License-Identifier: GPL-2.0-only /* * Tegra host1x activity monitor interfaces * * Copyright (c) 2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. */ #include #include #include #include #include "dev.h" #include "actmon.h" #include "hw/actmon.h" static void actmon_writel(struct host1x_actmon *actmon, u32 val, u32 offset) { writel(val, actmon->regs+offset); } static u32 actmon_readl(struct host1x_actmon *actmon, u32 offset) { return readl(actmon->regs+offset); } static void actmon_module_writel(struct host1x_actmon_module *module, u32 val, u32 offset) { writel(val, module->regs+offset); } static u32 actmon_module_readl(struct host1x_actmon_module *module, u32 offset) { return readl(module->regs+offset); } static void host1x_actmon_update_sample_period(struct host1x_actmon *actmon) { unsigned long actmon_mhz; u32 actmon_clks_per_sample, sample_period, val = 0; actmon_mhz = actmon->rate / 1000000; actmon_clks_per_sample = actmon_mhz * actmon->usecs_per_sample; val |= HOST1X_ACTMON_CTRL_SOURCE(2); if (actmon_clks_per_sample > 65536) { val |= HOST1X_ACTMON_CTRL_SAMPLE_TICK(1); sample_period = actmon_clks_per_sample / 65536; } else { val &= ~HOST1X_ACTMON_CTRL_SAMPLE_TICK(1); sample_period = actmon_clks_per_sample / 256; } val &= ~HOST1X_ACTMON_CTRL_SAMPLE_PERIOD_MASK; val |= HOST1X_ACTMON_CTRL_SAMPLE_PERIOD(sample_period); actmon_writel(actmon, val, HOST1X_ACTMON_CTRL_REG); } static int host1x_actmon_sample_period_get(void *data, u64 *val) { struct host1x_actmon *actmon = (struct host1x_actmon *)data; *val = (u64) actmon->usecs_per_sample; return 0; } static int host1x_actmon_sample_period_set(void *data, u64 val) { struct host1x_actmon *actmon = (struct host1x_actmon *)data; actmon->usecs_per_sample = (u32)val; host1x_actmon_update_sample_period(actmon); return 0; } DEFINE_SIMPLE_ATTRIBUTE(host1x_actmon_sample_period_fops, host1x_actmon_sample_period_get, host1x_actmon_sample_period_set, "%lld\n"); static void host1x_actmon_debug_init(struct host1x_actmon *actmon) { struct host1x *host = dev_get_drvdata(actmon->client->host->parent); struct dentry *debugfs = host->actmon_debugfs; if (!debugfs) return; actmon->debugfs = debugfs_create_dir(actmon->name, debugfs); /* R/W files */ debugfs_create_file("sample_period", 0644, actmon->debugfs, actmon, &host1x_actmon_sample_period_fops); } static int host1x_actmon_module_k_get(void *data, u64 *val) { struct host1x_actmon_module *module = (struct host1x_actmon_module *)data; *val = (u64) module->k; return 0; } static int host1x_actmon_module_k_set(void *data, u64 val) { struct host1x_actmon_module *module = (struct host1x_actmon_module *)data; u32 val32; module->k = (u32)val; val32 = actmon_module_readl(module, HOST1X_ACTMON_MODULE_CTRL_REG); val32 &= ~HOST1X_ACTMON_MODULE_CTRL_K_VAL_MASK; val32 |= HOST1X_ACTMON_MODULE_CTRL_K_VAL(module->k); actmon_module_writel(module, val32, HOST1X_ACTMON_MODULE_CTRL_REG); return 0; } DEFINE_SIMPLE_ATTRIBUTE(host1x_actmon_module_k_fops, host1x_actmon_module_k_get, host1x_actmon_module_k_set, "%lld\n"); static int host1x_actmon_module_consec_upper_num_get(void *data, u64 *val) { struct host1x_actmon_module *module = (struct host1x_actmon_module *)data; *val = (u64) module->consec_upper_num; return 0; } static int host1x_actmon_module_consec_upper_num_set(void *data, u64 val) { struct host1x_actmon_module *module = (struct host1x_actmon_module *)data; u32 val32; module->consec_upper_num = (u32)val; val32 = actmon_module_readl(module, HOST1X_ACTMON_MODULE_CTRL_REG); val32 &= ~HOST1X_ACTMON_MODULE_CTRL_CONSEC_UPPER_NUM_MASK; val32 |= HOST1X_ACTMON_MODULE_CTRL_CONSEC_UPPER_NUM(module->consec_upper_num); actmon_module_writel(module, val32, HOST1X_ACTMON_MODULE_CTRL_REG); return 0; } DEFINE_SIMPLE_ATTRIBUTE(host1x_actmon_module_consec_upper_num_fops, host1x_actmon_module_consec_upper_num_get, host1x_actmon_module_consec_upper_num_set, "%lld\n"); static int host1x_actmon_module_consec_lower_num_get(void *data, u64 *val) { struct host1x_actmon_module *module = (struct host1x_actmon_module *)data; *val = (u64) module->consec_lower_num; return 0; } static int host1x_actmon_module_consec_lower_num_set(void *data, u64 val) { struct host1x_actmon_module *module = (struct host1x_actmon_module *)data; u32 val32; module->consec_lower_num = (u32)val; val32 = actmon_module_readl(module, HOST1X_ACTMON_MODULE_CTRL_REG); val32 &= ~HOST1X_ACTMON_MODULE_CTRL_CONSEC_LOWER_NUM_MASK; val32 |= HOST1X_ACTMON_MODULE_CTRL_CONSEC_LOWER_NUM(module->consec_lower_num); actmon_module_writel(module, val32, HOST1X_ACTMON_MODULE_CTRL_REG); return 0; } DEFINE_SIMPLE_ATTRIBUTE(host1x_actmon_module_consec_lower_num_fops, host1x_actmon_module_consec_lower_num_get, host1x_actmon_module_consec_lower_num_set, "%lld\n"); static int host1x_actmon_module_avg_norm_get(void *data, u64 *val) { struct host1x_actmon_module *module = (struct host1x_actmon_module *)data; struct host1x_actmon *actmon = module->actmon; struct host1x_client *client = actmon->client; unsigned long client_freq; u32 active_clks, client_clks; active_clks = actmon_module_readl(module, HOST1X_ACTMON_MODULE_AVG_COUNT_REG); client_freq = client->ops->get_rate(client); client_clks = ((client_freq / 1000) * actmon->usecs_per_sample) / 1000; *val = (u64) (active_clks * 1000) / client_clks; return 0; } DEFINE_SIMPLE_ATTRIBUTE(host1x_actmon_module_avg_norm_fops, host1x_actmon_module_avg_norm_get, NULL, "%lld\n"); static void host1x_actmon_module_debug_init(struct host1x_actmon_module *module) { struct dentry *debugfs = module->actmon->debugfs; char dirname[8]; if (!debugfs) return; snprintf(dirname, sizeof(dirname), "module%d", module->type); module->debugfs = debugfs_create_dir(dirname, debugfs); /* R/W files */ debugfs_create_file("k", 0644, module->debugfs, module, &host1x_actmon_module_k_fops); debugfs_create_file("consec_upper_num", 0644, module->debugfs, module, &host1x_actmon_module_consec_upper_num_fops); debugfs_create_file("consec_lower_num", 0644, module->debugfs, module, &host1x_actmon_module_consec_lower_num_fops); /* R files */ debugfs_create_file("usage", 0444, module->debugfs, module, &host1x_actmon_module_avg_norm_fops); } static void host1x_actmon_init(struct host1x_actmon *actmon) { u32 val; /* Global control register */ host1x_actmon_update_sample_period(actmon); /* Global interrupt enable register */ val = (1 << actmon->num_modules) - 1; actmon_writel(actmon, val, HOST1X_ACTMON_INTR_ENB_REG); } static void host1x_actmon_deinit(struct host1x_actmon *actmon) { actmon_writel(actmon, 0, HOST1X_ACTMON_CTRL_REG); actmon_writel(actmon, 0, HOST1X_ACTMON_INTR_ENB_REG); } static void host1x_actmon_module_init(struct host1x_actmon_module *module) { struct host1x_actmon *actmon = module->actmon; u32 val; /* Local control register */ val = 0; val |= HOST1X_ACTMON_MODULE_CTRL_ACTMON_ENB(1); val |= HOST1X_ACTMON_MODULE_CTRL_ENB_PERIODIC(1); val |= HOST1X_ACTMON_MODULE_CTRL_K_VAL(module->k); val |= HOST1X_ACTMON_MODULE_CTRL_CONSEC_UPPER_NUM(module->consec_upper_num); val |= HOST1X_ACTMON_MODULE_CTRL_CONSEC_LOWER_NUM(module->consec_lower_num); actmon_module_writel(module, val, HOST1X_ACTMON_MODULE_CTRL_REG); /* Interrupt enable register */ val = 0; val |= HOST1X_ACTMON_MODULE_INTR_AVG_BELOW_WORK_ENB(1); val |= HOST1X_ACTMON_MODULE_INTR_AVG_ABOVE_WORK_ENB(1); val |= HOST1X_ACTMON_MODULE_INTR_CONSEC_BELOW_WORK_ENB(1); val |= HOST1X_ACTMON_MODULE_INTR_CONSEC_ABOVE_WORK_ENB(1); actmon_module_writel(module, val, HOST1X_ACTMON_MODULE_INTR_ENB_REG); /* Interrupt status register */ actmon_module_writel(module, 0xffffffff, HOST1X_ACTMON_MODULE_INTR_STATUS_REG); /* Consecutive watermark registers */ actmon_module_writel(module, 0xffffffff, HOST1X_ACTMON_MODULE_UPPER_WMARK_REG); actmon_module_writel(module, 0, HOST1X_ACTMON_MODULE_LOWER_WMARK_REG); /* Moving-average watermark registers */ actmon_module_writel(module, 0xffffffff, HOST1X_ACTMON_MODULE_AVG_UPPER_WMARK_REG); actmon_module_writel(module, 0, HOST1X_ACTMON_MODULE_AVG_LOWER_WMARK_REG); /* Init average value register */ actmon_module_writel(module, 0, HOST1X_ACTMON_MODULE_INIT_AVG_REG); } static void host1x_actmon_module_deinit(struct host1x_actmon_module *module) { actmon_module_writel(module, 0, HOST1X_ACTMON_MODULE_CTRL_REG); actmon_module_writel(module, 0, HOST1X_ACTMON_MODULE_INTR_ENB_REG); actmon_module_writel(module, 0xffffffff, HOST1X_ACTMON_MODULE_INTR_STATUS_REG); actmon_module_writel(module, 0, HOST1X_ACTMON_MODULE_UPPER_WMARK_REG); actmon_module_writel(module, 0, HOST1X_ACTMON_MODULE_LOWER_WMARK_REG); actmon_module_writel(module, 0, HOST1X_ACTMON_MODULE_AVG_UPPER_WMARK_REG); actmon_module_writel(module, 0, HOST1X_ACTMON_MODULE_AVG_LOWER_WMARK_REG); actmon_module_writel(module, 0, HOST1X_ACTMON_MODULE_INIT_AVG_REG); actmon_module_writel(module, 0, HOST1X_ACTMON_MODULE_COUNT_WEIGHT_REG); } int host1x_actmon_register(struct host1x_client *client) { struct host1x *host = dev_get_drvdata(client->host->parent); struct host1x_info *info = host->info; struct host1x_actmon_entry *entry = NULL; struct host1x_actmon_module *module; struct host1x_actmon *actmon; int i, err; if (!host->actmon_regs || !host->actmon_clk) return -ENODEV; for (i = 0; i < info->num_actmon_entries; i++) { if (info->actmon_table[i].classid == client->class) entry = &info->actmon_table[i]; } if (!entry) return -ENODEV; actmon = devm_kzalloc(client->dev, sizeof(*actmon), GFP_KERNEL); if (!actmon) return -ENOMEM; actmon->client = client; actmon->rate = clk_get_rate(host->actmon_clk); actmon->regs = host->actmon_regs + entry->offset; actmon->irq = entry->irq; actmon->name = entry->name; actmon->num_modules = entry->num_modules; actmon->usecs_per_sample = 1500; /* Configure actmon registers */ host1x_actmon_init(actmon); /* Create debugfs for the actmon */ host1x_actmon_debug_init(actmon); /* Configure actmon module registers */ for (i = 0; i < actmon->num_modules; i++) { module = &actmon->modules[i]; module->actmon = actmon; module->type = i; module->regs = actmon->regs + (i * HOST1X_ACTMON_MODULE_OFFSET); module->k = 6; module->consec_upper_num = 7; module->consec_lower_num = 7; host1x_actmon_module_init(module); /* Create debugfs for the actmon module */ host1x_actmon_module_debug_init(module); } client->actmon = actmon; return 0; } EXPORT_SYMBOL(host1x_actmon_register); int host1x_actmon_unregister(struct host1x_client *client) { struct host1x_actmon_module *module; struct host1x *host = dev_get_drvdata(client->host->parent); struct host1x_actmon *actmon = client->actmon; int i; if (!host->actmon_regs || !host->actmon_clk) return 0; if (!actmon) return 0; for (i = 0; i < actmon->num_modules; i++) { module = &actmon->modules[i]; host1x_actmon_module_deinit(module); debugfs_remove_recursive(module->debugfs); } debugfs_remove_recursive(actmon->debugfs); host1x_actmon_deinit(actmon); return 0; } EXPORT_SYMBOL(host1x_actmon_unregister); void host1x_actmon_update_client_rate(struct host1x_client *client, unsigned long rate, u32 *weight) { struct host1x_actmon *actmon = client->actmon; struct host1x_actmon_module *module; u32 val; int i; if (!actmon) { *weight = 0; return; } val = (rate / actmon->rate) << 2; for (i = 0; i < actmon->num_modules; i++) { module = &actmon->modules[i]; actmon_module_writel(module, val, HOST1X_ACTMON_MODULE_COUNT_WEIGHT_REG); } *weight = val; } EXPORT_SYMBOL(host1x_actmon_update_client_rate); int host1x_actmon_read_avg_count(struct host1x_client *client) { struct host1x *host = dev_get_drvdata(client->host->parent); unsigned int offset; if (!host->actmon_regs) return -ENODEV; /* FIXME: Only T234 supported */ switch (client->class) { case HOST1X_CLASS_NVENC: offset = 0x0; break; case HOST1X_CLASS_VIC: offset = 0x10000; break; case HOST1X_CLASS_NVDEC: offset = 0x20000; break; default: return -EINVAL; } return readl(host->actmon_regs + offset + 0xa4); } EXPORT_SYMBOL(host1x_actmon_read_avg_count);