Skip to content

Instantly share code, notes, and snippets.

@updateing
Last active December 12, 2016 04:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save updateing/a3101c9d99463445b6fc2d41f63e21cd to your computer and use it in GitHub Desktop.
Save updateing/a3101c9d99463445b6fc2d41f63e21cd to your computer and use it in GitHub Desktop.
Enable thermal sensors on Linksys EA8500 with LEDE
From 4d6dd770cea0336c276e43c6900af6edb1e9e981 Mon Sep 17 00:00:00 2001
From: Narendran Rajan <nrajan@codeaurora.org>
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 <nrajan@codeaurora.org>
Reviewed-on: https://chromium-review.googlesource.com/243449
Reviewed-by: Bryan Freed <bfreed@chromium.org>
Tested-by: Narendran M R <narendranmr@gmail.com>
Commit-Queue: Grant Grundler <grundler@chromium.org>
(cherry picked from commit 8ad83e875a8913399772339a029029e8f98c3f9f)
Reviewed-on: https://chromium-review.googlesource.com/257850
Reviewed-by: Patrick Sosinski <sosinski@google.com>
Commit-Queue: Patrick Sosinski <sosinski@google.com>
Tested-by: Patrick Sosinski <sosinski@google.com>
---
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 {
From 61a8bc938c1d86c8783b7c2401bc35305ad81537 Mon Sep 17 00:00:00 2001
From: Hamster Tian <haotia@gmail.com>
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
--- 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 {
From 57b4f0ff5624360a5abe894daa9cf7c9fe4ef928 Mon Sep 17 00:00:00 2001
From: Narendran Rajan <nrajan@codeaurora.org>
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 <smohanad@codeaurora.org>
Cc: Stephen Boyd <sboyd@codeaurora.org>
Change-Id: I12090471d6d82283a04fecbcf1f0cb0f05d08f1e
Signed-off-by: Narendran Rajan <nrajan@codeaurora.org>
Reviewed-on: https://chromium-review.googlesource.com/243448
Tested-by: Narendran M R <narendranmr@gmail.com>
Reviewed-by: Bryan Freed <bfreed@chromium.org>
---
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, &reg);
+
+ 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, &reg_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, &reg_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");
--- 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;
From 71c8cde9599b999610f1b329ad54c3dfcdaf01c8 Mon Sep 17 00:00:00 2001
From: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
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 <srinivas.kandagatla@linaro.org>
Signed-off-by: Narendran Rajan <nrajan@codeaurora.org>
Reviewed-on: https://chromium-review.googlesource.com/246398
Reviewed-by: Bryan Freed <bfreed@chromium.org>
Commit-Queue: Grant Grundler <grundler@chromium.org>
---
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)
From 342d8c2dc41c0a462db32b2bf5395b4242aa44f1 Mon Sep 17 00:00:00 2001
From: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
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 <srinivas.kandagatla@linaro.org>
Reviewed-on: https://chromium-review.googlesource.com/246399
Reviewed-by: Bryan Freed <bfreed@chromium.org>
Tested-by: Narendran Rajan <nrajan@codeaurora.org>
Commit-Queue: Grant Grundler <grundler@chromium.org>
---
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";
From 10092bc607db0c5bf14bf6eca1f235131af9de8e Mon Sep 17 00:00:00 2001
From: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
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 <srinivas.kandagatla@linaro.org>
Signed-off-by: Narendran Rajan <nrajan@codeaurora.org>
Reviewed-on: https://chromium-review.googlesource.com/246450
Tested-by: Narendran M R <narendranmr@gmail.com>
Reviewed-by: Bryan Freed <bfreed@chromium.org>
Commit-Queue: Grant Grundler <grundler@chromium.org>
---
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");
From 24741e5924ca2fd36a1bfdeef10da0265749d576 Mon Sep 17 00:00:00 2001
From: Stephen Boyd <sboyd@codeaurora.org>
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 <sboyd@codeaurora.org>
Reviewed-on: https://chromium-review.googlesource.com/231799
Reviewed-by: Bryan Freed <bfreed@chromium.org>
Commit-Queue: Viswanath Kraleti <vkraleti@codeaurora.org>
Tested-by: Viswanath Kraleti <vkraleti@codeaurora.org>
---
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>;
From a167628a883936268bf58f2cd2a8aa8805898da4 Mon Sep 17 00:00:00 2001
From: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
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 <srinivas.kandagatla@linaro.org>
Signed-off-by: Narendran Rajan <nrajan@codeaurora.org>
Reviewed-on: https://chromium-review.googlesource.com/246451
Reviewed-by: Bryan Freed <bfreed@chromium.org>
Tested-by: Narendran M R <narendranmr@gmail.com>
Commit-Queue: Grant Grundler <grundler@chromium.org>
---
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__ */
From 3486263f736ed6a51b1e295c5856013f3d7d959a Mon Sep 17 00:00:00 2001
From: Narendran Rajan <nrajan@codeaurora.org>
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 <nrajan@codeaurora.org>
Reviewed-on: https://chromium-review.googlesource.com/246453
Tested-by: Narendran M R <narendranmr@gmail.com>
Reviewed-by: Bryan Freed <bfreed@chromium.org>
Commit-Queue: Grant Grundler <grundler@chromium.org>
---
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