ipq806x: add thermal sensor driver
Allows to check cpu temperature. Huge thanks to @hnyman for valuable assistance! Signed-off-by: Pavel Kubelun <be.dissent@gmail.com>
This commit is contained in:
parent
2b71f958b1
commit
fef6a96d9e
16 changed files with 2293 additions and 13 deletions
|
@ -352,6 +352,7 @@ CONFIG_QCOM_SCM=y
|
|||
CONFIG_QCOM_SCM_32=y
|
||||
# CONFIG_QCOM_SMD is not set
|
||||
CONFIG_QCOM_SMEM=y
|
||||
CONFIG_QCOM_TSENS=y
|
||||
CONFIG_QCOM_WDT=y
|
||||
CONFIG_RAS=y
|
||||
CONFIG_RATIONAL=y
|
||||
|
|
|
@ -79,6 +79,92 @@
|
|||
};
|
||||
};
|
||||
|
||||
thermal-zones {
|
||||
cpu-thermal0 {
|
||||
polling-delay-passive = <250>;
|
||||
polling-delay = <1000>;
|
||||
|
||||
thermal-sensors = <&gcc 5>;
|
||||
coefficients = <1132 0>;
|
||||
|
||||
trips {
|
||||
cpu_alert0: trip0 {
|
||||
temperature = <75000>;
|
||||
hysteresis = <2000>;
|
||||
type = "passive";
|
||||
};
|
||||
cpu_crit0: trip1 {
|
||||
temperature = <110000>;
|
||||
hysteresis = <2000>;
|
||||
type = "critical";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
cpu-thermal1 {
|
||||
polling-delay-passive = <250>;
|
||||
polling-delay = <1000>;
|
||||
|
||||
thermal-sensors = <&gcc 6>;
|
||||
coefficients = <1132 0>;
|
||||
|
||||
trips {
|
||||
cpu_alert1: trip0 {
|
||||
temperature = <75000>;
|
||||
hysteresis = <2000>;
|
||||
type = "passive";
|
||||
};
|
||||
cpu_crit1: trip1 {
|
||||
temperature = <110000>;
|
||||
hysteresis = <2000>;
|
||||
type = "critical";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
cpu-thermal2 {
|
||||
polling-delay-passive = <250>;
|
||||
polling-delay = <1000>;
|
||||
|
||||
thermal-sensors = <&gcc 7>;
|
||||
coefficients = <1199 0>;
|
||||
|
||||
trips {
|
||||
cpu_alert2: trip0 {
|
||||
temperature = <75000>;
|
||||
hysteresis = <2000>;
|
||||
type = "passive";
|
||||
};
|
||||
cpu_crit2: trip1 {
|
||||
temperature = <110000>;
|
||||
hysteresis = <2000>;
|
||||
type = "critical";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
cpu-thermal3 {
|
||||
polling-delay-passive = <250>;
|
||||
polling-delay = <1000>;
|
||||
|
||||
thermal-sensors = <&gcc 8>;
|
||||
coefficients = <1132 0>;
|
||||
|
||||
trips {
|
||||
cpu_alert3: trip0 {
|
||||
temperature = <75000>;
|
||||
hysteresis = <2000>;
|
||||
type = "passive";
|
||||
};
|
||||
cpu_crit3: trip1 {
|
||||
temperature = <110000>;
|
||||
hysteresis = <2000>;
|
||||
type = "critical";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
cpu-pmu {
|
||||
compatible = "qcom,krait-pmu";
|
||||
interrupts = <1 10 0x304>;
|
||||
|
@ -205,8 +291,14 @@
|
|||
reg = <0x00700000 0x1000>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
stride = <1>;
|
||||
ranges = <0x0 0x00700000 0x1000>;
|
||||
ranges;
|
||||
|
||||
tsens_calib: calib {
|
||||
reg = <0x400 0x10>;
|
||||
};
|
||||
tsens_backup: backup_calib {
|
||||
reg = <0x410 0x10>;
|
||||
};
|
||||
};
|
||||
|
||||
rpm@108000 {
|
||||
|
@ -687,9 +779,12 @@
|
|||
gcc: clock-controller@900000 {
|
||||
compatible = "qcom,gcc-ipq8064";
|
||||
reg = <0x00900000 0x4000>;
|
||||
nvmem-cells = <&tsens_calib>, <&tsens_backup>;
|
||||
nvmem-cell-names = "calib", "calib_backup";
|
||||
#clock-cells = <1>;
|
||||
#reset-cells = <1>;
|
||||
#power-domain-cells = <1>;
|
||||
#thermal-sensor-cells = <1>;
|
||||
};
|
||||
|
||||
lcc: clock-controller@28000000 {
|
||||
|
@ -704,16 +799,6 @@
|
|||
reg = <0x1a400000 0x100>;
|
||||
};
|
||||
|
||||
tsens: tsens-ipq806x {
|
||||
compatible = "qcom,ipq806x-tsens";
|
||||
reg = <0x900000 0x3678>, <0x700000 0x420>;
|
||||
reg-names = "tsens_physical", "tsens_eeprom_physical";
|
||||
interrupts = <0 178 0>;
|
||||
qcom,sensors = <11>;
|
||||
qcom,tsens_factor = <1000>;
|
||||
qcom,slope = <1176 1176 1154 1176 1111 1132 1132 1199 1132 1199 1132>;
|
||||
};
|
||||
|
||||
qcom,msm-thermal {
|
||||
compatible = "qcom,msm-thermal";
|
||||
qcom,sensor-id = <0>;
|
||||
|
|
|
@ -0,0 +1,536 @@
|
|||
From 9066073c6c27994a30187abf3b674770b4088348 Mon Sep 17 00:00:00 2001
|
||||
From: Rajendra Nayak <rnayak@codeaurora.org>
|
||||
Date: Thu, 5 May 2016 14:21:39 +0530
|
||||
Subject: thermal: qcom: tsens: Add a skeletal TSENS drivers
|
||||
|
||||
TSENS is Qualcomms' thermal temperature sensor device. It
|
||||
supports reading temperatures from multiple thermal sensors
|
||||
present on various QCOM SoCs.
|
||||
Calibration data is generally read from a non-volatile memory
|
||||
(eeprom) device.
|
||||
|
||||
Add a skeleton driver with all the necessary abstractions so
|
||||
a variety of qcom device families which support TSENS can
|
||||
add driver extensions.
|
||||
|
||||
Also add the required device tree bindings which can be used
|
||||
to describe the TSENS device in DT.
|
||||
|
||||
Signed-off-by: Rajendra Nayak <rnayak@codeaurora.org>
|
||||
Reviewed-by: Lina Iyer <lina.iyer@linaro.org>
|
||||
Signed-off-by: Eduardo Valentin <edubezval@gmail.com>
|
||||
Signed-off-by: Zhang Rui <rui.zhang@intel.com>
|
||||
---
|
||||
.../devicetree/bindings/thermal/qcom-tsens.txt | 21 +++
|
||||
drivers/thermal/Kconfig | 5 +
|
||||
drivers/thermal/Makefile | 1 +
|
||||
drivers/thermal/qcom/Kconfig | 11 ++
|
||||
drivers/thermal/qcom/Makefile | 2 +
|
||||
drivers/thermal/qcom/tsens-common.c | 141 +++++++++++++++
|
||||
drivers/thermal/qcom/tsens.c | 195 +++++++++++++++++++++
|
||||
drivers/thermal/qcom/tsens.h | 90 ++++++++++
|
||||
8 files changed, 466 insertions(+)
|
||||
create mode 100644 Documentation/devicetree/bindings/thermal/qcom-tsens.txt
|
||||
create mode 100644 drivers/thermal/qcom/Kconfig
|
||||
create mode 100644 drivers/thermal/qcom/Makefile
|
||||
create mode 100644 drivers/thermal/qcom/tsens-common.c
|
||||
create mode 100644 drivers/thermal/qcom/tsens.c
|
||||
create mode 100644 drivers/thermal/qcom/tsens.h
|
||||
|
||||
--- /dev/null
|
||||
+++ b/Documentation/devicetree/bindings/thermal/qcom-tsens.txt
|
||||
@@ -0,0 +1,21 @@
|
||||
+* QCOM SoC Temperature Sensor (TSENS)
|
||||
+
|
||||
+Required properties:
|
||||
+- compatible :
|
||||
+ - "qcom,msm8916-tsens" : For 8916 Family of SoCs
|
||||
+ - "qcom,msm8974-tsens" : For 8974 Family of SoCs
|
||||
+ - "qcom,msm8996-tsens" : For 8996 Family of SoCs
|
||||
+
|
||||
+- reg: Address range of the thermal registers
|
||||
+- #thermal-sensor-cells : Should be 1. See ./thermal.txt for a description.
|
||||
+- Refer to Documentation/devicetree/bindings/nvmem/nvmem.txt to know how to specify
|
||||
+nvmem cells
|
||||
+
|
||||
+Example:
|
||||
+tsens: thermal-sensor@900000 {
|
||||
+ compatible = "qcom,msm8916-tsens";
|
||||
+ reg = <0x4a8000 0x2000>;
|
||||
+ nvmem-cells = <&tsens_caldata>, <&tsens_calsel>;
|
||||
+ nvmem-cell-names = "caldata", "calsel";
|
||||
+ #thermal-sensor-cells = <1>;
|
||||
+ };
|
||||
--- a/drivers/thermal/Kconfig
|
||||
+++ b/drivers/thermal/Kconfig
|
||||
@@ -391,4 +391,9 @@ config QCOM_SPMI_TEMP_ALARM
|
||||
real time die temperature if an ADC is present or an estimate of the
|
||||
temperature based upon the over temperature stage value.
|
||||
|
||||
+menu "Qualcomm thermal drivers"
|
||||
+depends on (ARCH_QCOM && OF) || COMPILE_TEST
|
||||
+source "drivers/thermal/qcom/Kconfig"
|
||||
+endmenu
|
||||
+
|
||||
endif
|
||||
--- a/drivers/thermal/Makefile
|
||||
+++ b/drivers/thermal/Makefile
|
||||
@@ -48,3 +48,4 @@ obj-$(CONFIG_INTEL_PCH_THERMAL) += intel
|
||||
obj-$(CONFIG_ST_THERMAL) += st/
|
||||
obj-$(CONFIG_TEGRA_SOCTHERM) += tegra_soctherm.o
|
||||
obj-$(CONFIG_HISI_THERMAL) += hisi_thermal.o
|
||||
+obj-$(CONFIG_QCOM_TSENS) += qcom/
|
||||
--- /dev/null
|
||||
+++ b/drivers/thermal/qcom/Kconfig
|
||||
@@ -0,0 +1,11 @@
|
||||
+config QCOM_TSENS
|
||||
+ tristate "Qualcomm TSENS Temperature Alarm"
|
||||
+ depends on THERMAL
|
||||
+ depends on QCOM_QFPROM
|
||||
+ depends on ARCH_QCOM || COMPILE_TEST
|
||||
+ help
|
||||
+ This enables the thermal sysfs driver for the TSENS device. It shows
|
||||
+ up in Sysfs as a thermal zone with multiple trip points. Disabling the
|
||||
+ thermal zone device via the mode file results in disabling the sensor.
|
||||
+ Also able to set threshold temperature for both hot and cold and update
|
||||
+ when a threshold is reached.
|
||||
--- /dev/null
|
||||
+++ b/drivers/thermal/qcom/Makefile
|
||||
@@ -0,0 +1,2 @@
|
||||
+obj-$(CONFIG_QCOM_TSENS) += qcom_tsens.o
|
||||
+qcom_tsens-y += tsens.o tsens-common.o
|
||||
--- /dev/null
|
||||
+++ b/drivers/thermal/qcom/tsens-common.c
|
||||
@@ -0,0 +1,141 @@
|
||||
+/*
|
||||
+ * 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/err.h>
|
||||
+#include <linux/io.h>
|
||||
+#include <linux/nvmem-consumer.h>
|
||||
+#include <linux/of_address.h>
|
||||
+#include <linux/platform_device.h>
|
||||
+#include <linux/regmap.h>
|
||||
+#include "tsens.h"
|
||||
+
|
||||
+#define S0_ST_ADDR 0x1030
|
||||
+#define SN_ADDR_OFFSET 0x4
|
||||
+#define SN_ST_TEMP_MASK 0x3ff
|
||||
+#define CAL_DEGC_PT1 30
|
||||
+#define CAL_DEGC_PT2 120
|
||||
+#define SLOPE_FACTOR 1000
|
||||
+#define SLOPE_DEFAULT 3200
|
||||
+
|
||||
+char *qfprom_read(struct device *dev, const char *cname)
|
||||
+{
|
||||
+ struct nvmem_cell *cell;
|
||||
+ ssize_t data;
|
||||
+ char *ret;
|
||||
+
|
||||
+ cell = nvmem_cell_get(dev, cname);
|
||||
+ if (IS_ERR(cell))
|
||||
+ return ERR_CAST(cell);
|
||||
+
|
||||
+ ret = nvmem_cell_read(cell, &data);
|
||||
+ nvmem_cell_put(cell);
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
+ * Use this function on devices where slope and offset calculations
|
||||
+ * depend on calibration data read from qfprom. On others the slope
|
||||
+ * and offset values are derived from tz->tzp->slope and tz->tzp->offset
|
||||
+ * resp.
|
||||
+ */
|
||||
+void compute_intercept_slope(struct tsens_device *tmdev, u32 *p1,
|
||||
+ u32 *p2, u32 mode)
|
||||
+{
|
||||
+ int i;
|
||||
+ int num, den;
|
||||
+
|
||||
+ for (i = 0; i < tmdev->num_sensors; i++) {
|
||||
+ dev_dbg(tmdev->dev,
|
||||
+ "sensor%d - data_point1:%#x data_point2:%#x\n",
|
||||
+ i, p1[i], p2[i]);
|
||||
+
|
||||
+ tmdev->sensor[i].slope = SLOPE_DEFAULT;
|
||||
+ if (mode == TWO_PT_CALIB) {
|
||||
+ /*
|
||||
+ * slope (m) = adc_code2 - adc_code1 (y2 - y1)/
|
||||
+ * temp_120_degc - temp_30_degc (x2 - x1)
|
||||
+ */
|
||||
+ num = p2[i] - p1[i];
|
||||
+ num *= SLOPE_FACTOR;
|
||||
+ den = CAL_DEGC_PT2 - CAL_DEGC_PT1;
|
||||
+ tmdev->sensor[i].slope = num / den;
|
||||
+ }
|
||||
+
|
||||
+ tmdev->sensor[i].offset = (p1[i] * SLOPE_FACTOR) -
|
||||
+ (CAL_DEGC_PT1 *
|
||||
+ tmdev->sensor[i].slope);
|
||||
+ dev_dbg(tmdev->dev, "offset:%d\n", tmdev->sensor[i].offset);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static inline int code_to_degc(u32 adc_code, const struct tsens_sensor *s)
|
||||
+{
|
||||
+ int degc, num, den;
|
||||
+
|
||||
+ num = (adc_code * SLOPE_FACTOR) - s->offset;
|
||||
+ den = s->slope;
|
||||
+
|
||||
+ if (num > 0)
|
||||
+ degc = num + (den / 2);
|
||||
+ else if (num < 0)
|
||||
+ degc = num - (den / 2);
|
||||
+ else
|
||||
+ degc = num;
|
||||
+
|
||||
+ degc /= den;
|
||||
+
|
||||
+ return degc;
|
||||
+}
|
||||
+
|
||||
+int get_temp_common(struct tsens_device *tmdev, int id, int *temp)
|
||||
+{
|
||||
+ struct tsens_sensor *s = &tmdev->sensor[id];
|
||||
+ u32 code;
|
||||
+ unsigned int sensor_addr;
|
||||
+ int last_temp = 0, ret;
|
||||
+
|
||||
+ sensor_addr = S0_ST_ADDR + s->hw_id * SN_ADDR_OFFSET;
|
||||
+ ret = regmap_read(tmdev->map, sensor_addr, &code);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+ last_temp = code & SN_ST_TEMP_MASK;
|
||||
+
|
||||
+ *temp = code_to_degc(last_temp, s) * 1000;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static const struct regmap_config tsens_config = {
|
||||
+ .reg_bits = 32,
|
||||
+ .val_bits = 32,
|
||||
+ .reg_stride = 4,
|
||||
+};
|
||||
+
|
||||
+int __init init_common(struct tsens_device *tmdev)
|
||||
+{
|
||||
+ void __iomem *base;
|
||||
+
|
||||
+ base = of_iomap(tmdev->dev->of_node, 0);
|
||||
+ if (IS_ERR(base))
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ tmdev->map = devm_regmap_init_mmio(tmdev->dev, base, &tsens_config);
|
||||
+ if (!tmdev->map) {
|
||||
+ iounmap(base);
|
||||
+ return -ENODEV;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
--- /dev/null
|
||||
+++ b/drivers/thermal/qcom/tsens.c
|
||||
@@ -0,0 +1,195 @@
|
||||
+/*
|
||||
+ * 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/err.h>
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/of.h>
|
||||
+#include <linux/platform_device.h>
|
||||
+#include <linux/pm.h>
|
||||
+#include <linux/slab.h>
|
||||
+#include <linux/thermal.h>
|
||||
+#include "tsens.h"
|
||||
+
|
||||
+static int tsens_get_temp(void *data, int *temp)
|
||||
+{
|
||||
+ const struct tsens_sensor *s = data;
|
||||
+ struct tsens_device *tmdev = s->tmdev;
|
||||
+
|
||||
+ return tmdev->ops->get_temp(tmdev, s->id, temp);
|
||||
+}
|
||||
+
|
||||
+static int tsens_get_trend(void *data, long *temp)
|
||||
+{
|
||||
+ const struct tsens_sensor *s = data;
|
||||
+ struct tsens_device *tmdev = s->tmdev;
|
||||
+
|
||||
+ if (tmdev->ops->get_trend)
|
||||
+ return tmdev->ops->get_trend(tmdev, s->id, temp);
|
||||
+
|
||||
+ return -ENOTSUPP;
|
||||
+}
|
||||
+
|
||||
+static int tsens_suspend(struct device *dev)
|
||||
+{
|
||||
+ struct tsens_device *tmdev = dev_get_drvdata(dev);
|
||||
+
|
||||
+ if (tmdev->ops && tmdev->ops->suspend)
|
||||
+ return tmdev->ops->suspend(tmdev);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int tsens_resume(struct device *dev)
|
||||
+{
|
||||
+ struct tsens_device *tmdev = dev_get_drvdata(dev);
|
||||
+
|
||||
+ if (tmdev->ops && tmdev->ops->resume)
|
||||
+ return tmdev->ops->resume(tmdev);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static SIMPLE_DEV_PM_OPS(tsens_pm_ops, tsens_suspend, tsens_resume);
|
||||
+
|
||||
+static const struct of_device_id tsens_table[] = {
|
||||
+ {
|
||||
+ .compatible = "qcom,msm8916-tsens",
|
||||
+ }, {
|
||||
+ .compatible = "qcom,msm8974-tsens",
|
||||
+ },
|
||||
+ {}
|
||||
+};
|
||||
+MODULE_DEVICE_TABLE(of, tsens_table);
|
||||
+
|
||||
+static const struct thermal_zone_of_device_ops tsens_of_ops = {
|
||||
+ .get_temp = tsens_get_temp,
|
||||
+ .get_trend = tsens_get_trend,
|
||||
+};
|
||||
+
|
||||
+static int tsens_register(struct tsens_device *tmdev)
|
||||
+{
|
||||
+ int i;
|
||||
+ struct thermal_zone_device *tzd;
|
||||
+ u32 *hw_id, n = tmdev->num_sensors;
|
||||
+
|
||||
+ hw_id = devm_kcalloc(tmdev->dev, n, sizeof(u32), GFP_KERNEL);
|
||||
+ if (!hw_id)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ for (i = 0; i < tmdev->num_sensors; i++) {
|
||||
+ tmdev->sensor[i].tmdev = tmdev;
|
||||
+ tmdev->sensor[i].id = i;
|
||||
+ tzd = devm_thermal_zone_of_sensor_register(tmdev->dev, i,
|
||||
+ &tmdev->sensor[i],
|
||||
+ &tsens_of_ops);
|
||||
+ if (IS_ERR(tzd))
|
||||
+ continue;
|
||||
+ tmdev->sensor[i].tzd = tzd;
|
||||
+ if (tmdev->ops->enable)
|
||||
+ tmdev->ops->enable(tmdev, i);
|
||||
+ }
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int tsens_probe(struct platform_device *pdev)
|
||||
+{
|
||||
+ int ret, i;
|
||||
+ struct device *dev;
|
||||
+ struct device_node *np;
|
||||
+ struct tsens_sensor *s;
|
||||
+ struct tsens_device *tmdev;
|
||||
+ const struct tsens_data *data;
|
||||
+ const struct of_device_id *id;
|
||||
+
|
||||
+ if (pdev->dev.of_node)
|
||||
+ dev = &pdev->dev;
|
||||
+ else
|
||||
+ dev = pdev->dev.parent;
|
||||
+
|
||||
+ np = dev->of_node;
|
||||
+
|
||||
+ id = of_match_node(tsens_table, np);
|
||||
+ if (!id)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ data = id->data;
|
||||
+
|
||||
+ if (data->num_sensors <= 0) {
|
||||
+ dev_err(dev, "invalid number of sensors\n");
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ tmdev = devm_kzalloc(dev, sizeof(*tmdev) +
|
||||
+ data->num_sensors * sizeof(*s), GFP_KERNEL);
|
||||
+ if (!tmdev)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ tmdev->dev = dev;
|
||||
+ tmdev->num_sensors = data->num_sensors;
|
||||
+ tmdev->ops = data->ops;
|
||||
+ for (i = 0; i < tmdev->num_sensors; i++) {
|
||||
+ if (data->hw_ids)
|
||||
+ tmdev->sensor[i].hw_id = data->hw_ids[i];
|
||||
+ else
|
||||
+ tmdev->sensor[i].hw_id = i;
|
||||
+ }
|
||||
+
|
||||
+ if (!tmdev->ops || !tmdev->ops->init || !tmdev->ops->get_temp)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ ret = tmdev->ops->init(tmdev);
|
||||
+ if (ret < 0) {
|
||||
+ dev_err(dev, "tsens init failed\n");
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ if (tmdev->ops->calibrate) {
|
||||
+ ret = tmdev->ops->calibrate(tmdev);
|
||||
+ if (ret < 0) {
|
||||
+ dev_err(dev, "tsens calibration failed\n");
|
||||
+ return ret;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ ret = tsens_register(tmdev);
|
||||
+
|
||||
+ platform_set_drvdata(pdev, tmdev);
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static int tsens_remove(struct platform_device *pdev)
|
||||
+{
|
||||
+ struct tsens_device *tmdev = platform_get_drvdata(pdev);
|
||||
+
|
||||
+ if (tmdev->ops->disable)
|
||||
+ tmdev->ops->disable(tmdev);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static struct platform_driver tsens_driver = {
|
||||
+ .probe = tsens_probe,
|
||||
+ .remove = tsens_remove,
|
||||
+ .driver = {
|
||||
+ .name = "qcom-tsens",
|
||||
+ .pm = &tsens_pm_ops,
|
||||
+ .of_match_table = tsens_table,
|
||||
+ },
|
||||
+};
|
||||
+module_platform_driver(tsens_driver);
|
||||
+
|
||||
+MODULE_LICENSE("GPL v2");
|
||||
+MODULE_DESCRIPTION("QCOM Temperature Sensor driver");
|
||||
+MODULE_ALIAS("platform:qcom-tsens");
|
||||
--- /dev/null
|
||||
+++ b/drivers/thermal/qcom/tsens.h
|
||||
@@ -0,0 +1,90 @@
|
||||
+/*
|
||||
+ * Copyright (c) 2015, The Linux Foundation. All rights reserved.
|
||||
+ *
|
||||
+ * This software is licensed under the terms of the GNU General Public
|
||||
+ * License version 2, as published by the Free Software Foundation, and
|
||||
+ * may be copied, distributed, and modified under those terms.
|
||||
+ *
|
||||
+ * 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.
|
||||
+ */
|
||||
+#ifndef __QCOM_TSENS_H__
|
||||
+#define __QCOM_TSENS_H__
|
||||
+
|
||||
+#define ONE_PT_CALIB 0x1
|
||||
+#define ONE_PT_CALIB2 0x2
|
||||
+#define TWO_PT_CALIB 0x3
|
||||
+
|
||||
+struct tsens_device;
|
||||
+
|
||||
+struct tsens_sensor {
|
||||
+ struct tsens_device *tmdev;
|
||||
+ struct thermal_zone_device *tzd;
|
||||
+ int offset;
|
||||
+ int id;
|
||||
+ int hw_id;
|
||||
+ int slope;
|
||||
+ u32 status;
|
||||
+};
|
||||
+
|
||||
+/**
|
||||
+ * struct tsens_ops - operations as supported by the tsens device
|
||||
+ * @init: Function to initialize the tsens device
|
||||
+ * @calibrate: Function to calibrate the tsens device
|
||||
+ * @get_temp: Function which returns the temp in millidegC
|
||||
+ * @enable: Function to enable (clocks/power) tsens device
|
||||
+ * @disable: Function to disable the tsens device
|
||||
+ * @suspend: Function to suspend the tsens device
|
||||
+ * @resume: Function to resume the tsens device
|
||||
+ * @get_trend: Function to get the thermal/temp trend
|
||||
+ */
|
||||
+struct tsens_ops {
|
||||
+ /* mandatory callbacks */
|
||||
+ int (*init)(struct tsens_device *);
|
||||
+ int (*calibrate)(struct tsens_device *);
|
||||
+ int (*get_temp)(struct tsens_device *, int, int *);
|
||||
+ /* optional callbacks */
|
||||
+ int (*enable)(struct tsens_device *, int);
|
||||
+ void (*disable)(struct tsens_device *);
|
||||
+ int (*suspend)(struct tsens_device *);
|
||||
+ int (*resume)(struct tsens_device *);
|
||||
+ int (*get_trend)(struct tsens_device *, int, long *);
|
||||
+};
|
||||
+
|
||||
+/**
|
||||
+ * struct tsens_data - tsens instance specific data
|
||||
+ * @num_sensors: Max number of sensors supported by platform
|
||||
+ * @ops: operations the tsens instance supports
|
||||
+ * @hw_ids: Subset of sensors ids supported by platform, if not the first n
|
||||
+ */
|
||||
+struct tsens_data {
|
||||
+ const u32 num_sensors;
|
||||
+ const struct tsens_ops *ops;
|
||||
+ unsigned int *hw_ids;
|
||||
+};
|
||||
+
|
||||
+/* Registers to be saved/restored across a context loss */
|
||||
+struct tsens_context {
|
||||
+ int threshold;
|
||||
+ int control;
|
||||
+};
|
||||
+
|
||||
+struct tsens_device {
|
||||
+ struct device *dev;
|
||||
+ u32 num_sensors;
|
||||
+ struct regmap *map;
|
||||
+ struct regmap_field *status_field;
|
||||
+ struct tsens_context ctx;
|
||||
+ bool trdy;
|
||||
+ const struct tsens_ops *ops;
|
||||
+ struct tsens_sensor sensor[0];
|
||||
+};
|
||||
+
|
||||
+char *qfprom_read(struct device *, const char *);
|
||||
+void compute_intercept_slope(struct tsens_device *, u32 *, u32 *, u32);
|
||||
+int init_common(struct tsens_device *);
|
||||
+int get_temp_common(struct tsens_device *, int, int *);
|
||||
+
|
||||
+#endif /* __QCOM_TSENS_H__ */
|
|
@ -0,0 +1,165 @@
|
|||
From 840a5bd3ed3fdd62456d4d26c3128ec10496555b Mon Sep 17 00:00:00 2001
|
||||
From: Rajendra Nayak <rnayak@codeaurora.org>
|
||||
Date: Thu, 5 May 2016 14:21:40 +0530
|
||||
Subject: thermal: qcom: tsens-8916: Add support for 8916 family of SoCs
|
||||
|
||||
Add support to calibrate sensors on 8916 family and also add common
|
||||
functions to read temperature from sensors (This can be reused on
|
||||
other SoCs having similar TSENS device)
|
||||
The calibration data is read from eeprom using the generic nvmem
|
||||
framework apis.
|
||||
|
||||
Based on the original code by Siddartha Mohanadoss and Stephen Boyd.
|
||||
|
||||
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-8916.c | 113 ++++++++++++++++++++++++++++++++++++++
|
||||
drivers/thermal/qcom/tsens.c | 1 +
|
||||
drivers/thermal/qcom/tsens.h | 2 +
|
||||
4 files changed, 117 insertions(+), 1 deletion(-)
|
||||
create mode 100644 drivers/thermal/qcom/tsens-8916.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
|
||||
+qcom_tsens-y += tsens.o tsens-common.o tsens-8916.o
|
||||
--- /dev/null
|
||||
+++ b/drivers/thermal/qcom/tsens-8916.c
|
||||
@@ -0,0 +1,113 @@
|
||||
+/*
|
||||
+ * 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 "tsens.h"
|
||||
+
|
||||
+/* eeprom layout data for 8916 */
|
||||
+#define BASE0_MASK 0x0000007f
|
||||
+#define BASE1_MASK 0xfe000000
|
||||
+#define BASE0_SHIFT 0
|
||||
+#define BASE1_SHIFT 25
|
||||
+
|
||||
+#define S0_P1_MASK 0x00000f80
|
||||
+#define S1_P1_MASK 0x003e0000
|
||||
+#define S2_P1_MASK 0xf8000000
|
||||
+#define S3_P1_MASK 0x000003e0
|
||||
+#define S4_P1_MASK 0x000f8000
|
||||
+
|
||||
+#define S0_P2_MASK 0x0001f000
|
||||
+#define S1_P2_MASK 0x07c00000
|
||||
+#define S2_P2_MASK 0x0000001f
|
||||
+#define S3_P2_MASK 0x00007c00
|
||||
+#define S4_P2_MASK 0x01f00000
|
||||
+
|
||||
+#define S0_P1_SHIFT 7
|
||||
+#define S1_P1_SHIFT 17
|
||||
+#define S2_P1_SHIFT 27
|
||||
+#define S3_P1_SHIFT 5
|
||||
+#define S4_P1_SHIFT 15
|
||||
+
|
||||
+#define S0_P2_SHIFT 12
|
||||
+#define S1_P2_SHIFT 22
|
||||
+#define S2_P2_SHIFT 0
|
||||
+#define S3_P2_SHIFT 10
|
||||
+#define S4_P2_SHIFT 20
|
||||
+
|
||||
+#define CAL_SEL_MASK 0xe0000000
|
||||
+#define CAL_SEL_SHIFT 29
|
||||
+
|
||||
+static int calibrate_8916(struct tsens_device *tmdev)
|
||||
+{
|
||||
+ int base0 = 0, base1 = 0, i;
|
||||
+ u32 p1[5], p2[5];
|
||||
+ int mode = 0;
|
||||
+ u32 *qfprom_cdata, *qfprom_csel;
|
||||
+
|
||||
+ qfprom_cdata = (u32 *)qfprom_read(tmdev->dev, "calib");
|
||||
+ if (IS_ERR(qfprom_cdata))
|
||||
+ return PTR_ERR(qfprom_cdata);
|
||||
+
|
||||
+ qfprom_csel = (u32 *)qfprom_read(tmdev->dev, "calib_sel");
|
||||
+ if (IS_ERR(qfprom_csel))
|
||||
+ return PTR_ERR(qfprom_csel);
|
||||
+
|
||||
+ mode = (qfprom_csel[0] & CAL_SEL_MASK) >> CAL_SEL_SHIFT;
|
||||
+ dev_dbg(tmdev->dev, "calibration mode is %d\n", mode);
|
||||
+
|
||||
+ switch (mode) {
|
||||
+ case TWO_PT_CALIB:
|
||||
+ base1 = (qfprom_cdata[1] & BASE1_MASK) >> BASE1_SHIFT;
|
||||
+ p2[0] = (qfprom_cdata[0] & S0_P2_MASK) >> S0_P2_SHIFT;
|
||||
+ p2[1] = (qfprom_cdata[0] & S1_P2_MASK) >> S1_P2_SHIFT;
|
||||
+ p2[2] = (qfprom_cdata[1] & S2_P2_MASK) >> S2_P2_SHIFT;
|
||||
+ p2[3] = (qfprom_cdata[1] & S3_P2_MASK) >> S3_P2_SHIFT;
|
||||
+ p2[4] = (qfprom_cdata[1] & S4_P2_MASK) >> S4_P2_SHIFT;
|
||||
+ for (i = 0; i < tmdev->num_sensors; i++)
|
||||
+ p2[i] = ((base1 + p2[i]) << 3);
|
||||
+ /* Fall through */
|
||||
+ case ONE_PT_CALIB2:
|
||||
+ base0 = (qfprom_cdata[0] & BASE0_MASK);
|
||||
+ p1[0] = (qfprom_cdata[0] & S0_P1_MASK) >> S0_P1_SHIFT;
|
||||
+ p1[1] = (qfprom_cdata[0] & S1_P1_MASK) >> S1_P1_SHIFT;
|
||||
+ p1[2] = (qfprom_cdata[0] & S2_P1_MASK) >> S2_P1_SHIFT;
|
||||
+ p1[3] = (qfprom_cdata[1] & S3_P1_MASK) >> S3_P1_SHIFT;
|
||||
+ p1[4] = (qfprom_cdata[1] & S4_P1_MASK) >> S4_P1_SHIFT;
|
||||
+ for (i = 0; i < tmdev->num_sensors; i++)
|
||||
+ p1[i] = (((base0) + p1[i]) << 3);
|
||||
+ break;
|
||||
+ default:
|
||||
+ for (i = 0; i < tmdev->num_sensors; i++) {
|
||||
+ p1[i] = 500;
|
||||
+ p2[i] = 780;
|
||||
+ }
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ compute_intercept_slope(tmdev, p1, p2, mode);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+const struct tsens_ops ops_8916 = {
|
||||
+ .init = init_common,
|
||||
+ .calibrate = calibrate_8916,
|
||||
+ .get_temp = get_temp_common,
|
||||
+};
|
||||
+
|
||||
+const struct tsens_data data_8916 = {
|
||||
+ .num_sensors = 5,
|
||||
+ .ops = &ops_8916,
|
||||
+ .hw_ids = (unsigned int []){0, 1, 2, 4, 5 },
|
||||
+};
|
||||
--- a/drivers/thermal/qcom/tsens.c
|
||||
+++ b/drivers/thermal/qcom/tsens.c
|
||||
@@ -65,6 +65,7 @@ static SIMPLE_DEV_PM_OPS(tsens_pm_ops, t
|
||||
static const struct of_device_id tsens_table[] = {
|
||||
{
|
||||
.compatible = "qcom,msm8916-tsens",
|
||||
+ .data = &data_8916,
|
||||
}, {
|
||||
.compatible = "qcom,msm8974-tsens",
|
||||
},
|
||||
--- a/drivers/thermal/qcom/tsens.h
|
||||
+++ b/drivers/thermal/qcom/tsens.h
|
||||
@@ -87,4 +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;
|
||||
+
|
||||
#endif /* __QCOM_TSENS_H__ */
|
|
@ -0,0 +1,293 @@
|
|||
From 5e6703bd2d83548998848865cb9a9a795f31a311 Mon Sep 17 00:00:00 2001
|
||||
From: Rajendra Nayak <rnayak@codeaurora.org>
|
||||
Date: Thu, 5 May 2016 14:21:41 +0530
|
||||
Subject: thermal: qcom: tsens-8974: Add support for 8974 family of SoCs
|
||||
|
||||
Add .calibrate support for 8974 family as part of tsens_ops.
|
||||
|
||||
Based on the original code by Siddartha Mohanadoss and Stephen Boyd.
|
||||
|
||||
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-8974.c | 244 ++++++++++++++++++++++++++++++++++++++
|
||||
drivers/thermal/qcom/tsens.c | 1 +
|
||||
drivers/thermal/qcom/tsens.h | 2 +-
|
||||
4 files changed, 247 insertions(+), 2 deletions(-)
|
||||
create mode 100644 drivers/thermal/qcom/tsens-8974.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
|
||||
+qcom_tsens-y += tsens.o tsens-common.o tsens-8916.o tsens-8974.o
|
||||
--- /dev/null
|
||||
+++ b/drivers/thermal/qcom/tsens-8974.c
|
||||
@@ -0,0 +1,244 @@
|
||||
+/*
|
||||
+ * 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 "tsens.h"
|
||||
+
|
||||
+/* eeprom layout data for 8974 */
|
||||
+#define BASE1_MASK 0xff
|
||||
+#define S0_P1_MASK 0x3f00
|
||||
+#define S1_P1_MASK 0xfc000
|
||||
+#define S2_P1_MASK 0x3f00000
|
||||
+#define S3_P1_MASK 0xfc000000
|
||||
+#define S4_P1_MASK 0x3f
|
||||
+#define S5_P1_MASK 0xfc0
|
||||
+#define S6_P1_MASK 0x3f000
|
||||
+#define S7_P1_MASK 0xfc0000
|
||||
+#define S8_P1_MASK 0x3f000000
|
||||
+#define S8_P1_MASK_BKP 0x3f
|
||||
+#define S9_P1_MASK 0x3f
|
||||
+#define S9_P1_MASK_BKP 0xfc0
|
||||
+#define S10_P1_MASK 0xfc0
|
||||
+#define S10_P1_MASK_BKP 0x3f000
|
||||
+#define CAL_SEL_0_1 0xc0000000
|
||||
+#define CAL_SEL_2 0x40000000
|
||||
+#define CAL_SEL_SHIFT 30
|
||||
+#define CAL_SEL_SHIFT_2 28
|
||||
+
|
||||
+#define S0_P1_SHIFT 8
|
||||
+#define S1_P1_SHIFT 14
|
||||
+#define S2_P1_SHIFT 20
|
||||
+#define S3_P1_SHIFT 26
|
||||
+#define S5_P1_SHIFT 6
|
||||
+#define S6_P1_SHIFT 12
|
||||
+#define S7_P1_SHIFT 18
|
||||
+#define S8_P1_SHIFT 24
|
||||
+#define S9_P1_BKP_SHIFT 6
|
||||
+#define S10_P1_SHIFT 6
|
||||
+#define S10_P1_BKP_SHIFT 12
|
||||
+
|
||||
+#define BASE2_SHIFT 12
|
||||
+#define BASE2_BKP_SHIFT 18
|
||||
+#define S0_P2_SHIFT 20
|
||||
+#define S0_P2_BKP_SHIFT 26
|
||||
+#define S1_P2_SHIFT 26
|
||||
+#define S2_P2_BKP_SHIFT 6
|
||||
+#define S3_P2_SHIFT 6
|
||||
+#define S3_P2_BKP_SHIFT 12
|
||||
+#define S4_P2_SHIFT 12
|
||||
+#define S4_P2_BKP_SHIFT 18
|
||||
+#define S5_P2_SHIFT 18
|
||||
+#define S5_P2_BKP_SHIFT 24
|
||||
+#define S6_P2_SHIFT 24
|
||||
+#define S7_P2_BKP_SHIFT 6
|
||||
+#define S8_P2_SHIFT 6
|
||||
+#define S8_P2_BKP_SHIFT 12
|
||||
+#define S9_P2_SHIFT 12
|
||||
+#define S9_P2_BKP_SHIFT 18
|
||||
+#define S10_P2_SHIFT 18
|
||||
+#define S10_P2_BKP_SHIFT 24
|
||||
+
|
||||
+#define BASE2_MASK 0xff000
|
||||
+#define BASE2_BKP_MASK 0xfc0000
|
||||
+#define S0_P2_MASK 0x3f00000
|
||||
+#define S0_P2_BKP_MASK 0xfc000000
|
||||
+#define S1_P2_MASK 0xfc000000
|
||||
+#define S1_P2_BKP_MASK 0x3f
|
||||
+#define S2_P2_MASK 0x3f
|
||||
+#define S2_P2_BKP_MASK 0xfc0
|
||||
+#define S3_P2_MASK 0xfc0
|
||||
+#define S3_P2_BKP_MASK 0x3f000
|
||||
+#define S4_P2_MASK 0x3f000
|
||||
+#define S4_P2_BKP_MASK 0xfc0000
|
||||
+#define S5_P2_MASK 0xfc0000
|
||||
+#define S5_P2_BKP_MASK 0x3f000000
|
||||
+#define S6_P2_MASK 0x3f000000
|
||||
+#define S6_P2_BKP_MASK 0x3f
|
||||
+#define S7_P2_MASK 0x3f
|
||||
+#define S7_P2_BKP_MASK 0xfc0
|
||||
+#define S8_P2_MASK 0xfc0
|
||||
+#define S8_P2_BKP_MASK 0x3f000
|
||||
+#define S9_P2_MASK 0x3f000
|
||||
+#define S9_P2_BKP_MASK 0xfc0000
|
||||
+#define S10_P2_MASK 0xfc0000
|
||||
+#define S10_P2_BKP_MASK 0x3f000000
|
||||
+
|
||||
+#define BKP_SEL 0x3
|
||||
+#define BKP_REDUN_SEL 0xe0000000
|
||||
+#define BKP_REDUN_SHIFT 29
|
||||
+
|
||||
+#define BIT_APPEND 0x3
|
||||
+
|
||||
+static int calibrate_8974(struct tsens_device *tmdev)
|
||||
+{
|
||||
+ int base1 = 0, base2 = 0, i;
|
||||
+ u32 p1[11], p2[11];
|
||||
+ int mode = 0;
|
||||
+ u32 *calib, *bkp;
|
||||
+ u32 calib_redun_sel;
|
||||
+
|
||||
+ calib = (u32 *)qfprom_read(tmdev->dev, "calib");
|
||||
+ if (IS_ERR(calib))
|
||||
+ return PTR_ERR(calib);
|
||||
+
|
||||
+ bkp = (u32 *)qfprom_read(tmdev->dev, "calib_backup");
|
||||
+ if (IS_ERR(bkp))
|
||||
+ return PTR_ERR(bkp);
|
||||
+
|
||||
+ calib_redun_sel = bkp[1] & BKP_REDUN_SEL;
|
||||
+ calib_redun_sel >>= BKP_REDUN_SHIFT;
|
||||
+
|
||||
+ if (calib_redun_sel == BKP_SEL) {
|
||||
+ mode = (calib[4] & CAL_SEL_0_1) >> CAL_SEL_SHIFT;
|
||||
+ mode |= (calib[5] & CAL_SEL_2) >> CAL_SEL_SHIFT_2;
|
||||
+
|
||||
+ switch (mode) {
|
||||
+ case TWO_PT_CALIB:
|
||||
+ base2 = (bkp[2] & BASE2_BKP_MASK) >> BASE2_BKP_SHIFT;
|
||||
+ p2[0] = (bkp[2] & S0_P2_BKP_MASK) >> S0_P2_BKP_SHIFT;
|
||||
+ p2[1] = (bkp[3] & S1_P2_BKP_MASK);
|
||||
+ p2[2] = (bkp[3] & S2_P2_BKP_MASK) >> S2_P2_BKP_SHIFT;
|
||||
+ p2[3] = (bkp[3] & S3_P2_BKP_MASK) >> S3_P2_BKP_SHIFT;
|
||||
+ p2[4] = (bkp[3] & S4_P2_BKP_MASK) >> S4_P2_BKP_SHIFT;
|
||||
+ p2[5] = (calib[4] & S5_P2_BKP_MASK) >> S5_P2_BKP_SHIFT;
|
||||
+ p2[6] = (calib[5] & S6_P2_BKP_MASK);
|
||||
+ p2[7] = (calib[5] & S7_P2_BKP_MASK) >> S7_P2_BKP_SHIFT;
|
||||
+ p2[8] = (calib[5] & S8_P2_BKP_MASK) >> S8_P2_BKP_SHIFT;
|
||||
+ p2[9] = (calib[5] & S9_P2_BKP_MASK) >> S9_P2_BKP_SHIFT;
|
||||
+ p2[10] = (calib[5] & S10_P2_BKP_MASK) >> S10_P2_BKP_SHIFT;
|
||||
+ /* Fall through */
|
||||
+ case ONE_PT_CALIB:
|
||||
+ case ONE_PT_CALIB2:
|
||||
+ base1 = bkp[0] & BASE1_MASK;
|
||||
+ p1[0] = (bkp[0] & S0_P1_MASK) >> S0_P1_SHIFT;
|
||||
+ p1[1] = (bkp[0] & S1_P1_MASK) >> S1_P1_SHIFT;
|
||||
+ p1[2] = (bkp[0] & S2_P1_MASK) >> S2_P1_SHIFT;
|
||||
+ p1[3] = (bkp[0] & S3_P1_MASK) >> S3_P1_SHIFT;
|
||||
+ p1[4] = (bkp[1] & S4_P1_MASK);
|
||||
+ p1[5] = (bkp[1] & S5_P1_MASK) >> S5_P1_SHIFT;
|
||||
+ p1[6] = (bkp[1] & S6_P1_MASK) >> S6_P1_SHIFT;
|
||||
+ p1[7] = (bkp[1] & S7_P1_MASK) >> S7_P1_SHIFT;
|
||||
+ p1[8] = (bkp[2] & S8_P1_MASK_BKP) >> S8_P1_SHIFT;
|
||||
+ p1[9] = (bkp[2] & S9_P1_MASK_BKP) >> S9_P1_BKP_SHIFT;
|
||||
+ p1[10] = (bkp[2] & S10_P1_MASK_BKP) >> S10_P1_BKP_SHIFT;
|
||||
+ break;
|
||||
+ }
|
||||
+ } else {
|
||||
+ mode = (calib[1] & CAL_SEL_0_1) >> CAL_SEL_SHIFT;
|
||||
+ mode |= (calib[3] & CAL_SEL_2) >> CAL_SEL_SHIFT_2;
|
||||
+
|
||||
+ switch (mode) {
|
||||
+ case TWO_PT_CALIB:
|
||||
+ base2 = (calib[2] & BASE2_MASK) >> BASE2_SHIFT;
|
||||
+ p2[0] = (calib[2] & S0_P2_MASK) >> S0_P2_SHIFT;
|
||||
+ p2[1] = (calib[2] & S1_P2_MASK) >> S1_P2_SHIFT;
|
||||
+ p2[2] = (calib[3] & S2_P2_MASK);
|
||||
+ p2[3] = (calib[3] & S3_P2_MASK) >> S3_P2_SHIFT;
|
||||
+ p2[4] = (calib[3] & S4_P2_MASK) >> S4_P2_SHIFT;
|
||||
+ p2[5] = (calib[3] & S5_P2_MASK) >> S5_P2_SHIFT;
|
||||
+ p2[6] = (calib[3] & S6_P2_MASK) >> S6_P2_SHIFT;
|
||||
+ p2[7] = (calib[4] & S7_P2_MASK);
|
||||
+ p2[8] = (calib[4] & S8_P2_MASK) >> S8_P2_SHIFT;
|
||||
+ p2[9] = (calib[4] & S9_P2_MASK) >> S9_P2_SHIFT;
|
||||
+ p2[10] = (calib[4] & S10_P2_MASK) >> S10_P2_SHIFT;
|
||||
+ /* Fall through */
|
||||
+ case ONE_PT_CALIB:
|
||||
+ case ONE_PT_CALIB2:
|
||||
+ base1 = calib[0] & BASE1_MASK;
|
||||
+ p1[0] = (calib[0] & S0_P1_MASK) >> S0_P1_SHIFT;
|
||||
+ p1[1] = (calib[0] & S1_P1_MASK) >> S1_P1_SHIFT;
|
||||
+ p1[2] = (calib[0] & S2_P1_MASK) >> S2_P1_SHIFT;
|
||||
+ p1[3] = (calib[0] & S3_P1_MASK) >> S3_P1_SHIFT;
|
||||
+ p1[4] = (calib[1] & S4_P1_MASK);
|
||||
+ p1[5] = (calib[1] & S5_P1_MASK) >> S5_P1_SHIFT;
|
||||
+ p1[6] = (calib[1] & S6_P1_MASK) >> S6_P1_SHIFT;
|
||||
+ p1[7] = (calib[1] & S7_P1_MASK) >> S7_P1_SHIFT;
|
||||
+ p1[8] = (calib[1] & S8_P1_MASK) >> S8_P1_SHIFT;
|
||||
+ p1[9] = (calib[2] & S9_P1_MASK);
|
||||
+ p1[10] = (calib[2] & S10_P1_MASK) >> S10_P1_SHIFT;
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ switch (mode) {
|
||||
+ case ONE_PT_CALIB:
|
||||
+ for (i = 0; i < tmdev->num_sensors; i++)
|
||||
+ p1[i] += (base1 << 2) | BIT_APPEND;
|
||||
+ break;
|
||||
+ case TWO_PT_CALIB:
|
||||
+ for (i = 0; i < tmdev->num_sensors; i++) {
|
||||
+ p2[i] += base2;
|
||||
+ p2[i] <<= 2;
|
||||
+ p2[i] |= BIT_APPEND;
|
||||
+ }
|
||||
+ /* Fall through */
|
||||
+ case ONE_PT_CALIB2:
|
||||
+ for (i = 0; i < tmdev->num_sensors; i++) {
|
||||
+ p1[i] += base1;
|
||||
+ p1[i] <<= 2;
|
||||
+ p1[i] |= BIT_APPEND;
|
||||
+ }
|
||||
+ break;
|
||||
+ default:
|
||||
+ for (i = 0; i < tmdev->num_sensors; i++)
|
||||
+ p2[i] = 780;
|
||||
+ p1[0] = 502;
|
||||
+ p1[1] = 509;
|
||||
+ p1[2] = 503;
|
||||
+ p1[3] = 509;
|
||||
+ p1[4] = 505;
|
||||
+ p1[5] = 509;
|
||||
+ p1[6] = 507;
|
||||
+ p1[7] = 510;
|
||||
+ p1[8] = 508;
|
||||
+ p1[9] = 509;
|
||||
+ p1[10] = 508;
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ compute_intercept_slope(tmdev, p1, p2, mode);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+const struct tsens_ops ops_8974 = {
|
||||
+ .init = init_common,
|
||||
+ .calibrate = calibrate_8974,
|
||||
+ .get_temp = get_temp_common,
|
||||
+};
|
||||
+
|
||||
+const struct tsens_data data_8974 = {
|
||||
+ .num_sensors = 11,
|
||||
+ .ops = &ops_8974,
|
||||
+};
|
||||
--- a/drivers/thermal/qcom/tsens.c
|
||||
+++ b/drivers/thermal/qcom/tsens.c
|
||||
@@ -68,6 +68,7 @@ static const struct of_device_id tsens_t
|
||||
.data = &data_8916,
|
||||
}, {
|
||||
.compatible = "qcom,msm8974-tsens",
|
||||
+ .data = &data_8974,
|
||||
},
|
||||
{}
|
||||
};
|
||||
--- 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;
|
||||
+extern const struct tsens_data data_8916, data_8974;
|
||||
|
||||
#endif /* __QCOM_TSENS_H__ */
|
|
@ -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__ */
|
|
@ -0,0 +1,46 @@
|
|||
From 5b97469a55872a30a0d53a1279a8ae8b1c68b52c Mon Sep 17 00:00:00 2001
|
||||
From: Arnd Bergmann <arnd@arndb.de>
|
||||
Date: Mon, 4 Jul 2016 15:12:28 +0200
|
||||
Subject: thermal: qcom: tsens-8916: mark PM functions __maybe_unused
|
||||
|
||||
The newly added tsens-8916 driver produces warnings when CONFIG_PM
|
||||
is disabled:
|
||||
|
||||
drivers/thermal/qcom/tsens.c:53:12: error: 'tsens_resume' defined but not used [-Werror=unused-function]
|
||||
static int tsens_resume(struct device *dev)
|
||||
^~~~~~~~~~~~
|
||||
drivers/thermal/qcom/tsens.c:43:12: error: 'tsens_suspend' defined but not used [-Werror=unused-function]
|
||||
static int tsens_suspend(struct device *dev)
|
||||
^~~~~~~~~~~~~
|
||||
|
||||
This marks both functions __maybe_unused to let the compiler
|
||||
know that they might be used in other configurations, without
|
||||
adding ugly #ifdef logic.
|
||||
|
||||
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
|
||||
Reviewed-by: Rajendra Nayak <rnayak@codeaurora.org>
|
||||
Signed-off-by: Zhang Rui <rui.zhang@intel.com>
|
||||
---
|
||||
drivers/thermal/qcom/tsens.c | 4 ++--
|
||||
1 file changed, 2 insertions(+), 2 deletions(-)
|
||||
|
||||
--- a/drivers/thermal/qcom/tsens.c
|
||||
+++ b/drivers/thermal/qcom/tsens.c
|
||||
@@ -40,7 +40,7 @@ static int tsens_get_trend(void *data, l
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
-static int tsens_suspend(struct device *dev)
|
||||
+static int __maybe_unused tsens_suspend(struct device *dev)
|
||||
{
|
||||
struct tsens_device *tmdev = dev_get_drvdata(dev);
|
||||
|
||||
@@ -50,7 +50,7 @@ static int tsens_suspend(struct device *
|
||||
return 0;
|
||||
}
|
||||
|
||||
-static int tsens_resume(struct device *dev)
|
||||
+static int __maybe_unused tsens_resume(struct device *dev)
|
||||
{
|
||||
struct tsens_device *tmdev = dev_get_drvdata(dev);
|
||||
|
|
@ -0,0 +1,143 @@
|
|||
From e498b4984db82b4ba3ceea7dba813222a31e9c2e Mon Sep 17 00:00:00 2001
|
||||
From: Laxman Dewangan <ldewangan@nvidia.com>
|
||||
Date: Wed, 9 Mar 2016 18:40:06 +0530
|
||||
Subject: thermal: of-thermal: Add devm version of
|
||||
thermal_zone_of_sensor_register
|
||||
|
||||
Add resource managed version of thermal_zone_of_sensor_register() and
|
||||
thermal_zone_of_sensor_unregister().
|
||||
|
||||
This helps in reducing the code size in error path, remove of
|
||||
driver remove callbacks and making proper sequence for deallocations.
|
||||
|
||||
Signed-off-by: Laxman Dewangan <ldewangan@nvidia.com>
|
||||
Signed-off-by: Eduardo Valentin <edubezval@gmail.com>
|
||||
---
|
||||
drivers/thermal/of-thermal.c | 81 ++++++++++++++++++++++++++++++++++++++++++++
|
||||
include/linux/thermal.h | 18 ++++++++++
|
||||
2 files changed, 99 insertions(+)
|
||||
|
||||
--- a/drivers/thermal/of-thermal.c
|
||||
+++ b/drivers/thermal/of-thermal.c
|
||||
@@ -559,6 +559,87 @@ void thermal_zone_of_sensor_unregister(s
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(thermal_zone_of_sensor_unregister);
|
||||
|
||||
+static void devm_thermal_zone_of_sensor_release(struct device *dev, void *res)
|
||||
+{
|
||||
+ thermal_zone_of_sensor_unregister(dev,
|
||||
+ *(struct thermal_zone_device **)res);
|
||||
+}
|
||||
+
|
||||
+static int devm_thermal_zone_of_sensor_match(struct device *dev, void *res,
|
||||
+ void *data)
|
||||
+{
|
||||
+ struct thermal_zone_device **r = res;
|
||||
+
|
||||
+ if (WARN_ON(!r || !*r))
|
||||
+ return 0;
|
||||
+
|
||||
+ return *r == data;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * devm_thermal_zone_of_sensor_register - Resource managed version of
|
||||
+ * thermal_zone_of_sensor_register()
|
||||
+ * @dev: a valid struct device pointer of a sensor device. Must contain
|
||||
+ * a valid .of_node, for the sensor node.
|
||||
+ * @sensor_id: a sensor identifier, in case the sensor IP has more
|
||||
+ * than one sensors
|
||||
+ * @data: a private pointer (owned by the caller) that will be passed
|
||||
+ * back, when a temperature reading is needed.
|
||||
+ * @ops: struct thermal_zone_of_device_ops *. Must contain at least .get_temp.
|
||||
+ *
|
||||
+ * Refer thermal_zone_of_sensor_register() for more details.
|
||||
+ *
|
||||
+ * Return: On success returns a valid struct thermal_zone_device,
|
||||
+ * otherwise, it returns a corresponding ERR_PTR(). Caller must
|
||||
+ * check the return value with help of IS_ERR() helper.
|
||||
+ * Registered hermal_zone_device device will automatically be
|
||||
+ * released when device is unbounded.
|
||||
+ */
|
||||
+struct thermal_zone_device *devm_thermal_zone_of_sensor_register(
|
||||
+ struct device *dev, int sensor_id,
|
||||
+ void *data, const struct thermal_zone_of_device_ops *ops)
|
||||
+{
|
||||
+ struct thermal_zone_device **ptr, *tzd;
|
||||
+
|
||||
+ ptr = devres_alloc(devm_thermal_zone_of_sensor_release, sizeof(*ptr),
|
||||
+ GFP_KERNEL);
|
||||
+ if (!ptr)
|
||||
+ return ERR_PTR(-ENOMEM);
|
||||
+
|
||||
+ tzd = thermal_zone_of_sensor_register(dev, sensor_id, data, ops);
|
||||
+ if (IS_ERR(tzd)) {
|
||||
+ devres_free(ptr);
|
||||
+ return tzd;
|
||||
+ }
|
||||
+
|
||||
+ *ptr = tzd;
|
||||
+ devres_add(dev, ptr);
|
||||
+
|
||||
+ return tzd;
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(devm_thermal_zone_of_sensor_register);
|
||||
+
|
||||
+/**
|
||||
+ * devm_thermal_zone_of_sensor_unregister - Resource managed version of
|
||||
+ * thermal_zone_of_sensor_unregister().
|
||||
+ * @dev: Device for which which resource was allocated.
|
||||
+ * @tzd: a pointer to struct thermal_zone_device where the sensor is registered.
|
||||
+ *
|
||||
+ * This function removes the sensor callbacks and private data from the
|
||||
+ * thermal zone device registered with devm_thermal_zone_of_sensor_register()
|
||||
+ * API. It will also silent the zone by remove the .get_temp() and .get_trend()
|
||||
+ * thermal zone device callbacks.
|
||||
+ * Normally this function will not need to be called and the resource
|
||||
+ * management code will ensure that the resource is freed.
|
||||
+ */
|
||||
+void devm_thermal_zone_of_sensor_unregister(struct device *dev,
|
||||
+ struct thermal_zone_device *tzd)
|
||||
+{
|
||||
+ WARN_ON(devres_release(dev, devm_thermal_zone_of_sensor_release,
|
||||
+ devm_thermal_zone_of_sensor_match, tzd));
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(devm_thermal_zone_of_sensor_unregister);
|
||||
+
|
||||
/*** functions parsing device tree nodes ***/
|
||||
|
||||
/**
|
||||
--- a/include/linux/thermal.h
|
||||
+++ b/include/linux/thermal.h
|
||||
@@ -364,6 +364,11 @@ thermal_zone_of_sensor_register(struct d
|
||||
const struct thermal_zone_of_device_ops *ops);
|
||||
void thermal_zone_of_sensor_unregister(struct device *dev,
|
||||
struct thermal_zone_device *tz);
|
||||
+struct thermal_zone_device *devm_thermal_zone_of_sensor_register(
|
||||
+ struct device *dev, int id, void *data,
|
||||
+ const struct thermal_zone_of_device_ops *ops);
|
||||
+void devm_thermal_zone_of_sensor_unregister(struct device *dev,
|
||||
+ struct thermal_zone_device *tz);
|
||||
#else
|
||||
static inline struct thermal_zone_device *
|
||||
thermal_zone_of_sensor_register(struct device *dev, int id, void *data,
|
||||
@@ -378,6 +383,19 @@ void thermal_zone_of_sensor_unregister(s
|
||||
{
|
||||
}
|
||||
|
||||
+static inline struct thermal_zone_device *devm_thermal_zone_of_sensor_register(
|
||||
+ struct device *dev, int id, void *data,
|
||||
+ const struct thermal_zone_of_device_ops *ops)
|
||||
+{
|
||||
+ return ERR_PTR(-ENODEV);
|
||||
+}
|
||||
+
|
||||
+static inline
|
||||
+void devm_thermal_zone_of_sensor_unregister(struct device *dev,
|
||||
+ struct thermal_zone_device *tz)
|
||||
+{
|
||||
+}
|
||||
+
|
||||
#endif
|
||||
|
||||
#if IS_ENABLED(CONFIG_THERMAL)
|
|
@ -0,0 +1,101 @@
|
|||
From 4a7069a32c99a81950de035535b0a064dcceaeba Mon Sep 17 00:00:00 2001
|
||||
From: Rajendra Nayak <rnayak@codeaurora.org>
|
||||
Date: Thu, 5 May 2016 14:21:42 +0530
|
||||
Subject: [PATCH] thermal: core: export apis to get slope and offset
|
||||
|
||||
Add apis for platform thermal drivers to query for slope and offset
|
||||
attributes, which might be needed for temperature calculations.
|
||||
|
||||
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>
|
||||
---
|
||||
Documentation/thermal/sysfs-api.txt | 12 ++++++++++++
|
||||
drivers/thermal/thermal_core.c | 30 ++++++++++++++++++++++++++++++
|
||||
include/linux/thermal.h | 8 ++++++++
|
||||
3 files changed, 50 insertions(+)
|
||||
|
||||
--- a/Documentation/thermal/sysfs-api.txt
|
||||
+++ b/Documentation/thermal/sysfs-api.txt
|
||||
@@ -72,6 +72,18 @@ temperature) and throttle appropriate de
|
||||
It deletes the corresponding entry form /sys/class/thermal folder and
|
||||
unbind all the thermal cooling devices it uses.
|
||||
|
||||
+1.1.7 int thermal_zone_get_slope(struct thermal_zone_device *tz)
|
||||
+
|
||||
+ This interface is used to read the slope attribute value
|
||||
+ for the thermal zone device, which might be useful for platform
|
||||
+ drivers for temperature calculations.
|
||||
+
|
||||
+1.1.8 int thermal_zone_get_offset(struct thermal_zone_device *tz)
|
||||
+
|
||||
+ This interface is used to read the offset attribute value
|
||||
+ for the thermal zone device, which might be useful for platform
|
||||
+ drivers for temperature calculations.
|
||||
+
|
||||
1.2 thermal cooling device interface
|
||||
1.2.1 struct thermal_cooling_device *thermal_cooling_device_register(char *name,
|
||||
void *devdata, struct thermal_cooling_device_ops *)
|
||||
--- a/drivers/thermal/thermal_core.c
|
||||
+++ b/drivers/thermal/thermal_core.c
|
||||
@@ -2061,6 +2061,36 @@ exit:
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(thermal_zone_get_zone_by_name);
|
||||
|
||||
+/**
|
||||
+ * thermal_zone_get_slope - return the slope attribute of the thermal zone
|
||||
+ * @tz: thermal zone device with the slope attribute
|
||||
+ *
|
||||
+ * Return: If the thermal zone device has a slope attribute, return it, else
|
||||
+ * return 1.
|
||||
+ */
|
||||
+int thermal_zone_get_slope(struct thermal_zone_device *tz)
|
||||
+{
|
||||
+ if (tz && tz->tzp)
|
||||
+ return tz->tzp->slope;
|
||||
+ return 1;
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(thermal_zone_get_slope);
|
||||
+
|
||||
+/**
|
||||
+ * thermal_zone_get_offset - return the offset attribute of the thermal zone
|
||||
+ * @tz: thermal zone device with the offset attribute
|
||||
+ *
|
||||
+ * Return: If the thermal zone device has a offset attribute, return it, else
|
||||
+ * return 0.
|
||||
+ */
|
||||
+int thermal_zone_get_offset(struct thermal_zone_device *tz)
|
||||
+{
|
||||
+ if (tz && tz->tzp)
|
||||
+ return tz->tzp->offset;
|
||||
+ return 0;
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(thermal_zone_get_offset);
|
||||
+
|
||||
#ifdef CONFIG_NET
|
||||
static const struct genl_multicast_group thermal_event_mcgrps[] = {
|
||||
{ .name = THERMAL_GENL_MCAST_GROUP_NAME, },
|
||||
--- a/include/linux/thermal.h
|
||||
+++ b/include/linux/thermal.h
|
||||
@@ -432,6 +432,8 @@ thermal_of_cooling_device_register(struc
|
||||
void thermal_cooling_device_unregister(struct thermal_cooling_device *);
|
||||
struct thermal_zone_device *thermal_zone_get_zone_by_name(const char *name);
|
||||
int thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp);
|
||||
+int thermal_zone_get_slope(struct thermal_zone_device *tz);
|
||||
+int thermal_zone_get_offset(struct thermal_zone_device *tz);
|
||||
|
||||
int get_tz_trend(struct thermal_zone_device *, int);
|
||||
struct thermal_instance *get_thermal_instance(struct thermal_zone_device *,
|
||||
@@ -489,6 +491,12 @@ static inline struct thermal_zone_device
|
||||
static inline int thermal_zone_get_temp(
|
||||
struct thermal_zone_device *tz, int *temp)
|
||||
{ return -ENODEV; }
|
||||
+static inline int thermal_zone_get_slope(
|
||||
+ struct thermal_zone_device *tz)
|
||||
+{ return -ENODEV; }
|
||||
+static inline int thermal_zone_get_offset(
|
||||
+ struct thermal_zone_device *tz)
|
||||
+{ return -ENODEV; }
|
||||
static inline int get_tz_trend(struct thermal_zone_device *tz, int trip)
|
||||
{ return -ENODEV; }
|
||||
static inline struct thermal_instance *
|
|
@ -0,0 +1,42 @@
|
|||
From 313a72ff983cc2e00ac4dcb791d40ebf2f9d5718 Mon Sep 17 00:00:00 2001
|
||||
From: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
|
||||
Date: Tue, 17 Nov 2015 09:12:41 +0000
|
||||
Subject: nvmem: core: return error for non word aligned access
|
||||
|
||||
nvmem providers have restrictions on register strides, so return error
|
||||
when users attempt to read/write buffers with sizes which are less
|
||||
than word size.
|
||||
|
||||
Without this patch the userspace would continue to try as it does not
|
||||
get any error from the nvmem core, resulting in a hang or endless loop
|
||||
in userspace.
|
||||
|
||||
Reported-by: Ariel D'Alessandro <ariel@vanguardiasur.com.ar>
|
||||
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
|
||||
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
||||
---
|
||||
drivers/nvmem/core.c | 6 ++++++
|
||||
1 file changed, 6 insertions(+)
|
||||
|
||||
--- a/drivers/nvmem/core.c
|
||||
+++ b/drivers/nvmem/core.c
|
||||
@@ -70,6 +70,9 @@ static ssize_t bin_attr_nvmem_read(struc
|
||||
if (pos >= nvmem->size)
|
||||
return 0;
|
||||
|
||||
+ if (count < nvmem->word_size)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
if (pos + count > nvmem->size)
|
||||
count = nvmem->size - pos;
|
||||
|
||||
@@ -95,6 +98,9 @@ static ssize_t bin_attr_nvmem_write(stru
|
||||
if (pos >= nvmem->size)
|
||||
return 0;
|
||||
|
||||
+ if (count < nvmem->word_size)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
if (pos + count > nvmem->size)
|
||||
count = nvmem->size - pos;
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
From dfdf141429f0895b63c882facc42c86f225033cb Mon Sep 17 00:00:00 2001
|
||||
From: Rasmus Villemoes <linux@rasmusvillemoes.dk>
|
||||
Date: Mon, 8 Feb 2016 22:04:29 +0100
|
||||
Subject: nvmem: core: fix error path in nvmem_add_cells()
|
||||
|
||||
The current code fails to nvmem_cell_drop(cells[0]) - even worse, if
|
||||
the loop above fails already at i==0, we'll enter an essentially
|
||||
infinite loop doing nvmem_cell_drop on cells[-1], cells[-2], ... which
|
||||
is unlikely to end well.
|
||||
|
||||
Also, we're not freeing the temporary backing array cells on the error
|
||||
path.
|
||||
|
||||
Signed-off-by: Rasmus Villemoes <linux@rasmusvillemoes.dk>
|
||||
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
||||
---
|
||||
drivers/nvmem/core.c | 4 +++-
|
||||
1 file changed, 3 insertions(+), 1 deletion(-)
|
||||
|
||||
--- a/drivers/nvmem/core.c
|
||||
+++ b/drivers/nvmem/core.c
|
||||
@@ -294,9 +294,11 @@ static int nvmem_add_cells(struct nvmem_
|
||||
|
||||
return 0;
|
||||
err:
|
||||
- while (--i)
|
||||
+ while (i--)
|
||||
nvmem_cell_drop(cells[i]);
|
||||
|
||||
+ kfree(cells);
|
||||
+
|
||||
return rval;
|
||||
}
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
From 811b0d6538b9f26f3eb0f90fe4e6118f2480ec6f Mon Sep 17 00:00:00 2001
|
||||
From: Andrew Lunn <andrew@lunn.ch>
|
||||
Date: Fri, 26 Feb 2016 20:59:18 +0100
|
||||
Subject: nvmem: Add flag to export NVMEM to root only
|
||||
|
||||
Legacy AT24, AT25 EEPROMs are exported in sys so that only root can
|
||||
read the contents. The EEPROMs may contain sensitive information. Add
|
||||
a flag so the provide can indicate that NVMEM should also restrict
|
||||
access to root only.
|
||||
|
||||
Signed-off-by: Andrew Lunn <andrew@lunn.ch>
|
||||
Acked-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
|
||||
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
||||
---
|
||||
drivers/nvmem/core.c | 57 ++++++++++++++++++++++++++++++++++++++++--
|
||||
include/linux/nvmem-provider.h | 1 +
|
||||
2 files changed, 56 insertions(+), 2 deletions(-)
|
||||
|
||||
--- a/drivers/nvmem/core.c
|
||||
+++ b/drivers/nvmem/core.c
|
||||
@@ -161,6 +161,53 @@ static const struct attribute_group *nvm
|
||||
NULL,
|
||||
};
|
||||
|
||||
+/* default read/write permissions, root only */
|
||||
+static struct bin_attribute bin_attr_rw_root_nvmem = {
|
||||
+ .attr = {
|
||||
+ .name = "nvmem",
|
||||
+ .mode = S_IWUSR | S_IRUSR,
|
||||
+ },
|
||||
+ .read = bin_attr_nvmem_read,
|
||||
+ .write = bin_attr_nvmem_write,
|
||||
+};
|
||||
+
|
||||
+static struct bin_attribute *nvmem_bin_rw_root_attributes[] = {
|
||||
+ &bin_attr_rw_root_nvmem,
|
||||
+ NULL,
|
||||
+};
|
||||
+
|
||||
+static const struct attribute_group nvmem_bin_rw_root_group = {
|
||||
+ .bin_attrs = nvmem_bin_rw_root_attributes,
|
||||
+};
|
||||
+
|
||||
+static const struct attribute_group *nvmem_rw_root_dev_groups[] = {
|
||||
+ &nvmem_bin_rw_root_group,
|
||||
+ NULL,
|
||||
+};
|
||||
+
|
||||
+/* read only permission, root only */
|
||||
+static struct bin_attribute bin_attr_ro_root_nvmem = {
|
||||
+ .attr = {
|
||||
+ .name = "nvmem",
|
||||
+ .mode = S_IRUSR,
|
||||
+ },
|
||||
+ .read = bin_attr_nvmem_read,
|
||||
+};
|
||||
+
|
||||
+static struct bin_attribute *nvmem_bin_ro_root_attributes[] = {
|
||||
+ &bin_attr_ro_root_nvmem,
|
||||
+ NULL,
|
||||
+};
|
||||
+
|
||||
+static const struct attribute_group nvmem_bin_ro_root_group = {
|
||||
+ .bin_attrs = nvmem_bin_ro_root_attributes,
|
||||
+};
|
||||
+
|
||||
+static const struct attribute_group *nvmem_ro_root_dev_groups[] = {
|
||||
+ &nvmem_bin_ro_root_group,
|
||||
+ NULL,
|
||||
+};
|
||||
+
|
||||
static void nvmem_release(struct device *dev)
|
||||
{
|
||||
struct nvmem_device *nvmem = to_nvmem_device(dev);
|
||||
@@ -355,8 +402,14 @@ struct nvmem_device *nvmem_register(cons
|
||||
nvmem->read_only = of_property_read_bool(np, "read-only") |
|
||||
config->read_only;
|
||||
|
||||
- nvmem->dev.groups = nvmem->read_only ? nvmem_ro_dev_groups :
|
||||
- nvmem_rw_dev_groups;
|
||||
+ if (config->root_only)
|
||||
+ nvmem->dev.groups = nvmem->read_only ?
|
||||
+ nvmem_ro_root_dev_groups :
|
||||
+ nvmem_rw_root_dev_groups;
|
||||
+ else
|
||||
+ nvmem->dev.groups = nvmem->read_only ?
|
||||
+ nvmem_ro_dev_groups :
|
||||
+ nvmem_rw_dev_groups;
|
||||
|
||||
device_initialize(&nvmem->dev);
|
||||
|
||||
--- a/include/linux/nvmem-provider.h
|
||||
+++ b/include/linux/nvmem-provider.h
|
||||
@@ -23,6 +23,7 @@ struct nvmem_config {
|
||||
const struct nvmem_cell_info *cells;
|
||||
int ncells;
|
||||
bool read_only;
|
||||
+ bool root_only;
|
||||
};
|
||||
|
||||
#if IS_ENABLED(CONFIG_NVMEM)
|
|
@ -0,0 +1,181 @@
|
|||
From b6c217ab9be6895384cf0b284ace84ad79e5c53b Mon Sep 17 00:00:00 2001
|
||||
From: Andrew Lunn <andrew@lunn.ch>
|
||||
Date: Fri, 26 Feb 2016 20:59:19 +0100
|
||||
Subject: nvmem: Add backwards compatibility support for older EEPROM drivers.
|
||||
|
||||
Older drivers made an 'eeprom' file available in the /sys device
|
||||
directory. Have the NVMEM core provide this to retain backwards
|
||||
compatibility.
|
||||
|
||||
Signed-off-by: Andrew Lunn <andrew@lunn.ch>
|
||||
Acked-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
|
||||
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
||||
---
|
||||
drivers/nvmem/core.c | 84 ++++++++++++++++++++++++++++++++++++++----
|
||||
include/linux/nvmem-provider.h | 4 +-
|
||||
2 files changed, 79 insertions(+), 9 deletions(-)
|
||||
|
||||
--- a/drivers/nvmem/core.c
|
||||
+++ b/drivers/nvmem/core.c
|
||||
@@ -38,8 +38,13 @@ struct nvmem_device {
|
||||
int users;
|
||||
size_t size;
|
||||
bool read_only;
|
||||
+ int flags;
|
||||
+ struct bin_attribute eeprom;
|
||||
+ struct device *base_dev;
|
||||
};
|
||||
|
||||
+#define FLAG_COMPAT BIT(0)
|
||||
+
|
||||
struct nvmem_cell {
|
||||
const char *name;
|
||||
int offset;
|
||||
@@ -56,16 +61,26 @@ static DEFINE_IDA(nvmem_ida);
|
||||
static LIST_HEAD(nvmem_cells);
|
||||
static DEFINE_MUTEX(nvmem_cells_mutex);
|
||||
|
||||
+#ifdef CONFIG_DEBUG_LOCK_ALLOC
|
||||
+static struct lock_class_key eeprom_lock_key;
|
||||
+#endif
|
||||
+
|
||||
#define to_nvmem_device(d) container_of(d, struct nvmem_device, dev)
|
||||
|
||||
static ssize_t bin_attr_nvmem_read(struct file *filp, struct kobject *kobj,
|
||||
struct bin_attribute *attr,
|
||||
char *buf, loff_t pos, size_t count)
|
||||
{
|
||||
- struct device *dev = container_of(kobj, struct device, kobj);
|
||||
- struct nvmem_device *nvmem = to_nvmem_device(dev);
|
||||
+ struct device *dev;
|
||||
+ struct nvmem_device *nvmem;
|
||||
int rc;
|
||||
|
||||
+ if (attr->private)
|
||||
+ dev = attr->private;
|
||||
+ else
|
||||
+ dev = container_of(kobj, struct device, kobj);
|
||||
+ nvmem = to_nvmem_device(dev);
|
||||
+
|
||||
/* Stop the user from reading */
|
||||
if (pos >= nvmem->size)
|
||||
return 0;
|
||||
@@ -90,10 +105,16 @@ static ssize_t bin_attr_nvmem_write(stru
|
||||
struct bin_attribute *attr,
|
||||
char *buf, loff_t pos, size_t count)
|
||||
{
|
||||
- struct device *dev = container_of(kobj, struct device, kobj);
|
||||
- struct nvmem_device *nvmem = to_nvmem_device(dev);
|
||||
+ struct device *dev;
|
||||
+ struct nvmem_device *nvmem;
|
||||
int rc;
|
||||
|
||||
+ if (attr->private)
|
||||
+ dev = attr->private;
|
||||
+ else
|
||||
+ dev = container_of(kobj, struct device, kobj);
|
||||
+ nvmem = to_nvmem_device(dev);
|
||||
+
|
||||
/* Stop the user from writing */
|
||||
if (pos >= nvmem->size)
|
||||
return 0;
|
||||
@@ -349,6 +370,43 @@ err:
|
||||
return rval;
|
||||
}
|
||||
|
||||
+/*
|
||||
+ * nvmem_setup_compat() - Create an additional binary entry in
|
||||
+ * drivers sys directory, to be backwards compatible with the older
|
||||
+ * drivers/misc/eeprom drivers.
|
||||
+ */
|
||||
+static int nvmem_setup_compat(struct nvmem_device *nvmem,
|
||||
+ const struct nvmem_config *config)
|
||||
+{
|
||||
+ int rval;
|
||||
+
|
||||
+ if (!config->base_dev)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ if (nvmem->read_only)
|
||||
+ nvmem->eeprom = bin_attr_ro_root_nvmem;
|
||||
+ else
|
||||
+ nvmem->eeprom = bin_attr_rw_root_nvmem;
|
||||
+ nvmem->eeprom.attr.name = "eeprom";
|
||||
+ nvmem->eeprom.size = nvmem->size;
|
||||
+#ifdef CONFIG_DEBUG_LOCK_ALLOC
|
||||
+ nvmem->eeprom.attr.key = &eeprom_lock_key;
|
||||
+#endif
|
||||
+ nvmem->eeprom.private = &nvmem->dev;
|
||||
+ nvmem->base_dev = config->base_dev;
|
||||
+
|
||||
+ rval = device_create_bin_file(nvmem->base_dev, &nvmem->eeprom);
|
||||
+ if (rval) {
|
||||
+ dev_err(&nvmem->dev,
|
||||
+ "Failed to create eeprom binary file %d\n", rval);
|
||||
+ return rval;
|
||||
+ }
|
||||
+
|
||||
+ nvmem->flags |= FLAG_COMPAT;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
/**
|
||||
* nvmem_register() - Register a nvmem device for given nvmem_config.
|
||||
* Also creates an binary entry in /sys/bus/nvmem/devices/dev-name/nvmem
|
||||
@@ -416,16 +474,23 @@ struct nvmem_device *nvmem_register(cons
|
||||
dev_dbg(&nvmem->dev, "Registering nvmem device %s\n", config->name);
|
||||
|
||||
rval = device_add(&nvmem->dev);
|
||||
- if (rval) {
|
||||
- ida_simple_remove(&nvmem_ida, nvmem->id);
|
||||
- kfree(nvmem);
|
||||
- return ERR_PTR(rval);
|
||||
+ if (rval)
|
||||
+ goto out;
|
||||
+
|
||||
+ if (config->compat) {
|
||||
+ rval = nvmem_setup_compat(nvmem, config);
|
||||
+ if (rval)
|
||||
+ goto out;
|
||||
}
|
||||
|
||||
if (config->cells)
|
||||
nvmem_add_cells(nvmem, config);
|
||||
|
||||
return nvmem;
|
||||
+out:
|
||||
+ ida_simple_remove(&nvmem_ida, nvmem->id);
|
||||
+ kfree(nvmem);
|
||||
+ return ERR_PTR(rval);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nvmem_register);
|
||||
|
||||
@@ -445,6 +510,9 @@ int nvmem_unregister(struct nvmem_device
|
||||
}
|
||||
mutex_unlock(&nvmem_mutex);
|
||||
|
||||
+ if (nvmem->flags & FLAG_COMPAT)
|
||||
+ device_remove_bin_file(nvmem->base_dev, &nvmem->eeprom);
|
||||
+
|
||||
nvmem_device_remove_all_cells(nvmem);
|
||||
device_del(&nvmem->dev);
|
||||
|
||||
--- a/include/linux/nvmem-provider.h
|
||||
+++ b/include/linux/nvmem-provider.h
|
||||
@@ -24,6 +24,9 @@ struct nvmem_config {
|
||||
int ncells;
|
||||
bool read_only;
|
||||
bool root_only;
|
||||
+ /* To be only used by old driver/misc/eeprom drivers */
|
||||
+ bool compat;
|
||||
+ struct device *base_dev;
|
||||
};
|
||||
|
||||
#if IS_ENABLED(CONFIG_NVMEM)
|
||||
@@ -44,5 +47,4 @@ static inline int nvmem_unregister(struc
|
||||
}
|
||||
|
||||
#endif /* CONFIG_NVMEM */
|
||||
-
|
||||
#endif /* ifndef _LINUX_NVMEM_PROVIDER_H */
|
|
@ -0,0 +1,38 @@
|
|||
From 856371ca1561ca9b3280cc323ff296c7c5e1fa93 Mon Sep 17 00:00:00 2001
|
||||
From: Pavel Kubelun <be.dissent@gmail.com>
|
||||
Date: Tue, 22 Nov 2016 17:37:56 +0300
|
||||
Subject: [PATCH] ipq806x: clk: gcc: add tsens child node
|
||||
|
||||
Thermal sensors in ipq806x are inside a Global clock controller.
|
||||
Add a child node into it to be used by the TSENS driver.
|
||||
|
||||
Signed-off-by: Pavel Kubelun <be.dissent@gmail.com>
|
||||
|
||||
---
|
||||
drivers/clk/qcom/gcc-ipq806x.c | 8 ++++++++
|
||||
1 file changed, 8 insertions(+)
|
||||
|
||||
--- a/drivers/clk/qcom/gcc-ipq806x.c
|
||||
+++ b/drivers/clk/qcom/gcc-ipq806x.c
|
||||
@@ -3109,6 +3109,7 @@ MODULE_DEVICE_TABLE(of, gcc_ipq806x_matc
|
||||
static int gcc_ipq806x_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
+ struct platform_device *tsens;
|
||||
struct regmap *regmap;
|
||||
int ret;
|
||||
|
||||
@@ -3138,6 +3139,13 @@ static int gcc_ipq806x_probe(struct plat
|
||||
regmap_write(regmap, 0x3cf8, 8);
|
||||
regmap_write(regmap, 0x3d18, 8);
|
||||
|
||||
+ tsens = platform_device_register_data(&pdev->dev, "qcom-tsens", -1,
|
||||
+ NULL, 0);
|
||||
+ if (IS_ERR(tsens))
|
||||
+ return PTR_ERR(tsens);
|
||||
+
|
||||
+ platform_set_drvdata(pdev, tsens);
|
||||
+
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,150 @@
|
|||
--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi
|
||||
+++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi
|
||||
@@ -31,6 +31,9 @@
|
||||
clock-latency = <100000>;
|
||||
cpu-supply = <&smb208_s2a>;
|
||||
voltage-tolerance = <5>;
|
||||
+ cooling-min-state = <0>;
|
||||
+ cooling-max-state = <10>;
|
||||
+ #cooling-cells = <2>;
|
||||
cpu-idle-states = <&CPU_SPC>;
|
||||
};
|
||||
|
||||
@@ -46,6 +49,9 @@
|
||||
clock-names = "cpu", "l2";
|
||||
clock-latency = <100000>;
|
||||
cpu-supply = <&smb208_s2b>;
|
||||
+ cooling-min-state = <0>;
|
||||
+ cooling-max-state = <10>;
|
||||
+ #cooling-cells = <2>;
|
||||
cpu-idle-states = <&CPU_SPC>;
|
||||
};
|
||||
|
||||
@@ -70,6 +76,92 @@
|
||||
};
|
||||
};
|
||||
|
||||
+ thermal-zones {
|
||||
+ cpu-thermal0 {
|
||||
+ polling-delay-passive = <250>;
|
||||
+ polling-delay = <1000>;
|
||||
+
|
||||
+ thermal-sensors = <&gcc 5>;
|
||||
+ coefficients = <1132 0>;
|
||||
+
|
||||
+ trips {
|
||||
+ cpu_alert0: trip0 {
|
||||
+ temperature = <75000>;
|
||||
+ hysteresis = <2000>;
|
||||
+ type = "passive";
|
||||
+ };
|
||||
+ cpu_crit0: trip1 {
|
||||
+ temperature = <110000>;
|
||||
+ hysteresis = <2000>;
|
||||
+ type = "critical";
|
||||
+ };
|
||||
+ };
|
||||
+ };
|
||||
+
|
||||
+ cpu-thermal1 {
|
||||
+ polling-delay-passive = <250>;
|
||||
+ polling-delay = <1000>;
|
||||
+
|
||||
+ thermal-sensors = <&gcc 6>;
|
||||
+ coefficients = <1132 0>;
|
||||
+
|
||||
+ trips {
|
||||
+ cpu_alert1: trip0 {
|
||||
+ temperature = <75000>;
|
||||
+ hysteresis = <2000>;
|
||||
+ type = "passive";
|
||||
+ };
|
||||
+ cpu_crit1: trip1 {
|
||||
+ temperature = <110000>;
|
||||
+ hysteresis = <2000>;
|
||||
+ type = "critical";
|
||||
+ };
|
||||
+ };
|
||||
+ };
|
||||
+
|
||||
+ cpu-thermal2 {
|
||||
+ polling-delay-passive = <250>;
|
||||
+ polling-delay = <1000>;
|
||||
+
|
||||
+ thermal-sensors = <&gcc 7>;
|
||||
+ coefficients = <1199 0>;
|
||||
+
|
||||
+ trips {
|
||||
+ cpu_alert2: trip0 {
|
||||
+ temperature = <75000>;
|
||||
+ hysteresis = <2000>;
|
||||
+ type = "passive";
|
||||
+ };
|
||||
+ cpu_crit2: trip1 {
|
||||
+ temperature = <110000>;
|
||||
+ hysteresis = <2000>;
|
||||
+ type = "critical";
|
||||
+ };
|
||||
+ };
|
||||
+ };
|
||||
+
|
||||
+ cpu-thermal3 {
|
||||
+ polling-delay-passive = <250>;
|
||||
+ polling-delay = <1000>;
|
||||
+
|
||||
+ thermal-sensors = <&gcc 8>;
|
||||
+ coefficients = <1132 0>;
|
||||
+
|
||||
+ trips {
|
||||
+ cpu_alert3: trip0 {
|
||||
+ temperature = <75000>;
|
||||
+ hysteresis = <2000>;
|
||||
+ type = "passive";
|
||||
+ };
|
||||
+ cpu_crit3: trip1 {
|
||||
+ temperature = <110000>;
|
||||
+ hysteresis = <2000>;
|
||||
+ type = "critical";
|
||||
+ };
|
||||
+ };
|
||||
+ };
|
||||
+ };
|
||||
+
|
||||
cpu-pmu {
|
||||
compatible = "qcom,krait-pmu";
|
||||
interrupts = <1 10 0x304>;
|
||||
@@ -172,6 +264,21 @@
|
||||
reg-names = "lpass-lpaif";
|
||||
};
|
||||
|
||||
+ qfprom: qfprom@700000 {
|
||||
+ compatible = "qcom,qfprom", "syscon";
|
||||
+ reg = <0x00700000 0x1000>;
|
||||
+ #address-cells = <1>;
|
||||
+ #size-cells = <1>;
|
||||
+ ranges;
|
||||
+
|
||||
+ tsens_calib: calib {
|
||||
+ reg = <0x400 0x10>;
|
||||
+ };
|
||||
+ tsens_backup: backup_calib {
|
||||
+ reg = <0x410 0x10>;
|
||||
+ };
|
||||
+ };
|
||||
+
|
||||
rpm@108000 {
|
||||
compatible = "qcom,rpm-ipq8064";
|
||||
reg = <0x108000 0x1000>;
|
||||
@@ -499,8 +606,12 @@
|
||||
gcc: clock-controller@900000 {
|
||||
compatible = "qcom,gcc-ipq8064";
|
||||
reg = <0x00900000 0x4000>;
|
||||
+ nvmem-cells = <&tsens_calib>, <&tsens_backup>;
|
||||
+ nvmem-cell-names = "calib", "calib_backup";
|
||||
#clock-cells = <1>;
|
||||
#reset-cells = <1>;
|
||||
+ #power-domain-cells = <1>;
|
||||
+ #thermal-sensor-cells = <1>;
|
||||
};
|
||||
|
||||
tcsr: syscon@1a400000 {
|
|
@ -121,7 +121,7 @@ Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org>
|
|||
};
|
||||
--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi
|
||||
+++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi
|
||||
@@ -793,6 +793,92 @@
|
||||
@@ -904,6 +904,92 @@
|
||||
|
||||
status = "disabled";
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue