[RFC 1/7] thermal: qcom: tsens: Add a skeletal tsens drivers
Lina Iyer
lina.iyer at linaro.org
Fri Jun 12 09:03:20 PDT 2015
On Thu, Apr 23 2015 at 07:51 -0600, Rajendra Nayak wrote:
>tsens is qualcomms' thermal temperature sensor device. It
/s/tsens/TSENS /s/qualcomm/Qualcomm
>supports reading temperatures from multiple thermal sensors
>present on various QCOM SoCs.
>Calibration data is generally read from a 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 descibe the tsens device in DT.
>
>Signed-off-by: Rajendra Nayak <rnayak at codeaurora.org>
Minor nits. Otherwise,
Reviewed-by: Lina Iyer <lina.iyer at linaro.org>
>---
> .../devicetree/bindings/thermal/qcom-tsens.txt | 36 ++++
> drivers/thermal/Kconfig | 5 +
> drivers/thermal/Makefile | 1 +
> drivers/thermal/qcom/Kconfig | 10 +
> drivers/thermal/qcom/Makefile | 2 +
> drivers/thermal/qcom/tsens.c | 206 +++++++++++++++++++++
> drivers/thermal/qcom/tsens.h | 58 ++++++
> 7 files changed, 318 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.c
> create mode 100644 drivers/thermal/qcom/tsens.h
>
>diff --git a/Documentation/devicetree/bindings/thermal/qcom-tsens.txt b/Documentation/devicetree/bindings/thermal/qcom-tsens.txt
>new file mode 100644
>index 0000000..90ec469
>--- /dev/null
>+++ b/Documentation/devicetree/bindings/thermal/qcom-tsens.txt
>@@ -0,0 +1,36 @@
>+* 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,msm8960-tsens" : For 8960 Family of SoCs
>+
>+- reg: Address range of the thermal registers
>+- qcom,tsens-slopes : Must contain slope value for each of the sensors controlled
>+ by this device
>+- #thermal-sensor-cells : Should be 1. See ./thermal.txt for a description.
>+- Refer to Documentation/devicetree/bindings/eeprom/eeprom.txt to know how to specify
>+an eeprom cell
>+
>+Optional properties:
>+- qcom,sensor-id: List of sensor instances used in a given SoC. A TSENS IP can
>+ have a fixed number of sensors (like 11) but a given SoC can
>+ use only 5 of these and they might not always the first 5. They
>+ could be sensors 0, 1, 4, 8 and 9. This property is used to
>+ describe the subset of the sensors used. If this property is
>+ missing they are assumed to be the first 'n'
>+ sensors numbered sequentially.
May want to explicitly call out that the numsensor defaults to number of
slope values.
>+
>+Example:
>+tsens: thermal-sesnor at 900000 {
/s/sesnor/sensor
>+ compatible = "qcom,msm8916-tsens";
>+ qcom,tsens-slopes = <1176 1176 1154 1176 1111
>+ 1132 1132 1199 1132 1199
>+ 1132>;
>+ calib_data = <&tsens_caldata>;
>+ calib_sel = <&tsens_calsel>;
>+ qcom,tsens-slopes = <3200 3200 3200 3200 3200>;
>+ qcom,sensor-id = <0 1 2 4 5>;
>+ #thermal-sensor-cells = <1>;
>+ };
>diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
>index af40db0..46b63ff 100644
>--- a/drivers/thermal/Kconfig
>+++ b/drivers/thermal/Kconfig
>@@ -299,4 +299,9 @@ depends on ARCH_STI && OF
> source "drivers/thermal/st/Kconfig"
> endmenu
>
>+menu "Qualcomm thermal drivers"
>+depends on ARCH_QCOM && OF
>+source "drivers/thermal/qcom/Kconfig"
>+endmenu
>+
> endif
>diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
>index fa0dc48..df83890 100644
>--- a/drivers/thermal/Makefile
>+++ b/drivers/thermal/Makefile
>@@ -38,4 +38,5 @@ obj-$(CONFIG_INTEL_SOC_DTS_THERMAL) += intel_soc_dts_thermal.o
> obj-$(CONFIG_TI_SOC_THERMAL) += ti-soc-thermal/
> obj-$(CONFIG_INT340X_THERMAL) += int340x_thermal/
> obj-$(CONFIG_ST_THERMAL) += st/
>+obj-$(CONFIG_QCOM_TSENS) += qcom/
> obj-$(CONFIG_TEGRA_SOCTHERM) += tegra_soctherm.o
>diff --git a/drivers/thermal/qcom/Kconfig b/drivers/thermal/qcom/Kconfig
>new file mode 100644
>index 0000000..ade0a03
>--- /dev/null
>+++ b/drivers/thermal/qcom/Kconfig
>@@ -0,0 +1,10 @@
>+config QCOM_TSENS
>+ tristate "Qualcomm Tsens Temperature Alarm"
TSENS is the correct usage.
>+ depends on THERMAL
>+ depends on QCOM_QFPROM
>+ 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.
>diff --git a/drivers/thermal/qcom/Makefile b/drivers/thermal/qcom/Makefile
>new file mode 100644
>index 0000000..401069b
>--- /dev/null
>+++ b/drivers/thermal/qcom/Makefile
>@@ -0,0 +1,2 @@
>+obj-$(CONFIG_QCOM_TSENS) += qcom_tsens.o
>+qcom_tsens-y += tsens.o
>diff --git a/drivers/thermal/qcom/tsens.c b/drivers/thermal/qcom/tsens.c
>new file mode 100644
>index 0000000..fa4a73a
>--- /dev/null
>+++ b/drivers/thermal/qcom/tsens.c
>@@ -0,0 +1,206 @@
>+/*
>+ * 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, long *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 -ENOSYS;
>+}
>+
>+#ifdef CONFIG_PM
>+static int tsens_suspend(struct device *dev)
>+{
>+ struct tsens_device *tmdev = dev_get_drvdata(dev);
>+
>+ if (tmdev->ops->suspend)
You seem to have checked for tmdev->ops in resume, why not here?
>+ 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);
>+#define TSENS_PM_OPS (&tsens_pm_ops)
>+
>+#else /* CONFIG_PM_SLEEP */
>+#define TSENS_PM_OPS NULL
>+#endif /* CONFIG_PM_SLEEP */
>+
>+static const struct of_device_id tsens_table[] = {
>+ {
>+ .compatible = "qcom,msm8960-tsens",
>+ }, {
>+ .compatible = "qcom,msm8916-tsens",
>+ }, {
>+ .compatible = "qcom,msm8974-tsens",
>+ },
{ .compatible = "..." },
Makes for an easier read, unless, you have ops that have long names.
>+ {}
>+};
>+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, ret;
>+ struct thermal_zone_device *tzd;
>+ u32 *hw_id, n = tmdev->num_sensors;
>+ struct device_node *np = tmdev->dev->of_node;
>+
>+ hw_id = devm_kcalloc(tmdev->dev, n, sizeof(u32), GFP_KERNEL);
>+ if (!hw_id)
>+ return -ENOMEM;
>+
>+ ret = of_property_read_u32_array(np, "qcom,sensor-id", hw_id, n);
>+ if (ret)
>+ for (i = 0; i < tmdev->num_sensors; i++)
>+ tmdev->sensor[i].hw_id = i;
>+ else
>+ for (i = 0; i < tmdev->num_sensors; i++)
>+ tmdev->sensor[i].hw_id = hw_id[i];
>+
>+ for (i = 0; i < tmdev->num_sensors; i++) {
>+ tmdev->sensor[i].tmdev = tmdev;
>+ tmdev->sensor[i].id = i;
>+ tzd = 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, num;
>+ struct device_node *np = pdev->dev.of_node;
>+ struct tsens_sensor *s;
>+ struct tsens_device *tmdev;
>+ const struct of_device_id *id;
>+
>+ 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(*s), GFP_KERNEL);
>+ if (!tmdev)
>+ return -ENOMEM;
>+
>+ tmdev->dev = &pdev->dev;
>+ tmdev->num_sensors = num;
>+
>+ for (i = 0, s = tmdev->sensor; i < tmdev->num_sensors; i++, s++)
>+ of_property_read_u32_index(np, "qcom,tsens-slopes", i,
>+ &s->slope);
>+
>+ id = of_match_node(tsens_table, np);
>+ if (!id)
>+ return -ENODEV;
>+
>+ tmdev->ops = id->data;
>+ if (!tmdev->ops || !tmdev->ops->init || !tmdev->ops->calibrate ||
>+ !tmdev->ops->get_temp)
>+ return -EINVAL;
>+
>+ ret = tmdev->ops->init(tmdev);
>+ if (ret < 0) {
>+ dev_err(&pdev->dev, "tsens init failed\n");
>+ return ret;
>+ }
>+
>+ ret = tmdev->ops->calibrate(tmdev);
>+ if (ret < 0) {
>+ dev_err(&pdev->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)
>+{
>+ int i;
>+ struct tsens_device *tmdev = platform_get_drvdata(pdev);
>+ struct thermal_zone_device *tzd;
>+
>+ if (tmdev->ops->disable)
>+ tmdev->ops->disable(tmdev);
>+
>+ for (i = 0; i < tmdev->num_sensors; i++) {
>+ tzd = tmdev->sensor[i].tzd;
>+ thermal_zone_of_sensor_unregister(&pdev->dev, tzd);
>+ }
>+
>+ 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");
>diff --git a/drivers/thermal/qcom/tsens.h b/drivers/thermal/qcom/tsens.h
>new file mode 100644
>index 0000000..6e3c365
>--- /dev/null
>+++ b/drivers/thermal/qcom/tsens.h
>@@ -0,0 +1,58 @@
>+/*
>+ * 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__
>+
>+struct tsens_device;
>+
>+struct tsens_sensor {
>+ struct tsens_device *tmdev;
>+ struct thermal_zone_device *tzd;
>+ int offset;
>+ int id;
>+ int hw_id;
>+ u32 slope;
>+ u32 status;
>+};
>+
>+struct tsens_ops {
>+ /* mandatory callbacks */
>+ int (*init)(struct tsens_device *);
>+ int (*calibrate)(struct tsens_device *);
>+ int (*get_temp)(struct tsens_device *, int, long *);
>+ /* 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 *);
>+};
>+
>+/* 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 c;
Could have had a better variable name - ctx, perhaps?
>+ bool trdy;
>+ const struct tsens_ops *ops;
>+ struct tsens_sensor sensor[0];
>+};
>+
>+#endif /* __QCOM_TSENS_H__ */
>--
>QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member
>of Code Aurora Forum, hosted by The Linux Foundation
>
More information about the linux-arm-kernel
mailing list