From 14bdf751cbd06f96c4b61883288775b10532a2b9 Mon Sep 17 00:00:00 2001 From: Ninad Malwade Date: Fri, 5 May 2023 13:18:57 +0800 Subject: [PATCH] nvhost: nvdla: add support for dla clock capping For K5.15 the nvhost_acm driver is not used and thus the central mechanism for clock capping for the nvhost cliets like DLA is not available. The clock capping functionality should be part of the individual nvhost drivers from K5.15 onwards and thus adding the related support in nvhost-nvdla drivers. Bug 4029003 Change-Id: I966187c97ba4ab6b90fabc96be19c76aa32ac905 Signed-off-by: Ninad Malwade Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/2899031 Reviewed-by: svcacv Reviewed-by: svc-mobile-coverity Reviewed-by: svc-mobile-cert Reviewed-by: Mikko Perttunen Reviewed-by: Rajkumar Kasirajan GVS: Gerrit_Virtual_Submit --- drivers/video/tegra/host/nvdla/nvdla.c | 134 ++++++++++++++++++++++++- 1 file changed, 133 insertions(+), 1 deletion(-) diff --git a/drivers/video/tegra/host/nvdla/nvdla.c b/drivers/video/tegra/host/nvdla/nvdla.c index 52b372e8..eaa66194 100644 --- a/drivers/video/tegra/host/nvdla/nvdla.c +++ b/drivers/video/tegra/host/nvdla/nvdla.c @@ -28,6 +28,12 @@ #include #endif /* CONFIG_TEGRA_HSIERRRPTINJ */ +#if !IS_ENABLED(CONFIG_TEGRA_GRHOST) +#include +#include +#include +#endif + #include "nvdla.h" #include "nvdla_hw_flcn.h" #include "nvdla_t194.h" @@ -884,6 +890,69 @@ out: } #endif +#if !IS_ENABLED(CONFIG_TEGRA_GRHOST) +static ssize_t clk_cap_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + struct nvhost_device_data *pdata = + container_of(kobj, struct nvhost_device_data, clk_cap_kobj); + /* i is indeed 'index' here after type conversion */ + int ret, i = attr - pdata->clk_cap_attrs; + struct clk_bulk_data *clks = &pdata->clks[i]; + struct clk *clk = clks->clk; + unsigned long freq_cap; + long freq_cap_signed; + + ret = kstrtoul(buf, 0, &freq_cap); + if (ret) + return -EINVAL; + /* Remove previous freq cap to get correct rounted rate for new cap */ + ret = clk_set_max_rate(clk, UINT_MAX); + if (ret < 0) + return ret; + + freq_cap_signed = clk_round_rate(clk, freq_cap); + if (freq_cap_signed < 0) + return -EINVAL; + + freq_cap = (unsigned long)freq_cap_signed; + /* Apply new freq cap */ + ret = clk_set_max_rate(clk, freq_cap); + if (ret < 0) + return ret; + + /* Update the clock rate */ + clk_set_rate(clks->clk, freq_cap); + if (ret < 0) + return ret; + + return count; +} + +static ssize_t clk_cap_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct nvhost_device_data *pdata = + container_of(kobj, struct nvhost_device_data, clk_cap_kobj); + /* i is indeed 'index' here after type conversion */ + int i = attr - pdata->clk_cap_attrs; + struct clk_bulk_data *clks = &pdata->clks[i]; + struct clk *clk = clks->clk; + long max_rate; + + max_rate = clk_round_rate(clk, UINT_MAX); + if (max_rate < 0) + return max_rate; + + return snprintf(buf, PAGE_SIZE, "%ld\n", max_rate); +} + +static struct kobj_type nvdla_kobj_ktype = { + .sysfs_ops = &kobj_sysfs_ops, +}; + +#endif + /* driver probe and init */ static struct of_device_id tegra_nvdla_of_match[] = { { @@ -917,6 +986,13 @@ static int nvdla_probe(struct platform_device *pdev) struct tegra_soc_hwpm_ip_ops hwpm_ip_ops; #endif +#if !IS_ENABLED(CONFIG_TEGRA_GRHOST) + struct kobj_attribute *attr = NULL; + int i = 0; + struct clk_bulk_data *clks; + struct clk *c; +#endif + if (pdev->dev.of_node) { const struct of_device_id *match; @@ -1062,7 +1138,6 @@ static int nvdla_probe(struct platform_device *pdev) tegra_soc_hwpm_ip_register(&hwpm_ip_ops); #endif - #if (IS_ENABLED(CONFIG_TEGRA_HSIERRRPTINJ)) err = nvdla_error_inj_handler_init(nvdla_dev); if (err) { @@ -1071,10 +1146,53 @@ static int nvdla_probe(struct platform_device *pdev) } #endif /* CONFIG_TEGRA_HSIERRRPTINJ */ +#if !IS_ENABLED(CONFIG_TEGRA_GRHOST) + if (pdata->num_clks > 0) { + err = kobject_init_and_add(&pdata->clk_cap_kobj, &nvdla_kobj_ktype, + &pdev->dev.kobj, "%s", "clk_cap"); + if (err) { + dev_err(dev, "Could not add dir 'clk_cap'\n"); + goto err_clk_cap_fail; + } + + pdata->clk_cap_attrs = devm_kcalloc(dev, pdata->num_clks, + sizeof(*attr), GFP_KERNEL); + if (!pdata->clk_cap_attrs) + goto err_cleanup_sysfs; + + for (i = 0; i < pdata->num_clks; ++i) { + clks = &pdata->clks[i]; + c = clks->clk; + if (!c) + continue; + + attr = &pdata->clk_cap_attrs[i]; + attr->attr.name = __clk_get_name(c); + /* octal permission is preferred nowadays */ + attr->attr.mode = 0644; + attr->show = clk_cap_show; + attr->store = clk_cap_store; + sysfs_attr_init(&attr->attr); + if (sysfs_create_file(&pdata->clk_cap_kobj, &attr->attr)) { + dev_err(dev, "Could not create sysfs attribute %s\n", + __clk_get_name(c)); + err = -EIO; + goto err_cleanup_sysfs; + } + } + } +#endif + nvdla_dbg_info(pdev, "pdata:%p initialized\n", pdata); return 0; +#if !IS_ENABLED(CONFIG_TEGRA_GRHOST) +err_cleanup_sysfs: + /* kobj of nvdla_kobj_ktype cleans up sysfs entries automatically */ + kobject_put(&pdata->clk_cap_kobj); +err_clk_cap_fail: +#endif #if (IS_ENABLED(CONFIG_TEGRA_HSIERRRPTINJ)) err_inj_handler_init: #ifdef CONFIG_TEGRA_SOC_HWPM @@ -1110,6 +1228,20 @@ static int __exit nvdla_remove(struct platform_device *pdev) struct nvhost_device_data *pdata = platform_get_drvdata(pdev); struct nvdla_device *nvdla_dev = pdata->private_data; +#if !IS_ENABLED(CONFIG_TEGRA_GRHOST) + int i; + struct kobj_attribute *attr = NULL; + + if (&pdata->clk_cap_kobj) { + for (i = 0; i < pdata->num_clks; i++) { + attr = &pdata->clk_cap_attrs[i]; + sysfs_remove_file(&pdata->clk_cap_kobj, &attr->attr); + } + + kobject_put(&pdata->clk_cap_kobj); + } +#endif + #ifdef CONFIG_TEGRA_SOC_HWPM struct tegra_soc_hwpm_ip_ops hwpm_ip_ops; nvdla_dbg_info(pdev, "hwpm ip %s unregister", pdev->name);