mirror of
git://nv-tegra.nvidia.com/linux-nv-oot.git
synced 2025-12-22 09:11:26 +03:00
The driver remove function is a function pointer and therefore, it does not make sense to define the function as an 'inline'. Update the coccinelle script and drivers to remove the inline statement. Bug 4749580 Change-Id: Ia03691b75c4edffe609f27468b911a92a5ddbd68 Signed-off-by: Jon Hunter <jonathanh@nvidia.com> Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/3233980 Tested-by: mobile promotions <svcmobile_promotions@nvidia.com> Reviewed-by: mobile promotions <svcmobile_promotions@nvidia.com>
272 lines
6.9 KiB
C
272 lines
6.9 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
// SPDX-FileCopyrightText: Copyright (c) 2023-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
|
|
#include <nvidia/conftest.h>
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/thermal.h>
|
|
#include <linux/wait.h>
|
|
|
|
enum cdev_states {
|
|
CDEV_INACTIVE,
|
|
CDEV_ACTIVE,
|
|
CDEV_DESTROY,
|
|
};
|
|
|
|
struct therm_trip_event {
|
|
unsigned int cur_state;
|
|
unsigned int max_state;
|
|
unsigned int event_timeout_ms;
|
|
struct mutex cur_state_lock;
|
|
struct mutex event_timeout_lock;
|
|
struct thermal_cooling_device *cdev;
|
|
wait_queue_head_t waitq_head;
|
|
};
|
|
|
|
static int tte_cdev_get_max_state(struct thermal_cooling_device *tcd,
|
|
unsigned long *state)
|
|
{
|
|
struct therm_trip_event *tte = tcd->devdata;
|
|
|
|
*state = tte->max_state;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int tte_cdev_get_cur_state(struct thermal_cooling_device *tcd,
|
|
unsigned long *state)
|
|
{
|
|
struct therm_trip_event *tte = tcd->devdata;
|
|
|
|
*state = tte->cur_state;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int tte_cdev_set_cur_state(struct thermal_cooling_device *tcd,
|
|
unsigned long state)
|
|
{
|
|
struct therm_trip_event *tte = tcd->devdata;
|
|
struct device *dev = &tcd->device;
|
|
|
|
if (state > tte->max_state)
|
|
return -EINVAL;
|
|
|
|
if (state == tte->cur_state)
|
|
return 0;
|
|
|
|
dev_notice(dev, "%s cooling state: %u -> %lu\n", tcd->type,
|
|
tte->cur_state, state);
|
|
|
|
/*
|
|
* Although tcd->lock is already held in the thermal framework, a lock
|
|
* is added here to avoid a race condition between set_cur_state() and
|
|
* driver removal.
|
|
*/
|
|
mutex_lock(&tte->cur_state_lock);
|
|
tte->cur_state = state;
|
|
mutex_unlock(&tte->cur_state_lock);
|
|
|
|
if (tte->cur_state != CDEV_INACTIVE) {
|
|
if (wq_has_sleeper(&tte->waitq_head)) {
|
|
wake_up_interruptible_all(&tte->waitq_head);
|
|
dev_dbg(dev, "THERMAL_EVENT_TRIPPED!\n");
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static ssize_t thermal_trip_event_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct therm_trip_event *tte = dev_get_drvdata(dev);
|
|
|
|
return sprintf(buf, "%d\n", tte->cur_state);
|
|
}
|
|
|
|
static ssize_t thermal_trip_event_block_show(struct device *dev,
|
|
struct device_attribute *attr,
|
|
char *buf)
|
|
{
|
|
struct therm_trip_event *tte = dev_get_drvdata(dev);
|
|
unsigned int event_timeout_ms = tte->event_timeout_ms;
|
|
int ret;
|
|
|
|
if (event_timeout_ms > 0)
|
|
ret = wait_event_interruptible_timeout(
|
|
tte->waitq_head, tte->cur_state != CDEV_INACTIVE,
|
|
msecs_to_jiffies(event_timeout_ms));
|
|
else
|
|
ret = wait_event_interruptible(tte->waitq_head,
|
|
tte->cur_state != CDEV_INACTIVE);
|
|
|
|
/*
|
|
* -ERESTARTSYS is returned to avoid false alarm and to resume the call.
|
|
*/
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
return sprintf(buf, "%d\n", tte->cur_state);
|
|
}
|
|
|
|
static ssize_t thermal_trip_event_block_store(struct device *dev,
|
|
struct device_attribute *attr,
|
|
const char *buf, size_t count)
|
|
{
|
|
unsigned int val;
|
|
struct therm_trip_event *tte = dev_get_drvdata(dev);
|
|
|
|
if (kstrtouint(buf, 0, &val))
|
|
return -EINVAL;
|
|
|
|
mutex_lock(&tte->event_timeout_lock);
|
|
tte->event_timeout_ms = val;
|
|
mutex_unlock(&tte->event_timeout_lock);
|
|
|
|
return count;
|
|
}
|
|
|
|
static struct thermal_cooling_device_ops tte_cdev_ops = {
|
|
.get_max_state = tte_cdev_get_max_state,
|
|
.get_cur_state = tte_cdev_get_cur_state,
|
|
.set_cur_state = tte_cdev_set_cur_state,
|
|
};
|
|
|
|
static DEVICE_ATTR_RO(thermal_trip_event);
|
|
static DEVICE_ATTR_RW(thermal_trip_event_block);
|
|
|
|
static const struct attribute *tte_cdev_attr[] = {
|
|
&dev_attr_thermal_trip_event.attr,
|
|
&dev_attr_thermal_trip_event_block.attr,
|
|
NULL,
|
|
};
|
|
|
|
static int thermal_trip_event_probe(struct platform_device *pdev)
|
|
{
|
|
struct device *dev = &pdev->dev;
|
|
struct device_node *np = dev->of_node;
|
|
struct therm_trip_event *tte;
|
|
const char *cdev_type;
|
|
int ret;
|
|
|
|
tte = devm_kzalloc(dev, sizeof(struct therm_trip_event), GFP_KERNEL);
|
|
if (!tte)
|
|
return -ENOMEM;
|
|
|
|
if (of_property_read_string(np, "cdev-type", &cdev_type) != 0) {
|
|
dev_err(dev, "invalid cdev-type property of %pOFn\n", np);
|
|
return -EINVAL;
|
|
}
|
|
|
|
tte->cur_state = CDEV_INACTIVE;
|
|
tte->max_state = CDEV_DESTROY;
|
|
mutex_init(&tte->cur_state_lock);
|
|
mutex_init(&tte->event_timeout_lock);
|
|
init_waitqueue_head(&tte->waitq_head);
|
|
dev_set_drvdata(dev, tte);
|
|
|
|
tte->cdev = thermal_of_cooling_device_register(np, cdev_type, tte,
|
|
&tte_cdev_ops);
|
|
if (IS_ERR(tte->cdev)) {
|
|
ret = PTR_ERR(tte->cdev);
|
|
goto destroy_lock;
|
|
}
|
|
|
|
ret = sysfs_create_files(&dev->kobj, tte_cdev_attr);
|
|
if (ret) {
|
|
dev_err(dev, "failed to create sysfs files\n");
|
|
goto free_cdev;
|
|
}
|
|
|
|
ret = sysfs_create_link(&tte->cdev->device.kobj, &dev->kobj,
|
|
"thermal_trip_event");
|
|
if (ret) {
|
|
dev_err(dev, "failed to create sysfs symlink\n");
|
|
goto free_sysfs_files;
|
|
}
|
|
|
|
dev_info(dev, "cooling device registered.\n");
|
|
return 0;
|
|
|
|
free_sysfs_files:
|
|
sysfs_remove_files(&dev->kobj, tte_cdev_attr);
|
|
free_cdev:
|
|
thermal_cooling_device_unregister(tte->cdev);
|
|
destroy_lock:
|
|
mutex_destroy(&tte->event_timeout_lock);
|
|
mutex_destroy(&tte->cur_state_lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int thermal_trip_event_remove(struct platform_device *pdev)
|
|
{
|
|
struct device *dev = &pdev->dev;
|
|
struct therm_trip_event *tte = dev_get_drvdata(dev);
|
|
struct thermal_cooling_device *cdev = tte->cdev;
|
|
|
|
/*
|
|
* There could be processes waiting for the event to happen while the
|
|
* driver is being removed. To have smooth removal, wake them up by
|
|
* updating the cur_state. The cooling state change here is not
|
|
* perceived by the thermal framework, but it's not a big deal as the
|
|
* cooling device is going to be destroyed soon.
|
|
*/
|
|
mutex_lock(&tte->cur_state_lock);
|
|
tte->cur_state = CDEV_DESTROY;
|
|
mutex_unlock(&tte->cur_state_lock);
|
|
|
|
if (wq_has_sleeper(&tte->waitq_head)) {
|
|
wake_up_interruptible_all(&tte->waitq_head);
|
|
dev_dbg(dev, "THERMAL_EVENT_DESTROYED!\n");
|
|
}
|
|
|
|
sysfs_remove_link(&cdev->device.kobj, "thermal_trip_event");
|
|
sysfs_remove_files(&dev->kobj, tte_cdev_attr);
|
|
thermal_cooling_device_unregister(cdev);
|
|
mutex_destroy(&tte->event_timeout_lock);
|
|
mutex_destroy(&tte->cur_state_lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct of_device_id thermal_trip_event_of_match[] = {
|
|
{
|
|
.compatible = "thermal-trip-event",
|
|
},
|
|
{},
|
|
};
|
|
MODULE_DEVICE_TABLE(of, thermal_trip_event_of_match);
|
|
|
|
#if defined(NV_PLATFORM_DRIVER_STRUCT_REMOVE_RETURNS_VOID) /* Linux v6.11 */
|
|
static void thermal_trip_event_remove_wrapper(struct platform_device *pdev)
|
|
{
|
|
thermal_trip_event_remove(pdev);
|
|
}
|
|
#else
|
|
static int thermal_trip_event_remove_wrapper(struct platform_device *pdev)
|
|
{
|
|
return thermal_trip_event_remove(pdev);
|
|
}
|
|
#endif
|
|
|
|
static struct platform_driver thermal_trip_event_driver = {
|
|
.driver = {
|
|
.name = "thermal-trip-event",
|
|
.owner = THIS_MODULE,
|
|
.of_match_table = thermal_trip_event_of_match,
|
|
},
|
|
.probe = thermal_trip_event_probe,
|
|
.remove = thermal_trip_event_remove_wrapper,
|
|
};
|
|
|
|
module_platform_driver(thermal_trip_event_driver);
|
|
|
|
MODULE_AUTHOR("Sreenivasulu Velpula <svelpula@nvidia.com>");
|
|
MODULE_AUTHOR("Yi-Wei Wang <yiweiw@nvidia.com>");
|
|
MODULE_DESCRIPTION("Thermal Trip Event Driver");
|
|
MODULE_LICENSE("GPL v2");
|