summaryrefslogtreecommitdiff
path: root/target/linux/ipq806x/patches-4.4/015-4-thermal-qcom-tsens-8960-Add-support-for-8960-family-of-SoCs.patch
diff options
context:
space:
mode:
Diffstat (limited to 'target/linux/ipq806x/patches-4.4/015-4-thermal-qcom-tsens-8960-Add-support-for-8960-family-of-SoCs.patch')
-rw-r--r--target/linux/ipq806x/patches-4.4/015-4-thermal-qcom-tsens-8960-Add-support-for-8960-family-of-SoCs.patch364
1 files changed, 364 insertions, 0 deletions
diff --git a/target/linux/ipq806x/patches-4.4/015-4-thermal-qcom-tsens-8960-Add-support-for-8960-family-of-SoCs.patch b/target/linux/ipq806x/patches-4.4/015-4-thermal-qcom-tsens-8960-Add-support-for-8960-family-of-SoCs.patch
new file mode 100644
index 0000000..05490cd
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.4/015-4-thermal-qcom-tsens-8960-Add-support-for-8960-family-of-SoCs.patch
@@ -0,0 +1,364 @@
+From 20d4fd84bf524ad91e2cc3e4ab4020c27cfc0081 Mon Sep 17 00:00:00 2001
+From: Rajendra Nayak <rnayak@codeaurora.org>
+Date: Thu, 5 May 2016 14:21:43 +0530
+Subject: thermal: qcom: tsens-8960: Add support for 8960 family of SoCs
+
+8960 family of SoCs have the TSENS device as part of GCC, hence
+the driver probes the virtual child device created by GCC and
+uses the parent to extract all DT properties and reuses the GCC
+regmap.
+
+Also GCC/TSENS are part of a domain thats not always ON.
+Hence add .suspend and .resume hooks to save and restore some of
+the inited register context.
+
+Also 8960 family have some of the TSENS init sequence thats
+required to be done by the HLOS driver (some later versions of TSENS
+do not export these registers to non-secure world, and hence need
+these initializations to be done by secure bootloaders)
+
+8660 from the same family has just one sensor and hence some register
+offset/layout differences which need special handling in the driver.
+
+Based on the original code from Siddartha Mohanadoss, Stephen Boyd and
+Narendran Rajan.
+
+Signed-off-by: Rajendra Nayak <rnayak@codeaurora.org>
+Signed-off-by: Eduardo Valentin <edubezval@gmail.com>
+Signed-off-by: Zhang Rui <rui.zhang@intel.com>
+---
+ drivers/thermal/qcom/Makefile | 2 +-
+ drivers/thermal/qcom/tsens-8960.c | 292 ++++++++++++++++++++++++++++++++++++++
+ drivers/thermal/qcom/tsens.c | 8 +-
+ drivers/thermal/qcom/tsens.h | 2 +-
+ 4 files changed, 298 insertions(+), 6 deletions(-)
+ create mode 100644 drivers/thermal/qcom/tsens-8960.c
+
+--- a/drivers/thermal/qcom/Makefile
++++ b/drivers/thermal/qcom/Makefile
+@@ -1,2 +1,2 @@
+ obj-$(CONFIG_QCOM_TSENS) += qcom_tsens.o
+-qcom_tsens-y += tsens.o tsens-common.o tsens-8916.o tsens-8974.o
++qcom_tsens-y += tsens.o tsens-common.o tsens-8916.o tsens-8974.o tsens-8960.o
+--- /dev/null
++++ b/drivers/thermal/qcom/tsens-8960.c
+@@ -0,0 +1,292 @@
++/*
++ * Copyright (c) 2015, The Linux Foundation. All rights reserved.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 and
++ * only version 2 as published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ */
++
++#include <linux/platform_device.h>
++#include <linux/delay.h>
++#include <linux/bitops.h>
++#include <linux/regmap.h>
++#include <linux/thermal.h>
++#include "tsens.h"
++
++#define CAL_MDEGC 30000
++
++#define CONFIG_ADDR 0x3640
++#define CONFIG_ADDR_8660 0x3620
++/* CONFIG_ADDR bitmasks */
++#define CONFIG 0x9b
++#define CONFIG_MASK 0xf
++#define CONFIG_8660 1
++#define CONFIG_SHIFT_8660 28
++#define CONFIG_MASK_8660 (3 << CONFIG_SHIFT_8660)
++
++#define STATUS_CNTL_ADDR_8064 0x3660
++#define CNTL_ADDR 0x3620
++/* 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
++#define SENSOR0_SHIFT 3
++
++/* INT_STATUS_ADDR bitmasks */
++#define MIN_STATUS_MASK BIT(0)
++#define LOWER_STATUS_CLR BIT(1)
++#define UPPER_STATUS_CLR BIT(2)
++#define MAX_STATUS_MASK BIT(3)
++
++#define THRESHOLD_ADDR 0x3624
++/* THRESHOLD_ADDR bitmasks */
++#define THRESHOLD_MAX_LIMIT_SHIFT 24
++#define THRESHOLD_MIN_LIMIT_SHIFT 16
++#define THRESHOLD_UPPER_LIMIT_SHIFT 8
++#define THRESHOLD_LOWER_LIMIT_SHIFT 0
++
++/* Initial temperature threshold values */
++#define LOWER_LIMIT_TH 0x50
++#define UPPER_LIMIT_TH 0xdf
++#define MIN_LIMIT_TH 0x0
++#define MAX_LIMIT_TH 0xff
++
++#define S0_STATUS_ADDR 0x3628
++#define INT_STATUS_ADDR 0x363c
++#define TRDY_MASK BIT(7)
++#define TIMEOUT_US 100
++
++static int suspend_8960(struct tsens_device *tmdev)
++{
++ int ret;
++ unsigned int mask;
++ struct regmap *map = tmdev->map;
++
++ ret = regmap_read(map, THRESHOLD_ADDR, &tmdev->ctx.threshold);
++ if (ret)
++ return ret;
++
++ ret = regmap_read(map, CNTL_ADDR, &tmdev->ctx.control);
++ if (ret)
++ return ret;
++
++ if (tmdev->num_sensors > 1)
++ mask = SLP_CLK_ENA | EN;
++ else
++ mask = SLP_CLK_ENA_8660 | EN;
++
++ ret = regmap_update_bits(map, CNTL_ADDR, mask, 0);
++ if (ret)
++ return ret;
++
++ return 0;
++}
++
++static int resume_8960(struct tsens_device *tmdev)
++{
++ int ret;
++ struct regmap *map = tmdev->map;
++
++ ret = regmap_update_bits(map, CNTL_ADDR, SW_RST, SW_RST);
++ if (ret)
++ return ret;
++
++ /*
++ * Separate CONFIG restore is not needed only for 8660 as
++ * config is part of CTRL Addr and its restored as such
++ */
++ if (tmdev->num_sensors > 1) {
++ ret = regmap_update_bits(map, CONFIG_ADDR, CONFIG_MASK, CONFIG);
++ if (ret)
++ return ret;
++ }
++
++ ret = regmap_write(map, THRESHOLD_ADDR, tmdev->ctx.threshold);
++ if (ret)
++ return ret;
++
++ ret = regmap_write(map, CNTL_ADDR, tmdev->ctx.control);
++ if (ret)
++ return ret;
++
++ return 0;
++}
++
++static int enable_8960(struct tsens_device *tmdev, int id)
++{
++ int ret;
++ u32 reg, mask;
++
++ ret = regmap_read(tmdev->map, CNTL_ADDR, &reg);
++ if (ret)
++ return ret;
++
++ mask = BIT(id + SENSOR0_SHIFT);
++ ret = regmap_write(tmdev->map, CNTL_ADDR, reg | SW_RST);
++ if (ret)
++ return ret;
++
++ if (tmdev->num_sensors > 1)
++ reg |= mask | SLP_CLK_ENA | EN;
++ else
++ reg |= mask | SLP_CLK_ENA_8660 | EN;
++
++ ret = regmap_write(tmdev->map, CNTL_ADDR, reg);
++ if (ret)
++ return ret;
++
++ return 0;
++}
++
++static void disable_8960(struct tsens_device *tmdev)
++{
++ int ret;
++ u32 reg_cntl;
++ u32 mask;
++
++ mask = GENMASK(tmdev->num_sensors - 1, 0);
++ mask <<= SENSOR0_SHIFT;
++ mask |= EN;
++
++ ret = regmap_read(tmdev->map, CNTL_ADDR, &reg_cntl);
++ if (ret)
++ return;
++
++ reg_cntl &= ~mask;
++
++ if (tmdev->num_sensors > 1)
++ reg_cntl &= ~SLP_CLK_ENA;
++ else
++ reg_cntl &= ~SLP_CLK_ENA_8660;
++
++ regmap_write(tmdev->map, CNTL_ADDR, reg_cntl);
++}
++
++static int init_8960(struct tsens_device *tmdev)
++{
++ int ret, i;
++ u32 reg_cntl;
++
++ tmdev->map = dev_get_regmap(tmdev->dev, NULL);
++ if (!tmdev->map)
++ return -ENODEV;
++
++ /*
++ * The status registers for each sensor are discontiguous
++ * because some SoCs have 5 sensors while others have more
++ * but the control registers stay in the same place, i.e
++ * directly after the first 5 status registers.
++ */
++ for (i = 0; i < tmdev->num_sensors; i++) {
++ if (i >= 5)
++ tmdev->sensor[i].status = S0_STATUS_ADDR + 40;
++ tmdev->sensor[i].status += i * 4;
++ }
++
++ reg_cntl = SW_RST;
++ ret = regmap_update_bits(tmdev->map, CNTL_ADDR, SW_RST, reg_cntl);
++ if (ret)
++ return ret;
++
++ if (tmdev->num_sensors > 1) {
++ reg_cntl |= SLP_CLK_ENA | (MEASURE_PERIOD << 18);
++ reg_cntl &= ~SW_RST;
++ ret = regmap_update_bits(tmdev->map, CONFIG_ADDR,
++ CONFIG_MASK, CONFIG);
++ } else {
++ reg_cntl |= SLP_CLK_ENA_8660 | (MEASURE_PERIOD << 16);
++ reg_cntl &= ~CONFIG_MASK_8660;
++ reg_cntl |= CONFIG_8660 << CONFIG_SHIFT_8660;
++ }
++
++ reg_cntl |= GENMASK(tmdev->num_sensors - 1, 0) << SENSOR0_SHIFT;
++ ret = regmap_write(tmdev->map, CNTL_ADDR, reg_cntl);
++ if (ret)
++ return ret;
++
++ reg_cntl |= EN;
++ ret = regmap_write(tmdev->map, CNTL_ADDR, reg_cntl);
++ if (ret)
++ return ret;
++
++ return 0;
++}
++
++static int calibrate_8960(struct tsens_device *tmdev)
++{
++ int i;
++ char *data;
++
++ ssize_t num_read = tmdev->num_sensors;
++ struct tsens_sensor *s = tmdev->sensor;
++
++ data = qfprom_read(tmdev->dev, "calib");
++ if (IS_ERR(data))
++ data = qfprom_read(tmdev->dev, "calib_backup");
++ if (IS_ERR(data))
++ return PTR_ERR(data);
++
++ for (i = 0; i < num_read; i++, s++)
++ s->offset = data[i];
++
++ 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;
++
++ slope = thermal_zone_get_slope(s->tzd);
++ offset = CAL_MDEGC - slope * s->offset;
++
++ return adc_code * slope + offset;
++}
++
++static int get_temp_8960(struct tsens_device *tmdev, int id, int *temp)
++{
++ int ret;
++ u32 code, trdy;
++ const struct tsens_sensor *s = &tmdev->sensor[id];
++ unsigned long timeout;
++
++ timeout = jiffies + usecs_to_jiffies(TIMEOUT_US);
++ do {
++ ret = regmap_read(tmdev->map, INT_STATUS_ADDR, &trdy);
++ if (ret)
++ return ret;
++ if (!(trdy & TRDY_MASK))
++ continue;
++ ret = regmap_read(tmdev->map, s->status, &code);
++ if (ret)
++ return ret;
++ *temp = code_to_mdegC(code, s);
++ return 0;
++ } while (time_before(jiffies, timeout));
++
++ return -ETIMEDOUT;
++}
++
++const struct tsens_ops ops_8960 = {
++ .init = init_8960,
++ .calibrate = calibrate_8960,
++ .get_temp = get_temp_8960,
++ .enable = enable_8960,
++ .disable = disable_8960,
++ .suspend = suspend_8960,
++ .resume = resume_8960,
++};
++
++const struct tsens_data data_8960 = {
++ .num_sensors = 11,
++ .ops = &ops_8960,
++};
+--- a/drivers/thermal/qcom/tsens.c
++++ b/drivers/thermal/qcom/tsens.c
+@@ -122,10 +122,10 @@ static int tsens_probe(struct platform_d
+ np = dev->of_node;
+
+ id = of_match_node(tsens_table, np);
+- if (!id)
+- return -EINVAL;
+-
+- data = id->data;
++ if (id)
++ data = id->data;
++ else
++ data = &data_8960;
+
+ if (data->num_sensors <= 0) {
+ dev_err(dev, "invalid number of sensors\n");
+--- a/drivers/thermal/qcom/tsens.h
++++ b/drivers/thermal/qcom/tsens.h
+@@ -87,6 +87,6 @@ void compute_intercept_slope(struct tsen
+ int init_common(struct tsens_device *);
+ int get_temp_common(struct tsens_device *, int, int *);
+
+-extern const struct tsens_data data_8916, data_8974;
++extern const struct tsens_data data_8916, data_8974, data_8960;
+
+ #endif /* __QCOM_TSENS_H__ */