// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2020 MediaTek Inc. */ #include #include #include #include #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); }