diff options
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.patch | 364 |
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, ®); ++ 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, ®_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__ */ |