@@ -8,6 +8,7 @@
#include <linux/bitops.h>
#include <linux/regmap.h>
#include <linux/mfd/syscon.h>
+#include <linux/interrupt.h>
#include <linux/thermal.h>
#include "tsens.h"
@@ -27,7 +28,6 @@
/* CNTL_ADDR bitmasks */
#define EN BIT(0)
#define SW_RST BIT(1)
-#define SENSOR0_EN BIT(3)
#define SLP_CLK_ENA BIT(26)
#define SLP_CLK_ENA_8660 BIT(24)
#define MEASURE_PERIOD 1
@@ -41,14 +41,26 @@
#define THRESHOLD_ADDR 0x3624
/* THRESHOLD_ADDR bitmasks */
+#define THRESHOLD_MAX_CODE 0x20000
+#define THRESHOLD_MIN_CODE 0
#define THRESHOLD_MAX_LIMIT_SHIFT 24
#define THRESHOLD_MIN_LIMIT_SHIFT 16
#define THRESHOLD_UPPER_LIMIT_SHIFT 8
#define THRESHOLD_LOWER_LIMIT_SHIFT 0
+#define THRESHOLD_MAX_LIMIT_MASK (THRESHOLD_MAX_CODE << \
+ THRESHOLD_MAX_LIMIT_SHIFT)
+#define THRESHOLD_MIN_LIMIT_MASK (THRESHOLD_MAX_CODE << \
+ THRESHOLD_MIN_LIMIT_SHIFT)
+#define THRESHOLD_UPPER_LIMIT_MASK (THRESHOLD_MAX_CODE << \
+ THRESHOLD_UPPER_LIMIT_SHIFT)
+#define THRESHOLD_LOWER_LIMIT_MASK (THRESHOLD_MAX_CODE << \
+ THRESHOLD_LOWER_LIMIT_SHIFT)
/* Initial temperature threshold values */
-#define LOWER_LIMIT_TH 0x50
-#define UPPER_LIMIT_TH 0xdf
+#define LOWER_LIMIT_TH_8960 0x50
+#define UPPER_LIMIT_TH_8960 0xdf
+#define LOWER_LIMIT_TH_8064 0x9d /* 95C */
+#define UPPER_LIMIT_TH_8064 0xa6 /* 105C */
#define MIN_LIMIT_TH 0x0
#define MAX_LIMIT_TH 0xff
@@ -57,6 +69,169 @@
#define TRDY_MASK BIT(7)
#define TIMEOUT_US 100
+#define TSENS_EN BIT(0)
+#define TSENS_SW_RST BIT(1)
+#define TSENS_ADC_CLK_SEL BIT(2)
+#define SENSOR0_EN BIT(3)
+#define SENSOR1_EN BIT(4)
+#define SENSOR2_EN BIT(5)
+#define SENSOR3_EN BIT(6)
+#define SENSOR4_EN BIT(7)
+#define SENSORS_EN (SENSOR0_EN | SENSOR1_EN | \
+ SENSOR2_EN | SENSOR3_EN | SENSOR4_EN)
+#define TSENS_8064_SENSOR5_EN BIT(8)
+#define TSENS_8064_SENSOR6_EN BIT(9)
+#define TSENS_8064_SENSOR7_EN BIT(10)
+#define TSENS_8064_SENSOR8_EN BIT(11)
+#define TSENS_8064_SENSOR9_EN BIT(12)
+#define TSENS_8064_SENSOR10_EN BIT(13)
+#define TSENS_8064_SENSORS_EN (SENSORS_EN | \
+ TSENS_8064_SENSOR5_EN | \
+ TSENS_8064_SENSOR6_EN | \
+ TSENS_8064_SENSOR7_EN | \
+ TSENS_8064_SENSOR8_EN | \
+ TSENS_8064_SENSOR9_EN | \
+ TSENS_8064_SENSOR10_EN)
+
+u32 tsens_8960_slope[] = {
+ 1176, 1176, 1154, 1176,
+ 1111, 1132, 1132, 1199,
+ 1132, 1199, 1132
+ };
+
+/* Temperature on y axis and ADC-code on x-axis */
+static inline int code_to_mdegC(u32 adc_code, const struct tsens_sensor *s)
+{
+ int slope, offset;
+
+ slope = thermal_zone_get_slope(s->tzd);
+ offset = CAL_MDEGC - slope * s->offset;
+
+ return adc_code * slope + offset;
+}
+
+static void notify_uspace_tsens_fn(struct work_struct *work)
+{
+ struct tsens_sensor *s = container_of(work, struct tsens_sensor,
+ notify_work);
+
+ sysfs_notify(&s->tzd->device.kobj, NULL, "type");
+}
+
+static void tsens_scheduler_fn(struct work_struct *work)
+{
+ struct tsens_priv *priv =
+ container_of(work, struct tsens_priv, tsens_work);
+ unsigned int threshold, threshold_low, code, reg, sensor, mask;
+ bool upper_th_x, lower_th_x;
+ int ret;
+
+ ret = regmap_read(priv->tm_map, STATUS_CNTL_ADDR_8064, ®);
+ if (ret)
+ return;
+ reg = reg | LOWER_STATUS_CLR | UPPER_STATUS_CLR;
+ ret = regmap_write(priv->tm_map, STATUS_CNTL_ADDR_8064, reg);
+ if (ret)
+ return;
+
+ mask = ~(LOWER_STATUS_CLR | UPPER_STATUS_CLR);
+ ret = regmap_read(priv->tm_map, THRESHOLD_ADDR, &threshold);
+ if (ret)
+ return;
+ threshold_low = (threshold & THRESHOLD_LOWER_LIMIT_MASK) >>
+ THRESHOLD_LOWER_LIMIT_SHIFT;
+ threshold = (threshold & THRESHOLD_UPPER_LIMIT_MASK) >>
+ THRESHOLD_UPPER_LIMIT_SHIFT;
+
+ ret = regmap_read(priv->tm_map, STATUS_CNTL_ADDR_8064, ®);
+ if (ret)
+ return;
+
+ ret = regmap_read(priv->tm_map, CNTL_ADDR, &sensor);
+ if (ret)
+ return;
+ sensor &= (uint32_t)TSENS_8064_SENSORS_EN;
+ sensor >>= SENSOR0_SHIFT;
+
+ /* Constraint: There is only 1 interrupt control register for all
+ * 11 temperature sensor. So monitoring more than 1 sensor based
+ * on interrupts will yield inconsistent result. To overcome this
+ * issue we will monitor only sensor 0 which is the master sensor.
+ */
+
+ /* Skip if the sensor is disabled */
+ if (sensor & 1) {
+ ret = regmap_read(priv->tm_map, priv->sensor[0].status, &code);
+ if (ret)
+ return;
+ upper_th_x = code >= threshold;
+ lower_th_x = code <= threshold_low;
+ if (upper_th_x)
+ mask |= UPPER_STATUS_CLR;
+ if (lower_th_x)
+ mask |= LOWER_STATUS_CLR;
+ if (upper_th_x || lower_th_x) {
+ /* Notify user space */
+ schedule_work(&priv->sensor[0].notify_work);
+ pr_debug("Trigger (%d degrees) for sensor %d\n",
+ code_to_mdegC(code, &priv->sensor[0]), 0);
+ }
+ }
+ regmap_write(priv->tm_map, STATUS_CNTL_ADDR_8064, reg & mask);
+}
+
+static irqreturn_t tsens_isr(int irq, void *data)
+{
+ struct tsens_priv *priv = data;
+
+ schedule_work(&priv->tsens_work);
+ return IRQ_HANDLED;
+}
+
+static void hw_init(struct tsens_priv *priv)
+{
+ int ret;
+ unsigned int reg_cntl = 0, reg_cfg = 0, reg_thr = 0;
+ unsigned int reg_status_cntl = 0;
+
+ regmap_read(priv->tm_map, CNTL_ADDR, ®_cntl);
+ regmap_write(priv->tm_map, CNTL_ADDR, reg_cntl | TSENS_SW_RST);
+
+ reg_cntl |= SLP_CLK_ENA | (MEASURE_PERIOD << 18) |
+ (((1 << priv->num_sensors) - 1) << SENSOR0_SHIFT);
+ regmap_write(priv->tm_map, CNTL_ADDR, reg_cntl);
+ regmap_read(priv->tm_map, STATUS_CNTL_ADDR_8064, ®_status_cntl);
+ reg_status_cntl |= LOWER_STATUS_CLR | UPPER_STATUS_CLR |
+ MIN_STATUS_MASK | MAX_STATUS_MASK;
+ regmap_write(priv->tm_map, STATUS_CNTL_ADDR_8064, reg_status_cntl);
+ reg_cntl |= TSENS_EN;
+ regmap_write(priv->tm_map, CNTL_ADDR, reg_cntl);
+
+ regmap_read(priv->tm_map, CONFIG_ADDR, ®_cfg);
+ if (priv->num_sensors > 1)
+ reg_cfg = (reg_cfg & ~CONFIG_MASK) | CONFIG;
+ else
+ reg_cfg = (reg_cfg & ~CONFIG_MASK) |
+ (CONFIG << CONFIG_SHIFT_8660);
+ regmap_write(priv->tm_map, CONFIG_ADDR, reg_cfg);
+
+ reg_thr |= (LOWER_LIMIT_TH_8064 << THRESHOLD_LOWER_LIMIT_SHIFT) |
+ (UPPER_LIMIT_TH_8064 << THRESHOLD_UPPER_LIMIT_SHIFT) |
+ (MIN_LIMIT_TH << THRESHOLD_MIN_LIMIT_SHIFT) |
+ (MAX_LIMIT_TH << THRESHOLD_MAX_LIMIT_SHIFT);
+
+ regmap_write(priv->tm_map, THRESHOLD_ADDR, reg_thr);
+
+ ret = devm_request_irq(priv->dev, priv->tsens_irq, tsens_isr,
+ IRQF_TRIGGER_RISING, "tsens_interrupt", priv);
+ if (ret < 0) {
+ dev_err(priv->dev, "request_irq FAIL: %d", ret);
+ return;
+ }
+
+ INIT_WORK(&priv->tsens_work, tsens_scheduler_fn);
+}
+
static int suspend_8960(struct tsens_priv *priv)
{
int ret;
@@ -191,6 +366,8 @@ static int init_8960(struct tsens_priv *priv)
if (i >= 5)
priv->sensor[i].status = S0_STATUS_ADDR + 40;
priv->sensor[i].status += i * 4;
+ priv->sensor[i].slope = tsens_8960_slope[i];
+ INIT_WORK(&priv->sensor[i].notify_work, notify_uspace_tsens_fn);
}
reg_cntl = SW_RST;
@@ -241,18 +418,9 @@ static int calibrate_8960(struct tsens_priv *priv)
kfree(data);
- return 0;
-}
-
-/* Temperature on y axis and ADC-code on x-axis */
-static inline int code_to_mdegC(u32 adc_code, const struct tsens_sensor *s)
-{
- int slope, offset;
+ hw_init(priv);
- slope = thermal_zone_get_slope(s->tzd);
- offset = CAL_MDEGC - slope * s->offset;
-
- return adc_code * slope + offset;
+ return 0;
}
static int get_temp_8960(const struct tsens_sensor *s, int *temp)
@@ -48,6 +48,7 @@ enum tsens_irq_type {
struct tsens_sensor {
struct tsens_priv *priv;
struct thermal_zone_device *tzd;
+ struct work_struct notify_work;
int offset;
unsigned int hw_id;
int slope;
Add interrupt support for 9860 tsens driver used to set thermal trip point for the system. Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> --- drivers/thermal/qcom/tsens-8960.c | 196 +++++++++++++++++++++++++++--- drivers/thermal/qcom/tsens.h | 1 + 2 files changed, 183 insertions(+), 14 deletions(-)