|
@@ -17,6 +17,7 @@
|
|
|
#include <linux/rwsem.h>
|
|
|
#include <linux/suspend.h>
|
|
|
#include <linux/timer.h>
|
|
|
+#include <linux/thermal.h>
|
|
|
#include <linux/version.h>
|
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 14, 0))
|
|
|
#include <linux/panic_notifier.h>
|
|
@@ -4543,6 +4544,195 @@ int cnss_set_wfc_mode(struct device *dev, struct cnss_wfc_cfg cfg)
|
|
|
}
|
|
|
EXPORT_SYMBOL(cnss_set_wfc_mode);
|
|
|
|
|
|
+static int cnss_tcdev_get_max_state(struct thermal_cooling_device *tcdev,
|
|
|
+ unsigned long *thermal_state)
|
|
|
+{
|
|
|
+ struct cnss_thermal_cdev *cnss_tcdev = NULL;
|
|
|
+
|
|
|
+ if (!tcdev || !tcdev->devdata) {
|
|
|
+ cnss_pr_err("tcdev or tcdev->devdata is null!\n");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ cnss_tcdev = tcdev->devdata;
|
|
|
+ *thermal_state = cnss_tcdev->max_thermal_state;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int cnss_tcdev_get_cur_state(struct thermal_cooling_device *tcdev,
|
|
|
+ unsigned long *thermal_state)
|
|
|
+{
|
|
|
+ struct cnss_thermal_cdev *cnss_tcdev = NULL;
|
|
|
+
|
|
|
+ if (!tcdev || !tcdev->devdata) {
|
|
|
+ cnss_pr_err("tcdev or tcdev->devdata is null!\n");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ cnss_tcdev = tcdev->devdata;
|
|
|
+ *thermal_state = cnss_tcdev->curr_thermal_state;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int cnss_tcdev_set_cur_state(struct thermal_cooling_device *tcdev,
|
|
|
+ unsigned long thermal_state)
|
|
|
+{
|
|
|
+ struct cnss_thermal_cdev *cnss_tcdev = NULL;
|
|
|
+ struct cnss_plat_data *plat_priv = cnss_get_plat_priv(NULL);
|
|
|
+ int ret = 0;
|
|
|
+
|
|
|
+ if (!tcdev || !tcdev->devdata) {
|
|
|
+ cnss_pr_err("tcdev or tcdev->devdata is null!\n");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ cnss_tcdev = tcdev->devdata;
|
|
|
+
|
|
|
+ if (thermal_state > cnss_tcdev->max_thermal_state)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ cnss_pr_vdbg("Cooling device set current state: %ld,for cdev id %d",
|
|
|
+ thermal_state, cnss_tcdev->tcdev_id);
|
|
|
+
|
|
|
+ mutex_lock(&plat_priv->tcdev_lock);
|
|
|
+ ret = cnss_bus_set_therm_cdev_state(plat_priv,
|
|
|
+ thermal_state,
|
|
|
+ cnss_tcdev->tcdev_id);
|
|
|
+ if (!ret)
|
|
|
+ cnss_tcdev->curr_thermal_state = thermal_state;
|
|
|
+ mutex_unlock(&plat_priv->tcdev_lock);
|
|
|
+ if (ret) {
|
|
|
+ cnss_pr_err("Setting Current Thermal State Failed: %d,for cdev id %d",
|
|
|
+ ret, cnss_tcdev->tcdev_id);
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static struct thermal_cooling_device_ops cnss_cooling_ops = {
|
|
|
+ .get_max_state = cnss_tcdev_get_max_state,
|
|
|
+ .get_cur_state = cnss_tcdev_get_cur_state,
|
|
|
+ .set_cur_state = cnss_tcdev_set_cur_state,
|
|
|
+};
|
|
|
+
|
|
|
+int cnss_thermal_cdev_register(struct device *dev, unsigned long max_state,
|
|
|
+ int tcdev_id)
|
|
|
+{
|
|
|
+ struct cnss_plat_data *priv = cnss_get_plat_priv(NULL);
|
|
|
+ struct cnss_thermal_cdev *cnss_tcdev = NULL;
|
|
|
+ char cdev_node_name[THERMAL_NAME_LENGTH] = "";
|
|
|
+ struct device_node *dev_node;
|
|
|
+ int ret = 0;
|
|
|
+
|
|
|
+ if (!priv) {
|
|
|
+ cnss_pr_err("Platform driver is not initialized!\n");
|
|
|
+ return -ENODEV;
|
|
|
+ }
|
|
|
+
|
|
|
+ cnss_tcdev = kzalloc(sizeof(*cnss_tcdev), GFP_KERNEL);
|
|
|
+ if (!cnss_tcdev) {
|
|
|
+ cnss_pr_err("Failed to allocate cnss_tcdev object!\n");
|
|
|
+ return -ENOMEM;
|
|
|
+ }
|
|
|
+
|
|
|
+ cnss_tcdev->tcdev_id = tcdev_id;
|
|
|
+ cnss_tcdev->max_thermal_state = max_state;
|
|
|
+
|
|
|
+ snprintf(cdev_node_name, THERMAL_NAME_LENGTH,
|
|
|
+ "qcom,cnss_cdev%d", tcdev_id);
|
|
|
+
|
|
|
+ dev_node = of_find_node_by_name(NULL, cdev_node_name);
|
|
|
+ if (!dev_node) {
|
|
|
+ cnss_pr_err("Failed to get cooling device node\n");
|
|
|
+ kfree(cnss_tcdev);
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ cnss_pr_dbg("tcdev node->name=%s\n", dev_node->name);
|
|
|
+
|
|
|
+ if (of_find_property(dev_node, "#cooling-cells", NULL)) {
|
|
|
+ cnss_tcdev->tcdev = thermal_of_cooling_device_register(dev_node,
|
|
|
+ cdev_node_name,
|
|
|
+ cnss_tcdev,
|
|
|
+ &cnss_cooling_ops);
|
|
|
+ if (IS_ERR_OR_NULL(cnss_tcdev->tcdev)) {
|
|
|
+ ret = PTR_ERR(cnss_tcdev->tcdev);
|
|
|
+ cnss_pr_err("Cooling device register failed: %d, for cdev id %d\n",
|
|
|
+ ret, cnss_tcdev->tcdev_id);
|
|
|
+ kfree(cnss_tcdev);
|
|
|
+ } else {
|
|
|
+ cnss_pr_dbg("Cooling device registered for cdev id %d",
|
|
|
+ cnss_tcdev->tcdev_id);
|
|
|
+ mutex_lock(&priv->tcdev_lock);
|
|
|
+ list_add(&cnss_tcdev->tcdev_list,
|
|
|
+ &priv->cnss_tcdev_list);
|
|
|
+ mutex_unlock(&priv->tcdev_lock);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ cnss_pr_dbg("Cooling device registration not supported");
|
|
|
+ kfree(cnss_tcdev);
|
|
|
+ ret = -EOPNOTSUPP;
|
|
|
+ }
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL(cnss_thermal_cdev_register);
|
|
|
+
|
|
|
+void cnss_thermal_cdev_unregister(struct device *dev, int tcdev_id)
|
|
|
+{
|
|
|
+ struct cnss_plat_data *priv = cnss_get_plat_priv(NULL);
|
|
|
+ struct cnss_thermal_cdev *cnss_tcdev = NULL;
|
|
|
+
|
|
|
+ if (!priv) {
|
|
|
+ cnss_pr_err("Platform driver is not initialized!\n");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ mutex_lock(&priv->tcdev_lock);
|
|
|
+ while (!list_empty(&priv->cnss_tcdev_list)) {
|
|
|
+ cnss_tcdev = list_first_entry(&priv->cnss_tcdev_list,
|
|
|
+ struct cnss_thermal_cdev,
|
|
|
+ tcdev_list);
|
|
|
+ thermal_cooling_device_unregister(cnss_tcdev->tcdev);
|
|
|
+ list_del(&cnss_tcdev->tcdev_list);
|
|
|
+ kfree(cnss_tcdev);
|
|
|
+ }
|
|
|
+ mutex_unlock(&priv->tcdev_lock);
|
|
|
+}
|
|
|
+EXPORT_SYMBOL(cnss_thermal_cdev_unregister);
|
|
|
+
|
|
|
+int cnss_get_curr_therm_cdev_state(struct device *dev,
|
|
|
+ unsigned long *thermal_state,
|
|
|
+ int tcdev_id)
|
|
|
+{
|
|
|
+ struct cnss_plat_data *priv = cnss_get_plat_priv(NULL);
|
|
|
+ struct cnss_thermal_cdev *cnss_tcdev = NULL;
|
|
|
+
|
|
|
+ if (!priv) {
|
|
|
+ cnss_pr_err("Platform driver is not initialized!\n");
|
|
|
+ return -ENODEV;
|
|
|
+ }
|
|
|
+
|
|
|
+ mutex_lock(&priv->tcdev_lock);
|
|
|
+ list_for_each_entry(cnss_tcdev, &priv->cnss_tcdev_list, tcdev_list) {
|
|
|
+ if (cnss_tcdev->tcdev_id != tcdev_id)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ *thermal_state = cnss_tcdev->curr_thermal_state;
|
|
|
+ mutex_unlock(&priv->tcdev_lock);
|
|
|
+ cnss_pr_dbg("Cooling device current state: %ld, for cdev id %d",
|
|
|
+ cnss_tcdev->curr_thermal_state, tcdev_id);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ mutex_unlock(&priv->tcdev_lock);
|
|
|
+ cnss_pr_dbg("Cooling device ID not found: %d", tcdev_id);
|
|
|
+ return -EINVAL;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL(cnss_get_curr_therm_cdev_state);
|
|
|
+
|
|
|
static int cnss_probe(struct platform_device *plat_dev)
|
|
|
{
|
|
|
int ret = 0;
|
|
@@ -4670,6 +4860,9 @@ static int cnss_probe(struct platform_device *plat_dev)
|
|
|
cnss_register_coex_service(plat_priv);
|
|
|
cnss_register_ims_service(plat_priv);
|
|
|
|
|
|
+ mutex_init(&plat_priv->tcdev_lock);
|
|
|
+ INIT_LIST_HEAD(&plat_priv->cnss_tcdev_list);
|
|
|
+
|
|
|
cnss_pr_info("Platform driver probed successfully.\n");
|
|
|
|
|
|
return 0;
|