294 lines
6.3 KiB
C
294 lines
6.3 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (c) 2020 MediaTek Inc.
|
|
*/
|
|
|
|
#include <linux/debugfs.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/uaccess.h>
|
|
#include "md_cooling.h"
|
|
|
|
#define MD_COOLING_DEBUGFS_ENTRY_RO(name) \
|
|
static int md_cooling_##name##_open(struct inode *i, struct file *file) \
|
|
{ \
|
|
return single_open(file, md_cooling_##name##_show, i->i_private); \
|
|
} \
|
|
\
|
|
static const struct file_operations md_cooling_##name##_fops = { \
|
|
.owner = THIS_MODULE, \
|
|
.open = md_cooling_##name##_open, \
|
|
.read = seq_read, \
|
|
.llseek = seq_lseek, \
|
|
.release = single_release, \
|
|
}
|
|
|
|
#define MD_COOLING_DEBUGFS_ENTRY_RW(name) \
|
|
static int md_cooling_##name##_open(struct inode *i, struct file *file) \
|
|
{ \
|
|
return single_open(file, md_cooling_##name##_show, i->i_private); \
|
|
} \
|
|
\
|
|
static const struct file_operations md_cooling_##name##_fops = { \
|
|
.owner = THIS_MODULE, \
|
|
.open = md_cooling_##name##_open, \
|
|
.read = seq_read, \
|
|
.write = md_cooling_##name##_write, \
|
|
.llseek = seq_lseek, \
|
|
.release = single_release, \
|
|
}
|
|
|
|
static struct dentry *mdc_debug_dir;
|
|
static int ca_ctrl;
|
|
static int pa_ctrl;
|
|
static int duty_ctrl;
|
|
|
|
static int md_cooling_status_show(struct seq_file *m, void *unused)
|
|
{
|
|
struct md_cooling_device *md_cdev;
|
|
int i, j;
|
|
unsigned int pa_num = get_pa_num();
|
|
|
|
seq_printf(m, "PA num = %d\n", pa_num);
|
|
seq_printf(m, "MD status = %d\n", get_md_cooling_status());
|
|
|
|
for (i = 0; i < NR_MD_COOLING_TYPE; i++) {
|
|
for (j = 0; j < pa_num; j++) {
|
|
md_cdev = get_md_cdev((enum md_cooling_type)i, j);
|
|
if (!md_cdev)
|
|
continue;
|
|
|
|
seq_printf(m, "\n[%s, %d]\n", md_cdev->name,
|
|
md_cdev->pa_id);
|
|
seq_printf(m, "target/max = %ld/%ld\n",
|
|
md_cdev->target_state, md_cdev->max_state);
|
|
|
|
if (md_cdev->type == MD_COOLING_TYPE_TX_PWR)
|
|
seq_printf(m, "throttle tx_pwr = %d/%d/%d\n",
|
|
md_cdev->throttle_tx_power[0],
|
|
md_cdev->throttle_tx_power[1],
|
|
md_cdev->throttle_tx_power[2]);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
MD_COOLING_DEBUGFS_ENTRY_RO(status);
|
|
|
|
static int md_cooling_duty_ctrl_show(struct seq_file *m, void *unused)
|
|
{
|
|
seq_printf(m, "duty ctrl is %s\n", duty_ctrl ? "ON" : "OFF");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static ssize_t md_cooling_duty_ctrl_write(struct file *flip,
|
|
const char *ubuf, size_t cnt, loff_t *data)
|
|
{
|
|
int ret = cnt, msg, no_ims, active, suspend, enable;
|
|
char *buf;
|
|
|
|
if (cnt > 32)
|
|
return -ENOMEM;
|
|
|
|
buf = kmalloc(cnt + 1, GFP_KERNEL);
|
|
if (buf == NULL)
|
|
return -ENOMEM;
|
|
|
|
if (copy_from_user(buf, ubuf, cnt)) {
|
|
ret = -EFAULT;
|
|
goto err;
|
|
}
|
|
buf[cnt] = '\0';
|
|
|
|
if ((sscanf(buf, "%d %d %d", &no_ims, &active, &suspend) != 3)) {
|
|
ret = -EINVAL;
|
|
goto err;
|
|
}
|
|
|
|
if (get_md_cooling_status() != MD_LV_THROTTLE_DISABLED) {
|
|
ret = -EBUSY;
|
|
goto err;
|
|
}
|
|
|
|
if (no_ims) {
|
|
/* set active/suspend=1/255 for no IMS case */
|
|
msg = duty_ctrl_to_tmc_msg(1, 255, 0);
|
|
enable = 1;
|
|
} else if (active >= 1 && active <= 255
|
|
&& suspend >= 1 && suspend <= 255) {
|
|
msg = duty_ctrl_to_tmc_msg(active, suspend, 1);
|
|
enable = 1;
|
|
} else {
|
|
msg = TMC_THROTTLE_DISABLE_MSG;
|
|
enable = 0;
|
|
}
|
|
|
|
if (send_throttle_msg(msg)) {
|
|
ret = -EBUSY;
|
|
goto err;
|
|
}
|
|
|
|
duty_ctrl = enable;
|
|
|
|
err:
|
|
kfree(buf);
|
|
|
|
return ret;
|
|
}
|
|
MD_COOLING_DEBUGFS_ENTRY_RW(duty_ctrl);
|
|
|
|
static int md_cooling_ca_ctrl_show(struct seq_file *m, void *unused)
|
|
{
|
|
seq_printf(m, "CA ctrl is %s\n", ca_ctrl ? "ON" : "OFF");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static ssize_t md_cooling_ca_ctrl_write(struct file *flip,
|
|
const char *ubuf, size_t cnt, loff_t *data)
|
|
{
|
|
int val;
|
|
|
|
if (kstrtoint_from_user(ubuf, cnt, 0, &val))
|
|
return -EFAULT;
|
|
|
|
if (get_md_cooling_status() != MD_LV_THROTTLE_DISABLED)
|
|
return -EBUSY;
|
|
|
|
if (send_throttle_msg(ca_ctrl_to_tmc_msg(val)))
|
|
return -EBUSY;
|
|
|
|
ca_ctrl = val;
|
|
|
|
return cnt;
|
|
}
|
|
MD_COOLING_DEBUGFS_ENTRY_RW(ca_ctrl);
|
|
|
|
static int md_cooling_pa_ctrl_show(struct seq_file *m, void *unused)
|
|
{
|
|
seq_printf(m, "PA ctrl is %s\n", pa_ctrl ? "ON" : "OFF");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static ssize_t md_cooling_pa_ctrl_write(struct file *flip,
|
|
const char *ubuf, size_t cnt, loff_t *data)
|
|
{
|
|
int val;
|
|
|
|
if (kstrtoint_from_user(ubuf, cnt, 0, &val))
|
|
return -EFAULT;
|
|
|
|
if (get_md_cooling_status() != MD_LV_THROTTLE_DISABLED)
|
|
return -EBUSY;
|
|
|
|
if (send_throttle_msg(pa_ctrl_to_tmc_msg(val)))
|
|
return -EBUSY;
|
|
|
|
pa_ctrl = val;
|
|
|
|
return cnt;
|
|
}
|
|
MD_COOLING_DEBUGFS_ENTRY_RW(pa_ctrl);
|
|
|
|
static int md_cooling_update_tx_pwr_show(struct seq_file *m, void *unused)
|
|
{
|
|
struct md_cooling_device *md_cdev;
|
|
int i;
|
|
|
|
for (i = 0; i < get_pa_num(); i++) {
|
|
md_cdev = get_md_cdev(MD_COOLING_TYPE_TX_PWR, i);
|
|
if (!md_cdev)
|
|
continue;
|
|
|
|
seq_printf(m, "PA%d throttle tx_pwr = %d/%d/%d\n",
|
|
i,
|
|
md_cdev->throttle_tx_power[0],
|
|
md_cdev->throttle_tx_power[1],
|
|
md_cdev->throttle_tx_power[2]);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static ssize_t md_cooling_update_tx_pwr_write(struct file *flip,
|
|
const char *ubuf, size_t cnt, loff_t *data)
|
|
{
|
|
int ret = cnt, id, tx_pwr_lv1, tx_pwr_lv2, tx_pwr_lv3;
|
|
unsigned int tx_pwr[MAX_NUM_TX_PWR_STATE];
|
|
char *buf;
|
|
|
|
if (cnt > 32)
|
|
return -ENOMEM;
|
|
|
|
buf = kmalloc(cnt + 1, GFP_KERNEL);
|
|
if (buf == NULL)
|
|
return -ENOMEM;
|
|
|
|
if (copy_from_user(buf, ubuf, cnt)) {
|
|
ret = -EFAULT;
|
|
goto err;
|
|
}
|
|
buf[cnt] = '\0';
|
|
|
|
if ((sscanf(buf, "%d %d %d %d", &id, &tx_pwr_lv1,
|
|
&tx_pwr_lv2, &tx_pwr_lv3) != (MAX_NUM_TX_PWR_STATE + 1))) {
|
|
ret = -EINVAL;
|
|
goto err;
|
|
}
|
|
|
|
if (id >= get_pa_num()) {
|
|
pr_err("Invalid PA id(%d)\n", id);
|
|
ret = -EINVAL;
|
|
goto err;
|
|
}
|
|
|
|
tx_pwr[0] = tx_pwr_lv1;
|
|
tx_pwr[1] = tx_pwr_lv2;
|
|
tx_pwr[2] = tx_pwr_lv3;
|
|
|
|
update_throttle_power(id, tx_pwr);
|
|
|
|
err:
|
|
kfree(buf);
|
|
|
|
return ret;
|
|
}
|
|
MD_COOLING_DEBUGFS_ENTRY_RW(update_tx_pwr);
|
|
|
|
int md_cooling_debugfs_init(void)
|
|
{
|
|
mdc_debug_dir = debugfs_create_dir("md_cooling", NULL);
|
|
if (!mdc_debug_dir) {
|
|
pr_err("failed to create mdc_debug_dir\n");
|
|
goto failed;
|
|
}
|
|
|
|
if (!debugfs_create_file("status", 0440,
|
|
mdc_debug_dir, NULL, &md_cooling_status_fops))
|
|
goto failed;
|
|
if (!debugfs_create_file("duty_ctrl", 0640,
|
|
mdc_debug_dir, NULL, &md_cooling_duty_ctrl_fops))
|
|
goto failed;
|
|
if (!debugfs_create_file("ca_ctrl", 0640,
|
|
mdc_debug_dir, NULL, &md_cooling_ca_ctrl_fops))
|
|
goto failed;
|
|
if (!debugfs_create_file("pa_ctrl", 0640,
|
|
mdc_debug_dir, NULL, &md_cooling_pa_ctrl_fops))
|
|
goto failed;
|
|
if (!debugfs_create_file("update_tx_pwr", 0640,
|
|
mdc_debug_dir, NULL, &md_cooling_update_tx_pwr_fops))
|
|
goto failed;
|
|
|
|
return 0;
|
|
|
|
failed:
|
|
return -ENODEV;
|
|
}
|
|
|
|
void md_cooling_debugfs_exit(void)
|
|
{
|
|
debugfs_remove_recursive(mdc_debug_dir);
|
|
}
|
|
|