ath10k: add thermal sensor device support
authorRajkumar Manoharan <rmanohar@qti.qualcomm.com>
Wed, 17 Dec 2014 10:22:26 +0000 (12:22 +0200)
committerKalle Valo <kvalo@qca.qualcomm.com>
Tue, 23 Dec 2014 15:18:41 +0000 (17:18 +0200)
Temperature sensor generates electrical analog voltage from temperature
of each chain. The analog voltage is converted to digital value through
ADC. For reading temperature values fom user space, hw monitoring device
is used.

Whenever the user requests for current temperature, the driver sends WMI
command and wait for response. For reading temperature,

cat /sys/class/ieee80211/phy*/device/hwmon/hwmon2/temp1_input

Signed-off-by: Rajkumar Manoharan <rmanohar@qti.qualcomm.com>
Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
drivers/net/wireless/ath/ath10k/core.c
drivers/net/wireless/ath/ath10k/thermal.c
drivers/net/wireless/ath/ath10k/thermal.h
drivers/net/wireless/ath/ath10k/wmi.c

index 70d53e65c53d5245f23b595d4c2a496ef164a2b4..2d0671ebcf2b306229c808f202389f2c371ffa7e 100644 (file)
@@ -844,6 +844,7 @@ static void ath10k_core_restart(struct work_struct *work)
        complete_all(&ar->offchan_tx_completed);
        complete_all(&ar->install_key_done);
        complete_all(&ar->vdev_setup_done);
+       complete_all(&ar->thermal.wmi_sync);
        wake_up(&ar->htt.empty_tx_wq);
        wake_up(&ar->wmi.tx_credits_wq);
        wake_up(&ar->peer_mapping_wq);
@@ -1316,6 +1317,7 @@ struct ath10k *ath10k_core_create(size_t priv_size, struct device *dev,
 
        init_completion(&ar->install_key_done);
        init_completion(&ar->vdev_setup_done);
+       init_completion(&ar->thermal.wmi_sync);
 
        INIT_DELAYED_WORK(&ar->scan.timeout, ath10k_scan_timeout_work);
 
index e98ce8ce27e2b145f25c22aed835102e14ef9f6f..d93913538d180bcbe7df7f45d945806b481304e6 100644 (file)
@@ -17,6 +17,8 @@
 #include <linux/device.h>
 #include <linux/sysfs.h>
 #include <linux/thermal.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
 #include "core.h"
 #include "debug.h"
 #include "wmi-ops.h"
@@ -119,9 +121,72 @@ static struct thermal_cooling_device_ops ath10k_thermal_ops = {
        .set_cur_state = ath10k_thermal_set_cur_dutycycle,
 };
 
+static ssize_t ath10k_thermal_show_temp(struct device *dev,
+                                       struct device_attribute *attr,
+                                       char *buf)
+{
+       struct ath10k *ar = dev_get_drvdata(dev);
+       int ret, temperature;
+
+       mutex_lock(&ar->conf_mutex);
+
+       /* Can't get temperature when the card is off */
+       if (ar->state != ATH10K_STATE_ON) {
+               ret = -ENETDOWN;
+               goto out;
+       }
+
+       reinit_completion(&ar->thermal.wmi_sync);
+       ret = ath10k_wmi_pdev_get_temperature(ar);
+       if (ret) {
+               ath10k_warn(ar, "failed to read temperature %d\n", ret);
+               goto out;
+       }
+
+       if (test_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags)) {
+               ret = -ESHUTDOWN;
+               goto out;
+       }
+
+       ret = wait_for_completion_timeout(&ar->thermal.wmi_sync,
+                                         ATH10K_THERMAL_SYNC_TIMEOUT_HZ);
+       if (ret == 0) {
+               ath10k_warn(ar, "failed to synchronize thermal read\n");
+               ret = -ETIMEDOUT;
+               goto out;
+       }
+
+       spin_lock_bh(&ar->data_lock);
+       temperature = ar->thermal.temperature;
+       spin_unlock_bh(&ar->data_lock);
+
+       ret = snprintf(buf, PAGE_SIZE, "%d", temperature);
+out:
+       mutex_unlock(&ar->conf_mutex);
+       return ret;
+}
+
+void ath10k_thermal_event_temperature(struct ath10k *ar, int temperature)
+{
+       spin_lock_bh(&ar->data_lock);
+       ar->thermal.temperature = temperature;
+       spin_unlock_bh(&ar->data_lock);
+       complete(&ar->thermal.wmi_sync);
+}
+
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, ath10k_thermal_show_temp,
+                         NULL, 0);
+
+static struct attribute *ath10k_hwmon_attrs[] = {
+       &sensor_dev_attr_temp1_input.dev_attr.attr,
+       NULL,
+};
+ATTRIBUTE_GROUPS(ath10k_hwmon);
+
 int ath10k_thermal_register(struct ath10k *ar)
 {
        struct thermal_cooling_device *cdev;
+       struct device *hwmon_dev;
        int ret;
 
        cdev = thermal_cooling_device_register("ath10k_thermal", ar,
@@ -141,8 +206,26 @@ int ath10k_thermal_register(struct ath10k *ar)
        }
 
        ar->thermal.cdev = cdev;
+
+       /* Do not register hwmon device when temperature reading is not
+        * supported by firmware
+        */
+       if (ar->wmi.op_version != ATH10K_FW_WMI_OP_VERSION_10_2_4)
+               return 0;
+
+       hwmon_dev = devm_hwmon_device_register_with_groups(ar->dev,
+                                                          "ath10k_hwmon", ar,
+                                                          ath10k_hwmon_groups);
+       if (IS_ERR(hwmon_dev)) {
+               ath10k_err(ar, "failed to register hwmon device: %ld\n",
+                          PTR_ERR(hwmon_dev));
+               ret = -EINVAL;
+               goto err_remove_link;
+       }
        return 0;
 
+err_remove_link:
+       sysfs_remove_link(&ar->dev->kobj, "thermal_sensor");
 err_cooling_destroy:
        thermal_cooling_device_unregister(cdev);
        return ret;
index e20bb87849aef3c0411f600b44cacec9ef6dd5a6..bccc17ae0fde12d61a7567596a56a8900cca7749 100644 (file)
 #define ATH10K_QUIET_PERIOD_MIN         25
 #define ATH10K_QUIET_START_OFFSET       10
 #define ATH10K_QUIET_DUTY_CYCLE_MAX     70
+#define ATH10K_HWMON_NAME_LEN           15
+#define ATH10K_THERMAL_SYNC_TIMEOUT_HZ (5*HZ)
 
 struct ath10k_thermal {
        struct thermal_cooling_device *cdev;
+       struct completion wmi_sync;
 
        /* protected by conf_mutex */
        u32 duty_cycle;
+       /* temperature value in Celcius degree
+        * protected by data_lock
+        */
+       int temperature;
 };
 
 #ifdef CONFIG_THERMAL
 int ath10k_thermal_register(struct ath10k *ar);
 void ath10k_thermal_unregister(struct ath10k *ar);
+void ath10k_thermal_event_temperature(struct ath10k *ar, int temperature);
 #else
 static inline int ath10k_thermal_register(struct ath10k *ar)
 {
@@ -40,5 +48,11 @@ static inline int ath10k_thermal_register(struct ath10k *ar)
 static inline void ath10k_thermal_unregister(struct ath10k *ar)
 {
 }
+
+static inline void ath10k_thermal_event_temperature(struct ath10k *ar,
+                                                   int temperature)
+{
+}
+
 #endif
 #endif /* _THERMAL_ */
index c2d7ac31fd78373b85d2b7acd85ccf726b7c80cc..ac742905331bd08aad670d9903c2c6d7b036ace7 100644 (file)
@@ -3038,6 +3038,7 @@ static int ath10k_wmi_event_temperature(struct ath10k *ar, struct sk_buff *skb)
        if (WARN_ON(skb->len < sizeof(*ev)))
                return -EPROTO;
 
+       ath10k_thermal_event_temperature(ar, __le32_to_cpu(ev->temperature));
        return 0;
 }