// SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note /* * * (C) COPYRIGHT 2019-2023 ARM Limited. All rights reserved. * * This program is free software and is provided to you under the terms of the * GNU General Public License version 2 as published by the Free Software * Foundation, and any use by you of this program is subject to the terms * of such GNU license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, you can access it online at * http://www.gnu.org/licenses/gpl-2.0.html. * */ #include #include #include #include #include #include "mali_kbase.h" #include "mali_kbase_config_defaults.h" #include "mali_kbase_csf_firmware.h" #include "mali_kbase_csf_timeout.h" #include "mali_kbase_reset_gpu.h" #include "backend/gpu/mali_kbase_pm_internal.h" /** * set_timeout - set a new global progress timeout. * * @kbdev: Instance of a GPU platform device that implements a CSF interface. * @timeout: the maximum number of GPU cycles without forward progress to allow * to elapse before terminating a GPU command queue group. * * Return: 0 on success, or negative on failure * (e.g. -ERANGE if the requested timeout is too large). */ static int set_timeout(struct kbase_device *const kbdev, u64 const timeout) { if (timeout > GLB_PROGRESS_TIMER_TIMEOUT_MAX) { dev_err(kbdev->dev, "Timeout %llu is too large.\n", timeout); return -ERANGE; } dev_dbg(kbdev->dev, "New progress timeout: %llu cycles\n", timeout); atomic64_set(&kbdev->csf.progress_timeout, (s64)timeout); kbase_device_set_timeout(kbdev, CSF_SCHED_PROTM_PROGRESS_TIMEOUT, timeout, 1); return 0; } /** * progress_timeout_store - Store the progress_timeout device attribute. * @dev: The device that has the attribute. * @attr: The attributes of the sysfs file. * @buf: The value written to the sysfs file. * @count: The number of bytes written to the sysfs file. * * This function is called when the progress_timeout sysfs file is written to. * It checks the data written, and if valid updates the progress timeout value. * The function also checks gpu reset status, if the gpu is in reset process, * the function will return an error code (-EBUSY), and no change for timeout * value. * * Return: @count if the function succeeded. An error code on failure. */ static ssize_t progress_timeout_store(struct device *const dev, struct device_attribute *const attr, const char *const buf, size_t const count) { struct kbase_device *const kbdev = dev_get_drvdata(dev); int err; u64 timeout; CSTD_UNUSED(attr); if (!kbdev) return -ENODEV; err = kbase_reset_gpu_try_prevent(kbdev); if (err) { dev_warn(kbdev->dev, "Couldn't process progress_timeout write operation for GPU reset.\n"); return -EBUSY; } err = kstrtou64(buf, 0, &timeout); if (err) dev_err(kbdev->dev, "Couldn't process progress_timeout write operation.\n" "Use format \n"); else err = set_timeout(kbdev, timeout); if (!err) { kbase_csf_scheduler_pm_active(kbdev); err = kbase_csf_scheduler_killable_wait_mcu_active(kbdev); if (!err) err = kbase_csf_firmware_set_timeout(kbdev, timeout); kbase_csf_scheduler_pm_idle(kbdev); } kbase_reset_gpu_allow(kbdev); if (err) return err; return (ssize_t)count; } /** * progress_timeout_show - Show the progress_timeout device attribute. * @dev: The device that has the attribute. * @attr: The attributes of the sysfs file. * @buf: The output buffer to receive the global timeout. * * This function is called to get the progress timeout value. * * Return: The number of bytes output to @buf. */ static ssize_t progress_timeout_show(struct device *const dev, struct device_attribute *const attr, char *const buf) { struct kbase_device *const kbdev = dev_get_drvdata(dev); int err; CSTD_UNUSED(attr); if (!kbdev) return -ENODEV; err = scnprintf(buf, PAGE_SIZE, "%llu\n", kbase_csf_timeout_get(kbdev)); return err; } static DEVICE_ATTR_RW(progress_timeout); int kbase_csf_timeout_init(struct kbase_device *const kbdev) { u64 timeout = DEFAULT_PROGRESS_TIMEOUT; int err; #if IS_ENABLED(CONFIG_OF) /* Read "progress-timeout" property and fallback to "progress_timeout" * if not found. */ err = of_property_read_u64(kbdev->dev->of_node, "progress-timeout", &timeout); if (err == -EINVAL) err = of_property_read_u64(kbdev->dev->of_node, "progress_timeout", &timeout); if (!err) dev_info(kbdev->dev, "Found progress_timeout = %llu in Devicetree\n", timeout); #endif err = set_timeout(kbdev, timeout); if (err) return err; err = sysfs_create_file(&kbdev->dev->kobj, &dev_attr_progress_timeout.attr); if (err) dev_err(kbdev->dev, "SysFS file creation failed\n"); return err; } void kbase_csf_timeout_term(struct kbase_device *const kbdev) { sysfs_remove_file(&kbdev->dev->kobj, &dev_attr_progress_timeout.attr); } u64 kbase_csf_timeout_get(struct kbase_device *const kbdev) { return (u64)atomic64_read(&kbdev->csf.progress_timeout); }