diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index 1390e27b91da..0391fee270f3 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -725,6 +725,16 @@ config QCOM_SOC_SLEEP_STATS various SoC level low power modes statistics and export to sysfs interface. +config QCOM_CDSP_RM + tristate "CDSP request manager" + depends on QCOM_GLINK + help + This driver serves CDSP requests for CPU L3 clock and CPU QoS thus + improving CDSP performance. Using this driver, CDSP can set appropriate + CPU L3 clock for improving IO-Coherent throughput and opt for QoS mode + to improve RPC latency. The driver also registers cooling devices for + CDSP subsystem and implements Cx ipeak limit management. + config QCOM_WATCHDOG tristate "Qualcomm Watchdog Support" depends on ARCH_QCOM diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index a26ecc39ee41..9b5209bc9949 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -70,6 +70,7 @@ obj-$(CONFIG_QTI_RPM_STATS_LOG) += rpmh_master_stat.o obj-$(CONFIG_QPNP_PBS) += qpnp-pbs.o obj-$(CONFIG_QCOM_WATCHDOG) += qcom_watchdog.o obj-$(CONFIG_MSM_SPCOM) += spcom.o +obj-$(CONFIG_QCOM_CDSP_RM) += cdsprm.o obj-$(CONFIG_QCOM_FSA4480_I2C) += fsa4480-i2c.o obj-$(CONFIG_QCOM_EUD) += eud.o obj-$(CONFIG_QCOM_GUESTVM) += guestvm_loader.o diff --git a/drivers/soc/qcom/cdsprm.c b/drivers/soc/qcom/cdsprm.c new file mode 100644 index 000000000000..f88cfc5d1656 --- /dev/null +++ b/drivers/soc/qcom/cdsprm.c @@ -0,0 +1,1244 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2018-2020, The Linux Foundation. All rights reserved. + */ + +/* + * This driver uses rpmsg to communicate with CDSP and receive requests + * for CPU L3 frequency and QoS along with Cx Limit management and + * thermal cooling handling. + */ + +#define pr_fmt(fmt) "cdsprm: " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SYSMON_CDSP_FEATURE_L3_RX 1 +#define SYSMON_CDSP_FEATURE_RM_RX 2 +#define SYSMON_CDSP_FEATURE_COMPUTE_PRIO_TX 3 +#define SYSMON_CDSP_FEATURE_NPU_LIMIT_TX 4 +#define SYSMON_CDSP_FEATURE_NPU_LIMIT_RX 5 +#define SYSMON_CDSP_FEATURE_NPU_ACTIVITY_TX 6 +#define SYSMON_CDSP_FEATURE_NPU_ACTIVITY_RX 7 +#define SYSMON_CDSP_FEATURE_NPU_CORNER_TX 8 +#define SYSMON_CDSP_FEATURE_NPU_CORNER_RX 9 +#define SYSMON_CDSP_FEATURE_THERMAL_LIMIT_TX 10 +#define SYSMON_CDSP_FEATURE_CAMERA_ACTIVITY_TX 11 +#define SYSMON_CDSP_FEATURE_VERSION_RX 12 + +#define SYSMON_CDSP_QOS_FLAG_IGNORE 0 +#define SYSMON_CDSP_QOS_FLAG_ENABLE 1 +#define SYSMON_CDSP_QOS_FLAG_DISABLE 2 +#define QOS_LATENCY_DISABLE_VALUE -1 +#define SYS_CLK_TICKS_PER_MS 19200 +#define CDSPRM_MSG_QUEUE_DEPTH 50 +#define CDSP_THERMAL_MAX_STATE 10 +#define HVX_THERMAL_MAX_STATE 10 + +struct sysmon_l3_msg { + unsigned int l3_clock_khz; +}; + +struct sysmon_rm_msg { + unsigned int b_qos_flag; + unsigned int timetick_low; + unsigned int timetick_high; +}; + +struct sysmon_npu_limit_msg { + unsigned int corner; +}; + +struct sysmon_npu_limit_ack { + unsigned int corner; +}; + +struct sysmon_compute_prio_msg { + unsigned int priority_idx; +}; + +struct sysmon_npu_activity_msg { + unsigned int b_enabled; +}; + +struct sysmon_npu_corner_msg { + unsigned int corner; +}; + +struct sysmon_thermal_msg { + unsigned short hvx_level; + unsigned short cdsp_level; +}; + +struct sysmon_camera_msg { + unsigned int b_enabled; +}; + +struct sysmon_version_msg { + unsigned int id; +}; + +struct sysmon_msg { + unsigned int feature_id; + union { + struct sysmon_l3_msg l3_struct; + struct sysmon_rm_msg rm_struct; + struct sysmon_npu_limit_msg npu_limit; + struct sysmon_npu_activity_msg npu_activity; + struct sysmon_npu_corner_msg npu_corner; + struct sysmon_version_msg version; + } fs; + unsigned int size; +}; + +struct sysmon_msg_tx { + unsigned int feature_id; + union { + struct sysmon_npu_limit_ack npu_limit_ack; + struct sysmon_compute_prio_msg compute_prio; + struct sysmon_npu_activity_msg npu_activity; + struct sysmon_thermal_msg thermal; + struct sysmon_npu_corner_msg npu_corner; + struct sysmon_camera_msg camera; + } fs; + unsigned int size; +}; + +enum delay_state { + CDSP_DELAY_THREAD_NOT_STARTED = 0, + CDSP_DELAY_THREAD_STARTED = 1, + CDSP_DELAY_THREAD_BEFORE_SLEEP = 2, + CDSP_DELAY_THREAD_AFTER_SLEEP = 3, + CDSP_DELAY_THREAD_EXITING = 4, +}; + +struct cdsprm_request { + struct list_head node; + struct sysmon_msg msg; + bool busy; +}; + +struct cdsprm { + unsigned int cdsp_version; + unsigned int event; + struct completion msg_avail; + struct cdsprm_request msg_queue[CDSPRM_MSG_QUEUE_DEPTH]; + unsigned int msg_queue_idx; + struct task_struct *cdsprm_wq_task; + struct workqueue_struct *delay_work_queue; + struct work_struct cdsprm_delay_work; + struct mutex rm_lock; + spinlock_t l3_lock; + spinlock_t list_lock; + struct mutex rpmsg_lock; + struct rpmsg_device *rpmsgdev; + enum delay_state dt_state; + unsigned long long timestamp; + struct pm_qos_request pm_qos_req; + unsigned int qos_latency_us; + unsigned int qos_max_ms; + unsigned int compute_prio_idx; + struct mutex npu_activity_lock; + bool b_cx_limit_en; + unsigned int b_npu_enabled; + unsigned int b_camera_enabled; + unsigned int b_npu_activity_waiting; + unsigned int b_npu_corner_waiting; + struct completion npu_activity_complete; + struct completion npu_corner_complete; + unsigned int npu_enable_cnt; + enum cdsprm_npu_corner npu_corner; + enum cdsprm_npu_corner allowed_npu_corner; + enum cdsprm_npu_corner npu_corner_limit; + struct mutex thermal_lock; + unsigned int thermal_cdsp_level; + unsigned int thermal_hvx_level; + struct thermal_cooling_device *cdsp_tcdev; + struct thermal_cooling_device *hvx_tcdev; + bool qos_request; + bool b_rpmsg_register; + bool b_qosinitdone; + bool b_applyingNpuLimit; + int latency_request; + int b_cdsprm_devinit; + int b_hvxrm_devinit; + struct dentry *debugfs_dir; + struct dentry *debugfs_file; + int (*set_l3_freq)(unsigned int freq_khz); + int (*set_l3_freq_cached)(unsigned int freq_khz); + int (*set_corner_limit)(enum cdsprm_npu_corner); + int (*set_corner_limit_cached)(enum cdsprm_npu_corner); +}; + +static struct cdsprm gcdsprm; +static LIST_HEAD(cdsprm_list); +static DECLARE_WAIT_QUEUE_HEAD(cdsprm_wq); + +/** + * cdsprm_register_cdspl3gov() - Register a method to set L3 clock + * frequency + * @arg: cdsprm_l3 structure with set L3 clock frequency method + * + * Note: To be called from cdspl3 governor only. Called when the governor is + * started. + */ +void cdsprm_register_cdspl3gov(struct cdsprm_l3 *arg) +{ + unsigned long flags; + + if (!arg) + return; + + spin_lock_irqsave(&gcdsprm.l3_lock, flags); + gcdsprm.set_l3_freq = arg->set_l3_freq; + spin_unlock_irqrestore(&gcdsprm.l3_lock, flags); +} +EXPORT_SYMBOL(cdsprm_register_cdspl3gov); + +int cdsprm_cxlimit_npu_limit_register( + const struct cdsprm_npu_limit_cbs *npu_limit_cb) +{ + if (!npu_limit_cb) + return -EINVAL; + + gcdsprm.set_corner_limit = npu_limit_cb->set_corner_limit; + + return 0; +} +EXPORT_SYMBOL(cdsprm_cxlimit_npu_limit_register); + +int cdsprm_cxlimit_npu_limit_deregister(void) +{ + if (!gcdsprm.set_corner_limit) + return -EINVAL; + + gcdsprm.set_corner_limit = NULL; + + return 0; +} +EXPORT_SYMBOL(cdsprm_cxlimit_npu_limit_deregister); + +int cdsprm_compute_core_set_priority(unsigned int priority_idx) +{ + struct sysmon_msg_tx rpmsg_msg_tx; + + gcdsprm.compute_prio_idx = priority_idx; + + if (gcdsprm.rpmsgdev && gcdsprm.cdsp_version) { + rpmsg_msg_tx.feature_id = + SYSMON_CDSP_FEATURE_COMPUTE_PRIO_TX; + rpmsg_msg_tx.fs.compute_prio.priority_idx = + priority_idx; + rpmsg_msg_tx.size = sizeof(rpmsg_msg_tx); + rpmsg_send(gcdsprm.rpmsgdev->ept, + &rpmsg_msg_tx, + sizeof(rpmsg_msg_tx)); + pr_debug("Compute core priority set to %d\n", + priority_idx); + } + + return 0; +} +EXPORT_SYMBOL(cdsprm_compute_core_set_priority); + +int cdsprm_cxlimit_npu_activity_notify(unsigned int b_enabled) +{ + int result = -EINVAL; + struct sysmon_msg_tx rpmsg_msg_tx; + + if (!gcdsprm.b_cx_limit_en) + return result; + + mutex_lock(&gcdsprm.npu_activity_lock); + if (b_enabled) + gcdsprm.npu_enable_cnt++; + else if (gcdsprm.npu_enable_cnt) + gcdsprm.npu_enable_cnt--; + + if ((gcdsprm.npu_enable_cnt && + gcdsprm.b_npu_enabled) || + (!gcdsprm.npu_enable_cnt && + !gcdsprm.b_npu_enabled)) { + mutex_unlock(&gcdsprm.npu_activity_lock); + return 0; + } + + gcdsprm.b_npu_enabled = b_enabled; + + if (gcdsprm.rpmsgdev && gcdsprm.cdsp_version) { + if (gcdsprm.b_npu_enabled) + gcdsprm.b_npu_activity_waiting++; + rpmsg_msg_tx.feature_id = + SYSMON_CDSP_FEATURE_NPU_ACTIVITY_TX; + rpmsg_msg_tx.fs.npu_activity.b_enabled = + gcdsprm.b_npu_enabled; + rpmsg_msg_tx.size = sizeof(rpmsg_msg_tx); + result = rpmsg_send(gcdsprm.rpmsgdev->ept, + &rpmsg_msg_tx, + sizeof(rpmsg_msg_tx)); + if (gcdsprm.b_npu_enabled && result) + gcdsprm.b_npu_activity_waiting--; + } + + if (gcdsprm.b_npu_enabled && !result) { + mutex_unlock(&gcdsprm.npu_activity_lock); + wait_for_completion(&gcdsprm.npu_activity_complete); + mutex_lock(&gcdsprm.npu_activity_lock); + gcdsprm.b_npu_activity_waiting--; + } + + mutex_unlock(&gcdsprm.npu_activity_lock); + return result; +} +EXPORT_SYMBOL(cdsprm_cxlimit_npu_activity_notify); + +enum cdsprm_npu_corner cdsprm_cxlimit_npu_corner_notify( + enum cdsprm_npu_corner corner) +{ + int result = -EINVAL; + enum cdsprm_npu_corner past_npu_corner; + enum cdsprm_npu_corner return_npu_corner = corner; + struct sysmon_msg_tx rpmsg_msg_tx; + + if (gcdsprm.b_applyingNpuLimit || !gcdsprm.b_cx_limit_en) + return corner; + + mutex_lock(&gcdsprm.npu_activity_lock); + past_npu_corner = gcdsprm.npu_corner; + gcdsprm.npu_corner = corner; + + if (gcdsprm.rpmsgdev && gcdsprm.cdsp_version) { + if ((gcdsprm.npu_corner > past_npu_corner) || + !gcdsprm.npu_corner) + gcdsprm.b_npu_corner_waiting++; + rpmsg_msg_tx.feature_id = + SYSMON_CDSP_FEATURE_NPU_CORNER_TX; + rpmsg_msg_tx.fs.npu_corner.corner = + (unsigned int)gcdsprm.npu_corner; + rpmsg_msg_tx.size = sizeof(rpmsg_msg_tx); + result = rpmsg_send(gcdsprm.rpmsgdev->ept, + &rpmsg_msg_tx, + sizeof(rpmsg_msg_tx)); + if (((gcdsprm.npu_corner > past_npu_corner) || + !gcdsprm.npu_corner) && result) + gcdsprm.b_npu_corner_waiting--; + } + + if (((gcdsprm.npu_corner > past_npu_corner) || + !gcdsprm.npu_corner) && !result) { + mutex_unlock(&gcdsprm.npu_activity_lock); + wait_for_completion(&gcdsprm.npu_corner_complete); + mutex_lock(&gcdsprm.npu_activity_lock); + if (gcdsprm.allowed_npu_corner) { + return_npu_corner = gcdsprm.allowed_npu_corner; + gcdsprm.npu_corner = gcdsprm.allowed_npu_corner; + } + gcdsprm.b_npu_corner_waiting--; + } + + mutex_unlock(&gcdsprm.npu_activity_lock); + return return_npu_corner; +} +EXPORT_SYMBOL(cdsprm_cxlimit_npu_corner_notify); + +int cdsprm_cxlimit_camera_activity_notify(unsigned int b_enabled) +{ + struct sysmon_msg_tx rpmsg_msg_tx; + + if (!gcdsprm.b_cx_limit_en) + return -EINVAL; + + gcdsprm.b_camera_enabled = b_enabled; + + if (gcdsprm.rpmsgdev && gcdsprm.cdsp_version) { + rpmsg_msg_tx.feature_id = + SYSMON_CDSP_FEATURE_CAMERA_ACTIVITY_TX; + rpmsg_msg_tx.fs.camera.b_enabled = + b_enabled; + rpmsg_msg_tx.size = sizeof(rpmsg_msg_tx); + rpmsg_send(gcdsprm.rpmsgdev->ept, + &rpmsg_msg_tx, + sizeof(rpmsg_msg_tx)); + } + + return 0; +} +EXPORT_SYMBOL(cdsprm_cxlimit_camera_activity_notify); + +static int cdsprm_thermal_cdsp_clk_limit(unsigned int level) +{ + int result = -EINVAL; + struct sysmon_msg_tx rpmsg_msg_tx; + + mutex_lock(&gcdsprm.thermal_lock); + + if (gcdsprm.rpmsgdev && gcdsprm.cdsp_version) { + rpmsg_msg_tx.feature_id = + SYSMON_CDSP_FEATURE_THERMAL_LIMIT_TX; + rpmsg_msg_tx.fs.thermal.hvx_level = + gcdsprm.thermal_hvx_level; + rpmsg_msg_tx.fs.thermal.cdsp_level = level; + rpmsg_msg_tx.size = sizeof(rpmsg_msg_tx); + result = rpmsg_send(gcdsprm.rpmsgdev->ept, + &rpmsg_msg_tx, + sizeof(rpmsg_msg_tx)); + } + + if (result == 0) + gcdsprm.thermal_cdsp_level = level; + + mutex_unlock(&gcdsprm.thermal_lock); + + return result; +} + +static int cdsprm_thermal_hvx_instruction_limit(unsigned int level) +{ + int result = -EINVAL; + struct sysmon_msg_tx rpmsg_msg_tx; + + mutex_lock(&gcdsprm.thermal_lock); + + if (gcdsprm.rpmsgdev && gcdsprm.cdsp_version) { + rpmsg_msg_tx.feature_id = + SYSMON_CDSP_FEATURE_THERMAL_LIMIT_TX; + rpmsg_msg_tx.fs.thermal.hvx_level = level; + rpmsg_msg_tx.fs.thermal.cdsp_level = + gcdsprm.thermal_cdsp_level; + rpmsg_msg_tx.size = sizeof(rpmsg_msg_tx); + result = rpmsg_send(gcdsprm.rpmsgdev->ept, + &rpmsg_msg_tx, + sizeof(rpmsg_msg_tx)); + } + + if (result == 0) + gcdsprm.thermal_hvx_level = level; + + mutex_unlock(&gcdsprm.thermal_lock); + + return result; +} + +/** + * cdsprm_unregister_cdspl3gov() - Unregister the method to set L3 clock + * frequency + * + * Note: To be called from cdspl3 governor only. Called when the governor is + * stopped + */ +void cdsprm_unregister_cdspl3gov(void) +{ + unsigned long flags; + + spin_lock_irqsave(&gcdsprm.l3_lock, flags); + gcdsprm.set_l3_freq = NULL; + spin_unlock_irqrestore(&gcdsprm.l3_lock, flags); +} +EXPORT_SYMBOL(cdsprm_unregister_cdspl3gov); + +static void set_qos_latency(int latency) +{ + if (!gcdsprm.qos_request) { + pm_qos_add_request(&gcdsprm.pm_qos_req, + PM_QOS_CPU_DMA_LATENCY, latency); + gcdsprm.qos_request = true; + } else { + pm_qos_update_request(&gcdsprm.pm_qos_req, + latency); + } +} + +static void process_rm_request(struct sysmon_msg *msg) +{ + struct sysmon_rm_msg *rm_msg; + + if (!msg) + return; + + if (msg->feature_id == SYSMON_CDSP_FEATURE_RM_RX) { + mutex_lock(&gcdsprm.rm_lock); + rm_msg = &msg->fs.rm_struct; + if (rm_msg->b_qos_flag == + SYSMON_CDSP_QOS_FLAG_ENABLE) { + if (gcdsprm.latency_request != + gcdsprm.qos_latency_us) { + set_qos_latency(gcdsprm.qos_latency_us); + gcdsprm.latency_request = + gcdsprm.qos_latency_us; + pr_debug("Set qos latency to %d\n", + gcdsprm.latency_request); + } + gcdsprm.timestamp = ((rm_msg->timetick_low) | + ((unsigned long long)rm_msg->timetick_high << 32)); + if (gcdsprm.dt_state >= CDSP_DELAY_THREAD_AFTER_SLEEP) { + flush_workqueue(gcdsprm.delay_work_queue); + if (gcdsprm.dt_state == + CDSP_DELAY_THREAD_EXITING) { + gcdsprm.dt_state = + CDSP_DELAY_THREAD_STARTED; + queue_work(gcdsprm.delay_work_queue, + &gcdsprm.cdsprm_delay_work); + } + } else if (gcdsprm.dt_state == + CDSP_DELAY_THREAD_NOT_STARTED) { + gcdsprm.dt_state = CDSP_DELAY_THREAD_STARTED; + queue_work(gcdsprm.delay_work_queue, + &gcdsprm.cdsprm_delay_work); + } + } else if ((rm_msg->b_qos_flag == + SYSMON_CDSP_QOS_FLAG_DISABLE) && + (gcdsprm.latency_request != + QOS_LATENCY_DISABLE_VALUE)) { + set_qos_latency(QOS_LATENCY_DISABLE_VALUE); + gcdsprm.latency_request = QOS_LATENCY_DISABLE_VALUE; + pr_debug("Set qos latency to %d\n", + gcdsprm.latency_request); + } + mutex_unlock(&gcdsprm.rm_lock); + } else { + pr_err("Received incorrect msg on rm queue: %d\n", + msg->feature_id); + } +} + +static void process_delayed_rm_request(struct work_struct *work) +{ + unsigned long long timestamp, curr_timestamp; + unsigned int time_ms = 0; + + mutex_lock(&gcdsprm.rm_lock); + + timestamp = gcdsprm.timestamp; + curr_timestamp = __arch_counter_get_cntvct(); + + while ((gcdsprm.latency_request == + gcdsprm.qos_latency_us) && + (curr_timestamp < timestamp)) { + if ((timestamp - curr_timestamp) < + (gcdsprm.qos_max_ms * SYS_CLK_TICKS_PER_MS)) + time_ms = (timestamp - curr_timestamp) / + SYS_CLK_TICKS_PER_MS; + else + break; + gcdsprm.dt_state = CDSP_DELAY_THREAD_BEFORE_SLEEP; + + mutex_unlock(&gcdsprm.rm_lock); + usleep_range(time_ms * 1000, (time_ms + 2) * 1000); + mutex_lock(&gcdsprm.rm_lock); + + gcdsprm.dt_state = CDSP_DELAY_THREAD_AFTER_SLEEP; + timestamp = gcdsprm.timestamp; + curr_timestamp = __arch_counter_get_cntvct(); + } + + set_qos_latency(QOS_LATENCY_DISABLE_VALUE); + gcdsprm.latency_request = QOS_LATENCY_DISABLE_VALUE; + pr_debug("Set qos latency to %d\n", gcdsprm.latency_request); + gcdsprm.dt_state = CDSP_DELAY_THREAD_EXITING; + + mutex_unlock(&gcdsprm.rm_lock); +} + +static void cdsprm_rpmsg_send_details(void) +{ + struct sysmon_msg_tx rpmsg_msg_tx; + + if (!gcdsprm.cdsp_version) + return; + + if (gcdsprm.b_cx_limit_en) { + reinit_completion(&gcdsprm.npu_activity_complete); + reinit_completion(&gcdsprm.npu_corner_complete); + + if (gcdsprm.npu_corner) { + rpmsg_msg_tx.feature_id = + SYSMON_CDSP_FEATURE_NPU_CORNER_TX; + rpmsg_msg_tx.fs.npu_corner.corner = + (unsigned int)gcdsprm.npu_corner; + rpmsg_msg_tx.size = sizeof(rpmsg_msg_tx); + rpmsg_send(gcdsprm.rpmsgdev->ept, + &rpmsg_msg_tx, + sizeof(rpmsg_msg_tx)); + } + + if (gcdsprm.b_npu_enabled) { + rpmsg_msg_tx.feature_id = + SYSMON_CDSP_FEATURE_NPU_ACTIVITY_TX; + rpmsg_msg_tx.fs.npu_activity.b_enabled = + gcdsprm.b_npu_enabled; + rpmsg_msg_tx.size = sizeof(rpmsg_msg_tx); + rpmsg_send(gcdsprm.rpmsgdev->ept, + &rpmsg_msg_tx, + sizeof(rpmsg_msg_tx)); + } + + cdsprm_compute_core_set_priority(gcdsprm.compute_prio_idx); + + if (gcdsprm.b_camera_enabled) { + rpmsg_msg_tx.feature_id = + SYSMON_CDSP_FEATURE_CAMERA_ACTIVITY_TX; + rpmsg_msg_tx.fs.camera.b_enabled = + gcdsprm.b_camera_enabled; + rpmsg_msg_tx.size = sizeof(rpmsg_msg_tx); + rpmsg_send(gcdsprm.rpmsgdev->ept, + &rpmsg_msg_tx, + sizeof(rpmsg_msg_tx)); + } + } + + if (gcdsprm.thermal_cdsp_level) { + cdsprm_thermal_cdsp_clk_limit( + gcdsprm.thermal_cdsp_level); + } else if (gcdsprm.thermal_hvx_level) { + cdsprm_thermal_hvx_instruction_limit( + gcdsprm.thermal_hvx_level); + } +} + +static struct cdsprm_request *get_next_request(void) +{ + struct cdsprm_request *req = NULL; + unsigned long flags; + + spin_lock_irqsave(&gcdsprm.list_lock, flags); + req = list_first_entry_or_null(&cdsprm_list, + struct cdsprm_request, node); + spin_unlock_irqrestore(&gcdsprm.list_lock, + flags); + + return req; +} + +static int process_cdsp_request_thread(void *data) +{ + struct cdsprm_request *req = NULL; + struct sysmon_msg *msg = NULL; + unsigned int l3_clock_khz; + unsigned long flags; + int result = 0; + struct sysmon_msg_tx rpmsg_msg_tx; + + while (!kthread_should_stop()) { + result = wait_event_interruptible(cdsprm_wq, + (kthread_should_stop() ? true : + (NULL != (req = get_next_request())))); + + if (kthread_should_stop()) + break; + + if (result) + continue; + + msg = &req->msg; + + if ((msg->feature_id == SYSMON_CDSP_FEATURE_RM_RX) && + gcdsprm.b_qosinitdone) { + process_rm_request(msg); + } else if (msg->feature_id == + SYSMON_CDSP_FEATURE_L3_RX) { + l3_clock_khz = msg->fs.l3_struct.l3_clock_khz; + + spin_lock_irqsave(&gcdsprm.l3_lock, flags); + gcdsprm.set_l3_freq_cached = gcdsprm.set_l3_freq; + spin_unlock_irqrestore(&gcdsprm.l3_lock, flags); + + if (gcdsprm.set_l3_freq_cached) { + gcdsprm.set_l3_freq_cached(l3_clock_khz); + pr_debug("Set L3 clock %d done\n", + l3_clock_khz); + } + } else if (msg->feature_id == + SYSMON_CDSP_FEATURE_NPU_LIMIT_RX) { + mutex_lock(&gcdsprm.npu_activity_lock); + + gcdsprm.set_corner_limit_cached = + gcdsprm.set_corner_limit; + + if (gcdsprm.set_corner_limit_cached) { + gcdsprm.npu_corner_limit = + msg->fs.npu_limit.corner; + gcdsprm.b_applyingNpuLimit = true; + result = gcdsprm.set_corner_limit_cached( + gcdsprm.npu_corner_limit); + gcdsprm.b_applyingNpuLimit = false; + pr_debug("Set NPU limit to %d\n", + msg->fs.npu_limit.corner); + } else { + result = -ENOMSG; + pr_debug("NPU limit not registered\n"); + } + + mutex_unlock(&gcdsprm.npu_activity_lock); + /* + * Send Limit ack back to DSP + */ + rpmsg_msg_tx.feature_id = + SYSMON_CDSP_FEATURE_NPU_LIMIT_TX; + + if (result == 0) { + rpmsg_msg_tx.fs.npu_limit_ack.corner = + msg->fs.npu_limit.corner; + } else { + rpmsg_msg_tx.fs.npu_limit_ack.corner = + CDSPRM_NPU_CLK_OFF; + } + + rpmsg_msg_tx.size = sizeof(rpmsg_msg_tx); + result = rpmsg_send(gcdsprm.rpmsgdev->ept, + &rpmsg_msg_tx, + sizeof(rpmsg_msg_tx)); + + if (result) + pr_err("rpmsg send failed %d\n", result); + else + pr_debug("NPU limit ack sent\n"); + } else if (msg->feature_id == + SYSMON_CDSP_FEATURE_VERSION_RX) { + cdsprm_rpmsg_send_details(); + pr_debug("Sent preserved data to DSP\n"); + } + + spin_lock_irqsave(&gcdsprm.list_lock, flags); + list_del(&req->node); + req->busy = false; + spin_unlock_irqrestore(&gcdsprm.list_lock, flags); + } + + do_exit(0); +} + +static int cdsprm_rpmsg_probe(struct rpmsg_device *dev) +{ + /* Populate child nodes as platform devices */ + of_platform_populate(dev->dev.of_node, NULL, NULL, &dev->dev); + gcdsprm.rpmsgdev = dev; + dev_dbg(&dev->dev, "rpmsg probe called for cdsp\n"); + + return 0; +} + +static void cdsprm_rpmsg_remove(struct rpmsg_device *dev) +{ + gcdsprm.rpmsgdev = NULL; + gcdsprm.cdsp_version = 0; + + if (gcdsprm.b_cx_limit_en) { + mutex_lock(&gcdsprm.npu_activity_lock); + complete_all(&gcdsprm.npu_activity_complete); + complete_all(&gcdsprm.npu_corner_complete); + mutex_unlock(&gcdsprm.npu_activity_lock); + + gcdsprm.set_corner_limit_cached = gcdsprm.set_corner_limit; + + if ((gcdsprm.npu_corner_limit < CDSPRM_NPU_TURBO_L1) && + gcdsprm.set_corner_limit_cached) + gcdsprm.set_corner_limit_cached(CDSPRM_NPU_TURBO_L1); + } +} + +static int cdsprm_rpmsg_callback(struct rpmsg_device *dev, void *data, + int len, void *priv, u32 addr) +{ + struct sysmon_msg *msg = (struct sysmon_msg *)data; + bool b_valid = false; + struct cdsprm_request *req; + unsigned long flags; + + if (!data || (len < sizeof(*msg))) { + dev_err(&dev->dev, + "Invalid message in rpmsg callback, length: %d, expected: %lu\n", + len, sizeof(*msg)); + return -EINVAL; + } + + if ((msg->feature_id == SYSMON_CDSP_FEATURE_RM_RX) && + gcdsprm.b_qosinitdone) { + dev_dbg(&dev->dev, "Processing RM request\n"); + b_valid = true; + } else if (msg->feature_id == SYSMON_CDSP_FEATURE_L3_RX) { + dev_dbg(&dev->dev, "Processing L3 request\n"); + spin_lock_irqsave(&gcdsprm.l3_lock, flags); + gcdsprm.set_l3_freq_cached = gcdsprm.set_l3_freq; + spin_unlock_irqrestore(&gcdsprm.l3_lock, flags); + if (gcdsprm.set_l3_freq_cached) + b_valid = true; + } else if ((msg->feature_id == SYSMON_CDSP_FEATURE_NPU_CORNER_RX) && + (gcdsprm.b_cx_limit_en)) { + gcdsprm.allowed_npu_corner = msg->fs.npu_corner.corner; + dev_dbg(&dev->dev, + "Processing NPU corner request ack for %d\n", + gcdsprm.allowed_npu_corner); + if (gcdsprm.b_npu_corner_waiting) + complete(&gcdsprm.npu_corner_complete); + } else if ((msg->feature_id == SYSMON_CDSP_FEATURE_NPU_LIMIT_RX) && + (gcdsprm.b_cx_limit_en)) { + dev_dbg(&dev->dev, "Processing NPU limit request for %d\n", + msg->fs.npu_limit.corner); + b_valid = true; + } else if ((msg->feature_id == SYSMON_CDSP_FEATURE_NPU_ACTIVITY_RX) && + (gcdsprm.b_cx_limit_en)) { + dev_dbg(&dev->dev, "Processing NPU activity request ack\n"); + if (gcdsprm.b_npu_activity_waiting) + complete(&gcdsprm.npu_activity_complete); + } else if (msg->feature_id == SYSMON_CDSP_FEATURE_VERSION_RX) { + gcdsprm.cdsp_version = msg->fs.version.id; + b_valid = true; + dev_dbg(&dev->dev, "Received CDSP version 0x%x\n", + gcdsprm.cdsp_version); + } else { + dev_err(&dev->dev, "Received incorrect msg feature %d\n", + msg->feature_id); + } + + if (b_valid) { + spin_lock_irqsave(&gcdsprm.list_lock, flags); + + if (!gcdsprm.msg_queue[gcdsprm.msg_queue_idx].busy) { + req = &gcdsprm.msg_queue[gcdsprm.msg_queue_idx]; + req->busy = true; + req->msg = *msg; + if (gcdsprm.msg_queue_idx < + (CDSPRM_MSG_QUEUE_DEPTH - 1)) + gcdsprm.msg_queue_idx++; + else + gcdsprm.msg_queue_idx = 0; + } else { + spin_unlock_irqrestore(&gcdsprm.list_lock, flags); + dev_dbg(&dev->dev, + "Unable to queue cdsp request, no memory\n"); + return -ENOMEM; + } + + list_add_tail(&req->node, &cdsprm_list); + spin_unlock_irqrestore(&gcdsprm.list_lock, flags); + wake_up_interruptible(&cdsprm_wq); + } + + return 0; +} + +static int cdsp_get_max_state(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + *state = CDSP_THERMAL_MAX_STATE; + + return 0; +} + +static int cdsp_get_cur_state(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + *state = gcdsprm.thermal_cdsp_level; + + return 0; +} + +static int cdsp_set_cur_state(struct thermal_cooling_device *cdev, + unsigned long state) +{ + if (gcdsprm.thermal_cdsp_level == state) + return 0; + + cdsprm_thermal_cdsp_clk_limit(state); + + return 0; +} + +static const struct thermal_cooling_device_ops cdsp_cooling_ops = { + .get_max_state = cdsp_get_max_state, + .get_cur_state = cdsp_get_cur_state, + .set_cur_state = cdsp_set_cur_state, +}; + +static int hvx_get_max_state(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + *state = HVX_THERMAL_MAX_STATE; + + return 0; +} + +static int hvx_get_cur_state(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + *state = gcdsprm.thermal_hvx_level; + + return 0; +} + +static int hvx_set_cur_state(struct thermal_cooling_device *cdev, + unsigned long state) +{ + if (gcdsprm.thermal_hvx_level == state) + return 0; + + cdsprm_thermal_hvx_instruction_limit(state); + + return 0; +} + +static int cdsprm_compute_prio_read(void *data, u64 *val) +{ + *val = gcdsprm.compute_prio_idx; + + return 0; +} + +static int cdsprm_compute_prio_write(void *data, u64 val) +{ + cdsprm_compute_core_set_priority((unsigned int)val); + + return 0; +} + +DEFINE_DEBUGFS_ATTRIBUTE(cdsprm_debugfs_fops, + cdsprm_compute_prio_read, + cdsprm_compute_prio_write, + "%llu\n"); + +static const struct thermal_cooling_device_ops hvx_cooling_ops = { + .get_max_state = hvx_get_max_state, + .get_cur_state = hvx_get_cur_state, + .set_cur_state = hvx_set_cur_state, +}; + +static int cdsp_rm_driver_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct thermal_cooling_device *tcdev = 0; + unsigned int cooling_cells = 0; + + if (of_property_read_u32(dev->of_node, + "qcom,qos-latency-us", &gcdsprm.qos_latency_us)) { + return -EINVAL; + } + + if (of_property_read_u32(dev->of_node, + "qcom,qos-maxhold-ms", &gcdsprm.qos_max_ms)) { + return -EINVAL; + } + + gcdsprm.compute_prio_idx = CDSPRM_COMPUTE_AIX_OVER_HVX; + of_property_read_u32(dev->of_node, + "qcom,compute-priority-mode", + &gcdsprm.compute_prio_idx); + + gcdsprm.b_cx_limit_en = of_property_read_bool(dev->of_node, + "qcom,compute-cx-limit-en"); + + if (gcdsprm.b_cx_limit_en) { + gcdsprm.debugfs_dir = debugfs_create_dir("compute", NULL); + + if (!gcdsprm.debugfs_dir) { + dev_err(dev, + "Failed to create debugfs directory for cdsprm\n"); + } else { + gcdsprm.debugfs_file = debugfs_create_file("priority", + 0644, gcdsprm.debugfs_dir, + NULL, &cdsprm_debugfs_fops); + if (!gcdsprm.debugfs_file) { + debugfs_remove_recursive(gcdsprm.debugfs_dir); + dev_err(dev, + "Failed to create debugfs file\n"); + } + } + } + + of_property_read_u32(dev->of_node, + "#cooling-cells", + &cooling_cells); + + if (cooling_cells && IS_ENABLED(CONFIG_THERMAL)) { + tcdev = thermal_of_cooling_device_register(dev->of_node, + "cdsp", NULL, + &cdsp_cooling_ops); + if (IS_ERR(tcdev)) { + dev_err(dev, + "CDSP thermal driver reg failed\n"); + } + gcdsprm.cdsp_tcdev = tcdev; + thermal_cdev_update(tcdev); + } + + dev_dbg(dev, "CDSP request manager driver probe called\n"); + gcdsprm.b_qosinitdone = true; + + return 0; +} + +static int hvx_rm_driver_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct thermal_cooling_device *tcdev = 0; + unsigned int cooling_cells = 0; + + of_property_read_u32(dev->of_node, + "#cooling-cells", + &cooling_cells); + + if (cooling_cells && IS_ENABLED(CONFIG_THERMAL)) { + tcdev = thermal_of_cooling_device_register(dev->of_node, + "hvx", NULL, + &hvx_cooling_ops); + if (IS_ERR(tcdev)) { + dev_err(dev, + "HVX thermal driver reg failed\n"); + } + gcdsprm.hvx_tcdev = tcdev; + thermal_cdev_update(tcdev); + } + + dev_dbg(dev, "HVX request manager driver probe called\n"); + + return 0; +} + +static const struct rpmsg_device_id cdsprm_rpmsg_match[] = { + { "cdsprmglink-apps-dsp" }, + { }, +}; + +static const struct of_device_id cdsprm_rpmsg_of_match[] = { + { .compatible = "qcom,msm-cdsprm-rpmsg" }, + { }, +}; +MODULE_DEVICE_TABLE(of, cdsprm_rpmsg_of_match); + +static struct rpmsg_driver cdsprm_rpmsg_client = { + .id_table = cdsprm_rpmsg_match, + .probe = cdsprm_rpmsg_probe, + .remove = cdsprm_rpmsg_remove, + .callback = cdsprm_rpmsg_callback, + .drv = { + .name = "qcom,msm_cdsprm_rpmsg", + .of_match_table = cdsprm_rpmsg_of_match, + }, +}; + +static const struct of_device_id cdsp_rm_match_table[] = { + { .compatible = "qcom,msm-cdsp-rm" }, + { }, +}; + +static struct platform_driver cdsp_rm = { + .probe = cdsp_rm_driver_probe, + .driver = { + .name = "msm_cdsp_rm", + .of_match_table = cdsp_rm_match_table, + }, +}; + +static const struct of_device_id hvx_rm_match_table[] = { + { .compatible = "qcom,msm-hvx-rm" }, + { }, +}; + +static struct platform_driver hvx_rm = { + .probe = hvx_rm_driver_probe, + .driver = { + .name = "msm_hvx_rm", + .of_match_table = hvx_rm_match_table, + }, +}; + +static int __init cdsprm_init(void) +{ + int err; + + mutex_init(&gcdsprm.rm_lock); + mutex_init(&gcdsprm.rpmsg_lock); + mutex_init(&gcdsprm.npu_activity_lock); + mutex_init(&gcdsprm.thermal_lock); + spin_lock_init(&gcdsprm.l3_lock); + spin_lock_init(&gcdsprm.list_lock); + init_completion(&gcdsprm.msg_avail); + init_completion(&gcdsprm.npu_activity_complete); + init_completion(&gcdsprm.npu_corner_complete); + + gcdsprm.cdsprm_wq_task = kthread_run(process_cdsp_request_thread, + NULL, "cdsprm-wq"); + + if (!gcdsprm.cdsprm_wq_task) { + pr_err("Failed to create kernel thread\n"); + return -ENOMEM; + } + + gcdsprm.delay_work_queue = + create_singlethread_workqueue("cdsprm-wq-delay"); + + if (!gcdsprm.delay_work_queue) { + err = -ENOMEM; + pr_err("Failed to create rm delay work queue\n"); + goto err_wq; + } + + INIT_WORK(&gcdsprm.cdsprm_delay_work, process_delayed_rm_request); + err = platform_driver_register(&cdsp_rm); + + if (err) { + pr_err("Failed to register cdsprm platform driver: %d\n", + err); + goto bail; + } + + gcdsprm.b_cdsprm_devinit = true; + + err = platform_driver_register(&hvx_rm); + + if (err) { + pr_err("Failed to register hvxrm platform driver: %d\n", + err); + goto bail; + } + + gcdsprm.b_hvxrm_devinit = true; + + err = register_rpmsg_driver(&cdsprm_rpmsg_client); + + if (err) { + pr_err("Failed registering rpmsg driver with return %d\n", + err); + goto bail; + } + + gcdsprm.b_rpmsg_register = true; + + pr_info("Init successful\n"); + + return 0; +bail: + destroy_workqueue(gcdsprm.delay_work_queue); +err_wq: + kthread_stop(gcdsprm.cdsprm_wq_task); + + return err; +} + +static void __exit cdsprm_exit(void) +{ + struct sysmon_msg_tx rpmsg_msg_tx; + + pr_info("Exit module called\n"); + if (gcdsprm.qos_request) { + set_qos_latency(QOS_LATENCY_DISABLE_VALUE); + gcdsprm.latency_request = QOS_LATENCY_DISABLE_VALUE; + } + + if (gcdsprm.hvx_tcdev) + thermal_cooling_device_unregister(gcdsprm.hvx_tcdev); + + if (gcdsprm.cdsp_tcdev) + thermal_cooling_device_unregister(gcdsprm.cdsp_tcdev); + + if (gcdsprm.b_cx_limit_en) { + mutex_lock(&gcdsprm.npu_activity_lock); + complete_all(&gcdsprm.npu_activity_complete); + complete_all(&gcdsprm.npu_corner_complete); + mutex_unlock(&gcdsprm.npu_activity_lock); + + gcdsprm.set_corner_limit_cached = gcdsprm.set_corner_limit; + + if ((gcdsprm.npu_corner_limit < CDSPRM_NPU_TURBO_L1) && + gcdsprm.set_corner_limit_cached) + gcdsprm.set_corner_limit_cached(CDSPRM_NPU_TURBO_L1); + } + + if (gcdsprm.cdsp_version && gcdsprm.b_cx_limit_en) { + + if (gcdsprm.b_npu_enabled) { + rpmsg_msg_tx.feature_id = + SYSMON_CDSP_FEATURE_NPU_ACTIVITY_TX; + rpmsg_msg_tx.fs.npu_activity.b_enabled = 0; + rpmsg_msg_tx.size = sizeof(rpmsg_msg_tx); + rpmsg_send(gcdsprm.rpmsgdev->ept, + &rpmsg_msg_tx, + sizeof(rpmsg_msg_tx)); + gcdsprm.b_npu_enabled = 0; + } + + if (gcdsprm.npu_corner) { + rpmsg_msg_tx.feature_id = + SYSMON_CDSP_FEATURE_NPU_CORNER_TX; + rpmsg_msg_tx.fs.npu_corner.corner = 0; + rpmsg_msg_tx.size = sizeof(rpmsg_msg_tx); + rpmsg_send(gcdsprm.rpmsgdev->ept, + &rpmsg_msg_tx, + sizeof(rpmsg_msg_tx)); + gcdsprm.npu_corner = 0; + } + + if (gcdsprm.b_camera_enabled) { + rpmsg_msg_tx.feature_id = + SYSMON_CDSP_FEATURE_CAMERA_ACTIVITY_TX; + rpmsg_msg_tx.fs.camera.b_enabled = 0; + rpmsg_msg_tx.size = sizeof(rpmsg_msg_tx); + rpmsg_send(gcdsprm.rpmsgdev->ept, + &rpmsg_msg_tx, + sizeof(rpmsg_msg_tx)); + gcdsprm.b_camera_enabled = 0; + } + } + + if (gcdsprm.thermal_cdsp_level) { + cdsprm_thermal_cdsp_clk_limit(0); + gcdsprm.thermal_cdsp_level = 0; + } else if (gcdsprm.thermal_hvx_level) { + cdsprm_thermal_hvx_instruction_limit(0); + gcdsprm.thermal_hvx_level = 0; + } + + if (gcdsprm.b_rpmsg_register) { + unregister_rpmsg_driver(&cdsprm_rpmsg_client); + gcdsprm.rpmsgdev = NULL; + gcdsprm.cdsp_version = 0; + } + + if (gcdsprm.cdsprm_wq_task) + kthread_stop(gcdsprm.cdsprm_wq_task); + + if (gcdsprm.delay_work_queue) + destroy_workqueue(gcdsprm.delay_work_queue); + + if (gcdsprm.b_cdsprm_devinit) + platform_driver_unregister(&cdsp_rm); + + if (gcdsprm.b_hvxrm_devinit) + platform_driver_unregister(&hvx_rm); + + debugfs_remove_recursive(gcdsprm.debugfs_dir); + + gcdsprm.b_rpmsg_register = false; + gcdsprm.b_hvxrm_devinit = false; + gcdsprm.b_cdsprm_devinit = false; + gcdsprm.debugfs_dir = NULL; + gcdsprm.debugfs_file = NULL; + gcdsprm.hvx_tcdev = NULL; + gcdsprm.cdsp_tcdev = NULL; + + pr_info("Exited\n"); +} + +module_init(cdsprm_init); +module_exit(cdsprm_exit); + +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/soc/qcom/cdsprm.h b/include/linux/soc/qcom/cdsprm.h new file mode 100644 index 000000000000..79fd6c727fb6 --- /dev/null +++ b/include/linux/soc/qcom/cdsprm.h @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2018-2020, The Linux Foundation. All rights reserved. + */ + +/* + * This header is for cdspl3 devfreq governor in drivers/devfreq. + */ + +#ifndef __QCOM_CDSPRM_H__ +#define __QCOM_CDSPRM_H__ + +/** + * struct cdsprm_l3 - register with set L3 clock frequency method + * @set_l3_freq: Sets desired L3 clock frequency in kilo-hertz. + * cdsprm module would call this method to set L3 + * clock frequency as requested by CDSP subsystem. + */ +struct cdsprm_l3 { + int (*set_l3_freq)(unsigned int freq_khz); +}; + +/** + * cdsprm_register_cdspl3gov() - Register a method to set L3 clock + * frequency + * @arg: cdsprm_l3 structure with set L3 clock frequency method + * + * Note: To be called from cdspl3 governor only. Called when the governor is + * started. + */ +void cdsprm_register_cdspl3gov(struct cdsprm_l3 *arg); + +/** + * cdsprm_unregister_cdspl3gov() - Unregister the method to set L3 clock + * frequency + * + * Note: To be called from cdspl3 governor only. Called when the governor is + * stopped + */ +void cdsprm_unregister_cdspl3gov(void); + +#endif diff --git a/include/linux/soc/qcom/cdsprm_cxlimit.h b/include/linux/soc/qcom/cdsprm_cxlimit.h new file mode 100644 index 000000000000..b68ca5aaafed --- /dev/null +++ b/include/linux/soc/qcom/cdsprm_cxlimit.h @@ -0,0 +1,83 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2018-2020, The Linux Foundation. All rights reserved. + */ + +#ifndef __QCOM_CDSPRM_LIMITS_H__ +#define __QCOM_CDSPRM_LIMITS_H__ + +enum cdsprm_npu_corner { + CDSPRM_NPU_CLK_OFF = 0, + CDSPRM_NPU_MIN_SVS, + CDSPRM_NPU_LOW_SVS, + CDSPRM_NPU_SVS, + CDSPRM_NPU_SVS_L1, + CDSPRM_NPU_NOM, + CDSPRM_NPU_NOM_L1, + CDSPRM_NPU_TURBO, + CDSPRM_NPU_TURBO_L1, +}; + +struct cdsprm_npu_limit_cbs { + int (*set_corner_limit)(enum cdsprm_npu_corner); +}; + +enum cdsprm_compute_priority { + CDSPRM_COMPUTE_HVX_MAX = 1, + CDSPRM_COMPUTE_AIX_MAX = 2, + CDSPRM_COMPUTE_HVX_OVER_AIX = 3, + CDSPRM_COMPUTE_AIX_OVER_HVX = 4, + CDSPRM_COMPUTE_BALANCED = 5, +}; + +int cdsprm_compute_core_set_priority(enum cdsprm_compute_priority); + +/* For NPU driver */ + +/** + * cdsprm_cxlimit_npu_limit_register() - Register NPU corner limit method with + * cdspprm cxlimit driver. + * @arg: cdsprm_npu_limit_cbs structure with set_corner_limit method defined + * + * Note: To be called from NPU driver only. + */ +int cdsprm_cxlimit_npu_limit_register(const struct cdsprm_npu_limit_cbs *arg); +/** + * cdsprm_cxlimit_npu_limit_deregister() - deregister NPU corner limit + * notification from cdsprm cxlimit driver. + * + * Note: To be called from NPU driver only. + */ +int cdsprm_cxlimit_npu_limit_deregister(void); +/** + * cdsprm_cxlimit_npu_activity_notify() - Notify NPU activity status to + * cdsprm cxlimit driver. + * @arg: b_enabled 0 - After NPU activity stop + * 1 - Before NPU activity start + * + * Note: To be called from NPU driver only. + */ +int cdsprm_cxlimit_npu_activity_notify(unsigned int b_enabled); +/** + * cdsprm_cxlimit_npu_corner_notify() - Notify cdsprm cxlimit driver of NPU + * corner request. + * @arg: enum cdsprm_npu_corner - NPU corner value. + * CDSPRM_NPU_CLK_OFF for clock off notification. + * + * Note: To be called from NPU driver only. + */ +enum cdsprm_npu_corner cdsprm_cxlimit_npu_corner_notify(enum cdsprm_npu_corner); + +/* For Camera driver */ + +/** + * cdsprm_cxlimit_camera_activity_notify() - Notify cdsprm cxlimit driver of + * Camera activity + * @arg: b_enabled 0 - After Camera activity stop + * 1 - Before Camera activity start + * + * Note: To be called from Camera driver only. + */ +int cdsprm_cxlimit_camera_activity_notify(unsigned int b_enabled); + +#endif