ipq806x: rework ipq806x specific tsense temp driver
authorAnsuel Smith <ansuelsmth@gmail.com>
Thu, 19 Dec 2019 03:01:28 +0000 (04:01 +0100)
committerPetr Štetiar <ynezz@true.cz>
Thu, 26 Dec 2019 07:31:42 +0000 (08:31 +0100)
Tsense driver for ipq806x have various problem.
- Emit wrong error. On probing of this driver, nvmem driver can be
  not ready and this cause a EDEFER error. Actually this is not an
  error as the kernel will retry to probe the driver after the
  nvmem driver is loaded.
- Use uninitialized value on trigger of critical temp
- Doesn't free allocated memory

Because of this, rework the driver and improve it by removing extra
load of data.

Change the logic of loading data. Use the backup calib data only
when the calib data is not present. As the calibration is only
needed to set the temp offset, we don't really need to read
both calib data and set the offset based only on the backup one.
Also change how the notifier function work. At times when we
output the trigger message, we already have read the temp so
remove the extra read and the wrong uninitialized data that
probably caused a kernel panic for null pointer exception.
(Think we never experience this bug because the router
never reached that temp ever... So just lucky)

Tested-by: Stefan Lippers-Hollmann <s.l-h@gmx.de> [nbg6817/ipq8065]
Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
target/linux/ipq806x/patches-4.19/0063-4-ip806x-tsense-rework-driver.patch [new file with mode: 0644]

diff --git a/target/linux/ipq806x/patches-4.19/0063-4-ip806x-tsense-rework-driver.patch b/target/linux/ipq806x/patches-4.19/0063-4-ip806x-tsense-rework-driver.patch
new file mode 100644 (file)
index 0000000..7092552
--- /dev/null
@@ -0,0 +1,107 @@
+--- a/drivers/thermal/qcom/tsens-ipq8064.c
++++ b/drivers/thermal/qcom/tsens-ipq8064.c
+@@ -13,10 +13,12 @@
+  */
+ #include <linux/platform_device.h>
++#include <linux/err.h>
+ #include <linux/delay.h>
+ #include <linux/bitops.h>
+ #include <linux/regmap.h>
+ #include <linux/thermal.h>
++#include <linux/slab.h>
+ #include <linux/nvmem-consumer.h>
+ #include <linux/io.h>
+ #include <linux/interrupt.h>
+@@ -210,9 +212,8 @@
+       struct tsens_device *tmdev = container_of(work, struct tsens_device,
+                                       tsens_work);
+       unsigned int threshold, threshold_low, code, reg, sensor, mask;
+-      unsigned int sensor_addr;
+       bool upper_th_x, lower_th_x;
+-      int adc_code, ret;
++      int ret;
+       ret = regmap_read(tmdev->map, STATUS_CNTL_8064, &reg);
+       if (ret)
+@@ -261,9 +262,8 @@
+               if (upper_th_x || lower_th_x) {
+                       /* Notify user space */
+                       schedule_work(&tmdev->sensor[0].notify_work);
+-                      regmap_read(tmdev->map, sensor_addr, &adc_code);
+                       pr_debug("Trigger (%d degrees) for sensor %d\n",
+-                              code_to_degC(adc_code, &tmdev->sensor[0]), 0);
++                              code_to_degC(code, &tmdev->sensor[0]), 0);
+               }
+       }
+       regmap_write(tmdev->map, STATUS_CNTL_8064, reg & mask);
+@@ -372,40 +372,55 @@
+ static int calibrate_ipq8064(struct tsens_device *tmdev)
+ {
+       int i;
+-      char *data, *data_backup;
+-
++      int ret = 0;
++      u8 *data, *data_backup;
++      struct device *dev = tmdev->dev;
+       ssize_t num_read = tmdev->num_sensors;
+       struct tsens_sensor *s = tmdev->sensor;
+-      data = qfprom_read(tmdev->dev, "calib");
++      data = qfprom_read(dev, "calib");
+       if (IS_ERR(data)) {
+-              pr_err("Calibration not found.\n");
+-              return PTR_ERR(data);
++              ret = PTR_ERR(data);
++              if (ret != -EPROBE_DEFER)
++                      dev_err(dev, "Calibration not found.");
++              goto exit;
+       }
+-      data_backup = qfprom_read(tmdev->dev, "calib_backup");
++      data_backup = qfprom_read(dev, "calib_backup");
+       if (IS_ERR(data_backup)) {
+-              pr_err("Backup calibration not found.\n");
+-              return PTR_ERR(data_backup);
++              ret = PTR_ERR(data_backup);
++              if (ret != -EPROBE_DEFER)
++                      dev_err(dev, "Backup Calibration not found.");
++              goto free_data;
+       }
+       for (i = 0; i < num_read; i++) {
+               s[i].calib_data = readb_relaxed(data + i);
+-              s[i].calib_data_backup = readb_relaxed(data_backup + i);
++              
++              if (!s[i].calib_data) {
++                      s[i].calib_data_backup = readb_relaxed(data_backup + i);
++
++                      if (!s[i].calib_data_backup) {
++                              dev_err(dev, "QFPROM TSENS calibration data not present");
++                              ret = -ENODEV;
++                              goto free_backup;
++                      }
+-              if (s[i].calib_data_backup)
+                       s[i].calib_data = s[i].calib_data_backup;
+-              if (!s[i].calib_data) {
+-                      pr_err("QFPROM TSENS calibration data not present\n");
+-                      return -ENODEV;
+               }
++
+               s[i].slope = tsens_8064_slope[i];
+               s[i].offset = CAL_MDEGC - (s[i].calib_data * s[i].slope);
+       }
+       hw_init(tmdev);
+-      return 0;
++free_backup:
++      kfree(data_backup);
++free_data:
++      kfree(data);
++exit:
++      return ret;
+ }
+ static int get_temp_ipq8064(struct tsens_device *tmdev, int id, int *temp)