Last active
December 12, 2016 04:52
-
-
Save updateing/a3101c9d99463445b6fc2d41f63e21cd to your computer and use it in GitHub Desktop.
Enable thermal sensors on Linksys EA8500 with LEDE
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
From 4d6dd770cea0336c276e43c6900af6edb1e9e981 Mon Sep 17 00:00:00 2001 | |
From: Narendran Rajan <[email protected]> | |
Date: Fri, 31 Oct 2014 16:05:16 -0700 | |
Subject: [PATCH] CHROMIUM: ARM: qcom: dts: Add tsens data | |
BUG=None | |
TEST=build/boot on storm | |
Change-Id: I3121ca61f426b560e014c2b3569414ae8718c4ad | |
Signed-off-by: Narendran Rajan <[email protected]> | |
Reviewed-on: https://chromium-review.googlesource.com/243449 | |
Reviewed-by: Bryan Freed <[email protected]> | |
Tested-by: Narendran M R <[email protected]> | |
Commit-Queue: Grant Grundler <[email protected]> | |
(cherry picked from commit 8ad83e875a8913399772339a029029e8f98c3f9f) | |
Reviewed-on: https://chromium-review.googlesource.com/257850 | |
Reviewed-by: Patrick Sosinski <[email protected]> | |
Commit-Queue: Patrick Sosinski <[email protected]> | |
Tested-by: Patrick Sosinski <[email protected]> | |
--- | |
diff --git a/arch/arm/boot/dts/qcom-ipq8064-thermal.dtsi b/arch/arm/boot/dts/qcom-ipq8064-thermal.dtsi | |
new file mode 100644 | |
index 0000000..b5e2b03 | |
--- /dev/null | |
+++ b/arch/arm/boot/dts/qcom-ipq8064-thermal.dtsi | |
@@ -0,0 +1,91 @@ | |
+/* | |
+ * Device Tree Source for ipq8064 SoC thermal | |
+ * | |
+ * 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 <dt-bindings/thermal/thermal.h> | |
+ | |
+cpu_thermal0: cpu_thermal0 { | |
+ polling-delay-passive = <2000>; /* milliseconds */ | |
+ polling-delay = <5000>; /* milliseconds */ | |
+ | |
+ /* sensor ID */ | |
+ thermal-sensors = <&tsens 0>; | |
+ linux,hwmon; | |
+ | |
+ trips { | |
+ thermal0_cpu_alert: thermal0_cpu_alert { | |
+ temperature = <50000>; /* millicelsius */ | |
+ hysteresis = <2000>; /* millicelsius */ | |
+ type = "passive"; | |
+ }; | |
+ thermal0_cpu_crit: thermal0_cpu_crit { | |
+ temperature = <100000>; /* millicelsius */ | |
+ hysteresis = <2000>; /* millicelsius */ | |
+ type = "critical"; | |
+ }; | |
+ }; | |
+ | |
+ cooling-maps { | |
+ map0 { | |
+ trip = <&thermal0_cpu_alert>; | |
+ cooling-device = | |
+ <&cpu0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; | |
+ }; | |
+ }; | |
+}; | |
+ | |
+cpu_thermal1: cpu_thermal1 { | |
+ polling-delay-passive = <2000>; /* milliseconds */ | |
+ polling-delay = <5000>; /* milliseconds */ | |
+ | |
+ /* sensor ID */ | |
+ thermal-sensors = <&tsens 1>; | |
+ linux,hwmon; | |
+ | |
+ trips { | |
+ thermal1_cpu_alert: thermal1_cpu_alert { | |
+ temperature = <50000>; /* millicelsius */ | |
+ hysteresis = <2000>; /* millicelsius */ | |
+ type = "passive"; | |
+ }; | |
+ thermal1_cpu_crit: thermal1_cpu_crit { | |
+ temperature = <100000>; /* millicelsius */ | |
+ hysteresis = <2000>; /* millicelsius */ | |
+ type = "critical"; | |
+ }; | |
+ }; | |
+}; | |
+ | |
+cpu_thermal2: cpu_thermal2 { | |
+ polling-delay-passive = <2000>; /* milliseconds */ | |
+ polling-delay = <5000>; /* milliseconds */ | |
+ | |
+ /* sensor ID */ | |
+ thermal-sensors = <&tsens 2>; | |
+ linux,hwmon; | |
+ | |
+ trips { | |
+ thermal2_cpu_alert: thermal2_cpu_alert { | |
+ temperature = <50000>; /* millicelsius */ | |
+ hysteresis = <2000>; /* millicelsius */ | |
+ type = "passive"; | |
+ }; | |
+ thermal2_cpu_crit: thermal2_cpu_crit { | |
+ temperature = <100000>; /* millicelsius */ | |
+ hysteresis = <2000>; /* millicelsius */ | |
+ type = "critical"; | |
+ }; | |
+ }; | |
+}; | |
diff --git a/arch/arm/boot/dts/qcom-ipq8064.dtsi b/arch/arm/boot/dts/qcom-ipq8064.dtsi | |
index c04a8f5..5a4e95d 100644 | |
--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi | |
+++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi | |
@@ -18,7 +18,7 @@ | |
#address-cells = <1>; | |
#size-cells = <0>; | |
- cpu@0 { | |
+ cpu0: cpu@0 { | |
compatible = "qcom,krait"; | |
enable-method = "qcom,kpss-acc-v1"; | |
device_type = "cpu"; | |
@@ -75,7 +75,7 @@ | |
#cooling-cells = <2>; | |
}; | |
- cpu@1 { | |
+ cpu1: cpu@1 { | |
compatible = "qcom,krait"; | |
enable-method = "qcom,kpss-acc-v1"; | |
device_type = "cpu"; | |
@@ -562,11 +562,26 @@ | |
qcom,controller-type = "pmic-arbiter"; | |
}; | |
+ thermal-zones { | |
+ #include "qcom-ipq8064-thermal.dtsi" | |
+ }; | |
+ | |
gcc: clock-controller@900000 { | |
compatible = "qcom,gcc-ipq8064"; | |
reg = <0x00900000 0x4000>; | |
#clock-cells = <1>; | |
#reset-cells = <1>; | |
+ | |
+ tsens: tsens-msm8960 { | |
+ compatible = "qcom,qcom-tsens"; | |
+ qcom,calib-offsets = <0x400 0x410>; | |
+ qcom,tsens-slopes = <1176 1176 1154 1176 1111 | |
+ 1132 1132 1199 1132 1199 | |
+ 1132>; | |
+ qcom,qfprom = <&imem 0x400 11>, <&imem 0x410 11>; | |
+ qcom,qfprom-names = "calib", "backup_calib"; | |
+ #thermal-sensor-cells = <1>; | |
+ }; | |
}; | |
lcc: clock-controller@28000000 { |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
From 61a8bc938c1d86c8783b7c2401bc35305ad81537 Mon Sep 17 00:00:00 2001 | |
From: Hamster Tian <[email protected]> | |
Date: Mon, 7 Nov 2016 11:46:00 +0800 | |
Subject: [PATCH] ipq806x: Enable thermal sensors | |
--- | |
target/linux/ipq806x/config-4.4 | 1 + | |
1 file changed, 1 insertion(+) | |
diff --git a/target/linux/ipq806x/config-4.4 b/target/linux/ipq806x/config-4.4 | |
index e67d0f1..d9a7d4f 100644 | |
--- a/target/linux/ipq806x/config-4.4 | |
+++ b/target/linux/ipq806x/config-4.4 | |
@@ -395,6 +395,7 @@ CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE=y | |
CONFIG_THERMAL_GOV_STEP_WISE=y | |
CONFIG_THERMAL_HWMON=y | |
CONFIG_THERMAL_OF=y | |
+CONFIG_THERMAL_QCOMTSENS=y | |
# CONFIG_THUMB2_KERNEL is not set | |
CONFIG_TICK_CPU_ACCOUNTING=y | |
CONFIG_TREE_RCU=y | |
-- | |
2.10.1 | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi 2016-11-06 15:56:12.945359846 +0800 | |
+++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi 2016-11-06 16:02:30.610355496 +0800 | |
@@ -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>; | |
}; | |
cpu1: cpu@1 { | |
@@ -45,6 +48,9 @@ | |
clock-names = "cpu", "l2"; | |
clock-latency = <100000>; | |
cpu-supply = <&smb208_s2b>; | |
+ cooling-min-state = <0>; | |
+ cooling-max-state = <10>; | |
+ #cooling-cells = <2>; | |
}; | |
L2: l2-cache { |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
From 57b4f0ff5624360a5abe894daa9cf7c9fe4ef928 Mon Sep 17 00:00:00 2001 | |
From: Narendran Rajan <[email protected]> | |
Date: Mon, 26 Jan 2015 12:07:03 -0800 | |
Subject: [PATCH] FROMLIST: thermal: Add msm tsens thermal sensor driver | |
TSENS supports reading temperature from multiple thermal | |
sensors present in the soc. | |
TSENS HW is enabled only when the main sensor is requested. | |
The TSENS block is disabled if the main senors is disabled | |
irrespective of any other sensors that are being enabled. | |
Based on code by Siddartha Mohanadoss and Stephen Boyd. | |
(from https://patchwork.kernel.org/patch/5786851/) | |
BUG=None | |
TEST=Two separate tests were conducted beyond basic | |
build/boot on storm. | |
1) run stressapp test and monitor temperature | |
$ stressapptest -s 20 -M 256 -m 8 -C 8 -W -i 8 | |
//In parallel monitor temperature | |
$ cat /sys/class/thermal/thermal_zone0/temp | |
Temperature should go up with load and once test execution is | |
completed, reported temperature should come down | |
2) keep the device in a thermal chamber where temperature | |
is controlled and then periodically check if the reported temperature | |
matches the expected range | |
Cc: Siddartha Mohanadoss <[email protected]> | |
Cc: Stephen Boyd <[email protected]> | |
Change-Id: I12090471d6d82283a04fecbcf1f0cb0f05d08f1e | |
Signed-off-by: Narendran Rajan <[email protected]> | |
Reviewed-on: https://chromium-review.googlesource.com/243448 | |
Tested-by: Narendran M R <[email protected]> | |
Reviewed-by: Bryan Freed <[email protected]> | |
--- | |
diff --git a/Documentation/devicetree/bindings/thermal/qcom-thermal.txt b/Documentation/devicetree/bindings/thermal/qcom-thermal.txt | |
new file mode 100644 | |
index 0000000..b4f8481 | |
--- /dev/null | |
+++ b/Documentation/devicetree/bindings/thermal/qcom-thermal.txt | |
@@ -0,0 +1,59 @@ | |
+* QCOM SoC Temperature Sensor (TSENS) | |
+ | |
+Required properties: | |
+- compatible : "qcom,qcom-tsens" | |
+- qcom,tsens-slopes : Must contain slope value for each of the sensors controlled | |
+ by this device | |
+- qcom,qfprom : An arry of triplets containing tsens calibration data. | |
+ The first element in the triplet is a phandle | |
+ to qfprom compatible node, second element is an offset to calibration | |
+ data within this node, third element indicates size of calibration | |
+ data. In general, there would be two elements in this array - the | |
+ first containing information on primary calibration data | |
+ and second containing information on backup calibration data | |
+- qcom,qfprom-names : Names for each calibration data | |
+- #thermal-sensor-cells : Should be 1. See ./thermal.txt for a description. | |
+ | |
+Example: | |
+ | |
+tsens: tsens-msm8960 { | |
+ compatible = "qcom,qcom-tsens"; | |
+ qcom,tsens-slopes = <1176 1176 1154 1176 1111 | |
+ 1132 1132 1199 1132 1199 | |
+ 1132>; | |
+ qcom,qfprom = <&imem 0x400 11>, <&imem 0x410 11>; | |
+ qcom,qfprom-names = "calib", "backup_calib"; | |
+ #thermal-sensor-cells = <1>; | |
+ }; | |
+ | |
+Example: referring to thermal sensors: | |
+thermal-zones { | |
+ cpu_thermal: cpu_thermal { | |
+ polling-delay-passive = <1000>; /* milliseconds */ | |
+ polling-delay = <5000>; /* milliseconds */ | |
+ | |
+ /* sensor ID */ | |
+ thermal-sensors = <&tsens 1>; | |
+ | |
+ trips { | |
+ cpu_alert0: cpu_alert { | |
+ temperature = <80000>; /* millicelsius */ | |
+ hysteresis = <2000>; /* millicelsius */ | |
+ type = "passive"; | |
+ }; | |
+ cpu_crit: cpu_crit { | |
+ temperature = <120000>; /* millicelsius */ | |
+ hysteresis = <2000>; /* millicelsius */ | |
+ type = "critical"; | |
+ }; | |
+ }; | |
+ | |
+ cooling-maps { | |
+ map0 { | |
+ trip = <&cpu_alert0>; | |
+ cooling-device = | |
+ <&cpu0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; | |
+ }; | |
+ }; | |
+ }; | |
+}; | |
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig | |
index 5c7ef8a..e68c6f9 100644 | |
--- a/drivers/thermal/Kconfig | |
+++ b/drivers/thermal/Kconfig | |
@@ -276,6 +276,20 @@ | |
the Intel Thermal Daemon can use this information to allow the user | |
to select his laptop to run without turning on the fans. | |
+config THERMAL_QCOMTSENS | |
+ tristate "Qualcomm Tsens Temperature driver" | |
+ depends on THERMAL | |
+ depends on ARCH_QCOM | |
+ help | |
+ QCOM tsens thermal driver provides support for Temperature sensor | |
+ (TSENS) found on QCOM SoCs. It supports four configurable trip points | |
+ and controls multiple sensors on the SOC. The four trip points are | |
+ common across all sensors present in the SoC. The number of sensors | |
+ present varies from chip to chip and are set through device tree entry. | |
+ The driver presents as a standard thermal zone device with configurable | |
+ trip points and cooling device mapping through standard thermal zone | |
+ device tree. | |
+ | |
menu "Texas Instruments thermal drivers" | |
source "drivers/thermal/ti-soc-thermal/Kconfig" | |
endmenu | |
diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile | |
index 45ba663..cd87363 100644 | |
--- a/drivers/thermal/Makefile | |
+++ b/drivers/thermal/Makefile | |
@@ -40,3 +40,4 @@ | |
# platform thermally-reactive drivers | |
obj-$(CONFIG_TEGRA_DFLL_THERMAL_REACTION) += tegra_dfll_action.o | |
obj-$(CONFIG_TEGRA_CPU_EDP_LIMITS) += tegra_cpu_edp_action.o | |
+obj-$(CONFIG_THERMAL_QCOMTSENS) += qcom-tsens.o | |
diff --git a/drivers/thermal/qcom-tsens.c b/drivers/thermal/qcom-tsens.c | |
new file mode 100644 | |
index 0000000..b714e2c | |
--- /dev/null | |
+++ b/drivers/thermal/qcom-tsens.c | |
@@ -0,0 +1,491 @@ | |
+/* 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/kernel.h> | |
+#include <linux/module.h> | |
+#include <linux/platform_device.h> | |
+#include <linux/thermal.h> | |
+#include <linux/delay.h> | |
+#include <linux/slab.h> | |
+#include <linux/err.h> | |
+#include <linux/pm.h> | |
+#include <linux/bitops.h> | |
+#include <linux/regmap.h> | |
+#include <linux/of.h> | |
+#include <linux/of_platform.h> | |
+#include <soc/qcom/qfprom.h> | |
+ | |
+#define TSENS_CAL_MDEGC 30000 | |
+ | |
+#define TSENS_8960_CONFIG_ADDR 0x3640 | |
+#define TSENS_8960_CONFIG 0x9b | |
+#define TSENS_8960_CONFIG_MASK 0xff | |
+ | |
+#define TSENS_CNTL_ADDR 0x3620 | |
+#define TSENS_EN BIT(0) | |
+#define TSENS_SW_RST BIT(1) | |
+#define SENSOR0_EN BIT(3) | |
+#define TSENS_MIN_STATUS_MASK BIT(0) | |
+#define TSENS_LOWER_STATUS_CLR BIT(1) | |
+#define TSENS_UPPER_STATUS_CLR BIT(2) | |
+#define TSENS_MAX_STATUS_MASK BIT(3) | |
+#define TSENS_8960_MEASURE_PERIOD BIT(18) | |
+#define TSENS_8660_MEASURE_PERIOD BIT(16) | |
+#define TSENS_8960_SLP_CLK_ENA BIT(26) | |
+#define TSENS_8660_SLP_CLK_ENA BIT(24) | |
+#define TSENS_8064_STATUS_CNTL 0x3660 | |
+ | |
+#define TSENS_THRESHOLD_ADDR 0x3624 | |
+#define TSENS_THRESHOLD_MAX_CODE 0xff | |
+#define TSENS_THRESHOLD_MIN_CODE 0 | |
+#define TSENS_THRESHOLD_MAX_LIMIT_SHIFT 24 | |
+#define TSENS_THRESHOLD_MIN_LIMIT_SHIFT 16 | |
+#define TSENS_THRESHOLD_UPPER_LIMIT_SHIFT 8 | |
+#define TSENS_THRESHOLD_LOWER_LIMIT_SHIFT 0 | |
+ | |
+/* Initial temperature threshold values */ | |
+#define TSENS_LOWER_LIMIT_TH 0x50 | |
+#define TSENS_UPPER_LIMIT_TH 0xdf | |
+#define TSENS_MIN_LIMIT_TH 0x0 | |
+#define TSENS_MAX_LIMIT_TH 0xff | |
+ | |
+#define TSENS_S0_STATUS_ADDR 0x3628 | |
+ | |
+#define TSENS_INT_STATUS_ADDR 0x363c | |
+#define TSENS_TRDY_MASK BIT(7) | |
+ | |
+#define TSENS_SENSOR0_SHIFT 3 | |
+ | |
+#define TSENS_8660_CONFIG 1 | |
+#define TSENS_8660_CONFIG_SHIFT 28 | |
+#define TSENS_8660_CONFIG_MASK (3 << TSENS_8660_CONFIG_SHIFT) | |
+#define TSENS_SENSORABOVEFIVE_OFFSET 40 | |
+ | |
+struct tsens_device; | |
+ | |
+struct tsens_sensor { | |
+ struct thermal_zone_device *tz_dev; | |
+ enum thermal_device_mode mode; | |
+ unsigned int sensor_num; | |
+ int offset; | |
+ int slope; | |
+ struct tsens_device *tmdev; | |
+ u32 status; | |
+}; | |
+ | |
+struct tsens_device { | |
+ struct device *dev; | |
+ bool prev_reading_avail; | |
+ unsigned int num_sensors; | |
+ int pm_tsens_thr_data; | |
+ int pm_tsens_cntl; | |
+ u32 slp_clk_enable; | |
+ struct regmap *map; | |
+ struct regmap_field *status_field; | |
+ struct tsens_sensor sensor[0]; | |
+}; | |
+ | |
+/* Temperature on y axis and ADC-code on x-axis */ | |
+static int | |
+tsens_tz_code_to_mdegC(u32 adc_code, const struct tsens_sensor *s) | |
+{ | |
+ return s->slope * adc_code + s->offset; | |
+} | |
+ | |
+static int tsens_tz_get_temp(void *_sensor, | |
+ long *temp) | |
+{ | |
+ const struct tsens_sensor *tm_sensor = _sensor; | |
+ struct tsens_device *tmdev = tm_sensor->tmdev; | |
+ u32 code, trdy; | |
+ | |
+ if (tm_sensor->mode != THERMAL_DEVICE_ENABLED) | |
+ return -EINVAL; | |
+ | |
+ if (!tmdev->prev_reading_avail) { | |
+ while (!regmap_read(tmdev->map, TSENS_INT_STATUS_ADDR, &trdy) && | |
+ !(trdy & TSENS_TRDY_MASK)) | |
+ usleep_range(1000, 1100); | |
+ tmdev->prev_reading_avail = true; | |
+ } | |
+ | |
+ regmap_read(tmdev->map, tm_sensor->status, &code); | |
+ *temp = tsens_tz_code_to_mdegC(code, tm_sensor); | |
+ | |
+ tmdev->prev_reading_avail = false; | |
+ | |
+ dev_dbg(tmdev->dev, "Sensor %d temp is: %ld", | |
+ tm_sensor->sensor_num, *temp); | |
+ return 0; | |
+} | |
+ | |
+/* | |
+ * If the main sensor is disabled, rest of the sensors are disabled | |
+ * along with the clock. | |
+ * If the main sensor is disabled and a sub-sensor is enabled | |
+ * return with an error. | |
+ */ | |
+static int tsens_tz_set_mode(struct tsens_sensor *tm_sensor, | |
+ enum thermal_device_mode mode) | |
+{ | |
+ struct tsens_device *tmdev = tm_sensor->tmdev; | |
+ unsigned int i, n = tmdev->num_sensors; | |
+ u32 reg, mask; | |
+ | |
+ if (mode == tm_sensor->mode) | |
+ return 0; | |
+ | |
+ regmap_read(tmdev->map, TSENS_CNTL_ADDR, ®); | |
+ | |
+ mask = BIT(tm_sensor->sensor_num + TSENS_SENSOR0_SHIFT); | |
+ if (mode == THERMAL_DEVICE_ENABLED) { | |
+ if (!(reg & SENSOR0_EN) && mask != SENSOR0_EN) { | |
+ dev_warn(tmdev->dev, "Main sensor not enabled\n"); | |
+ return -EINVAL; | |
+ } | |
+ | |
+ regmap_write(tmdev->map, TSENS_CNTL_ADDR, reg | TSENS_SW_RST); | |
+ reg |= mask | tmdev->slp_clk_enable | TSENS_EN; | |
+ | |
+ tmdev->prev_reading_avail = false; | |
+ } else { | |
+ reg &= ~mask; | |
+ if (!(reg & SENSOR0_EN)) { | |
+ dev_warn(tmdev->dev, "Main sensor not enabled. Disabling subsensors\n"); | |
+ | |
+ reg &= ~GENMASK(n + TSENS_SENSOR0_SHIFT - 1, | |
+ TSENS_SENSOR0_SHIFT); | |
+ reg &= ~TSENS_EN; | |
+ | |
+ reg &= ~tmdev->slp_clk_enable; | |
+ | |
+ /* Disable all sub-sensors */ | |
+ for (i = 1; i < n; i++) | |
+ tmdev->sensor[i].mode = mode; | |
+ } | |
+ } | |
+ | |
+ regmap_write(tmdev->map, TSENS_CNTL_ADDR, reg); | |
+ tm_sensor->mode = mode; | |
+ | |
+ return 0; | |
+} | |
+ | |
+#ifdef CONFIG_PM_SLEEP | |
+static int tsens_suspend(struct device *dev) | |
+{ | |
+ int i; | |
+ unsigned int mask; | |
+ struct tsens_device *tmdev = dev_get_drvdata(dev); | |
+ struct regmap *map = tmdev->map; | |
+ | |
+ regmap_read(map, TSENS_THRESHOLD_ADDR, &tmdev->pm_tsens_thr_data); | |
+ regmap_read(map, TSENS_CNTL_ADDR, &tmdev->pm_tsens_cntl); | |
+ | |
+ mask = tmdev->slp_clk_enable | TSENS_EN; | |
+ regmap_update_bits(map, TSENS_CNTL_ADDR, mask, 0); | |
+ | |
+ tmdev->prev_reading_avail = false; | |
+ for (i = 0; i < tmdev->num_sensors; i++) | |
+ tmdev->sensor[i].mode = THERMAL_DEVICE_DISABLED; | |
+ | |
+ return 0; | |
+} | |
+ | |
+static int tsens_resume(struct device *dev) | |
+{ | |
+ int i; | |
+ unsigned long reg_cntl; | |
+ struct tsens_device *tmdev = dev_get_drvdata(dev); | |
+ struct regmap *map = tmdev->map; | |
+ | |
+ regmap_update_bits(map, TSENS_CNTL_ADDR, TSENS_SW_RST, TSENS_SW_RST); | |
+ regmap_field_update_bits(tmdev->status_field, | |
+ TSENS_MIN_STATUS_MASK | TSENS_MAX_STATUS_MASK, | |
+ TSENS_MIN_STATUS_MASK | TSENS_MAX_STATUS_MASK); | |
+ | |
+ /* | |
+ * Separate CONFIG restore is needed only for 8960. For 8660 | |
+ * config is part of CTRL Addr and its restored as such | |
+ */ | |
+ if (tmdev->num_sensors > 1) | |
+ regmap_update_bits(map, TSENS_8960_CONFIG_ADDR, | |
+ TSENS_8960_CONFIG_MASK, | |
+ TSENS_8960_CONFIG); | |
+ | |
+ regmap_write(map, TSENS_THRESHOLD_ADDR, tmdev->pm_tsens_thr_data); | |
+ regmap_write(map, TSENS_CNTL_ADDR, tmdev->pm_tsens_cntl); | |
+ | |
+ reg_cntl = tmdev->pm_tsens_cntl; | |
+ reg_cntl >>= TSENS_SENSOR0_SHIFT; | |
+ for_each_set_bit(i, ®_cntl, tmdev->num_sensors) | |
+ tmdev->sensor[i].mode = THERMAL_DEVICE_ENABLED; | |
+ | |
+ return 0; | |
+} | |
+ | |
+static SIMPLE_DEV_PM_OPS(tsens_pm_ops, tsens_suspend, tsens_resume); | |
+#define TSENS_PM_OPS (&tsens_pm_ops) | |
+ | |
+#else /* CONFIG_PM_SLEEP */ | |
+ | |
+#define TSENS_PM_OPS NULL | |
+ | |
+#endif /* CONFIG_PM_SLEEP */ | |
+ | |
+static void tsens_disable_mode(const struct tsens_device *tmdev) | |
+{ | |
+ u32 reg_cntl; | |
+ u32 mask; | |
+ | |
+ mask = GENMASK(tmdev->num_sensors - 1, 0); | |
+ mask <<= TSENS_SENSOR0_SHIFT; | |
+ mask |= TSENS_EN; | |
+ | |
+ regmap_read(tmdev->map, TSENS_CNTL_ADDR, ®_cntl); | |
+ reg_cntl &= ~mask; | |
+ | |
+ reg_cntl &= ~tmdev->slp_clk_enable; | |
+ regmap_write(tmdev->map, TSENS_CNTL_ADDR, reg_cntl); | |
+} | |
+ | |
+static void tsens_hw_init(struct tsens_device *tmdev) | |
+{ | |
+ u32 reg_cntl, reg_thr; | |
+ | |
+ reg_cntl = TSENS_SW_RST; | |
+ regmap_update_bits(tmdev->map, TSENS_CNTL_ADDR, TSENS_SW_RST, reg_cntl); | |
+ | |
+ if (tmdev->num_sensors > 1) { | |
+ reg_cntl |= TSENS_8960_SLP_CLK_ENA | TSENS_8960_MEASURE_PERIOD; | |
+ reg_cntl &= ~TSENS_SW_RST; | |
+ regmap_update_bits(tmdev->map, TSENS_8960_CONFIG_ADDR, | |
+ TSENS_8960_CONFIG_MASK, TSENS_8960_CONFIG); | |
+ tmdev->slp_clk_enable = TSENS_8960_SLP_CLK_ENA; | |
+ | |
+ } else { | |
+ reg_cntl |= TSENS_8660_SLP_CLK_ENA | TSENS_8660_MEASURE_PERIOD; | |
+ reg_cntl &= ~TSENS_8660_CONFIG_MASK; | |
+ reg_cntl |= TSENS_8660_CONFIG << TSENS_8660_CONFIG_SHIFT; | |
+ tmdev->slp_clk_enable = TSENS_8660_SLP_CLK_ENA; | |
+ } | |
+ | |
+ reg_cntl |= GENMASK(tmdev->num_sensors - 1, 0) << TSENS_SENSOR0_SHIFT; | |
+ regmap_write(tmdev->map, TSENS_CNTL_ADDR, reg_cntl); | |
+ | |
+ regmap_field_update_bits(tmdev->status_field, | |
+ TSENS_LOWER_STATUS_CLR | TSENS_UPPER_STATUS_CLR | | |
+ TSENS_MIN_STATUS_MASK | TSENS_MAX_STATUS_MASK, | |
+ TSENS_LOWER_STATUS_CLR | TSENS_UPPER_STATUS_CLR | | |
+ TSENS_MIN_STATUS_MASK | TSENS_MAX_STATUS_MASK); | |
+ | |
+ reg_cntl |= TSENS_EN; | |
+ regmap_write(tmdev->map, TSENS_CNTL_ADDR, reg_cntl); | |
+ | |
+ reg_thr = (TSENS_LOWER_LIMIT_TH << TSENS_THRESHOLD_LOWER_LIMIT_SHIFT) | | |
+ (TSENS_UPPER_LIMIT_TH << TSENS_THRESHOLD_UPPER_LIMIT_SHIFT) | | |
+ (TSENS_MIN_LIMIT_TH << TSENS_THRESHOLD_MIN_LIMIT_SHIFT) | | |
+ (TSENS_MAX_LIMIT_TH << TSENS_THRESHOLD_MAX_LIMIT_SHIFT); | |
+ regmap_write(tmdev->map, TSENS_THRESHOLD_ADDR, reg_thr); | |
+} | |
+ | |
+static int | |
+tsens_calib_sensors(struct tsens_device *tmdev) | |
+{ | |
+ int i; | |
+ u8 *byte_data; | |
+ u32 num_read; | |
+ struct tsens_sensor *s = tmdev->sensor; | |
+ | |
+ byte_data = devm_qfprom_get_data_byname(tmdev->dev, "calib", &num_read); | |
+ | |
+ if (IS_ERR(byte_data) || !byte_data[0] | |
+ || num_read != tmdev->num_sensors) | |
+ byte_data = devm_qfprom_get_data_byname(tmdev->dev, | |
+ "backup_calib", &num_read); | |
+ | |
+ if (IS_ERR(byte_data) || !byte_data[0] | |
+ || num_read != tmdev->num_sensors) | |
+ return -EINVAL; | |
+ | |
+ for (i = 0; i < num_read; i++, s++) | |
+ s->offset = TSENS_CAL_MDEGC - s->slope * byte_data[i]; | |
+ | |
+ return 0; | |
+} | |
+ | |
+static int tsens_register(struct tsens_device *tmdev, int i) | |
+{ | |
+ u32 addr = TSENS_S0_STATUS_ADDR; | |
+ struct tsens_sensor *s = &tmdev->sensor[i]; | |
+ | |
+ /* | |
+ * 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. | |
+ */ | |
+ if (i >= 5) | |
+ addr += TSENS_SENSORABOVEFIVE_OFFSET; | |
+ | |
+ addr += i * 4; | |
+ | |
+ s->mode = THERMAL_DEVICE_ENABLED; | |
+ s->sensor_num = i; | |
+ s->status = addr; | |
+ s->tmdev = tmdev; | |
+ s->tz_dev = thermal_zone_of_sensor_register(tmdev->dev, i, s, | |
+ tsens_tz_get_temp, | |
+ NULL, | |
+ NULL); | |
+ | |
+ if (IS_ERR(s->tz_dev)) | |
+ return -ENODEV; | |
+ | |
+ return 0; | |
+} | |
+ | |
+static int tsens_probe(struct platform_device *pdev) | |
+{ | |
+ struct device_node *np = pdev->dev.of_node; | |
+ int ret, i, num; | |
+ struct tsens_sensor *s; | |
+ struct tsens_device *tmdev; | |
+ struct regmap *map; | |
+ struct reg_field *field; | |
+ static struct reg_field status_0 = { | |
+ .reg = TSENS_8064_STATUS_CNTL, | |
+ .lsb = 0, | |
+ .msb = 3, | |
+ }; | |
+ static struct reg_field status_8 = { | |
+ .reg = TSENS_CNTL_ADDR, | |
+ .lsb = 8, | |
+ .msb = 11, | |
+ }; | |
+ | |
+ num = of_property_count_u32_elems(np, "qcom,tsens-slopes"); | |
+ if (num <= 0) { | |
+ dev_err(&pdev->dev, "invalid tsens slopes\n"); | |
+ return -EINVAL; | |
+ } | |
+ | |
+ tmdev = devm_kzalloc(&pdev->dev, sizeof(*tmdev) + | |
+ num * sizeof(struct tsens_sensor), GFP_KERNEL); | |
+ if (tmdev == NULL) | |
+ return -ENOMEM; | |
+ | |
+ tmdev->dev = &pdev->dev; | |
+ tmdev->num_sensors = num; | |
+ | |
+ for (i = 0, s = tmdev->sensor; i < num; i++, s++) | |
+ of_property_read_u32_index(np, "qcom,tsens-slopes", i, | |
+ &s->slope); | |
+ | |
+ ret = tsens_calib_sensors(tmdev); | |
+ if (ret < 0) { | |
+ dev_err(&pdev->dev, "tsense calibration failed\n"); | |
+ return ret; | |
+ } | |
+ | |
+ tmdev->map = map = dev_get_regmap(pdev->dev.parent, NULL); | |
+ if (!map) | |
+ return -ENODEV; | |
+ | |
+ /* Status bits move when the sensor bits next to them overlap */ | |
+ if (num > 5) | |
+ field = &status_0; | |
+ else | |
+ field = &status_8; | |
+ | |
+ tmdev->status_field = devm_regmap_field_alloc(&pdev->dev, map, *field); | |
+ if (IS_ERR(tmdev->status_field)) { | |
+ dev_err(&pdev->dev, "regmap alloc failed\n"); | |
+ return PTR_ERR(tmdev->status_field); | |
+ } | |
+ | |
+ tsens_hw_init(tmdev); | |
+ | |
+ /* | |
+ * Register sensor 0 separately. This sensor is always | |
+ * expected to be present and if this fails, thermal | |
+ * sensor probe would fail. | |
+ * Other sensors are optional and if registration fails | |
+ * disable the sensor and continue | |
+ */ | |
+ ret = tsens_register(tmdev, 0); | |
+ if (ret < 0) { | |
+ dev_err(&pdev->dev, "Registering failed for primary sensor"); | |
+ ret = -ENODEV; | |
+ goto fail; | |
+ } else { | |
+ tsens_tz_set_mode(&tmdev->sensor[0], THERMAL_DEVICE_ENABLED); | |
+ } | |
+ | |
+ for (i = 1; i < tmdev->num_sensors; i++) { | |
+ ret = tsens_register(tmdev, i); | |
+ | |
+ if (ret < 0) { | |
+ dev_err(&pdev->dev, | |
+ "Registering sensor(%i) failed - disabled", i); | |
+ tsens_tz_set_mode(&tmdev->sensor[i], | |
+ THERMAL_DEVICE_DISABLED); | |
+ } else { | |
+ tsens_tz_set_mode(&tmdev->sensor[i], | |
+ THERMAL_DEVICE_ENABLED); | |
+ } | |
+ } | |
+ | |
+ platform_set_drvdata(pdev, tmdev); | |
+ | |
+ return 0; | |
+fail: | |
+ dev_err(&pdev->dev, "Tsens driver init failed\n"); | |
+ tsens_disable_mode(tmdev); | |
+ return ret; | |
+} | |
+ | |
+static int tsens_remove(struct platform_device *pdev) | |
+{ | |
+ int i; | |
+ struct tsens_sensor *s; | |
+ struct tsens_device *tmdev = platform_get_drvdata(pdev); | |
+ | |
+ tsens_disable_mode(tmdev); | |
+ for (i = 0, s = tmdev->sensor; i < tmdev->num_sensors; i++, s++) | |
+ thermal_zone_device_unregister(s->tz_dev); | |
+ | |
+ return 0; | |
+} | |
+ | |
+static struct of_device_id tsens_match_table[] = { | |
+ {.compatible = "qcom,qcom-tsens"}, | |
+ {}, | |
+}; | |
+ | |
+MODULE_DEVICE_TABLE(of, tsens_match_table); | |
+ | |
+static struct platform_driver tsens_driver = { | |
+ .probe = tsens_probe, | |
+ .remove = tsens_remove, | |
+ .driver = { | |
+ .of_match_table = tsens_match_table, | |
+ .name = "qcom-tsens", | |
+ .pm = TSENS_PM_OPS, | |
+ }, | |
+}; | |
+module_platform_driver(tsens_driver); | |
+ | |
+MODULE_LICENSE("GPL v2"); | |
+MODULE_DESCRIPTION("QCOM Temperature Sensor driver"); | |
+MODULE_ALIAS("platform:qcom-tsens"); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--- a/drivers/thermal/qcom-tsens.c 2016-11-07 11:52:20.284374815 +0800 | |
+++ b/drivers/thermal/qcom-tsens.c 2016-11-07 11:56:52.964039385 +0800 | |
@@ -324,6 +324,12 @@ | |
return 0; | |
} | |
+static const struct thermal_zone_of_device_ops qcom_tsens_thermal_zone_ops = { | |
+ .get_temp = tsens_tz_get_temp, | |
+ .get_trend = NULL, | |
+ .set_emul_temp = NULL | |
+}; | |
+ | |
static int tsens_register(struct tsens_device *tmdev, int i) | |
{ | |
u32 addr = TSENS_S0_STATUS_ADDR; | |
@@ -345,9 +351,7 @@ | |
s->status = addr; | |
s->tmdev = tmdev; | |
s->tz_dev = thermal_zone_of_sensor_register(tmdev->dev, i, s, | |
- tsens_tz_get_temp, | |
- NULL, | |
- NULL); | |
+ &qcom_tsens_thermal_zone_ops); | |
if (IS_ERR(s->tz_dev)) | |
return -ENODEV; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
From 71c8cde9599b999610f1b329ad54c3dfcdaf01c8 Mon Sep 17 00:00:00 2001 | |
From: Srinivas Kandagatla <[email protected]> | |
Date: Fri, 30 Jan 2015 09:51:27 +0000 | |
Subject: [PATCH] FROMLIST: clk: qcom: gcc-msm8960: add child devices support. | |
This patch adds support to add child devices to gcc as some of the | |
registers mapped by gcc are used by drivers like thermal sensors. | |
(am from https://patchwork.kernel.org/patch/5748661/) | |
BUG=None | |
TEST=build/boot on storm succeded | |
Change-Id: Icd2ea2fe0cebde1304ff7c2354ec55e7eae9685f | |
Signed-off-by: Srinivas Kandagatla <[email protected]> | |
Signed-off-by: Narendran Rajan <[email protected]> | |
Reviewed-on: https://chromium-review.googlesource.com/246398 | |
Reviewed-by: Bryan Freed <[email protected]> | |
Commit-Queue: Grant Grundler <[email protected]> | |
--- | |
diff --git a/drivers/clk/qcom/gcc-ipq806x.c b/drivers/clk/qcom/gcc-ipq806x.c | |
index 0ffb883..c5778e0 100644 | |
--- a/drivers/clk/qcom/gcc-ipq806x.c | |
+++ b/drivers/clk/qcom/gcc-ipq806x.c | |
@@ -3181,7 +3181,7 @@ | |
regmap_write(regmap, 0x3cf8, 8); | |
regmap_write(regmap, 0x3d18, 8); | |
- return 0; | |
+ return of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); | |
} | |
static int gcc_ipq806x_remove(struct platform_device *pdev) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
From 342d8c2dc41c0a462db32b2bf5395b4242aa44f1 Mon Sep 17 00:00:00 2001 | |
From: Srinivas Kandagatla <[email protected]> | |
Date: Fri, 30 Jan 2015 09:51:37 +0000 | |
Subject: [PATCH] FROMLIST: clk: qcom: gcc bindings: Update to add child node support. | |
This patch adds note in the bindings about the optional child nodes | |
which are currently possible with SOCs like MSM8960, APQ8064. | |
These child device nodes are typically for drivers like thermal sensor | |
which fall under the memory-map of gcc. | |
(am from https://patchwork.kernel.org/patch/5748681/) | |
BUG=None | |
TEST=build/boot on storm succeded | |
Change-Id: I1bd8260ac7e58f77ab6dece7381c9ca4f2d8217a | |
Signed-off-by: Srinivas Kandagatla <[email protected]> | |
Reviewed-on: https://chromium-review.googlesource.com/246399 | |
Reviewed-by: Bryan Freed <[email protected]> | |
Tested-by: Narendran Rajan <[email protected]> | |
Commit-Queue: Grant Grundler <[email protected]> | |
--- | |
diff --git a/Documentation/devicetree/bindings/clock/qcom,gcc.txt b/Documentation/devicetree/bindings/clock/qcom,gcc.txt | |
index aba3d254..c79225b 100644 | |
--- a/Documentation/devicetree/bindings/clock/qcom,gcc.txt | |
+++ b/Documentation/devicetree/bindings/clock/qcom,gcc.txt | |
@@ -17,6 +17,11 @@ | |
- #clock-cells : shall contain 1 | |
- #reset-cells : shall contain 1 | |
+Optional child nodes: | |
+ Child nodes could be any device nodes with its own bindings which | |
+ fall inside the memory-map of the Clock controller, for example | |
+ thermal sensor driver. | |
+ | |
Example: | |
clock-controller@900000 { | |
compatible = "qcom,gcc-msm8960"; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
From 10092bc607db0c5bf14bf6eca1f235131af9de8e Mon Sep 17 00:00:00 2001 | |
From: Srinivas Kandagatla <[email protected]> | |
Date: Mon, 26 Jan 2015 10:13:13 +0000 | |
Subject: [PATCH] FROMLIST: mfd: syscon: Add register stride to DT bindings. | |
This patch adds register stride to dt bindings so that the consumers of | |
the syscon could change it to their need. One of the the use cases for | |
this feature is Qualcomm qfprom which needs a byte access to regmap | |
returned from syscon. | |
(from https://patchwork.kernel.org/patch/5707771/) | |
BUG=None | |
TEST=build/boot on storm succeded | |
Change-Id: I067040d33f54f4b08594e296b7d9dd8420eda942 | |
Signed-off-by: Srinivas Kandagatla <[email protected]> | |
Signed-off-by: Narendran Rajan <[email protected]> | |
Reviewed-on: https://chromium-review.googlesource.com/246450 | |
Tested-by: Narendran M R <[email protected]> | |
Reviewed-by: Bryan Freed <[email protected]> | |
Commit-Queue: Grant Grundler <[email protected]> | |
--- | |
diff --git a/Documentation/devicetree/bindings/mfd/syscon.txt b/Documentation/devicetree/bindings/mfd/syscon.txt | |
index fe8150b..7f06ec1 100644 | |
--- a/Documentation/devicetree/bindings/mfd/syscon.txt | |
+++ b/Documentation/devicetree/bindings/mfd/syscon.txt | |
@@ -13,6 +13,9 @@ | |
- compatible: Should contain "syscon". | |
- reg: the register region can be accessed from syscon | |
+Optional properties: | |
+- stride : register address stride in bytes. | |
+ | |
Examples: | |
gpr: iomuxc-gpr@020e0000 { | |
compatible = "fsl,imx6q-iomuxc-gpr", "syscon"; | |
diff --git a/drivers/mfd/syscon.c b/drivers/mfd/syscon.c | |
index 227f222..9222f22 100644 | |
--- a/drivers/mfd/syscon.c | |
+++ b/drivers/mfd/syscon.c | |
@@ -47,6 +47,7 @@ | |
struct regmap *regmap; | |
void __iomem *base; | |
int ret; | |
+ u32 stride; | |
struct regmap_config syscon_config = syscon_regmap_config; | |
if (!of_device_is_compatible(np, "syscon")) | |
@@ -68,6 +69,14 @@ | |
else if (of_property_read_bool(np, "little-endian")) | |
syscon_config.val_format_endian = REGMAP_ENDIAN_LITTLE; | |
+ if (!of_property_read_u32(np, "stride", &stride)) { | |
+ if (stride > 4) | |
+ stride = 4; | |
+ | |
+ syscon_config.reg_stride = stride; | |
+ syscon_config.val_bits = 8 * stride; | |
+ } | |
+ | |
regmap = regmap_init_mmio(NULL, base, &syscon_config); | |
if (IS_ERR(regmap)) { | |
pr_err("regmap init failed\n"); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
From 24741e5924ca2fd36a1bfdeef10da0265749d576 Mon Sep 17 00:00:00 2001 | |
From: Stephen Boyd <[email protected]> | |
Date: Fri, 31 Oct 2014 16:05:16 -0700 | |
Subject: [PATCH] CHROMIUM: ARM: dts: qcom: add IMEM syscon to IPQ8064 | |
The IPQ8064 has an SRAM block known as IMEM, which is holds various data | |
about the chip. In particular, it houses the location whereby the | |
kernel can access the chip-specific characteristics written during | |
manufacturing, which will be used by the cpufreq driver to determine | |
which OPP table to use. | |
BUG=chrome-os-partner:32317 | |
TEST=Build/boot on storm succeeded | |
Change-Id: I9d6c35f7ad26566b680d668f02e9a981c9546aaa | |
Signed-off-by: Stephen Boyd <[email protected]> | |
Reviewed-on: https://chromium-review.googlesource.com/231799 | |
Reviewed-by: Bryan Freed <[email protected]> | |
Commit-Queue: Viswanath Kraleti <[email protected]> | |
Tested-by: Viswanath Kraleti <[email protected]> | |
--- | |
diff --git a/arch/arm/boot/dts/qcom-ipq8064.dtsi b/arch/arm/boot/dts/qcom-ipq8064.dtsi | |
index e157e17..a9fb187 100644 | |
--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi | |
+++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi | |
@@ -135,6 +135,14 @@ | |
reg-names = "lpass-lpaif-mem"; | |
}; | |
+ imem: memory@700000 { | |
+ compatible = "qcom,imem-ipq8064", "syscon"; | |
+ reg = <0x00700000 0x1000>; | |
+ #address-cells = <1>; | |
+ #size-cells = <1>; | |
+ ranges = <0x0 0x00700000 0x1000>; | |
+ }; | |
+ | |
qcom_pinmux: pinmux@800000 { | |
compatible = "qcom,ipq8064-pinctrl"; | |
reg = <0x800000 0x4000>; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
From a167628a883936268bf58f2cd2a8aa8805898da4 Mon Sep 17 00:00:00 2001 | |
From: Srinivas Kandagatla <[email protected]> | |
Date: Mon, 26 Jan 2015 10:13:28 +0000 | |
Subject: [PATCH] FROMLIST: Add wrappers for qfprom access via syscon | |
Syscon fits very well to access qfprom. This also means drivers which needs to | |
access qfprom have to talk to syscon and get regmap, offset, size and then do | |
regmap reads. This will be redone in every driver. Having a wrapper for | |
this would avoid code duplication and also provide higher level and | |
user friendly api for qfprom access. | |
This patch attempts to provide such a wrapper. | |
Advantages of this approch are: | |
- driver need not hardcode qfprom offsets or have soc specific | |
compatible strings to determine the offset. | |
- access multiple qfprom resources which is tricky with standard | |
syscon. | |
- no code duplication. | |
- light weight, single call. | |
- not a platform device driver level binding. | |
(from https://patchwork.kernel.org/patch/5707781/) | |
BUG=None | |
TEST=build/boot on storm succeded | |
Change-Id: I3020d17abaf20a6e2f34976910c379a040676a49 | |
Signed-off-by: Srinivas Kandagatla <[email protected]> | |
Signed-off-by: Narendran Rajan <[email protected]> | |
Reviewed-on: https://chromium-review.googlesource.com/246451 | |
Reviewed-by: Bryan Freed <[email protected]> | |
Tested-by: Narendran M R <[email protected]> | |
Commit-Queue: Grant Grundler <[email protected]> | |
--- | |
diff --git a/Documentation/devicetree/bindings/soc/qcom/qfprom.txt b/Documentation/devicetree/bindings/soc/qcom/qfprom.txt | |
new file mode 100644 | |
index 0000000..6eac222 | |
--- /dev/null | |
+++ b/Documentation/devicetree/bindings/soc/qcom/qfprom.txt | |
@@ -0,0 +1,28 @@ | |
+QCOM QFPROM | |
+ | |
+QFPROM is basically some efuses where things like calibration data, speed bins, | |
+etc are stored. This data is accessed by various drivers like the cpufreq, | |
+thermal, etc. | |
+ | |
+Required properties: | |
+- compatible: must contain "qcom,qfprom" followed by "syscon" | |
+- reg: Address range for QFPROM | |
+- stride : register address stride. | |
+ 1 for byte. | |
+ 2 for 2 bytes | |
+ 3 for 3 bytes | |
+ 4 for a word. | |
+ | |
+Example: | |
+ qfprom: qfprom@00700000 { | |
+ compatible = "qcom,qfprom", "syscon"; | |
+ reg = <0x00700000 0x1000>; | |
+ stride = <1>; | |
+ }; | |
+ | |
+ tsens@34000 { | |
+ compatible = "qcom,tsens-apq8064"; | |
+ reg = <0x34000 0x1000>; | |
+ qcom,qfprom = <&qfprom 0x18 0x10>, <&qfprom 0x28 0x10>; | |
+ qcom,qfprom-names = "calib", "backup_calib"; | |
+ }; | |
diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig | |
index 3e4486a..f4bd645 100644 | |
--- a/drivers/soc/qcom/Kconfig | |
+++ b/drivers/soc/qcom/Kconfig | |
@@ -15,3 +15,10 @@ | |
help | |
Say y here to enable TCSR support. The TCSR provides control | |
functions for various peripherals. | |
+ | |
+config QCOM_QFPROM | |
+ tristate "QCOM QFPROM Interface" | |
+ depends on ARCH_QCOM && OF | |
+ help | |
+ Say y here to enable QFPROM support. The QFPROM provides access | |
+ functions for QFPROM data to rest of the drivers via syscon wrappers. | |
diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile | |
index d299492..7c87bd0 100644 | |
--- a/drivers/soc/qcom/Makefile | |
+++ b/drivers/soc/qcom/Makefile | |
@@ -1,2 +1,4 @@ | |
obj-$(CONFIG_QCOM_GSBI) += qcom_gsbi.o | |
obj-$(CONFIG_QCOM_TCSR) += qcom_tcsr.o | |
+obj-$(CONFIG_QCOM_QFPROM) += qfprom.o | |
+ | |
diff --git a/drivers/soc/qcom/qfprom.c b/drivers/soc/qcom/qfprom.c | |
new file mode 100644 | |
index 0000000..fec56b3 | |
--- /dev/null | |
+++ b/drivers/soc/qcom/qfprom.c | |
@@ -0,0 +1,133 @@ | |
+#include <linux/err.h> | |
+#include <linux/of.h> | |
+#include <linux/device.h> | |
+#include <linux/regmap.h> | |
+#include <linux/mfd/syscon.h> | |
+#include <linux/slab.h> | |
+ | |
+#define QFPROM_MAX_ARGS 2 | |
+ | |
+static char *__qfprom_get_data(struct device *dev, | |
+ bool devm, int idx, int *len) | |
+{ | |
+ struct device_node *syscon_np, *np = dev->of_node; | |
+ struct regmap *rm; | |
+ struct of_phandle_args args; | |
+ int rc, stride = 4; | |
+ u32 offset, size; | |
+ char *data; | |
+ | |
+ if (!np) | |
+ return ERR_PTR(-EINVAL); | |
+ | |
+ rc = of_parse_phandle_with_fixed_args(np, "qcom,qfprom", | |
+ QFPROM_MAX_ARGS, idx, &args); | |
+ if (rc) | |
+ return ERR_PTR(rc); | |
+ | |
+ syscon_np = args.np; | |
+ | |
+ of_property_read_u32(syscon_np, "stride", &stride); | |
+ | |
+ if (stride > 4) | |
+ return ERR_PTR(-EINVAL); | |
+ | |
+ if (args.args_count < QFPROM_MAX_ARGS) { | |
+ dev_err(dev, "Insufficient qfprom arguments %d\n", | |
+ args.args_count); | |
+ return ERR_PTR(-EINVAL); | |
+ } | |
+ | |
+ rm = syscon_node_to_regmap(syscon_np); | |
+ if (IS_ERR(rm)) | |
+ return ERR_CAST(rm); | |
+ | |
+ offset = args.args[0]; | |
+ size = args.args[1]; | |
+ | |
+ of_node_put(syscon_np); | |
+ | |
+ if (devm) | |
+ data = devm_kzalloc(dev, size, GFP_KERNEL | GFP_ATOMIC); | |
+ else | |
+ data = kzalloc(size, GFP_KERNEL | GFP_ATOMIC); | |
+ | |
+ if (!data) | |
+ return ERR_PTR(-ENOMEM); | |
+ | |
+ rc = regmap_bulk_read(rm, offset, data, size/stride); | |
+ if (rc < 0) { | |
+ if (devm) | |
+ devm_kfree(dev, data); | |
+ else | |
+ kfree(data); | |
+ | |
+ return ERR_PTR(rc); | |
+ } | |
+ | |
+ *len = size; | |
+ | |
+ return data; | |
+} | |
+ | |
+static char *__qfprom_get_data_byname(struct device *dev, | |
+ bool devm, const char *name, int *len) | |
+{ | |
+ int index = 0; | |
+ | |
+ if (name) | |
+ index = of_property_match_string(dev->of_node, | |
+ "qcom,qfprom-names", name); | |
+ | |
+ return __qfprom_get_data(dev, devm, index, len); | |
+} | |
+ | |
+char *devm_qfprom_get_data_byname(struct device *dev, | |
+ const char *name, int *len) | |
+{ | |
+ return __qfprom_get_data_byname(dev, true, name, len); | |
+} | |
+EXPORT_SYMBOL_GPL(devm_qfprom_get_data_byname); | |
+ | |
+char *devm_qfprom_get_data(struct device *dev, | |
+ int index, int *len) | |
+{ | |
+ return __qfprom_get_data(dev, true, index, len); | |
+} | |
+EXPORT_SYMBOL_GPL(devm_qfprom_get_data); | |
+ | |
+/** | |
+ * qfprom_get_data_byname(): Reads qfprom data by name | |
+ * | |
+ * @dev: device which is requesting qfprom data | |
+ * @index: name of qfprom resources specified "qcom,qfprom-names" DT property. | |
+ * @len: length of data read from qfprom. | |
+ * | |
+ * The return value will be an ERR_PTR() on error or a valid pointer | |
+ * to a data buffer. The buffer should be freed by the user once its | |
+ * finished working with it kfree. | |
+ **/ | |
+char *qfprom_get_data_byname(struct device *dev, | |
+ const char *name, int *len) | |
+{ | |
+ return __qfprom_get_data_byname(dev, false, name, len); | |
+} | |
+EXPORT_SYMBOL_GPL(qfprom_get_data_byname); | |
+ | |
+/** | |
+ * qfprom_get_data(): Reads qfprom data from the index | |
+ * | |
+ * @dev: device which is requesting qfprom data | |
+ * @index: index into qfprom resources specified "qcom,qfprom" DT property. | |
+ * @len: length of data read from qfprom. | |
+ * | |
+ * The return value will be an ERR_PTR() on error or a valid pointer | |
+ * to a data buffer. The buffer should be freed by the user once its | |
+ * finished working with it kfree. | |
+ **/ | |
+char *qfprom_get_data(struct device *dev, | |
+ int index, int *len) | |
+{ | |
+ return __qfprom_get_data(dev, false, index, len); | |
+} | |
+EXPORT_SYMBOL_GPL(qfprom_get_data); | |
diff --git a/include/soc/qcom/qfprom.h b/include/soc/qcom/qfprom.h | |
new file mode 100644 | |
index 0000000..82ae6ea | |
--- /dev/null | |
+++ b/include/soc/qcom/qfprom.h | |
@@ -0,0 +1,52 @@ | |
+ | |
+/* 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. | |
+ * | |
+ */ | |
+ | |
+#ifndef __LINUX_SOC_QFPROM_H__ | |
+#define __LINUX_SOC_QFPROM_H__ | |
+ | |
+struct device; | |
+ | |
+#ifdef CONFIG_QCOM_QFPROM | |
+extern char *devm_qfprom_get_data_byname(struct device *dev, | |
+ const char *name, int *len); | |
+extern char *devm_qfprom_get_data(struct device *dev, int index, int *len); | |
+extern char *qfprom_get_data_byname(struct device *dev, | |
+ const char *name, int *len); | |
+#else | |
+static inline char *devm_qfprom_get_data_byname(struct device *dev, | |
+ const char *name, int *len) | |
+{ | |
+ return NULL; | |
+} | |
+ | |
+static inline char *devm_qfprom_get_data(struct device *dev, | |
+ int index, int *len) | |
+{ | |
+ return NULL; | |
+} | |
+ | |
+static inline char *qfprom_get_data_byname(struct device *dev, | |
+ const char *name, int *len) | |
+{ | |
+ return NULL; | |
+} | |
+ | |
+static inline char *qfprom_get_data(struct device *dev, | |
+ int index, int *len) | |
+{ | |
+ return NULL; | |
+} | |
+#endif | |
+ | |
+#endif /* __LINUX_SOC_QFPROM_H__ */ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
From 3486263f736ed6a51b1e295c5856013f3d7d959a Mon Sep 17 00:00:00 2001 | |
From: Narendran Rajan <[email protected]> | |
Date: Wed, 04 Feb 2015 21:54:23 -0800 | |
Subject: [PATCH] CHROMIUM: ARM: qcom: dts: Add qfprom compatibility | |
qfprom library provides convenience API to access imem regions. | |
Add compatibility to qfprom in imem node and also add stride index | |
to enable byte read | |
BUG=None | |
TEST=build/boot on storm | |
Change-Id: I18daddf4cf9463f82a76e878a9e4f01001dbd4b7 | |
Signed-off-by: Narendran Rajan <[email protected]> | |
Reviewed-on: https://chromium-review.googlesource.com/246453 | |
Tested-by: Narendran M R <[email protected]> | |
Reviewed-by: Bryan Freed <[email protected]> | |
Commit-Queue: Grant Grundler <[email protected]> | |
--- | |
diff --git a/arch/arm/boot/dts/qcom-ipq8064.dtsi b/arch/arm/boot/dts/qcom-ipq8064.dtsi | |
index c04a8f5..d366175 100644 | |
--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi | |
+++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi | |
@@ -198,10 +198,11 @@ | |
}; | |
imem: memory@700000 { | |
- compatible = "qcom,imem-ipq8064", "syscon"; | |
+ compatible = "qcom,qfprom", "syscon"; | |
reg = <0x00700000 0x1000>; | |
#address-cells = <1>; | |
#size-cells = <1>; | |
+ stride = <1>; | |
ranges = <0x0 0x00700000 0x1000>; | |
}; | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment