kernel-brax3-ubuntu-touch/drivers/thermal/mediatek/thermal_ipi.c
erascape f319b992b1 kernel-5.15: Initial import brax3 UT kernel
* halium configs enabled

Signed-off-by: erascape <erascape@proton.me>
2025-09-23 15:17:10 +00:00

268 lines
6.3 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2020 MediaTek Inc.
*/
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/kernel.h>
#include <linux/kobject.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
#include "thermal_ipi.h"
#define THERMAL_IPI_TIMEOUT_MS (2000)
#define for_each_ipi_target(i) \
for (i = 0; i < NUM_THERMAL_IPI_TARGET; i++)
#define is_target_invalid(t) (t != IPI_TARGET_ALL && t >= NUM_THERMAL_IPI_TARGET)
static DEFINE_MUTEX(ipi_send_lock);
static struct thermal_ipi_target_data ipi_data[NUM_THERMAL_IPI_TARGET] = {
[IPI_TARGET_SSPM] = {
.name = "sspm",
#if IS_ENABLED(CONFIG_MTK_TINYSYS_SSPM_SUPPORT)
.config = {
.dev = &sspm_ipidev,
.id = IPIS_C_THERMAL,
.use_platform_ipi = 0,
.opt = IPI_SEND_POLLING,
},
#endif
},
[IPI_TARGET_MCUPM] = {
.name = "mcupm",
#if IS_ENABLED(CONFIG_MTK_TINYSYS_MCUPM_SUPPORT)
.config = {
/* symbol is not exported */
/* .dev = &mcupm_ipidev, */
.id = CH_S_PLATFORM,
.use_platform_ipi = 1,
.opt = IPI_SEND_WAIT,
},
#endif
},
};
static int thermal_ipi_get_idx(char *name)
{
int i;
if (!strncasecmp(name, "all", 3))
return IPI_TARGET_ALL;
for_each_ipi_target(i) {
if (!strncasecmp(name, ipi_data[i].name, strlen(name)))
return i;
}
return -1;
}
static int thermal_ipi_register(enum thermal_ipi_target target)
{
struct thermal_ipi_config *cfg;
int ret = -1;
if (target >= NUM_THERMAL_IPI_TARGET)
return -EINVAL;
cfg = &ipi_data[target].config;
if (!cfg->dev) {
pr_err("[thermal_ipi] target %s is not support\n", ipi_data[target].name);
return -EPERM;
}
/* no need to register if we share IPI pin with platform driver */
if (cfg->use_platform_ipi)
goto register_done;
ret = mtk_ipi_register(cfg->dev, cfg->id, NULL, NULL, (void *)&cfg->ack_data);
if (ret != 0) {
pr_err("[thermal_ipi] Fail to register %s IPI, ret:%d\n",
ipi_data[target].name, ret);
return ret;
}
register_done:
pr_info("[thermal_ipi] register %s ipi done\n", ipi_data[target].name);
ipi_data[target].is_registered = 1;
return 0;
}
static int thermal_ipi_send(unsigned int cmd, enum thermal_ipi_target target,
struct thermal_ipi_data *thermal_data)
{
struct thermal_ipi_config *cfg;
unsigned int ack_data;
int ret = -1;
if (target >= NUM_THERMAL_IPI_TARGET)
return IPI_NOT_SUPPORT;
mutex_lock(&ipi_send_lock);
if (!ipi_data[target].is_registered) {
ack_data = IPI_NOT_SUPPORT;
goto end;
}
cfg = &ipi_data[target].config;
thermal_data->cmd = cmd;
ret = mtk_ipi_send_compl(cfg->dev, cfg->id, cfg->opt, thermal_data,
THERMAL_SLOT_NUM, THERMAL_IPI_TIMEOUT_MS);
if (ret != 0) {
pr_err("[thermal_ipi] send cmd(%d) to %s error ret:%d\n",
cmd, ipi_data[target].name, ret);
ack_data = IPI_FAIL;
goto end;
}
ack_data = cfg->ack_data;
pr_info("[thermal_ipi] %s cmd(%d) send done, ack: %u\n",
ipi_data[target].name, cmd, ack_data);
end:
mutex_unlock(&ipi_send_lock);
return ack_data;
}
static void thermal_ipi_send_all(unsigned int cmd, struct thermal_ipi_data *thermal_data)
{
unsigned int i;
for_each_ipi_target(i)
thermal_ipi_send(cmd, (enum thermal_ipi_target)i, thermal_data);
}
static int thermal_ipi_set_throttle_enable(enum thermal_ipi_target target, int enable)
{
struct thermal_ipi_data thermal_data;
unsigned int ipi_cmd;
int ret = 0;
if (is_target_invalid(target))
return -EINVAL;
ipi_cmd = THERMAL_THROTTLE_DISABLE;
thermal_data.arg[0] = (enable) ? 0 : 1;
thermal_data.arg[1] = 0;
thermal_data.arg[2] = 0;
if (target == IPI_TARGET_ALL)
thermal_ipi_send_all(ipi_cmd, &thermal_data);
else
ret = thermal_ipi_send(ipi_cmd, target, &thermal_data);
return ret;
}
static ssize_t setting_store(struct kobject *kobj,
struct kobj_attribute *attr, const char *buf, size_t count)
{
int idx;
char cmd[20], name[THERMAL_TARGET_NAME_LEN];
if (sscanf(buf, "%9s %19s", name, cmd) == 2) {
idx = thermal_ipi_get_idx(name);
if (idx < 0) {
pr_err("[thermal_ipi] target %s not found\n", name);
return -EINVAL;
}
pr_info("[thermal_ipi] idx(%d) cmd(%s)\n", idx, cmd);
if (!strncasecmp(cmd, "disable", 7)) {
thermal_ipi_set_throttle_enable((enum thermal_ipi_target)idx, 0);
} else if (!strncasecmp(cmd, "enable", 6)) {
thermal_ipi_set_throttle_enable((enum thermal_ipi_target)idx, 1);
} else {
pr_err("[thermal_ipi] invalid cmd(%s)\n", cmd);
return -EINVAL;
}
} else {
pr_err("[thermal_ipi] invalid input\n");
return -EINVAL;
}
return count;
}
static struct kobj_attribute thermal_ipi_attr = __ATTR_WO(setting);
static struct attribute *thermal_ipi_attrs[] = {
&thermal_ipi_attr.attr,
NULL
};
static struct attribute_group thermal_ipi_attr_group = {
.name = "thermal_ipi",
.attrs = thermal_ipi_attrs,
};
static const struct of_device_id thermal_ipi_of_match[] = {
{ .compatible = "mediatek,thermal-ipi", },
{},
};
MODULE_DEVICE_TABLE(of, thermal_ipi_of_match);
static int thermal_ipi_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct device *dev = &pdev->dev;
unsigned int target_bitmask;
int ret, i;
if (!np) {
dev_err(dev, "thermal_ipi DT node not found\n");
return -ENODEV;
}
ret = of_property_read_u32(np, "target-bitmask", &target_bitmask);
if (ret < 0) {
dev_err(dev, "failed to ipi target bitmask from device tree\n");
return -EINVAL;
}
#if IS_ENABLED(CONFIG_MTK_TINYSYS_MCUPM_SUPPORT)
ipi_data[IPI_TARGET_MCUPM].config.dev = (struct mtk_ipi_device *)get_mcupm_ipidev();
#endif
for_each_ipi_target(i) {
if (target_bitmask & (1 << i))
thermal_ipi_register((enum thermal_ipi_target)i);
}
ret = sysfs_create_group(kernel_kobj, &thermal_ipi_attr_group);
if (ret)
dev_err(dev, "failed to create thermal_ipi sysfs, ret=%d!\n", ret);
return ret;
}
static int thermal_ipi_remove(struct platform_device *pdev)
{
return sysfs_create_group(kernel_kobj, &thermal_ipi_attr_group);
}
static struct platform_driver thermal_ipi_driver = {
.probe = thermal_ipi_probe,
.remove = thermal_ipi_remove,
.driver = {
.name = "mtk-thermal-ipi",
.of_match_table = thermal_ipi_of_match,
},
};
module_platform_driver(thermal_ipi_driver);
MODULE_AUTHOR("Shun-Yao Yang <brian-sy.yang@mediatek.com>");
MODULE_DESCRIPTION("Mediatek thermal ipi driver");
MODULE_LICENSE("GPL v2");