* [RFC PATCH 0/6 V2] OMAP4: Temperature sensor driver
@ 2011-08-18 10:52 Keerthy
2011-08-18 10:52 ` [RFC PATCH 1/6 V2] OMAP4: Clock: Associate clocks for OMAP temperature sensor Keerthy
` (5 more replies)
0 siblings, 6 replies; 35+ messages in thread
From: Keerthy @ 2011-08-18 10:52 UTC (permalink / raw)
To: linux-omap; +Cc: Keerthy
Hello,
The rfc patch series for the on die temperature sensor driver. I need
feedback on the overall structure of the driver.
The rfc patch set has the device file, omap4 on die temperature sensor
hwmon driver. hwmod, clk support. The patch set compiles
on top of LO tree Master branch.
This patch series is tested for boot-up on OMAP4460. The temperature
reading and the interrupts generation on crossing the temperature
thresholds also tested.
V2:
(1) Fixed comments on return values in the driver.
(2) Moved the TEMPSOFF setting code to the activate/deactivate hooks.
(3) Used idr to pass for the device id.
Benoit Cousson (1):
OMAP4: Hwmod: OMAP temperature sensor
Keerthy (5):
OMAP4: Clock: Associate clocks for OMAP temperature sensor
OMAP4: Adding the temperature sensor register set bit fields
OMAP4460: Temperature sensor data
OMAP4: Temperature sensor device support
hwmon: OMAP4: On die temperature sensor driver
Documentation/hwmon/omap_temp_sensor | 27 +
arch/arm/mach-omap2/Makefile | 3 +-
arch/arm/mach-omap2/clock44xx_data.c | 2 +-
.../include/mach/ctrl_module_core_44xx.h | 70 ++-
arch/arm/mach-omap2/omap_hwmod_44xx_data.c | 61 ++
arch/arm/mach-omap2/temp_sensor4460_data.c | 63 ++
arch/arm/mach-omap2/temp_sensor_device.c | 178 ++++
arch/arm/plat-omap/Kconfig | 12 +
.../plat-omap/include/plat/temperature_sensor.h | 89 ++
drivers/hwmon/Kconfig | 11 +
drivers/hwmon/Makefile | 1 +
drivers/hwmon/omap_temp_sensor.c | 918 ++++++++++++++++++++
12 files changed, 1420 insertions(+), 15 deletions(-)
create mode 100644 Documentation/hwmon/omap_temp_sensor
create mode 100644 arch/arm/mach-omap2/temp_sensor4460_data.c
create mode 100644 arch/arm/mach-omap2/temp_sensor_device.c
create mode 100644 arch/arm/plat-omap/include/plat/temperature_sensor.h
create mode 100644 drivers/hwmon/omap_temp_sensor.c
^ permalink raw reply [flat|nested] 35+ messages in thread
* [RFC PATCH 1/6 V2] OMAP4: Clock: Associate clocks for OMAP temperature sensor
2011-08-18 10:52 [RFC PATCH 0/6 V2] OMAP4: Temperature sensor driver Keerthy
@ 2011-08-18 10:52 ` Keerthy
2011-08-29 21:47 ` Kevin Hilman
2011-08-18 10:52 ` [RFC PATCH 2/6 V2] OMAP4: Adding the temperature sensor register set bit fields Keerthy
` (4 subsequent siblings)
5 siblings, 1 reply; 35+ messages in thread
From: Keerthy @ 2011-08-18 10:52 UTC (permalink / raw)
To: linux-omap; +Cc: Keerthy, tony, rnayak
div_ts_ck feeds only the temperature sensor functional clock
and also has a clksel associated (for divider selection). Mapping this
as the functional clock for the temperature sensor in clkdev table,
so a clk_set_rate() in the driver would have the effect of changing the
temperature sensor clock rate indirectly.
Reviewd-by: Rajendra Nayak <rnayak@ti.com>
Signed-off-by: Keerthy <j-keerthy@ti.com>
Cc: tony@atomide.com
Cc: rnayak@ti.com
---
arch/arm/mach-omap2/clock44xx_data.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/arch/arm/mach-omap2/clock44xx_data.c b/arch/arm/mach-omap2/clock44xx_data.c
index 2af0e3f..4a788f4 100644
--- a/arch/arm/mach-omap2/clock44xx_data.c
+++ b/arch/arm/mach-omap2/clock44xx_data.c
@@ -3187,7 +3187,7 @@ static struct omap_clk omap44xx_clks[] = {
CLK(NULL, "bandgap_fclk", &bandgap_fclk, CK_443X),
CLK(NULL, "bandgap_ts_fclk", &bandgap_ts_fclk, CK_446X),
CLK(NULL, "des3des_fck", &des3des_fck, CK_443X),
- CLK(NULL, "div_ts_ck", &div_ts_ck, CK_446X),
+ CLK("omap_temp_sensor.0", "fck", &div_ts_ck, CK_446X),
CLK(NULL, "dmic_sync_mux_ck", &dmic_sync_mux_ck, CK_443X),
CLK(NULL, "dmic_fck", &dmic_fck, CK_443X),
CLK(NULL, "dsp_fck", &dsp_fck, CK_443X),
--
1.7.0.4
^ permalink raw reply related [flat|nested] 35+ messages in thread
* [RFC PATCH 2/6 V2] OMAP4: Adding the temperature sensor register set bit fields
2011-08-18 10:52 [RFC PATCH 0/6 V2] OMAP4: Temperature sensor driver Keerthy
2011-08-18 10:52 ` [RFC PATCH 1/6 V2] OMAP4: Clock: Associate clocks for OMAP temperature sensor Keerthy
@ 2011-08-18 10:52 ` Keerthy
2011-08-29 21:52 ` Kevin Hilman
2011-08-18 10:52 ` [RFC PATCH 3/6 V2] OMAP4460: Temperature sensor data Keerthy
` (3 subsequent siblings)
5 siblings, 1 reply; 35+ messages in thread
From: Keerthy @ 2011-08-18 10:52 UTC (permalink / raw)
To: linux-omap; +Cc: Keerthy, tony
OMAP4460 specific temperature sensor register bit fields are added.
Signed-off-by: Keerthy <j-keerthy@ti.com>
Cc: tony@atomide.com
---
.../include/mach/ctrl_module_core_44xx.h | 70 ++++++++++++++++----
1 files changed, 57 insertions(+), 13 deletions(-)
diff --git a/arch/arm/mach-omap2/include/mach/ctrl_module_core_44xx.h b/arch/arm/mach-omap2/include/mach/ctrl_module_core_44xx.h
index 2f7ac70..725c1e1 100644
--- a/arch/arm/mach-omap2/include/mach/ctrl_module_core_44xx.h
+++ b/arch/arm/mach-omap2/include/mach/ctrl_module_core_44xx.h
@@ -256,19 +256,63 @@
#define OMAP4_LDOSRAMCORE_ACTMODE_VSET_OUT_SHIFT 0
#define OMAP4_LDOSRAMCORE_ACTMODE_VSET_OUT_MASK (0x1f << 0)
-/* TEMP_SENSOR */
-#define OMAP4_BGAP_TEMPSOFF_SHIFT 12
-#define OMAP4_BGAP_TEMPSOFF_MASK (1 << 12)
-#define OMAP4_BGAP_TSHUT_SHIFT 11
-#define OMAP4_BGAP_TSHUT_MASK (1 << 11)
-#define OMAP4_BGAP_TEMP_SENSOR_CONTCONV_SHIFT 10
-#define OMAP4_BGAP_TEMP_SENSOR_CONTCONV_MASK (1 << 10)
-#define OMAP4_BGAP_TEMP_SENSOR_SOC_SHIFT 9
-#define OMAP4_BGAP_TEMP_SENSOR_SOC_MASK (1 << 9)
-#define OMAP4_BGAP_TEMP_SENSOR_EOCZ_SHIFT 8
-#define OMAP4_BGAP_TEMP_SENSOR_EOCZ_MASK (1 << 8)
-#define OMAP4_BGAP_TEMP_SENSOR_DTEMP_SHIFT 0
-#define OMAP4_BGAP_TEMP_SENSOR_DTEMP_MASK (0xff << 0)
+/* TEMP_SENSOR OMAP4430 */
+#define OMAP4430_BGAP_TEMPSOFF_SHIFT 12
+#define OMAP4430_BGAP_TEMPSOFF_MASK (1 << 12)
+#define OMAP4430_BGAP_TSHUT_SHIFT 11
+#define OMAP4430_BGAP_TSHUT_MASK (1 << 11)
+#define OMAP4430_BGAP_TEMP_SENSOR_CONTCONV_SHIFT 10
+#define OMAP4430_BGAP_TEMP_SENSOR_CONTCONV_MASK (1 << 10)
+#define OMAP4430_BGAP_TEMP_SENSOR_SOC_SHIFT 9
+#define OMAP4430_BGAP_TEMP_SENSOR_SOC_MASK (1 << 9)
+#define OMAP4430_BGAP_TEMP_SENSOR_EOCZ_SHIFT 8
+#define OMAP4430_BGAP_TEMP_SENSOR_EOCZ_MASK (1 << 8)
+#define OMAP4430_BGAP_TEMP_SENSOR_DTEMP_SHIFT 0
+#define OMAP4430_BGAP_TEMP_SENSOR_DTEMP_MASK (0x3ff << 0)
+
+/* TEMP_SENSOR OMAP4460 */
+#define OMAP4460_BGAP_TEMPSOFF_SHIFT 13
+#define OMAP4460_BGAP_TEMPSOFF_MASK (1 << 13)
+#define OMAP4460_BGAP_TEMP_SENSOR_SOC_SHIFT 11
+#define OMAP4460_BGAP_TEMP_SENSOR_SOC_MASK (1 << 11)
+#define OMAP4460_BGAP_TEMP_SENSOR_EOCZ_SHIFT 10
+#define OMAP4460_BGAP_TEMP_SENSOR_EOCZ_MASK (1 << 10)
+#define OMAP4460_BGAP_TEMP_SENSOR_DTEMP_SHIFT 0
+#define OMAP4460_BGAP_TEMP_SENSOR_DTEMP_MASK (0x3ff << 0)
+
+/* BANDGAP_CTRL */
+#define OMAP4460_SINGLE_MODE_SHIFT 31
+#define OMAP4460_SINGLE_MODE_MASK (1 << 31)
+#define OMAP4460_MASK_HOT_SHIFT 1
+#define OMAP4460_MASK_HOT_MASK (1 << 1)
+#define OMAP4460_MASK_COLD_SHIFT 0
+#define OMAP4460_MASK_COLD_MASK (1 << 0)
+
+/* BANDGAP_COUNTER */
+#define OMAP4460_COUNTER_SHIFT 0
+#define OMAP4460_COUNTER_MASK (0xffffff << 0)
+
+/* BANDGAP_THRESHOLD */
+#define OMAP4460_T_HOT_SHIFT 16
+#define OMAP4460_T_HOT_MASK (0x3ff << 16)
+#define OMAP4460_T_COLD_SHIFT 0
+#define OMAP4460_T_COLD_MASK (0x3ff << 0)
+
+/* TSHUT_THRESHOLD */
+#define OMAP4460_TSHUT_HOT_SHIFT 16
+#define OMAP4460_TSHUT_HOT_MASK (0x3ff << 16)
+#define OMAP4460_TSHUT_COLD_SHIFT 0
+#define OMAP4460_TSHUT_COLD_MASK (0x3ff << 0)
+
+/* BANDGAP_STATUS */
+#define OMAP4460_CLEAN_STOP_SHIFT 3
+#define OMAP4460_CLEAN_STOP_MASK (1 << 3)
+#define OMAP4460_BGAP_ALERT_SHIFT 2
+#define OMAP4460_BGAP_ALERT_MASK (1 << 2)
+#define OMAP4460_HOT_FLAG_SHIFT 1
+#define OMAP4460_HOT_FLAG_MASK (1 << 1)
+#define OMAP4460_COLD_FLAG_SHIFT 0
+#define OMAP4460_COLD_FLAG_MASK (1 << 0)
/* DPLL_NWELL_TRIM_0 */
#define OMAP4_DPLL_ABE_NWELL_TRIM_MUX_CTRL_SHIFT 29
--
1.7.0.4
^ permalink raw reply related [flat|nested] 35+ messages in thread
* [RFC PATCH 3/6 V2] OMAP4460: Temperature sensor data
2011-08-18 10:52 [RFC PATCH 0/6 V2] OMAP4: Temperature sensor driver Keerthy
2011-08-18 10:52 ` [RFC PATCH 1/6 V2] OMAP4: Clock: Associate clocks for OMAP temperature sensor Keerthy
2011-08-18 10:52 ` [RFC PATCH 2/6 V2] OMAP4: Adding the temperature sensor register set bit fields Keerthy
@ 2011-08-18 10:52 ` Keerthy
2011-08-18 11:32 ` Felipe Balbi
2011-08-18 10:52 ` [RFC PATCH 4/6 V2] OMAP4: Hwmod: OMAP temperature sensor Keerthy
` (2 subsequent siblings)
5 siblings, 1 reply; 35+ messages in thread
From: Keerthy @ 2011-08-18 10:52 UTC (permalink / raw)
To: linux-omap; +Cc: Keerthy, tony
The register set and the
bit fields might vary across OMAP versions. Hence
creating a structure comprising of all the registers
and bit fields to make the driver uniform for all the
versions with different register sets. The data file
contains the structure populated with register offsets
and bit fields corresponding to OMAP4460 on die sensor.
Signed-off-by: Keerthy <j-keerthy@ti.com>
Cc: tony@atomide.com
---
arch/arm/mach-omap2/Makefile | 2 +-
arch/arm/mach-omap2/temp_sensor4460_data.c | 63 ++++++++++++++
.../plat-omap/include/plat/temperature_sensor.h | 89 ++++++++++++++++++++
3 files changed, 153 insertions(+), 1 deletions(-)
create mode 100644 arch/arm/mach-omap2/temp_sensor4460_data.c
create mode 100644 arch/arm/plat-omap/include/plat/temperature_sensor.h
diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile
index fb02937..2d5d981 100644
--- a/arch/arm/mach-omap2/Makefile
+++ b/arch/arm/mach-omap2/Makefile
@@ -86,7 +86,7 @@ obj-$(CONFIG_ARCH_OMAP3) += prcm.o cm2xxx_3xxx.o prm2xxx_3xxx.o \
obj-$(CONFIG_ARCH_OMAP4) += prcm.o cm2xxx_3xxx.o cminst44xx.o \
cm44xx.o prcm_mpu44xx.o \
prminst44xx.o vc44xx_data.o \
- vp44xx_data.o
+ vp44xx_data.o temp_sensor4460_data.o
# OMAP voltage domains
ifeq ($(CONFIG_PM),y)
diff --git a/arch/arm/mach-omap2/temp_sensor4460_data.c b/arch/arm/mach-omap2/temp_sensor4460_data.c
new file mode 100644
index 0000000..294963d
--- /dev/null
+++ b/arch/arm/mach-omap2/temp_sensor4460_data.c
@@ -0,0 +1,63 @@
+/*
+ * OMAP4460 on die Temperature sensor data file
+ *
+ * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
+ * Author: J Keerthy <j-keerthy@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/slab.h>
+#include "control.h"
+#include <plat/temperature_sensor.h>
+
+/*
+ * OMAP4460 has one instance of thermal sensor for MPU
+ * need to describe the individual bit fields
+ */
+struct omap_temp_sensor_registers omap_mpu_temp_sensor_registers = {
+ .temp_sensor_ctrl = OMAP4460_TEMP_SENSOR_CTRL_OFFSET,
+ .bgap_tempsoff_mask = OMAP4460_BGAP_TEMPSOFF_MASK,
+ .bgap_soc_mask = OMAP4460_BGAP_TEMP_SENSOR_SOC_MASK,
+ .bgap_eocz_mask = OMAP4460_BGAP_TEMP_SENSOR_EOCZ_MASK,
+ .bgap_dtemp_mask = OMAP4460_BGAP_TEMP_SENSOR_DTEMP_MASK,
+
+ .bgap_mask_ctrl = OMAP4460_BGAP_CTRL_OFFSET,
+ .mask_hot_mask = OMAP4460_MASK_HOT_MASK,
+ .mask_cold_mask = OMAP4460_MASK_COLD_MASK,
+
+ .bgap_mode_ctrl = OMAP4460_BGAP_CTRL_OFFSET,
+ .mode_ctrl_mask = OMAP4460_SINGLE_MODE_MASK,
+
+ .bgap_counter = OMAP4460_BGAP_COUNTER_OFFSET,
+ .counter_mask = OMAP4460_COUNTER_MASK,
+
+ .bgap_threshold = OMAP4460_BGAP_THRESHOLD_OFFSET,
+ .threshold_thot_mask = OMAP4460_T_HOT_MASK,
+ .threshold_tcold_mask = OMAP4460_T_COLD_MASK,
+
+ .thsut_threshold = OMAP4460_BGAP_TSHUT_OFFSET,
+ .tshut_hot_mask = OMAP4460_TSHUT_HOT_MASK,
+ .tshut_cold_mask = OMAP4460_TSHUT_COLD_MASK,
+
+ .bgap_status = OMAP4460_BGAP_STATUS_OFFSET,
+ .status_clean_stop_mask = OMAP4460_CLEAN_STOP_MASK,
+ .status_bgap_alert_mask = OMAP4460_BGAP_ALERT_MASK,
+ .status_hot_mask = OMAP4460_HOT_FLAG_MASK,
+ .status_cold_mask = OMAP4460_COLD_FLAG_MASK,
+
+ .bgap_efuse = OMAP4460_FUSE_OPP_BGAP,
+};
diff --git a/arch/arm/plat-omap/include/plat/temperature_sensor.h b/arch/arm/plat-omap/include/plat/temperature_sensor.h
new file mode 100644
index 0000000..a3186f9
--- /dev/null
+++ b/arch/arm/plat-omap/include/plat/temperature_sensor.h
@@ -0,0 +1,89 @@
+/*
+ * OMAP Temperature sensor header file
+ *
+ * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
+ * Author: J Keerthy <j-keerthy@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __ARCH_ARM_PLAT_OMAP_INCLUDE_PLAT_TEMPERATURE_SENSOR_H
+#define __ARCH_ARM_PLAT_OMAP_INCLUDE_PLAT_TEMPERATURE_SENSOR_H
+
+/* Offsets from the base of temperature sensor registers */
+
+#define OMAP4460_TEMP_SENSOR_CTRL_OFFSET 0x00
+#define OMAP4460_BGAP_CTRL_OFFSET 0x4c
+#define OMAP4460_BGAP_COUNTER_OFFSET 0x50
+#define OMAP4460_BGAP_THRESHOLD_OFFSET 0x54
+#define OMAP4460_BGAP_TSHUT_OFFSET 0x58
+#define OMAP4460_BGAP_STATUS_OFFSET 0x5c
+#define OMAP4460_FUSE_OPP_BGAP -0xcc
+
+/*
+ * The register offsets and but fields might change across
+ * OMAP versions hence populating them in this structure.
+ */
+struct omap_temp_sensor_registers {
+ u32 temp_sensor_ctrl;
+ u32 bgap_tempsoff_mask;
+ u32 bgap_soc_mask;
+ u32 bgap_eocz_mask;
+ u32 bgap_dtemp_mask;
+
+ u32 bgap_mask_ctrl;
+ u32 mask_hot_mask;
+ u32 mask_cold_mask;
+
+ u32 bgap_mode_ctrl;
+ u32 mode_ctrl_mask;
+
+ u32 bgap_counter;
+ u32 counter_mask;
+
+ u32 bgap_threshold;
+ u32 threshold_thot_mask;
+ u32 threshold_tcold_mask;
+
+ u32 thsut_threshold;
+ u32 tshut_hot_mask;
+ u32 tshut_cold_mask;
+
+ u32 bgap_status;
+ u32 status_clean_stop_mask;
+ u32 status_bgap_alert_mask;
+ u32 status_hot_mask;
+ u32 status_cold_mask;
+
+ u32 bgap_efuse;
+};
+
+/* @name: Name of the domain of the temperature sensor */
+struct omap_temp_sensor_dev_attr {
+ const char *name;
+};
+
+extern struct omap_temp_sensor_registers omap_mpu_temp_sensor_registers;
+
+/*
+ * omap_temp_sensor platform data
+ * @registers - pointer to register set and thier bit fields information
+ */
+struct omap_temp_sensor_pdata {
+ struct omap_temp_sensor_registers *registers;
+};
+
+#endif
--
1.7.0.4
^ permalink raw reply related [flat|nested] 35+ messages in thread
* [RFC PATCH 4/6 V2] OMAP4: Hwmod: OMAP temperature sensor
2011-08-18 10:52 [RFC PATCH 0/6 V2] OMAP4: Temperature sensor driver Keerthy
` (2 preceding siblings ...)
2011-08-18 10:52 ` [RFC PATCH 3/6 V2] OMAP4460: Temperature sensor data Keerthy
@ 2011-08-18 10:52 ` Keerthy
2011-08-18 10:52 ` [RFC PATCH 5/6 V2] OMAP4: Temperature sensor device support Keerthy
2011-08-18 10:52 ` [RFC PATCH 6/6 V2] hwmon: OMAP4: On die temperature sensor driver Keerthy
5 siblings, 0 replies; 35+ messages in thread
From: Keerthy @ 2011-08-18 10:52 UTC (permalink / raw)
To: linux-omap; +Cc: Benoit Cousson, Keerthy, tony
From: Benoit Cousson <b-cousson@ti.com>
OMAP4460 temperature sensor hwmod cannot be auto generated
since it is part of ctrl module. Hence populating the
necessary hwmod info manually.
Signed-off-by: Benoit Cousson <b-cousson@ti.com>
Signed-off-by: Keerthy <j-keerthy@ti.com>
Cc: tony@atomide.com
Cc: b-cousson@ti.com
---
arch/arm/mach-omap2/omap_hwmod_44xx_data.c | 61 ++++++++++++++++++++++++++++
1 files changed, 61 insertions(+), 0 deletions(-)
diff --git a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c
index 6201422..28bf6d3 100644
--- a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c
+++ b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c
@@ -29,6 +29,7 @@
#include <plat/mcbsp.h>
#include <plat/mmc.h>
#include <plat/i2c.h>
+#include <plat/temperature_sensor.h>
#include "omap_hwmod_common_data.h"
@@ -832,6 +833,63 @@ static struct omap_hwmod omap44xx_aess_hwmod = {
};
/*
+ * 'temperature_sensor' class
+ * temperature sensor module inside the bandgap / control module
+ */
+
+static struct omap_hwmod_class omap44xx_temperature_sensor_hwmod_class = {
+ .name = "temperature_sensor",
+};
+
+static struct omap_hwmod_irq_info omap44xx_temperature_sensor_irqs[] = {
+ { .name = "thermal_alert", .irq = 126 + OMAP44XX_IRQ_GIC_START },
+};
+
+static struct omap_hwmod_addr_space omap44xx_temperature_sensor_addrs[] = {
+ {
+ .pa_start = 0x4a00232c,
+ .pa_end = 0x4a00238b,
+ },
+};
+
+static struct omap_hwmod omap44xx_temperature_sensor_hwmod;
+/* l4_cfg -> ctrl_module_core */
+static struct omap_hwmod_ocp_if omap44xx_l4_cfg__temperature_sensor = {
+ .master = &omap44xx_l4_cfg_hwmod,
+ .slave = &omap44xx_temperature_sensor_hwmod,
+ .clk = "l4_div_ck",
+ .addr = omap44xx_temperature_sensor_addrs,
+ .user = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+/* ctrl_module_core slave ports */
+static struct omap_hwmod_ocp_if *omap44xx_temperature_sensor_slaves[] = {
+ &omap44xx_l4_cfg__temperature_sensor,
+};
+
+/* temperature sensor dev_attr */
+static struct omap_temp_sensor_dev_attr temp_sensor_dev_attr = {
+ .name = "mpu",
+};
+
+static struct omap_hwmod omap44xx_temperature_sensor_hwmod = {
+ .name = "temperature_sensor_mpu",
+ .class = &omap44xx_temperature_sensor_hwmod_class,
+ .mpu_irqs = omap44xx_temperature_sensor_irqs,
+ .main_clk = "bandgap_ts_fclk",
+ .slaves = omap44xx_temperature_sensor_slaves,
+ .slaves_cnt = ARRAY_SIZE(omap44xx_temperature_sensor_slaves),
+ .clkdm_name = "l4_wkup_clkdm",
+ .prcm = {
+ .omap4 = {
+ .clkctrl_offs = OMAP4_CM_WKUP_BANDGAP_CLKCTRL_OFFSET,
+ },
+ },
+ .dev_attr = &temp_sensor_dev_attr,
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP4430),
+};
+
+/*
* 'bandgap' class
* bangap reference for ldo regulators
*/
@@ -5469,6 +5527,9 @@ static __initdata struct omap_hwmod *omap44xx_hwmods[] = {
&omap44xx_timer10_hwmod,
&omap44xx_timer11_hwmod,
+ /* temperature sensor hwmod */
+ &omap44xx_temperature_sensor_hwmod,
+
/* uart class */
&omap44xx_uart1_hwmod,
&omap44xx_uart2_hwmod,
--
1.7.0.4
^ permalink raw reply related [flat|nested] 35+ messages in thread
* [RFC PATCH 5/6 V2] OMAP4: Temperature sensor device support
2011-08-18 10:52 [RFC PATCH 0/6 V2] OMAP4: Temperature sensor driver Keerthy
` (3 preceding siblings ...)
2011-08-18 10:52 ` [RFC PATCH 4/6 V2] OMAP4: Hwmod: OMAP temperature sensor Keerthy
@ 2011-08-18 10:52 ` Keerthy
2011-08-19 5:47 ` Todd Poynor
2011-08-18 10:52 ` [RFC PATCH 6/6 V2] hwmon: OMAP4: On die temperature sensor driver Keerthy
5 siblings, 1 reply; 35+ messages in thread
From: Keerthy @ 2011-08-18 10:52 UTC (permalink / raw)
To: linux-omap; +Cc: Keerthy, tony
The device file adds the device support for OMAP4
on die temperature sensor.
Signed-off-by: Keerthy <j-keerthy@ti.com>
Cc: tony@atomide.com
---
arch/arm/mach-omap2/Makefile | 1 +
arch/arm/mach-omap2/temp_sensor_device.c | 178 ++++++++++++++++++++++++++++++
arch/arm/plat-omap/Kconfig | 12 ++
3 files changed, 191 insertions(+), 0 deletions(-)
create mode 100644 arch/arm/mach-omap2/temp_sensor_device.c
diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile
index 2d5d981..5812fb4 100644
--- a/arch/arm/mach-omap2/Makefile
+++ b/arch/arm/mach-omap2/Makefile
@@ -18,6 +18,7 @@ obj-$(CONFIG_ARCH_OMAP4) += prm44xx.o $(hwmod-common)
obj-$(CONFIG_OMAP_MCBSP) += mcbsp.o
+obj-$(CONFIG_OMAP_TEMP_SENSOR) += temp_sensor_device.o
obj-$(CONFIG_TWL4030_CORE) += omap_twl.o
# SMP support ONLY available for OMAP4
diff --git a/arch/arm/mach-omap2/temp_sensor_device.c b/arch/arm/mach-omap2/temp_sensor_device.c
new file mode 100644
index 0000000..083f267
--- /dev/null
+++ b/arch/arm/mach-omap2/temp_sensor_device.c
@@ -0,0 +1,178 @@
+/*
+ * OMAP on die Temperature sensor device file
+ *
+ * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
+ * Author: J Keerthy <j-keerthy@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/mutex.h>
+#include <linux/idr.h>
+#include <plat/omap_device.h>
+#include "pm.h"
+#include <plat/temperature_sensor.h>
+
+
+int omap_temp_sensor_device_idle(struct omap_device *od)
+{
+ struct omap_temp_sensor_registers *registers;
+ struct resource *mem;
+ void __iomem *phy_base;
+ unsigned long timeout;
+ u32 ret = 0, temp;
+
+ mem = platform_get_resource(&od->pdev, IORESOURCE_MEM, 0);
+ if (!mem) {
+ dev_err(&od->pdev.dev, "no mem resource\n");
+ ret = -EINVAL;
+ goto plat_res_err;
+ }
+
+ phy_base = ioremap(mem->start, resource_size(mem));
+
+ if (!strcmp(od->hwmods[0]->dev_attr, "mpu"))
+ registers = &omap_mpu_temp_sensor_registers;
+
+ temp = __raw_readl(phy_base + registers->temp_sensor_ctrl);
+ temp |= registers->bgap_tempsoff_mask;
+
+ /* BGAP_TEMPSOFF should be set to 1 before gating clock */
+ __raw_writel(temp, phy_base + registers->temp_sensor_ctrl);
+ temp = __raw_readl(phy_base + registers->bgap_status);
+ timeout = jiffies + msecs_to_jiffies(5);
+
+ /* wait till the clean stop bit is set or till the timeout expires */
+ while (!(temp | registers->status_clean_stop_mask) &&
+ !(time_after(jiffies, timeout))) {
+ temp = __raw_readl(phy_base + registers->bgap_status);
+ usleep_range(500, 2000);
+ }
+
+ if (time_after(jiffies, timeout))
+ dev_err(&od->pdev.dev, "Clean stop bit not set\n");
+
+ ret = omap_device_idle_hwmods(od);
+
+plat_res_err:
+ return ret;
+}
+
+int omap_temp_sensor_device_activate(struct omap_device *od)
+{
+ struct omap_temp_sensor_registers *registers;
+ struct resource *mem;
+ void __iomem *phy_base;
+ u32 ret = 0, temp;
+
+ ret = omap_device_enable_hwmods(od);
+ if (ret < 0)
+ return ret;
+ mem = platform_get_resource(&od->pdev, IORESOURCE_MEM, 0);
+ if (!mem) {
+ dev_err(&od->pdev.dev, "no mem resource\n");
+ return -EINVAL;
+ }
+
+ phy_base = ioremap(mem->start, resource_size(mem));
+
+ if (!strcmp(od->hwmods[0]->dev_attr, "mpu"))
+ registers = &omap_mpu_temp_sensor_registers;
+
+ temp = __raw_readl(phy_base + registers->temp_sensor_ctrl);
+ temp &= ~(registers->bgap_tempsoff_mask);
+ /* BGAP_TEMPSOFF should be reset to 0 */
+ __raw_writel(temp,
+ phy_base + registers->temp_sensor_ctrl);
+
+ return 0;
+}
+
+static struct omap_device_pm_latency omap_temp_sensor_latency[] = {
+ {
+ .deactivate_func = omap_temp_sensor_device_idle,
+ .activate_func = omap_temp_sensor_device_activate,
+ .flags = OMAP_DEVICE_LATENCY_AUTO_ADJUST,
+ }
+};
+
+static DEFINE_IDR(temp_sensor_device_idr);
+
+static int temp_sensor_dev_init(struct omap_hwmod *oh, void *user)
+{
+ struct omap_temp_sensor_pdata *temp_sensor_pdata;
+ struct omap_device *od;
+ struct omap_temp_sensor_dev_attr *temp_sensor_dev_attr;
+ int ret = 0;
+ int num;
+ struct mutex sensor_mutex; /* sensor mutex */
+
+ mutex_init(&sensor_mutex);
+
+ temp_sensor_pdata =
+ kzalloc(sizeof(*temp_sensor_pdata), GFP_KERNEL);
+ if (!temp_sensor_pdata) {
+ dev_err(&oh->od->pdev.dev,
+ "Unable to allocate memory for temp sensor pdata\n");
+ return -ENOMEM;
+ }
+
+ mutex_lock(&sensor_mutex);
+ ret = idr_pre_get(&temp_sensor_device_idr, GFP_KERNEL);
+ if (ret < 0) {
+ mutex_unlock(&sensor_mutex);
+ goto fail_id;
+ }
+ ret = idr_get_new(&temp_sensor_device_idr, temp_sensor_pdata, &num);
+ if (ret < 0) {
+ mutex_unlock(&sensor_mutex);
+ goto fail_id;
+ }
+ mutex_unlock(&sensor_mutex);
+
+ temp_sensor_dev_attr = oh->dev_attr;
+ if (!strcmp(temp_sensor_dev_attr->name, "mpu"))
+ temp_sensor_pdata->registers = &omap_mpu_temp_sensor_registers;
+
+ od = omap_device_build("omap_temp_sensor", num,
+ oh, temp_sensor_pdata, sizeof(*temp_sensor_pdata),
+ omap_temp_sensor_latency,
+ ARRAY_SIZE(omap_temp_sensor_latency), 0);
+ if (IS_ERR(od)) {
+ dev_warn(&oh->od->pdev.dev,
+ "Could not build omap_device for %s\n", oh->name);
+ ret = PTR_ERR(od);
+ }
+
+fail_id:
+ kfree(temp_sensor_pdata);
+
+ return ret;
+}
+
+int __init omap_devinit_temp_sensor(void)
+{
+ if (!cpu_is_omap446x())
+ return 0;
+
+ return omap_hwmod_for_each_by_class("temperature_sensor",
+ temp_sensor_dev_init, NULL);
+}
+
+arch_initcall(omap_devinit_temp_sensor);
diff --git a/arch/arm/plat-omap/Kconfig b/arch/arm/plat-omap/Kconfig
index 6e6735f..8fd8e80 100644
--- a/arch/arm/plat-omap/Kconfig
+++ b/arch/arm/plat-omap/Kconfig
@@ -115,6 +115,18 @@ config OMAP_MCBSP
Say Y here if you want support for the OMAP Multichannel
Buffered Serial Port.
+config OMAP_TEMP_SENSOR
+ bool "OMAP Temp Sensor Support"
+ depends on ARCH_OMAP
+ default n
+ help
+ Say Y here if you want support for the temp sensor
+ on OMAP4460.
+
+ This provides the temperature of the MPU
+ subsystem. Only one instance of on die temperature
+ sensor is present.
+
config OMAP_MBOX_FWK
tristate "Mailbox framework support"
depends on ARCH_OMAP
--
1.7.0.4
^ permalink raw reply related [flat|nested] 35+ messages in thread
* [RFC PATCH 6/6 V2] hwmon: OMAP4: On die temperature sensor driver
2011-08-18 10:52 [RFC PATCH 0/6 V2] OMAP4: Temperature sensor driver Keerthy
` (4 preceding siblings ...)
2011-08-18 10:52 ` [RFC PATCH 5/6 V2] OMAP4: Temperature sensor device support Keerthy
@ 2011-08-18 10:52 ` Keerthy
2011-08-18 11:37 ` [lm-sensors] [RFC PATCH 6/6 V2] hwmon: OMAP4: On die Felipe Balbi
` (2 more replies)
5 siblings, 3 replies; 35+ messages in thread
From: Keerthy @ 2011-08-18 10:52 UTC (permalink / raw)
To: linux-omap; +Cc: Keerthy, Jean Delvare, Guenter Roeck, lm-sensors
On chip temperature sensor driver. The driver monitors the temperature of
the MPU subsystem of the OMAP4. It sends notifications to the user space if
the temperature crosses user defined thresholds via kobject_uevent interface.
The user is allowed to configure the temperature thresholds vis sysfs nodes
exposed using hwmon interface.
Signed-off-by: Keerthy <j-keerthy@ti.com>
Cc: Jean Delvare <khali@linux-fr.org>
Cc: Guenter Roeck <guenter.roeck@ericsson.com>
Cc: lm-sensors@lm-sensors.org
---
Documentation/hwmon/omap_temp_sensor | 27 +
drivers/hwmon/Kconfig | 11 +
drivers/hwmon/Makefile | 1 +
drivers/hwmon/omap_temp_sensor.c | 918 ++++++++++++++++++++++++++++++++++
4 files changed, 957 insertions(+), 0 deletions(-)
create mode 100644 Documentation/hwmon/omap_temp_sensor
create mode 100644 drivers/hwmon/omap_temp_sensor.c
diff --git a/Documentation/hwmon/omap_temp_sensor b/Documentation/hwmon/omap_temp_sensor
new file mode 100644
index 0000000..e01a6d6
--- /dev/null
+++ b/Documentation/hwmon/omap_temp_sensor
@@ -0,0 +1,27 @@
+Kernel driver omap_temp_sensor
+==============================
+
+Supported chips:
+ * Texas Instruments OMAP4460
+ Prefix: 'omap_temp_sensor'
+ Datasheet: http://focus.ti.com/docs/prod/folders/print/tmp102.html
+
+Author:
+ J Keerthy <j-keerthy@ti.com>
+
+Description
+-----------
+
+The Texas Instruments OMAP4 family of chips have a bandgap temperature sensor.
+The temperature sensor feature is used to convert the temperature of the device
+into a decimal value coded on 10 bits. An internal ADC is used for conversion.
+The recommended operating temperatures must be in the range -40 degree Celsius
+to 123 degree celsius for standard conversion.
+The thresholds are programmable and upon crossing the thresholds an interrupt
+is generated. The OMAP temperature sensor has a programmable update rate in
+milli seconds.
+(Currently the driver programs a default of 2000 milli seconds).
+
+The driver provides the common sysfs-interface for temperatures (see
+Documentation/hwmon/sysfs-interface under Temperatures).
+
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 5f888f7..9c9cd8b 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -323,6 +323,17 @@ config SENSORS_F71805F
This driver can also be built as a module. If so, the module
will be called f71805f.
+config SENSORS_OMAP_BANDGAP_TEMP_SENSOR
+ bool "OMAP on-die temperature sensor hwmon driver"
+ depends on HWMON && ARCH_OMAP && OMAP_TEMP_SENSOR
+ help
+ If you say yes here you get support for hardware
+ monitoring features of the OMAP on die temperature
+ sensor.
+
+ Continuous conversion programmable delay
+ mode is used for temperature conversion.
+
config SENSORS_F71882FG
tristate "Fintek F71882FG and compatibles"
help
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 28061cf..d0f89f5 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -91,6 +91,7 @@ obj-$(CONFIG_SENSORS_MAX6639) += max6639.o
obj-$(CONFIG_SENSORS_MAX6642) += max6642.o
obj-$(CONFIG_SENSORS_MAX6650) += max6650.o
obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o
+obj-$(CONFIG_SENSORS_OMAP_BANDGAP_TEMP_SENSOR) += omap_temp_sensor.o
obj-$(CONFIG_SENSORS_PC87360) += pc87360.o
obj-$(CONFIG_SENSORS_PC87427) += pc87427.o
obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o
diff --git a/drivers/hwmon/omap_temp_sensor.c b/drivers/hwmon/omap_temp_sensor.c
new file mode 100644
index 0000000..586a361
--- /dev/null
+++ b/drivers/hwmon/omap_temp_sensor.c
@@ -0,0 +1,918 @@
+/*
+ * OMAP4 Temperature sensor driver file
+ *
+ * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
+ * Author: J Keerthy <j-keerthy@ti.com>
+ * Author: Moiz Sonasath <m-sonasath@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/interrupt.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <plat/omap_device.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/jiffies.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/stddef.h>
+#include <linux/sysfs.h>
+#include <linux/err.h>
+#include <linux/types.h>
+#include <linux/mutex.h>
+#include <linux/pm_runtime.h>
+#include <linux/delay.h>
+#include <plat/temperature_sensor.h>
+
+#define TSHUT_THRESHOLD_HOT 122000 /* 122 deg C */
+#define TSHUT_THRESHOLD_COLD 100000 /* 100 deg C */
+#define BGAP_THRESHOLD_T_HOT 73000 /* 73 deg C */
+#define BGAP_THRESHOLD_T_COLD 71000 /* 71 deg C */
+#define OMAP_ADC_START_VALUE 530
+#define OMAP_ADC_END_VALUE 923
+
+/*
+ * omap_temp_sensor structure
+ * @hwmon_dev - device pointer
+ * @clock - Clock pointer
+ * @registers - Pointer to structure with register offsets and bitfields
+ * @sensor_mutex - Mutex for sysfs, irq and PM
+ * @irq - MPU Irq number for thermal alert
+ * @phy_base - Physical base of the temp I/O
+ * @clk_rate - Holds current clock rate
+ * @temp_sensor_ctrl - temp sensor control register value
+ * @bg_ctrl - bandgap ctrl register value
+ * @bg_counter - bandgap counter value
+ * @bg_threshold - bandgap threshold register value
+ * @temp_sensor_tshut_threshold - bandgap tshut register value
+ * @is_efuse_valid - Flag to determine if efuse is valid or not
+ * @clk_on - Manages the current clock state
+ */
+struct omap_temp_sensor {
+ struct device *hwmon_dev;
+ struct clk *clock;
+ struct omap_temp_sensor_registers *registers;
+ struct mutex sensor_mutex; /* Mutex for sysfs, irq and PM */
+ unsigned int irq;
+ void __iomem *phy_base;
+ u32 clk_rate;
+ u32 temp_sensor_ctrl;
+ u32 bg_ctrl;
+ u32 bg_counter;
+ u32 bg_threshold;
+ u32 temp_sensor_tshut_threshold;
+ bool is_efuse_valid;
+ bool clk_on;
+};
+
+/*
+ * Temperature values in milli degree celsius
+ * ADC code values from 530 to 923
+ */
+static int adc_to_temp[] = {
+ -40000, -40000, -40000, -40000, -39800, -39400, -39000, -38600, -38200,
+ -37800, -37300, -36800, -36400, -36000, -35600, -35200, -34800,
+ -34300, -33800, -33400, -33000, -32600, -32200, -31800, -31300,
+ -30800, -30400, -30000, -29600, -29200, -28700, -28200, -27800,
+ -27400, -27000, -26600, -26200, -25700, -25200, -24800, -24400,
+ -24000, -23600, -23200, -22700, -22200, -21800, -21400, -21000,
+ -20600, -20200, -19700, -19200, -18800, -18400, -18000, -17600,
+ -17200, -16700, -16200, -15800, -15400, -15000, -14600, -14200,
+ -13700, -13200, -12800, -12400, -12000, -11600, -11200, -10700,
+ -10200, -9800, -9400, -9000, -8600, -8200, -7700, -7200, -6800,
+ -6400, -6000, -5600, -5200, -4800, -4300, -3800, -3400, -3000,
+ -2600, -2200, -1800, -1300, -800, -400, 0, 400, 800, 1200, 1600,
+ 2100, 2600, 3000, 3400, 3800, 4200, 4600, 5100, 5600, 6000, 6400,
+ 6800, 7200, 7600, 8000, 8500, 9000, 9400, 9800, 10200, 10600, 11000,
+ 11400, 11900, 12400, 12800, 13200, 13600, 14000, 14400, 14800,
+ 15300, 15800, 16200, 16600, 17000, 17400, 17800, 18200, 18700,
+ 19200, 19600, 20000, 20400, 20800, 21200, 21600, 22100, 22600,
+ 23000, 23400, 23800, 24200, 24600, 25000, 25400, 25900, 26400,
+ 26800, 27200, 27600, 28000, 28400, 28800, 29300, 29800, 30200,
+ 30600, 31000, 31400, 31800, 32200, 32600, 33100, 33600, 34000,
+ 34400, 34800, 35200, 35600, 36000, 36400, 36800, 37300, 37800,
+ 38200, 38600, 39000, 39400, 39800, 40200, 40600, 41100, 41600,
+ 42000, 42400, 42800, 43200, 43600, 44000, 44400, 44800, 45300,
+ 45800, 46200, 46600, 47000, 47400, 47800, 48200, 48600, 49000,
+ 49500, 50000, 50400, 50800, 51200, 51600, 52000, 52400, 52800,
+ 53200, 53700, 54200, 54600, 55000, 55400, 55800, 56200, 56600,
+ 57000, 57400, 57800, 58200, 58700, 59200, 59600, 60000, 60400,
+ 60800, 61200, 61600, 62000, 62400, 62800, 63300, 63800, 64200,
+ 64600, 65000, 65400, 65800, 66200, 66600, 67000, 67400, 67800,
+ 68200, 68700, 69200, 69600, 70000, 70400, 70800, 71200, 71600,
+ 72000, 72400, 72800, 73200, 73600, 74100, 74600, 75000, 75400,
+ 75800, 76200, 76600, 77000, 77400, 77800, 78200, 78600, 79000,
+ 79400, 79800, 80300, 80800, 81200, 81600, 82000, 82400, 82800,
+ 83200, 83600, 84000, 84400, 84800, 85200, 85600, 86000, 86400,
+ 86800, 87300, 87800, 88200, 88600, 89000, 89400, 89800, 90200,
+ 90600, 91000, 91400, 91800, 92200, 92600, 93000, 93400, 93800,
+ 94200, 94600, 95000, 95500, 96000, 96400, 96800, 97200, 97600,
+ 98000, 98400, 98800, 99200, 99600, 100000, 100400, 100800, 101200,
+ 101600, 102000, 102400, 102800, 103200, 103600, 104000, 104400,
+ 104800, 105200, 105600, 106100, 106600, 107000, 107400, 107800,
+ 108200, 108600, 109000, 109400, 109800, 110200, 110600, 111000,
+ 111400, 111800, 112200, 112600, 113000, 113400, 113800, 114200,
+ 114600, 115000, 115400, 115800, 116200, 116600, 117000, 117400,
+ 117800, 118200, 118600, 119000, 119400, 119800, 120200, 120600,
+ 121000, 121400, 121800, 122200, 122600, 123000
+};
+
+static unsigned long omap_temp_sensor_readl(struct omap_temp_sensor
+ *temp_sensor, u32 reg)
+{
+ return __raw_readl(temp_sensor->phy_base + reg);
+}
+
+static void omap_temp_sensor_writel(struct omap_temp_sensor *temp_sensor,
+ u32 val, u32 reg)
+{
+ __raw_writel(val, (temp_sensor->phy_base + reg));
+}
+
+static int adc_to_temp_conversion(int adc_val)
+{
+ return adc_to_temp[adc_val - OMAP_ADC_START_VALUE];
+}
+
+static int temp_to_adc_conversion(long temp)
+{
+ int i;
+
+ for (i = 0; i <= OMAP_ADC_END_VALUE - OMAP_ADC_START_VALUE; i++)
+ if (temp < adc_to_temp[i])
+ return OMAP_ADC_START_VALUE + i - 1;
+
+ return -EINVAL;
+}
+
+static void omap_configure_temp_sensor_thresholds(struct omap_temp_sensor
+ *temp_sensor)
+{
+ u32 temp, t_hot, t_cold, tshut_hot, tshut_cold;
+
+ t_hot = temp_to_adc_conversion(BGAP_THRESHOLD_T_HOT);
+ t_cold = temp_to_adc_conversion(BGAP_THRESHOLD_T_COLD);
+
+ /* Configure the TALERT thresholds */
+ temp = omap_temp_sensor_readl(temp_sensor,
+ temp_sensor->registers->bgap_threshold);
+ temp |= (t_hot << __ffs(temp_sensor->registers->threshold_thot_mask)) |
+ (t_cold << __ffs(temp_sensor->registers->threshold_tcold_mask));
+ omap_temp_sensor_writel(temp_sensor, temp,
+ temp_sensor->registers->bgap_threshold);
+
+ tshut_hot = temp_to_adc_conversion(TSHUT_THRESHOLD_HOT);
+ tshut_cold = temp_to_adc_conversion(TSHUT_THRESHOLD_COLD);
+
+ /* Configure the TSHUT thresholds */
+ temp = omap_temp_sensor_readl(temp_sensor,
+ temp_sensor->registers->thsut_threshold);
+ temp |= (tshut_hot << __ffs(temp_sensor->registers->tshut_hot_mask))
+ | (tshut_cold << __ffs(temp_sensor->registers->tshut_hot_mask));
+ omap_temp_sensor_writel(temp_sensor, temp,
+ temp_sensor->registers->thsut_threshold);
+}
+
+static void omap_configure_temp_sensor_counter(struct omap_temp_sensor
+ *temp_sensor, u32 counter)
+{
+ u32 val;
+
+ val = omap_temp_sensor_readl(temp_sensor,
+ temp_sensor->registers->bgap_counter);
+ val &= ~(temp_sensor->registers->counter_mask);
+ val |= (counter << __ffs(temp_sensor->registers->counter_mask));
+ omap_temp_sensor_writel(temp_sensor, val,
+ temp_sensor->registers->bgap_counter);
+}
+
+static void omap_enable_continuous_mode(struct omap_temp_sensor *temp_sensor,
+ bool enable)
+{
+ u32 val;
+
+ val = omap_temp_sensor_readl(temp_sensor,
+ temp_sensor->registers->bgap_mode_ctrl);
+
+ if (enable)
+ val |= (1 << __ffs(temp_sensor->registers->mode_ctrl_mask));
+ else
+ val &= ~(temp_sensor->registers->mode_ctrl_mask);
+
+ omap_temp_sensor_writel(temp_sensor, val,
+ temp_sensor->registers->bgap_mode_ctrl);
+}
+
+/* Sysfs hook functions */
+
+static ssize_t show_temp_max(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
+ int temp;
+
+ mutex_lock(&temp_sensor->sensor_mutex);
+
+ temp = omap_temp_sensor_readl(temp_sensor,
+ temp_sensor->registers->bgap_threshold);
+ temp = (temp & temp_sensor->registers->threshold_thot_mask)
+ >> __ffs(temp_sensor->registers->threshold_thot_mask);
+
+ if (temp < OMAP_ADC_START_VALUE || temp > OMAP_ADC_END_VALUE) {
+ dev_err(dev, "invalid value\n");
+ mutex_unlock(&temp_sensor->sensor_mutex);
+ return -EINVAL;
+ }
+
+ temp = adc_to_temp_conversion(temp);
+ mutex_unlock(&temp_sensor->sensor_mutex);
+
+ return snprintf(buf, 16, "%d\n", temp);
+}
+
+static ssize_t set_temp_max(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
+ long val;
+ u32 reg_val, t_cold, t_hot, temp;
+
+ mutex_lock(&temp_sensor->sensor_mutex);
+
+ if (strict_strtol(buf, 10, &val)) {
+ count = -EINVAL;
+ goto out;
+ }
+
+ t_hot = temp_to_adc_conversion(val);
+ if ((t_hot < OMAP_ADC_START_VALUE || t_hot > OMAP_ADC_END_VALUE)) {
+ dev_err(dev, "invalid range\n");
+ count = -EINVAL;
+ goto out;
+ }
+
+ /* obtain the T cold value */
+ t_cold = omap_temp_sensor_readl(temp_sensor,
+ temp_sensor->registers->bgap_threshold);
+ t_cold = (t_cold & temp_sensor->registers->threshold_tcold_mask) >>
+ __ffs(temp_sensor->registers->threshold_tcold_mask);
+
+ if (t_hot < t_cold) {
+ dev_err(dev, "Error! T_HOT value lesser than T_COLD\n");
+ count = -EINVAL;
+ goto out;
+ }
+
+ /* write the new t_hot value */
+ reg_val = omap_temp_sensor_readl(temp_sensor,
+ temp_sensor->registers->bgap_threshold);
+ reg_val &= ~(temp_sensor->registers->threshold_thot_mask);
+ reg_val |= (t_hot <<
+ __ffs(temp_sensor->registers->threshold_thot_mask));
+ omap_temp_sensor_writel(temp_sensor, reg_val,
+ temp_sensor->registers->bgap_threshold);
+
+ /* Read the current temperature */
+ temp = omap_temp_sensor_readl(temp_sensor,
+ temp_sensor->registers->temp_sensor_ctrl);
+ temp &= (temp_sensor->registers->bgap_dtemp_mask);
+
+ /*
+ * If user sets the HIGH threshold(t_hot) greater than the current
+ * temperature(temp) unmask the HOT interrupts
+ */
+ if (t_hot > temp) {
+ reg_val = omap_temp_sensor_readl(temp_sensor,
+ temp_sensor->registers->bgap_mask_ctrl);
+ reg_val &= ~(temp_sensor->registers->mask_cold_mask);
+ reg_val |= temp_sensor->registers->mask_hot_mask;
+ omap_temp_sensor_writel(temp_sensor, reg_val,
+ temp_sensor->registers->bgap_mask_ctrl);
+ }
+
+ /*
+ * If current temperature is in-between the hot and cold thresholds,
+ * Enable both masks.
+ */
+ if (temp > t_cold && temp < t_hot) {
+ reg_val = omap_temp_sensor_readl(temp_sensor,
+ temp_sensor->registers->bgap_mask_ctrl);
+ reg_val |= temp_sensor->registers->mask_cold_mask;
+ reg_val |= temp_sensor->registers->mask_hot_mask;
+ omap_temp_sensor_writel(temp_sensor, reg_val,
+ OMAP4460_BGAP_CTRL_OFFSET);
+ }
+ /*
+ * else no need to do anything since HW will immediately compare
+ * the new threshold and generate interrupt accordingly
+ */
+out:
+ mutex_unlock(&temp_sensor->sensor_mutex);
+ return count;
+}
+
+static ssize_t show_temp_max_hyst(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
+ u32 temp;
+
+ mutex_lock(&temp_sensor->sensor_mutex);
+
+ temp = omap_temp_sensor_readl(temp_sensor,
+ temp_sensor->registers->bgap_threshold);
+ temp = (temp & temp_sensor->registers->threshold_tcold_mask) >>
+ __ffs(temp_sensor->registers->threshold_tcold_mask);
+
+ if (temp < OMAP_ADC_START_VALUE || temp > OMAP_ADC_END_VALUE) {
+ dev_err(dev, "invalid value\n");
+ mutex_unlock(&temp_sensor->sensor_mutex);
+ return -EINVAL;
+ }
+
+ temp = adc_to_temp_conversion(temp);
+ mutex_unlock(&temp_sensor->sensor_mutex);
+
+ return snprintf(buf, 16, "%d\n", temp);
+}
+
+static ssize_t set_temp_max_hyst(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
+ u32 reg_val, t_hot, t_cold, temp;
+ long val;
+
+ mutex_lock(&temp_sensor->sensor_mutex);
+
+ if (strict_strtol(buf, 10, &val)) {
+ count = -EINVAL;
+ goto out;
+ }
+
+ t_cold = temp_to_adc_conversion(val);
+ if (t_cold < OMAP_ADC_START_VALUE || t_cold > OMAP_ADC_END_VALUE) {
+ dev_err(dev, "invalid range");
+ count = -EINVAL;
+ goto out;
+ }
+
+ /* obtain the T HOT value */
+ t_hot = omap_temp_sensor_readl(temp_sensor,
+ temp_sensor->registers->bgap_threshold);
+ t_hot = (t_hot & temp_sensor->registers->threshold_thot_mask) >>
+ __ffs(temp_sensor->registers->threshold_thot_mask);
+
+ if (t_cold > t_hot) {
+ dev_err(dev, "Error! T_COLD value greater than T_HOT\n");
+ count = -EINVAL;
+ goto out;
+ }
+
+ /* write the new t_cold value */
+ reg_val = omap_temp_sensor_readl(temp_sensor,
+ temp_sensor->registers->bgap_threshold);
+ reg_val &= ~(temp_sensor->registers->threshold_tcold_mask);
+ reg_val |= (t_cold <<
+ __ffs(temp_sensor->registers->threshold_tcold_mask));
+ omap_temp_sensor_writel(temp_sensor, reg_val,
+ temp_sensor->registers->bgap_threshold);
+
+ /* Read the current temperature */
+ temp = omap_temp_sensor_readl(temp_sensor,
+ temp_sensor->registers->temp_sensor_ctrl);
+ temp &= (temp_sensor->registers->bgap_dtemp_mask);
+
+ /*
+ * If user sets the LOW threshold(t_cold) lower than the current
+ * temperature(temp) unmask the COLD interrupts
+ */
+ if (t_cold < temp) {
+ reg_val = omap_temp_sensor_readl(temp_sensor,
+ temp_sensor->registers->bgap_mask_ctrl);
+ reg_val &= ~(temp_sensor->registers->mask_hot_mask);
+ reg_val |= temp_sensor->registers->mask_cold_mask;
+ omap_temp_sensor_writel(temp_sensor, reg_val,
+ temp_sensor->registers->bgap_mask_ctrl);
+ }
+
+ /*
+ * If current temperature is in-between the hot and cold thresholds,
+ * Enable both masks.
+ */
+ if (temp < t_hot && temp > t_cold) {
+ reg_val = omap_temp_sensor_readl(temp_sensor,
+ temp_sensor->registers->bgap_mask_ctrl);
+ reg_val |= temp_sensor->registers->mask_cold_mask;
+ reg_val |= temp_sensor->registers->mask_hot_mask;
+ omap_temp_sensor_writel(temp_sensor, reg_val,
+ temp_sensor->registers->bgap_mask_ctrl);
+ }
+
+ /*
+ * else no need to do anything since HW will immediately compare
+ * the new threshold and generate interrupt accordingly
+ */
+
+out:
+ mutex_unlock(&temp_sensor->sensor_mutex);
+ return count;
+}
+
+static ssize_t show_update_rate(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
+ u32 temp = 0, ret = 0;
+
+ mutex_lock(&temp_sensor->sensor_mutex);
+
+ if (!temp_sensor->clk_rate) {
+ dev_err(dev, "clk_rate is NULL\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ temp = omap_temp_sensor_readl(temp_sensor,
+ temp_sensor->registers->bgap_counter);
+ temp = (temp & temp_sensor->registers->counter_mask) >>
+ __ffs(temp_sensor->registers->counter_mask);
+ temp = temp * 1000 / (temp_sensor->clk_rate);
+
+out:
+ mutex_unlock(&temp_sensor->sensor_mutex);
+ if (!ret)
+ return sprintf(buf, "%d\n", temp);
+
+ return ret;
+}
+
+static ssize_t set_update_rate(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
+ u32 reg_val;
+ long val;
+
+ mutex_lock(&temp_sensor->sensor_mutex);
+
+ if (strict_strtol(buf, 10, &val)) {
+ count = -EINVAL;
+ goto out;
+ }
+
+ val *= temp_sensor->clk_rate / 1000;
+ reg_val = omap_temp_sensor_readl(temp_sensor,
+ temp_sensor->registers->bgap_counter);
+
+ reg_val &= ~(temp_sensor->registers->counter_mask);
+ reg_val |= val;
+ omap_temp_sensor_writel(temp_sensor, reg_val,
+ temp_sensor->registers->bgap_counter);
+
+out:
+ mutex_unlock(&temp_sensor->sensor_mutex);
+ return count;
+}
+
+static int omap_temp_sensor_read_temp(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf)
+{
+ struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
+ int temp, ret = 0;
+
+ mutex_lock(&temp_sensor->sensor_mutex);
+
+ temp = omap_temp_sensor_readl(temp_sensor,
+ temp_sensor->registers->temp_sensor_ctrl);
+ temp &= temp_sensor->registers->bgap_dtemp_mask;
+
+ if (!temp_sensor->is_efuse_valid)
+ dev_err(dev, "Invalid EFUSE, Non-trimmed BGAP, Temp not accurate\n");
+
+ /* look up for temperature in the table and return the temperature */
+ if (temp < OMAP_ADC_START_VALUE || temp > OMAP_ADC_END_VALUE) {
+ dev_err(dev, "invalid adc code reported %d", temp);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ temp = adc_to_temp[temp - OMAP_ADC_START_VALUE];
+
+out:
+ mutex_unlock(&temp_sensor->sensor_mutex);
+ if (!ret)
+ return sprintf(buf, "%d\n", temp);
+
+ return ret;
+}
+
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, omap_temp_sensor_read_temp,
+ NULL, 0);
+static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp_max,
+ set_temp_max, 0);
+static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO, show_temp_max_hyst,
+ set_temp_max_hyst, 0);
+static SENSOR_DEVICE_ATTR(update_rate, S_IWUSR | S_IRUGO, show_update_rate,
+ set_update_rate, 0);
+
+static struct attribute *omap_temp_sensor_attributes[] = {
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ &sensor_dev_attr_temp1_max.dev_attr.attr,
+ &sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
+ &sensor_dev_attr_update_rate.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group omap_temp_sensor_group = {
+ .attrs = omap_temp_sensor_attributes,
+};
+
+static int omap_temp_sensor_clk_enable(struct omap_temp_sensor *temp_sensor)
+{
+ u32 ret = 0;
+
+ if (temp_sensor->clk_on) {
+ dev_err(temp_sensor->hwmon_dev, "clock already on\n");
+ goto out;
+ }
+
+ ret = pm_runtime_get_sync(temp_sensor->hwmon_dev);
+ if (ret < 0) {
+ dev_err(temp_sensor->hwmon_dev, "get sync failed\n");
+ goto out;
+ }
+
+ temp_sensor->clk_on = 1;
+
+out:
+ return ret;
+}
+
+static int omap_temp_sensor_clk_disable(struct omap_temp_sensor *temp_sensor)
+{
+ u32 ret = 0;
+
+ if (!temp_sensor->clk_on) {
+ dev_err(temp_sensor->hwmon_dev, "clock already off\n");
+ goto out;
+ }
+
+ /* Gate the clock */
+ ret = pm_runtime_put_sync(temp_sensor->hwmon_dev);
+ if (ret < 0) {
+ dev_err(temp_sensor->hwmon_dev, "put sync failed\n");
+ goto out;
+ }
+ temp_sensor->clk_on = 0;
+
+out:
+ return ret;
+}
+
+static irqreturn_t omap_talert_irq_handler(int irq, void *data)
+{
+ struct omap_temp_sensor *temp_sensor;
+ int t_hot, t_cold, temp;
+
+ temp_sensor = data;
+ mutex_lock(&temp_sensor->sensor_mutex);
+
+ /* Read the status of t_hot */
+ t_hot = omap_temp_sensor_readl(temp_sensor,
+ temp_sensor->registers->bgap_status)
+ & temp_sensor->registers->status_hot_mask;
+
+ /* Read the status of t_cold */
+ t_cold = omap_temp_sensor_readl(temp_sensor,
+ temp_sensor->registers->bgap_status)
+ & temp_sensor->registers->status_cold_mask;
+
+ temp = omap_temp_sensor_readl(temp_sensor,
+ temp_sensor->registers->bgap_mask_ctrl);
+ /*
+ * One TALERT interrupt: Two sources
+ * If the interrupt is due to t_hot then mask t_hot and
+ * and unmask t_cold else mask t_cold and unmask t_hot
+ */
+ if (t_hot) {
+ temp &= ~(temp_sensor->registers->mask_hot_mask);
+ temp |= temp_sensor->registers->mask_cold_mask;
+ } else if (t_cold) {
+ temp &= ~(temp_sensor->registers->mask_cold_mask);
+ temp |= temp_sensor->registers->mask_hot_mask;
+ }
+
+ omap_temp_sensor_writel(temp_sensor, temp,
+ temp_sensor->registers->bgap_mask_ctrl);
+
+ /* kobject_uvent to user space telling thermal threshold crossed */
+ kobject_uevent(&temp_sensor->hwmon_dev->kobj, KOBJ_CHANGE);
+
+ mutex_unlock(&temp_sensor->sensor_mutex);
+
+ return IRQ_HANDLED;
+}
+
+static int __devinit omap_temp_sensor_probe(struct platform_device *pdev)
+{
+ struct omap_temp_sensor_pdata *pdata = pdev->dev.platform_data;
+ struct omap_temp_sensor *temp_sensor;
+ struct resource *mem;
+ int ret = 0;
+ int val, clk_rate;
+
+ if (!pdata) {
+ dev_err(&pdev->dev, "platform data missing\n");
+ return -EINVAL;
+ }
+
+ temp_sensor = kzalloc(sizeof(*temp_sensor), GFP_KERNEL);
+ if (!temp_sensor)
+ return -ENOMEM;
+
+ mutex_init(&temp_sensor->sensor_mutex);
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem) {
+ dev_err(&pdev->dev, "no mem resource\n");
+ ret = -ENOMEM;
+ goto plat_res_err;
+ }
+
+ temp_sensor->irq = platform_get_irq_byname(pdev, "thermal_alert");
+ if (temp_sensor->irq < 0) {
+ dev_err(&pdev->dev, "get_irq_byname failed\n");
+ ret = temp_sensor->irq;
+ goto plat_res_err;
+ }
+
+ temp_sensor->phy_base = ioremap(mem->start, resource_size(mem));
+ temp_sensor->clock = NULL;
+ temp_sensor->registers = pdata->registers;
+ temp_sensor->hwmon_dev = &pdev->dev;
+
+ pm_runtime_enable(&pdev->dev);
+ pm_runtime_irq_safe(&pdev->dev);
+
+ /*
+ * check if the efuse has a non-zero value if not
+ * it is an untrimmed sample and the temperatures
+ * may not be accurate
+ */
+
+ if (omap_temp_sensor_readl(temp_sensor,
+ temp_sensor->registers->bgap_efuse))
+ temp_sensor->is_efuse_valid = 1;
+
+ platform_set_drvdata(pdev, temp_sensor);
+ dev_set_drvdata(&pdev->dev, temp_sensor);
+ temp_sensor->clock = clk_get(temp_sensor->hwmon_dev, "fck");
+ if (IS_ERR(temp_sensor->clock)) {
+ ret = PTR_ERR(temp_sensor->clock);
+ dev_err(temp_sensor->hwmon_dev,
+ "unable to get fclk: %d\n", ret);
+ ret = -EINVAL;
+ goto plat_res_err;
+ }
+
+ ret = omap_temp_sensor_clk_enable(temp_sensor);
+ if (ret) {
+ dev_err(&pdev->dev, "Cannot enable temp sensor\n");
+ goto clken_err;
+ }
+
+ clk_rate = clk_round_rate(temp_sensor->clock, 2000000);
+ if (clk_rate < 1000000 || clk_rate == 0xffffffff) {
+ dev_err(&pdev->dev, "Error round rate\n");
+ ret = -EINVAL;
+ goto clken_err;
+ }
+
+ ret = clk_set_rate(temp_sensor->clock, clk_rate);
+ if (ret) {
+ dev_err(&pdev->dev, "Cannot set clock rate\n");
+ goto clken_err;
+ }
+
+ temp_sensor->clk_rate = clk_rate;
+ omap_enable_continuous_mode(temp_sensor, 1);
+ omap_configure_temp_sensor_thresholds(temp_sensor);
+ /* 1 ms */
+ omap_configure_temp_sensor_counter(temp_sensor, 1);
+
+ /* Wait till the first conversion is done wait for at least 1ms */
+ usleep_range(1000, 2000);
+
+ /* Read the temperature once due to hw issue*/
+ omap_temp_sensor_readl(temp_sensor,
+ temp_sensor->registers->temp_sensor_ctrl);
+
+ /* Set 2 seconds time as default counter */
+ omap_configure_temp_sensor_counter(temp_sensor,
+ temp_sensor->clk_rate * 2);
+
+ temp_sensor->hwmon_dev = hwmon_device_register(&pdev->dev);
+ if (IS_ERR(temp_sensor->hwmon_dev)) {
+ dev_err(&pdev->dev, "hwmon_device_register failed.\n");
+ ret = PTR_ERR(temp_sensor->hwmon_dev);
+ goto hwmon_reg_err;
+ }
+
+ ret = sysfs_create_group(&pdev->dev.kobj,
+ &omap_temp_sensor_group);
+ if (ret) {
+ dev_err(&pdev->dev, "could not create sysfs files\n");
+ goto sysfs_create_err;
+ }
+
+ kobject_uevent(&temp_sensor->hwmon_dev->kobj, KOBJ_ADD);
+
+ ret = request_threaded_irq(temp_sensor->irq, NULL,
+ omap_talert_irq_handler, IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ "temp_sensor", temp_sensor);
+ if (ret) {
+ dev_err(&pdev->dev, "Request threaded irq failed.\n");
+ goto req_irq_err;
+ }
+
+ /* unmask the T_COLD and unmask T_HOT at init */
+ val = omap_temp_sensor_readl(temp_sensor,
+ temp_sensor->registers->bgap_mask_ctrl);
+ val |= temp_sensor->registers->mask_cold_mask
+ | temp_sensor->registers->mask_hot_mask;
+
+ omap_temp_sensor_writel(temp_sensor, val,
+ temp_sensor->registers->bgap_mask_ctrl);
+
+ return 0;
+
+req_irq_err:
+ kobject_uevent(&temp_sensor->hwmon_dev->kobj, KOBJ_REMOVE);
+ sysfs_remove_group(&temp_sensor->hwmon_dev->kobj,
+ &omap_temp_sensor_group);
+sysfs_create_err:
+ hwmon_device_unregister(&pdev->dev);
+hwmon_reg_err:
+ omap_temp_sensor_clk_disable(temp_sensor);
+clken_err:
+ clk_put(temp_sensor->clock);
+plat_res_err:
+ mutex_destroy(&temp_sensor->sensor_mutex);
+ kfree(temp_sensor);
+ return ret;
+}
+
+static int __devexit omap_temp_sensor_remove(struct platform_device *pdev)
+{
+ struct omap_temp_sensor *temp_sensor = platform_get_drvdata(pdev);
+
+ hwmon_device_unregister(&pdev->dev);
+ kobject_uevent(&temp_sensor->hwmon_dev->kobj, KOBJ_REMOVE);
+ sysfs_remove_group(&temp_sensor->hwmon_dev->kobj,
+ &omap_temp_sensor_group);
+ omap_temp_sensor_clk_disable(temp_sensor);
+ clk_put(temp_sensor->clock);
+ platform_set_drvdata(pdev, NULL);
+ free_irq(temp_sensor->irq, temp_sensor);
+ mutex_destroy(&temp_sensor->sensor_mutex);
+ kfree(temp_sensor);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static void omap_temp_sensor_save_ctxt(struct omap_temp_sensor *temp_sensor)
+{
+ temp_sensor->temp_sensor_ctrl = omap_temp_sensor_readl(temp_sensor,
+ temp_sensor->registers->temp_sensor_ctrl);
+ temp_sensor->bg_ctrl = omap_temp_sensor_readl(temp_sensor,
+ temp_sensor->registers->bgap_mask_ctrl);
+ temp_sensor->bg_counter = omap_temp_sensor_readl(temp_sensor,
+ temp_sensor->registers->bgap_counter);
+ temp_sensor->bg_threshold = omap_temp_sensor_readl(temp_sensor,
+ temp_sensor->registers->bgap_threshold);
+ temp_sensor->temp_sensor_tshut_threshold =
+ omap_temp_sensor_readl(temp_sensor,
+ temp_sensor->registers->thsut_threshold);
+}
+
+static void omap_temp_sensor_restore_ctxt(struct omap_temp_sensor *temp_sensor)
+{
+ omap_temp_sensor_writel(temp_sensor,
+ temp_sensor->temp_sensor_ctrl,
+ temp_sensor->registers->temp_sensor_ctrl);
+ omap_temp_sensor_writel(temp_sensor,
+ temp_sensor->bg_ctrl,
+ temp_sensor->registers->bgap_mask_ctrl);
+ omap_temp_sensor_writel(temp_sensor,
+ temp_sensor->bg_counter,
+ temp_sensor->registers->bgap_counter);
+ omap_temp_sensor_writel(temp_sensor,
+ temp_sensor->bg_threshold,
+ temp_sensor->registers->bgap_threshold);
+ omap_temp_sensor_writel(temp_sensor,
+ temp_sensor->temp_sensor_tshut_threshold,
+ temp_sensor->registers->thsut_threshold);
+}
+
+static int omap_temp_sensor_suspend(struct device *dev)
+{
+ struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
+
+ omap_temp_sensor_save_ctxt(temp_sensor);
+
+ return 0;
+}
+
+static int omap_temp_sensor_resume(struct device *dev)
+{
+ struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
+
+ omap_temp_sensor_restore_ctxt(temp_sensor);
+
+ return 0;
+}
+
+static int omap_temp_sensor_runtime_suspend(struct device *dev)
+{
+ struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
+
+ omap_temp_sensor_save_ctxt(temp_sensor);
+
+ return 0;
+}
+
+static int omap_temp_sensor_runtime_resume(struct device *dev)
+{
+ static int context_loss_count;
+ int temp;
+ struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
+
+ temp = omap_device_get_context_loss_count(to_platform_device(dev));
+
+ if (temp != context_loss_count && context_loss_count != 0)
+ omap_temp_sensor_restore_ctxt(temp_sensor);
+
+ context_loss_count = temp;
+
+ return 0;
+}
+
+static const struct dev_pm_ops omap_temp_sensor_dev_pm_ops = {
+ .suspend = omap_temp_sensor_suspend,
+ .resume = omap_temp_sensor_resume,
+ .runtime_suspend = omap_temp_sensor_runtime_suspend,
+ .runtime_resume = omap_temp_sensor_runtime_resume,
+};
+
+#endif
+
+static struct platform_driver omap_temp_sensor_driver = {
+ .probe = omap_temp_sensor_probe,
+ .remove = omap_temp_sensor_remove,
+ .driver = {
+ .name = "omap_temp_sensor",
+#ifdef CONFIG_PM
+ .pm = &omap_temp_sensor_dev_pm_ops,
+#endif
+ },
+};
+
+int __init omap_temp_sensor_init(void)
+{
+ return platform_driver_register(&omap_temp_sensor_driver);
+}
+module_init(omap_temp_sensor_init);
+
+static void __exit omap_temp_sensor_exit(void)
+{
+ platform_driver_unregister(&omap_temp_sensor_driver);
+}
+module_exit(omap_temp_sensor_exit);
+
+MODULE_DESCRIPTION("OMAP446X temperature sensor Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRIVER_NAME);
+MODULE_AUTHOR("J Keerthy <j-keerthy@ti.com>");
--
1.7.0.4
^ permalink raw reply related [flat|nested] 35+ messages in thread
* Re: [RFC PATCH 3/6 V2] OMAP4460: Temperature sensor data
2011-08-18 10:52 ` [RFC PATCH 3/6 V2] OMAP4460: Temperature sensor data Keerthy
@ 2011-08-18 11:32 ` Felipe Balbi
0 siblings, 0 replies; 35+ messages in thread
From: Felipe Balbi @ 2011-08-18 11:32 UTC (permalink / raw)
To: Keerthy; +Cc: linux-omap, tony
[-- Attachment #1: Type: text/plain, Size: 6092 bytes --]
Hi,
On Thu, Aug 18, 2011 at 04:22:12PM +0530, Keerthy wrote:
> The register set and the
> bit fields might vary across OMAP versions. Hence
> creating a structure comprising of all the registers
> and bit fields to make the driver uniform for all the
> versions with different register sets. The data file
> contains the structure populated with register offsets
> and bit fields corresponding to OMAP4460 on die sensor.
>
> Signed-off-by: Keerthy <j-keerthy@ti.com>
> Cc: tony@atomide.com
> ---
> arch/arm/mach-omap2/Makefile | 2 +-
> arch/arm/mach-omap2/temp_sensor4460_data.c | 63 ++++++++++++++
> .../plat-omap/include/plat/temperature_sensor.h | 89 ++++++++++++++++++++
> 3 files changed, 153 insertions(+), 1 deletions(-)
> create mode 100644 arch/arm/mach-omap2/temp_sensor4460_data.c
> create mode 100644 arch/arm/plat-omap/include/plat/temperature_sensor.h
>
> diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile
> index fb02937..2d5d981 100644
> --- a/arch/arm/mach-omap2/Makefile
> +++ b/arch/arm/mach-omap2/Makefile
> @@ -86,7 +86,7 @@ obj-$(CONFIG_ARCH_OMAP3) += prcm.o cm2xxx_3xxx.o prm2xxx_3xxx.o \
> obj-$(CONFIG_ARCH_OMAP4) += prcm.o cm2xxx_3xxx.o cminst44xx.o \
> cm44xx.o prcm_mpu44xx.o \
> prminst44xx.o vc44xx_data.o \
> - vp44xx_data.o
> + vp44xx_data.o temp_sensor4460_data.o
>
> # OMAP voltage domains
> ifeq ($(CONFIG_PM),y)
> diff --git a/arch/arm/mach-omap2/temp_sensor4460_data.c b/arch/arm/mach-omap2/temp_sensor4460_data.c
> new file mode 100644
> index 0000000..294963d
> --- /dev/null
> +++ b/arch/arm/mach-omap2/temp_sensor4460_data.c
> @@ -0,0 +1,63 @@
> +/*
> + * OMAP4460 on die Temperature sensor data file
> + *
> + * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
> + * Author: J Keerthy <j-keerthy@ti.com>
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * 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.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
> + * 02110-1301 USA
> + *
> + */
> +
> +#include <linux/slab.h>
> +#include "control.h"
> +#include <plat/temperature_sensor.h>
> +
> +/*
> + * OMAP4460 has one instance of thermal sensor for MPU
> + * need to describe the individual bit fields
> + */
> +struct omap_temp_sensor_registers omap_mpu_temp_sensor_registers = {
> + .temp_sensor_ctrl = OMAP4460_TEMP_SENSOR_CTRL_OFFSET,
> + .bgap_tempsoff_mask = OMAP4460_BGAP_TEMPSOFF_MASK,
> + .bgap_soc_mask = OMAP4460_BGAP_TEMP_SENSOR_SOC_MASK,
> + .bgap_eocz_mask = OMAP4460_BGAP_TEMP_SENSOR_EOCZ_MASK,
> + .bgap_dtemp_mask = OMAP4460_BGAP_TEMP_SENSOR_DTEMP_MASK,
> +
> + .bgap_mask_ctrl = OMAP4460_BGAP_CTRL_OFFSET,
> + .mask_hot_mask = OMAP4460_MASK_HOT_MASK,
> + .mask_cold_mask = OMAP4460_MASK_COLD_MASK,
> +
> + .bgap_mode_ctrl = OMAP4460_BGAP_CTRL_OFFSET,
> + .mode_ctrl_mask = OMAP4460_SINGLE_MODE_MASK,
> +
> + .bgap_counter = OMAP4460_BGAP_COUNTER_OFFSET,
> + .counter_mask = OMAP4460_COUNTER_MASK,
> +
> + .bgap_threshold = OMAP4460_BGAP_THRESHOLD_OFFSET,
> + .threshold_thot_mask = OMAP4460_T_HOT_MASK,
> + .threshold_tcold_mask = OMAP4460_T_COLD_MASK,
> +
> + .thsut_threshold = OMAP4460_BGAP_TSHUT_OFFSET,
> + .tshut_hot_mask = OMAP4460_TSHUT_HOT_MASK,
> + .tshut_cold_mask = OMAP4460_TSHUT_COLD_MASK,
> +
> + .bgap_status = OMAP4460_BGAP_STATUS_OFFSET,
> + .status_clean_stop_mask = OMAP4460_CLEAN_STOP_MASK,
> + .status_bgap_alert_mask = OMAP4460_BGAP_ALERT_MASK,
> + .status_hot_mask = OMAP4460_HOT_FLAG_MASK,
> + .status_cold_mask = OMAP4460_COLD_FLAG_MASK,
> +
> + .bgap_efuse = OMAP4460_FUSE_OPP_BGAP,
> +};
> diff --git a/arch/arm/plat-omap/include/plat/temperature_sensor.h b/arch/arm/plat-omap/include/plat/temperature_sensor.h
> new file mode 100644
> index 0000000..a3186f9
> --- /dev/null
> +++ b/arch/arm/plat-omap/include/plat/temperature_sensor.h
> @@ -0,0 +1,89 @@
> +/*
> + * OMAP Temperature sensor header file
> + *
> + * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
> + * Author: J Keerthy <j-keerthy@ti.com>
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * 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.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
> + * 02110-1301 USA
> + *
> + */
> +
> +#ifndef __ARCH_ARM_PLAT_OMAP_INCLUDE_PLAT_TEMPERATURE_SENSOR_H
> +#define __ARCH_ARM_PLAT_OMAP_INCLUDE_PLAT_TEMPERATURE_SENSOR_H
> +
> +/* Offsets from the base of temperature sensor registers */
> +
> +#define OMAP4460_TEMP_SENSOR_CTRL_OFFSET 0x00
> +#define OMAP4460_BGAP_CTRL_OFFSET 0x4c
> +#define OMAP4460_BGAP_COUNTER_OFFSET 0x50
> +#define OMAP4460_BGAP_THRESHOLD_OFFSET 0x54
> +#define OMAP4460_BGAP_TSHUT_OFFSET 0x58
> +#define OMAP4460_BGAP_STATUS_OFFSET 0x5c
> +#define OMAP4460_FUSE_OPP_BGAP -0xcc
> +
> +/*
> + * The register offsets and but fields might change across
^ bit fields.
--
balbi
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 490 bytes --]
^ permalink raw reply [flat|nested] 35+ messages in thread
* Re: [RFC PATCH 6/6 V2] hwmon: OMAP4: On die temperature sensor driver
2011-08-18 10:52 ` [RFC PATCH 6/6 V2] hwmon: OMAP4: On die temperature sensor driver Keerthy
@ 2011-08-18 11:37 ` Felipe Balbi
2011-08-19 2:13 ` [lm-sensors] [RFC PATCH 6/6 V2] hwmon: OMAP4: On die Guenter Roeck
2011-08-19 5:34 ` [lm-sensors] [RFC PATCH 6/6 V2] hwmon: OMAP4: On die Todd Poynor
2 siblings, 0 replies; 35+ messages in thread
From: Felipe Balbi @ 2011-08-18 11:37 UTC (permalink / raw)
To: Keerthy; +Cc: linux-omap, Jean Delvare, Guenter Roeck, lm-sensors
[-- Attachment #1: Type: text/plain, Size: 35223 bytes --]
Hi,
On Thu, Aug 18, 2011 at 04:22:15PM +0530, Keerthy wrote:
> On chip temperature sensor driver. The driver monitors the temperature of
> the MPU subsystem of the OMAP4. It sends notifications to the user space if
> the temperature crosses user defined thresholds via kobject_uevent interface.
> The user is allowed to configure the temperature thresholds vis sysfs nodes
> exposed using hwmon interface.
>
> Signed-off-by: Keerthy <j-keerthy@ti.com>
> Cc: Jean Delvare <khali@linux-fr.org>
> Cc: Guenter Roeck <guenter.roeck@ericsson.com>
> Cc: lm-sensors@lm-sensors.org
> ---
> Documentation/hwmon/omap_temp_sensor | 27 +
> drivers/hwmon/Kconfig | 11 +
> drivers/hwmon/Makefile | 1 +
> drivers/hwmon/omap_temp_sensor.c | 918 ++++++++++++++++++++++++++++++++++
> 4 files changed, 957 insertions(+), 0 deletions(-)
> create mode 100644 Documentation/hwmon/omap_temp_sensor
> create mode 100644 drivers/hwmon/omap_temp_sensor.c
>
> diff --git a/Documentation/hwmon/omap_temp_sensor b/Documentation/hwmon/omap_temp_sensor
> new file mode 100644
> index 0000000..e01a6d6
> --- /dev/null
> +++ b/Documentation/hwmon/omap_temp_sensor
> @@ -0,0 +1,27 @@
> +Kernel driver omap_temp_sensor
> +==============================
> +
> +Supported chips:
> + * Texas Instruments OMAP4460
> + Prefix: 'omap_temp_sensor'
> + Datasheet: http://focus.ti.com/docs/prod/folders/print/tmp102.html
> +
> +Author:
> + J Keerthy <j-keerthy@ti.com>
> +
> +Description
> +-----------
> +
> +The Texas Instruments OMAP4 family of chips have a bandgap temperature sensor.
> +The temperature sensor feature is used to convert the temperature of the device
> +into a decimal value coded on 10 bits. An internal ADC is used for conversion.
> +The recommended operating temperatures must be in the range -40 degree Celsius
> +to 123 degree celsius for standard conversion.
> +The thresholds are programmable and upon crossing the thresholds an interrupt
> +is generated. The OMAP temperature sensor has a programmable update rate in
> +milli seconds.
> +(Currently the driver programs a default of 2000 milli seconds).
> +
> +The driver provides the common sysfs-interface for temperatures (see
> +Documentation/hwmon/sysfs-interface under Temperatures).
> +
> diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
> index 5f888f7..9c9cd8b 100644
> --- a/drivers/hwmon/Kconfig
> +++ b/drivers/hwmon/Kconfig
> @@ -323,6 +323,17 @@ config SENSORS_F71805F
> This driver can also be built as a module. If so, the module
> will be called f71805f.
>
> +config SENSORS_OMAP_BANDGAP_TEMP_SENSOR
> + bool "OMAP on-die temperature sensor hwmon driver"
> + depends on HWMON && ARCH_OMAP && OMAP_TEMP_SENSOR
> + help
> + If you say yes here you get support for hardware
> + monitoring features of the OMAP on die temperature
> + sensor.
> +
> + Continuous conversion programmable delay
> + mode is used for temperature conversion.
> +
> config SENSORS_F71882FG
> tristate "Fintek F71882FG and compatibles"
> help
> diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
> index 28061cf..d0f89f5 100644
> --- a/drivers/hwmon/Makefile
> +++ b/drivers/hwmon/Makefile
> @@ -91,6 +91,7 @@ obj-$(CONFIG_SENSORS_MAX6639) += max6639.o
> obj-$(CONFIG_SENSORS_MAX6642) += max6642.o
> obj-$(CONFIG_SENSORS_MAX6650) += max6650.o
> obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o
> +obj-$(CONFIG_SENSORS_OMAP_BANDGAP_TEMP_SENSOR) += omap_temp_sensor.o
> obj-$(CONFIG_SENSORS_PC87360) += pc87360.o
> obj-$(CONFIG_SENSORS_PC87427) += pc87427.o
> obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o
> diff --git a/drivers/hwmon/omap_temp_sensor.c b/drivers/hwmon/omap_temp_sensor.c
> new file mode 100644
> index 0000000..586a361
> --- /dev/null
> +++ b/drivers/hwmon/omap_temp_sensor.c
> @@ -0,0 +1,918 @@
> +/*
> + * OMAP4 Temperature sensor driver file
> + *
> + * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
> + * Author: J Keerthy <j-keerthy@ti.com>
> + * Author: Moiz Sonasath <m-sonasath@ti.com>
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * 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.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
> + * 02110-1301 USA
> + *
> + */
> +
> +#include <linux/interrupt.h>
> +#include <linux/clk.h>
> +#include <linux/io.h>
> +#include <linux/slab.h>
> +#include <linux/init.h>
> +#include <plat/omap_device.h>
> +#include <linux/kernel.h>
> +#include <linux/device.h>
> +#include <linux/jiffies.h>
> +#include <linux/hwmon.h>
> +#include <linux/hwmon-sysfs.h>
> +#include <linux/stddef.h>
> +#include <linux/sysfs.h>
> +#include <linux/err.h>
> +#include <linux/types.h>
> +#include <linux/mutex.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/delay.h>
> +#include <plat/temperature_sensor.h>
> +
> +#define TSHUT_THRESHOLD_HOT 122000 /* 122 deg C */
> +#define TSHUT_THRESHOLD_COLD 100000 /* 100 deg C */
> +#define BGAP_THRESHOLD_T_HOT 73000 /* 73 deg C */
> +#define BGAP_THRESHOLD_T_COLD 71000 /* 71 deg C */
> +#define OMAP_ADC_START_VALUE 530
> +#define OMAP_ADC_END_VALUE 923
> +
> +/*
> + * omap_temp_sensor structure
> + * @hwmon_dev - device pointer
> + * @clock - Clock pointer
> + * @registers - Pointer to structure with register offsets and bitfields
> + * @sensor_mutex - Mutex for sysfs, irq and PM
> + * @irq - MPU Irq number for thermal alert
> + * @phy_base - Physical base of the temp I/O
> + * @clk_rate - Holds current clock rate
> + * @temp_sensor_ctrl - temp sensor control register value
> + * @bg_ctrl - bandgap ctrl register value
> + * @bg_counter - bandgap counter value
> + * @bg_threshold - bandgap threshold register value
> + * @temp_sensor_tshut_threshold - bandgap tshut register value
> + * @is_efuse_valid - Flag to determine if efuse is valid or not
> + * @clk_on - Manages the current clock state
> + */
> +struct omap_temp_sensor {
> + struct device *hwmon_dev;
> + struct clk *clock;
> + struct omap_temp_sensor_registers *registers;
> + struct mutex sensor_mutex; /* Mutex for sysfs, irq and PM */
> + unsigned int irq;
> + void __iomem *phy_base;
> + u32 clk_rate;
> + u32 temp_sensor_ctrl;
> + u32 bg_ctrl;
> + u32 bg_counter;
> + u32 bg_threshold;
> + u32 temp_sensor_tshut_threshold;
> + bool is_efuse_valid;
> + bool clk_on;
> +};
> +
> +/*
> + * Temperature values in milli degree celsius
> + * ADC code values from 530 to 923
> + */
> +static int adc_to_temp[] = {
> + -40000, -40000, -40000, -40000, -39800, -39400, -39000, -38600, -38200,
> + -37800, -37300, -36800, -36400, -36000, -35600, -35200, -34800,
> + -34300, -33800, -33400, -33000, -32600, -32200, -31800, -31300,
> + -30800, -30400, -30000, -29600, -29200, -28700, -28200, -27800,
> + -27400, -27000, -26600, -26200, -25700, -25200, -24800, -24400,
> + -24000, -23600, -23200, -22700, -22200, -21800, -21400, -21000,
> + -20600, -20200, -19700, -19200, -18800, -18400, -18000, -17600,
> + -17200, -16700, -16200, -15800, -15400, -15000, -14600, -14200,
> + -13700, -13200, -12800, -12400, -12000, -11600, -11200, -10700,
> + -10200, -9800, -9400, -9000, -8600, -8200, -7700, -7200, -6800,
> + -6400, -6000, -5600, -5200, -4800, -4300, -3800, -3400, -3000,
> + -2600, -2200, -1800, -1300, -800, -400, 0, 400, 800, 1200, 1600,
> + 2100, 2600, 3000, 3400, 3800, 4200, 4600, 5100, 5600, 6000, 6400,
> + 6800, 7200, 7600, 8000, 8500, 9000, 9400, 9800, 10200, 10600, 11000,
> + 11400, 11900, 12400, 12800, 13200, 13600, 14000, 14400, 14800,
> + 15300, 15800, 16200, 16600, 17000, 17400, 17800, 18200, 18700,
> + 19200, 19600, 20000, 20400, 20800, 21200, 21600, 22100, 22600,
> + 23000, 23400, 23800, 24200, 24600, 25000, 25400, 25900, 26400,
> + 26800, 27200, 27600, 28000, 28400, 28800, 29300, 29800, 30200,
> + 30600, 31000, 31400, 31800, 32200, 32600, 33100, 33600, 34000,
> + 34400, 34800, 35200, 35600, 36000, 36400, 36800, 37300, 37800,
> + 38200, 38600, 39000, 39400, 39800, 40200, 40600, 41100, 41600,
> + 42000, 42400, 42800, 43200, 43600, 44000, 44400, 44800, 45300,
> + 45800, 46200, 46600, 47000, 47400, 47800, 48200, 48600, 49000,
> + 49500, 50000, 50400, 50800, 51200, 51600, 52000, 52400, 52800,
> + 53200, 53700, 54200, 54600, 55000, 55400, 55800, 56200, 56600,
> + 57000, 57400, 57800, 58200, 58700, 59200, 59600, 60000, 60400,
> + 60800, 61200, 61600, 62000, 62400, 62800, 63300, 63800, 64200,
> + 64600, 65000, 65400, 65800, 66200, 66600, 67000, 67400, 67800,
> + 68200, 68700, 69200, 69600, 70000, 70400, 70800, 71200, 71600,
> + 72000, 72400, 72800, 73200, 73600, 74100, 74600, 75000, 75400,
> + 75800, 76200, 76600, 77000, 77400, 77800, 78200, 78600, 79000,
> + 79400, 79800, 80300, 80800, 81200, 81600, 82000, 82400, 82800,
> + 83200, 83600, 84000, 84400, 84800, 85200, 85600, 86000, 86400,
> + 86800, 87300, 87800, 88200, 88600, 89000, 89400, 89800, 90200,
> + 90600, 91000, 91400, 91800, 92200, 92600, 93000, 93400, 93800,
> + 94200, 94600, 95000, 95500, 96000, 96400, 96800, 97200, 97600,
> + 98000, 98400, 98800, 99200, 99600, 100000, 100400, 100800, 101200,
> + 101600, 102000, 102400, 102800, 103200, 103600, 104000, 104400,
> + 104800, 105200, 105600, 106100, 106600, 107000, 107400, 107800,
> + 108200, 108600, 109000, 109400, 109800, 110200, 110600, 111000,
> + 111400, 111800, 112200, 112600, 113000, 113400, 113800, 114200,
> + 114600, 115000, 115400, 115800, 116200, 116600, 117000, 117400,
> + 117800, 118200, 118600, 119000, 119400, 119800, 120200, 120600,
> + 121000, 121400, 121800, 122200, 122600, 123000
> +};
> +
> +static unsigned long omap_temp_sensor_readl(struct omap_temp_sensor
> + *temp_sensor, u32 reg)
> +{
> + return __raw_readl(temp_sensor->phy_base + reg);
> +}
> +
> +static void omap_temp_sensor_writel(struct omap_temp_sensor *temp_sensor,
> + u32 val, u32 reg)
> +{
> + __raw_writel(val, (temp_sensor->phy_base + reg));
> +}
> +
> +static int adc_to_temp_conversion(int adc_val)
> +{
> + return adc_to_temp[adc_val - OMAP_ADC_START_VALUE];
> +}
> +
> +static int temp_to_adc_conversion(long temp)
> +{
> + int i;
> +
> + for (i = 0; i <= OMAP_ADC_END_VALUE - OMAP_ADC_START_VALUE; i++)
> + if (temp < adc_to_temp[i])
> + return OMAP_ADC_START_VALUE + i - 1;
> +
> + return -EINVAL;
> +}
> +
> +static void omap_configure_temp_sensor_thresholds(struct omap_temp_sensor
> + *temp_sensor)
> +{
> + u32 temp, t_hot, t_cold, tshut_hot, tshut_cold;
> +
> + t_hot = temp_to_adc_conversion(BGAP_THRESHOLD_T_HOT);
> + t_cold = temp_to_adc_conversion(BGAP_THRESHOLD_T_COLD);
> +
> + /* Configure the TALERT thresholds */
> + temp = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_threshold);
> + temp |= (t_hot << __ffs(temp_sensor->registers->threshold_thot_mask)) |
> + (t_cold << __ffs(temp_sensor->registers->threshold_tcold_mask));
> + omap_temp_sensor_writel(temp_sensor, temp,
> + temp_sensor->registers->bgap_threshold);
> +
> + tshut_hot = temp_to_adc_conversion(TSHUT_THRESHOLD_HOT);
> + tshut_cold = temp_to_adc_conversion(TSHUT_THRESHOLD_COLD);
> +
> + /* Configure the TSHUT thresholds */
> + temp = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->thsut_threshold);
> + temp |= (tshut_hot << __ffs(temp_sensor->registers->tshut_hot_mask))
> + | (tshut_cold << __ffs(temp_sensor->registers->tshut_hot_mask));
> + omap_temp_sensor_writel(temp_sensor, temp,
> + temp_sensor->registers->thsut_threshold);
> +}
> +
> +static void omap_configure_temp_sensor_counter(struct omap_temp_sensor
> + *temp_sensor, u32 counter)
> +{
> + u32 val;
> +
> + val = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_counter);
> + val &= ~(temp_sensor->registers->counter_mask);
> + val |= (counter << __ffs(temp_sensor->registers->counter_mask));
> + omap_temp_sensor_writel(temp_sensor, val,
> + temp_sensor->registers->bgap_counter);
> +}
> +
> +static void omap_enable_continuous_mode(struct omap_temp_sensor *temp_sensor,
> + bool enable)
> +{
> + u32 val;
> +
> + val = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_mode_ctrl);
> +
> + if (enable)
> + val |= (1 << __ffs(temp_sensor->registers->mode_ctrl_mask));
> + else
> + val &= ~(temp_sensor->registers->mode_ctrl_mask);
> +
> + omap_temp_sensor_writel(temp_sensor, val,
> + temp_sensor->registers->bgap_mode_ctrl);
> +}
> +
> +/* Sysfs hook functions */
> +
> +static ssize_t show_temp_max(struct device *dev,
> + struct device_attribute *devattr, char *buf)
> +{
> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
> + int temp;
> +
> + mutex_lock(&temp_sensor->sensor_mutex);
> +
> + temp = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_threshold);
> + temp = (temp & temp_sensor->registers->threshold_thot_mask)
> + >> __ffs(temp_sensor->registers->threshold_thot_mask);
> +
> + if (temp < OMAP_ADC_START_VALUE || temp > OMAP_ADC_END_VALUE) {
> + dev_err(dev, "invalid value\n");
> + mutex_unlock(&temp_sensor->sensor_mutex);
> + return -EINVAL;
> + }
> +
> + temp = adc_to_temp_conversion(temp);
> + mutex_unlock(&temp_sensor->sensor_mutex);
> +
> + return snprintf(buf, 16, "%d\n", temp);
> +}
> +
> +static ssize_t set_temp_max(struct device *dev,
> + struct device_attribute *devattr,
> + const char *buf, size_t count)
> +{
> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
> + long val;
> + u32 reg_val, t_cold, t_hot, temp;
> +
> + mutex_lock(&temp_sensor->sensor_mutex);
> +
> + if (strict_strtol(buf, 10, &val)) {
> + count = -EINVAL;
> + goto out;
> + }
> +
> + t_hot = temp_to_adc_conversion(val);
> + if ((t_hot < OMAP_ADC_START_VALUE || t_hot > OMAP_ADC_END_VALUE)) {
> + dev_err(dev, "invalid range\n");
> + count = -EINVAL;
> + goto out;
> + }
> +
> + /* obtain the T cold value */
> + t_cold = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_threshold);
> + t_cold = (t_cold & temp_sensor->registers->threshold_tcold_mask) >>
> + __ffs(temp_sensor->registers->threshold_tcold_mask);
> +
> + if (t_hot < t_cold) {
> + dev_err(dev, "Error! T_HOT value lesser than T_COLD\n");
> + count = -EINVAL;
> + goto out;
> + }
> +
> + /* write the new t_hot value */
> + reg_val = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_threshold);
> + reg_val &= ~(temp_sensor->registers->threshold_thot_mask);
> + reg_val |= (t_hot <<
> + __ffs(temp_sensor->registers->threshold_thot_mask));
> + omap_temp_sensor_writel(temp_sensor, reg_val,
> + temp_sensor->registers->bgap_threshold);
> +
> + /* Read the current temperature */
> + temp = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->temp_sensor_ctrl);
> + temp &= (temp_sensor->registers->bgap_dtemp_mask);
> +
> + /*
> + * If user sets the HIGH threshold(t_hot) greater than the current
> + * temperature(temp) unmask the HOT interrupts
> + */
> + if (t_hot > temp) {
> + reg_val = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_mask_ctrl);
> + reg_val &= ~(temp_sensor->registers->mask_cold_mask);
> + reg_val |= temp_sensor->registers->mask_hot_mask;
> + omap_temp_sensor_writel(temp_sensor, reg_val,
> + temp_sensor->registers->bgap_mask_ctrl);
> + }
> +
> + /*
> + * If current temperature is in-between the hot and cold thresholds,
> + * Enable both masks.
> + */
> + if (temp > t_cold && temp < t_hot) {
> + reg_val = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_mask_ctrl);
> + reg_val |= temp_sensor->registers->mask_cold_mask;
> + reg_val |= temp_sensor->registers->mask_hot_mask;
> + omap_temp_sensor_writel(temp_sensor, reg_val,
> + OMAP4460_BGAP_CTRL_OFFSET);
> + }
> + /*
> + * else no need to do anything since HW will immediately compare
> + * the new threshold and generate interrupt accordingly
> + */
> +out:
> + mutex_unlock(&temp_sensor->sensor_mutex);
> + return count;
> +}
> +
> +static ssize_t show_temp_max_hyst(struct device *dev,
> + struct device_attribute *devattr, char *buf)
> +{
> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
> + u32 temp;
> +
> + mutex_lock(&temp_sensor->sensor_mutex);
> +
> + temp = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_threshold);
> + temp = (temp & temp_sensor->registers->threshold_tcold_mask) >>
> + __ffs(temp_sensor->registers->threshold_tcold_mask);
> +
> + if (temp < OMAP_ADC_START_VALUE || temp > OMAP_ADC_END_VALUE) {
> + dev_err(dev, "invalid value\n");
> + mutex_unlock(&temp_sensor->sensor_mutex);
> + return -EINVAL;
> + }
> +
> + temp = adc_to_temp_conversion(temp);
> + mutex_unlock(&temp_sensor->sensor_mutex);
> +
> + return snprintf(buf, 16, "%d\n", temp);
> +}
> +
> +static ssize_t set_temp_max_hyst(struct device *dev,
> + struct device_attribute *devattr,
> + const char *buf, size_t count)
> +{
> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
> + u32 reg_val, t_hot, t_cold, temp;
> + long val;
> +
> + mutex_lock(&temp_sensor->sensor_mutex);
> +
> + if (strict_strtol(buf, 10, &val)) {
> + count = -EINVAL;
> + goto out;
> + }
> +
> + t_cold = temp_to_adc_conversion(val);
> + if (t_cold < OMAP_ADC_START_VALUE || t_cold > OMAP_ADC_END_VALUE) {
> + dev_err(dev, "invalid range");
> + count = -EINVAL;
> + goto out;
> + }
> +
> + /* obtain the T HOT value */
> + t_hot = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_threshold);
> + t_hot = (t_hot & temp_sensor->registers->threshold_thot_mask) >>
> + __ffs(temp_sensor->registers->threshold_thot_mask);
> +
> + if (t_cold > t_hot) {
> + dev_err(dev, "Error! T_COLD value greater than T_HOT\n");
> + count = -EINVAL;
> + goto out;
> + }
> +
> + /* write the new t_cold value */
> + reg_val = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_threshold);
> + reg_val &= ~(temp_sensor->registers->threshold_tcold_mask);
> + reg_val |= (t_cold <<
> + __ffs(temp_sensor->registers->threshold_tcold_mask));
> + omap_temp_sensor_writel(temp_sensor, reg_val,
> + temp_sensor->registers->bgap_threshold);
> +
> + /* Read the current temperature */
> + temp = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->temp_sensor_ctrl);
> + temp &= (temp_sensor->registers->bgap_dtemp_mask);
> +
> + /*
> + * If user sets the LOW threshold(t_cold) lower than the current
> + * temperature(temp) unmask the COLD interrupts
> + */
> + if (t_cold < temp) {
> + reg_val = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_mask_ctrl);
> + reg_val &= ~(temp_sensor->registers->mask_hot_mask);
> + reg_val |= temp_sensor->registers->mask_cold_mask;
> + omap_temp_sensor_writel(temp_sensor, reg_val,
> + temp_sensor->registers->bgap_mask_ctrl);
> + }
> +
> + /*
> + * If current temperature is in-between the hot and cold thresholds,
> + * Enable both masks.
> + */
> + if (temp < t_hot && temp > t_cold) {
> + reg_val = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_mask_ctrl);
> + reg_val |= temp_sensor->registers->mask_cold_mask;
> + reg_val |= temp_sensor->registers->mask_hot_mask;
> + omap_temp_sensor_writel(temp_sensor, reg_val,
> + temp_sensor->registers->bgap_mask_ctrl);
> + }
> +
> + /*
> + * else no need to do anything since HW will immediately compare
> + * the new threshold and generate interrupt accordingly
> + */
> +
> +out:
> + mutex_unlock(&temp_sensor->sensor_mutex);
> + return count;
> +}
> +
> +static ssize_t show_update_rate(struct device *dev,
> + struct device_attribute *devattr, char *buf)
> +{
> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
> + u32 temp = 0, ret = 0;
> +
> + mutex_lock(&temp_sensor->sensor_mutex);
> +
> + if (!temp_sensor->clk_rate) {
> + dev_err(dev, "clk_rate is NULL\n");
> + ret = -EINVAL;
> + goto out;
> + }
> +
> + temp = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_counter);
> + temp = (temp & temp_sensor->registers->counter_mask) >>
> + __ffs(temp_sensor->registers->counter_mask);
> + temp = temp * 1000 / (temp_sensor->clk_rate);
> +
> +out:
> + mutex_unlock(&temp_sensor->sensor_mutex);
> + if (!ret)
> + return sprintf(buf, "%d\n", temp);
> +
> + return ret;
> +}
> +
> +static ssize_t set_update_rate(struct device *dev,
> + struct device_attribute *devattr,
> + const char *buf, size_t count)
> +{
> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
> + u32 reg_val;
> + long val;
> +
> + mutex_lock(&temp_sensor->sensor_mutex);
> +
> + if (strict_strtol(buf, 10, &val)) {
> + count = -EINVAL;
> + goto out;
> + }
> +
> + val *= temp_sensor->clk_rate / 1000;
> + reg_val = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_counter);
> +
> + reg_val &= ~(temp_sensor->registers->counter_mask);
> + reg_val |= val;
> + omap_temp_sensor_writel(temp_sensor, reg_val,
> + temp_sensor->registers->bgap_counter);
> +
> +out:
> + mutex_unlock(&temp_sensor->sensor_mutex);
> + return count;
> +}
> +
> +static int omap_temp_sensor_read_temp(struct device *dev,
> + struct device_attribute *devattr,
> + char *buf)
> +{
> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
> + int temp, ret = 0;
> +
> + mutex_lock(&temp_sensor->sensor_mutex);
> +
> + temp = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->temp_sensor_ctrl);
> + temp &= temp_sensor->registers->bgap_dtemp_mask;
> +
> + if (!temp_sensor->is_efuse_valid)
> + dev_err(dev, "Invalid EFUSE, Non-trimmed BGAP, Temp not accurate\n");
> +
> + /* look up for temperature in the table and return the temperature */
> + if (temp < OMAP_ADC_START_VALUE || temp > OMAP_ADC_END_VALUE) {
> + dev_err(dev, "invalid adc code reported %d", temp);
> + ret = -EINVAL;
> + goto out;
> + }
> +
> + temp = adc_to_temp[temp - OMAP_ADC_START_VALUE];
> +
> +out:
> + mutex_unlock(&temp_sensor->sensor_mutex);
> + if (!ret)
> + return sprintf(buf, "%d\n", temp);
> +
> + return ret;
> +}
> +
> +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, omap_temp_sensor_read_temp,
> + NULL, 0);
> +static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp_max,
> + set_temp_max, 0);
> +static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO, show_temp_max_hyst,
> + set_temp_max_hyst, 0);
> +static SENSOR_DEVICE_ATTR(update_rate, S_IWUSR | S_IRUGO, show_update_rate,
> + set_update_rate, 0);
> +
> +static struct attribute *omap_temp_sensor_attributes[] = {
> + &sensor_dev_attr_temp1_input.dev_attr.attr,
> + &sensor_dev_attr_temp1_max.dev_attr.attr,
> + &sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
> + &sensor_dev_attr_update_rate.dev_attr.attr,
> + NULL
> +};
> +
> +static const struct attribute_group omap_temp_sensor_group = {
> + .attrs = omap_temp_sensor_attributes,
> +};
> +
> +static int omap_temp_sensor_clk_enable(struct omap_temp_sensor *temp_sensor)
> +{
> + u32 ret = 0;
> +
> + if (temp_sensor->clk_on) {
> + dev_err(temp_sensor->hwmon_dev, "clock already on\n");
> + goto out;
> + }
> +
> + ret = pm_runtime_get_sync(temp_sensor->hwmon_dev);
> + if (ret < 0) {
> + dev_err(temp_sensor->hwmon_dev, "get sync failed\n");
> + goto out;
> + }
> +
> + temp_sensor->clk_on = 1;
> +
> +out:
> + return ret;
> +}
> +
> +static int omap_temp_sensor_clk_disable(struct omap_temp_sensor *temp_sensor)
> +{
> + u32 ret = 0;
> +
> + if (!temp_sensor->clk_on) {
> + dev_err(temp_sensor->hwmon_dev, "clock already off\n");
> + goto out;
> + }
> +
> + /* Gate the clock */
> + ret = pm_runtime_put_sync(temp_sensor->hwmon_dev);
> + if (ret < 0) {
> + dev_err(temp_sensor->hwmon_dev, "put sync failed\n");
> + goto out;
> + }
> + temp_sensor->clk_on = 0;
> +
> +out:
> + return ret;
> +}
> +
> +static irqreturn_t omap_talert_irq_handler(int irq, void *data)
> +{
> + struct omap_temp_sensor *temp_sensor;
> + int t_hot, t_cold, temp;
> +
> + temp_sensor = data;
> + mutex_lock(&temp_sensor->sensor_mutex);
> +
> + /* Read the status of t_hot */
> + t_hot = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_status)
> + & temp_sensor->registers->status_hot_mask;
> +
> + /* Read the status of t_cold */
> + t_cold = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_status)
> + & temp_sensor->registers->status_cold_mask;
> +
> + temp = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_mask_ctrl);
> + /*
> + * One TALERT interrupt: Two sources
> + * If the interrupt is due to t_hot then mask t_hot and
> + * and unmask t_cold else mask t_cold and unmask t_hot
> + */
> + if (t_hot) {
> + temp &= ~(temp_sensor->registers->mask_hot_mask);
> + temp |= temp_sensor->registers->mask_cold_mask;
> + } else if (t_cold) {
> + temp &= ~(temp_sensor->registers->mask_cold_mask);
> + temp |= temp_sensor->registers->mask_hot_mask;
> + }
> +
> + omap_temp_sensor_writel(temp_sensor, temp,
> + temp_sensor->registers->bgap_mask_ctrl);
> +
> + /* kobject_uvent to user space telling thermal threshold crossed */
> + kobject_uevent(&temp_sensor->hwmon_dev->kobj, KOBJ_CHANGE);
> +
> + mutex_unlock(&temp_sensor->sensor_mutex);
> +
> + return IRQ_HANDLED;
> +}
> +
> +static int __devinit omap_temp_sensor_probe(struct platform_device *pdev)
> +{
> + struct omap_temp_sensor_pdata *pdata = pdev->dev.platform_data;
> + struct omap_temp_sensor *temp_sensor;
> + struct resource *mem;
> + int ret = 0;
> + int val, clk_rate;
> +
> + if (!pdata) {
> + dev_err(&pdev->dev, "platform data missing\n");
> + return -EINVAL;
> + }
> +
> + temp_sensor = kzalloc(sizeof(*temp_sensor), GFP_KERNEL);
> + if (!temp_sensor)
> + return -ENOMEM;
> +
> + mutex_init(&temp_sensor->sensor_mutex);
> +
> + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + if (!mem) {
> + dev_err(&pdev->dev, "no mem resource\n");
> + ret = -ENOMEM;
> + goto plat_res_err;
> + }
> +
> + temp_sensor->irq = platform_get_irq_byname(pdev, "thermal_alert");
> + if (temp_sensor->irq < 0) {
> + dev_err(&pdev->dev, "get_irq_byname failed\n");
> + ret = temp_sensor->irq;
> + goto plat_res_err;
> + }
> +
> + temp_sensor->phy_base = ioremap(mem->start, resource_size(mem));
> + temp_sensor->clock = NULL;
> + temp_sensor->registers = pdata->registers;
> + temp_sensor->hwmon_dev = &pdev->dev;
> +
> + pm_runtime_enable(&pdev->dev);
> + pm_runtime_irq_safe(&pdev->dev);
> +
> + /*
> + * check if the efuse has a non-zero value if not
> + * it is an untrimmed sample and the temperatures
> + * may not be accurate
> + */
> +
> + if (omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_efuse))
> + temp_sensor->is_efuse_valid = 1;
> +
> + platform_set_drvdata(pdev, temp_sensor);
> + dev_set_drvdata(&pdev->dev, temp_sensor);
> + temp_sensor->clock = clk_get(temp_sensor->hwmon_dev, "fck");
> + if (IS_ERR(temp_sensor->clock)) {
> + ret = PTR_ERR(temp_sensor->clock);
> + dev_err(temp_sensor->hwmon_dev,
> + "unable to get fclk: %d\n", ret);
> + ret = -EINVAL;
> + goto plat_res_err;
> + }
> +
> + ret = omap_temp_sensor_clk_enable(temp_sensor);
> + if (ret) {
> + dev_err(&pdev->dev, "Cannot enable temp sensor\n");
> + goto clken_err;
> + }
> +
> + clk_rate = clk_round_rate(temp_sensor->clock, 2000000);
> + if (clk_rate < 1000000 || clk_rate == 0xffffffff) {
> + dev_err(&pdev->dev, "Error round rate\n");
> + ret = -EINVAL;
> + goto clken_err;
> + }
> +
> + ret = clk_set_rate(temp_sensor->clock, clk_rate);
> + if (ret) {
> + dev_err(&pdev->dev, "Cannot set clock rate\n");
> + goto clken_err;
> + }
> +
> + temp_sensor->clk_rate = clk_rate;
> + omap_enable_continuous_mode(temp_sensor, 1);
> + omap_configure_temp_sensor_thresholds(temp_sensor);
> + /* 1 ms */
> + omap_configure_temp_sensor_counter(temp_sensor, 1);
> +
> + /* Wait till the first conversion is done wait for at least 1ms */
> + usleep_range(1000, 2000);
> +
> + /* Read the temperature once due to hw issue*/
> + omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->temp_sensor_ctrl);
> +
> + /* Set 2 seconds time as default counter */
> + omap_configure_temp_sensor_counter(temp_sensor,
> + temp_sensor->clk_rate * 2);
> +
> + temp_sensor->hwmon_dev = hwmon_device_register(&pdev->dev);
> + if (IS_ERR(temp_sensor->hwmon_dev)) {
> + dev_err(&pdev->dev, "hwmon_device_register failed.\n");
> + ret = PTR_ERR(temp_sensor->hwmon_dev);
> + goto hwmon_reg_err;
> + }
> +
> + ret = sysfs_create_group(&pdev->dev.kobj,
> + &omap_temp_sensor_group);
> + if (ret) {
> + dev_err(&pdev->dev, "could not create sysfs files\n");
> + goto sysfs_create_err;
> + }
> +
> + kobject_uevent(&temp_sensor->hwmon_dev->kobj, KOBJ_ADD);
> +
> + ret = request_threaded_irq(temp_sensor->irq, NULL,
> + omap_talert_irq_handler, IRQF_TRIGGER_RISING | IRQF_ONESHOT,
> + "temp_sensor", temp_sensor);
> + if (ret) {
> + dev_err(&pdev->dev, "Request threaded irq failed.\n");
> + goto req_irq_err;
> + }
> +
> + /* unmask the T_COLD and unmask T_HOT at init */
> + val = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_mask_ctrl);
> + val |= temp_sensor->registers->mask_cold_mask
> + | temp_sensor->registers->mask_hot_mask;
> +
> + omap_temp_sensor_writel(temp_sensor, val,
> + temp_sensor->registers->bgap_mask_ctrl);
> +
> + return 0;
> +
> +req_irq_err:
> + kobject_uevent(&temp_sensor->hwmon_dev->kobj, KOBJ_REMOVE);
> + sysfs_remove_group(&temp_sensor->hwmon_dev->kobj,
> + &omap_temp_sensor_group);
> +sysfs_create_err:
> + hwmon_device_unregister(&pdev->dev);
> +hwmon_reg_err:
> + omap_temp_sensor_clk_disable(temp_sensor);
> +clken_err:
> + clk_put(temp_sensor->clock);
> +plat_res_err:
> + mutex_destroy(&temp_sensor->sensor_mutex);
> + kfree(temp_sensor);
> + return ret;
> +}
> +
> +static int __devexit omap_temp_sensor_remove(struct platform_device *pdev)
> +{
> + struct omap_temp_sensor *temp_sensor = platform_get_drvdata(pdev);
> +
> + hwmon_device_unregister(&pdev->dev);
> + kobject_uevent(&temp_sensor->hwmon_dev->kobj, KOBJ_REMOVE);
> + sysfs_remove_group(&temp_sensor->hwmon_dev->kobj,
> + &omap_temp_sensor_group);
> + omap_temp_sensor_clk_disable(temp_sensor);
> + clk_put(temp_sensor->clock);
> + platform_set_drvdata(pdev, NULL);
> + free_irq(temp_sensor->irq, temp_sensor);
> + mutex_destroy(&temp_sensor->sensor_mutex);
> + kfree(temp_sensor);
> +
> + return 0;
> +}
> +
> +#ifdef CONFIG_PM
> +static void omap_temp_sensor_save_ctxt(struct omap_temp_sensor *temp_sensor)
> +{
> + temp_sensor->temp_sensor_ctrl = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->temp_sensor_ctrl);
> + temp_sensor->bg_ctrl = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_mask_ctrl);
> + temp_sensor->bg_counter = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_counter);
> + temp_sensor->bg_threshold = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_threshold);
> + temp_sensor->temp_sensor_tshut_threshold =
> + omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->thsut_threshold);
> +}
> +
> +static void omap_temp_sensor_restore_ctxt(struct omap_temp_sensor *temp_sensor)
> +{
> + omap_temp_sensor_writel(temp_sensor,
> + temp_sensor->temp_sensor_ctrl,
> + temp_sensor->registers->temp_sensor_ctrl);
> + omap_temp_sensor_writel(temp_sensor,
> + temp_sensor->bg_ctrl,
> + temp_sensor->registers->bgap_mask_ctrl);
> + omap_temp_sensor_writel(temp_sensor,
> + temp_sensor->bg_counter,
> + temp_sensor->registers->bgap_counter);
> + omap_temp_sensor_writel(temp_sensor,
> + temp_sensor->bg_threshold,
> + temp_sensor->registers->bgap_threshold);
> + omap_temp_sensor_writel(temp_sensor,
> + temp_sensor->temp_sensor_tshut_threshold,
> + temp_sensor->registers->thsut_threshold);
> +}
> +
> +static int omap_temp_sensor_suspend(struct device *dev)
> +{
> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
> +
> + omap_temp_sensor_save_ctxt(temp_sensor);
> +
> + return 0;
> +}
> +
> +static int omap_temp_sensor_resume(struct device *dev)
> +{
> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
> +
> + omap_temp_sensor_restore_ctxt(temp_sensor);
> +
> + return 0;
> +}
> +
> +static int omap_temp_sensor_runtime_suspend(struct device *dev)
> +{
> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
> +
> + omap_temp_sensor_save_ctxt(temp_sensor);
> +
> + return 0;
> +}
> +
> +static int omap_temp_sensor_runtime_resume(struct device *dev)
> +{
> + static int context_loss_count;
> + int temp;
> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
> +
> + temp = omap_device_get_context_loss_count(to_platform_device(dev));
> +
> + if (temp != context_loss_count && context_loss_count != 0)
> + omap_temp_sensor_restore_ctxt(temp_sensor);
> +
> + context_loss_count = temp;
> +
> + return 0;
> +}
> +
> +static const struct dev_pm_ops omap_temp_sensor_dev_pm_ops = {
> + .suspend = omap_temp_sensor_suspend,
> + .resume = omap_temp_sensor_resume,
> + .runtime_suspend = omap_temp_sensor_runtime_suspend,
> + .runtime_resume = omap_temp_sensor_runtime_resume,
> +};
this is not enough... you need to populate the other fields. You might
want to use something like:
static const struct dev_pm_ops omap_temp_sensor_dev_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(omap_temp_sensor_suspend,
omap_temp_sensor_resume)
SET_RUNTIME_PM_OPS(omap_temp_sensor_runtime_suspend,
omap_temp_sensor_runtime_resume, omap_temp_sensor_idle)
};
> +
> +#endif
instead of endif, try something like:
dev_pm_ops....
#define DEV_PM_OPS (&omap_temp_sensor_dev_pm_ops)
#else
#define DEV_PM_OPS NULL
#endif
then you can remove the ifdef on the driver structure.
--
balbi
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 490 bytes --]
^ permalink raw reply [flat|nested] 35+ messages in thread
* Re: [lm-sensors] [RFC PATCH 6/6 V2] hwmon: OMAP4: On die
@ 2011-08-18 11:37 ` Felipe Balbi
0 siblings, 0 replies; 35+ messages in thread
From: Felipe Balbi @ 2011-08-18 11:37 UTC (permalink / raw)
To: Keerthy; +Cc: linux-omap, Jean Delvare, Guenter Roeck, lm-sensors
[-- Attachment #1.1: Type: text/plain, Size: 35223 bytes --]
Hi,
On Thu, Aug 18, 2011 at 04:22:15PM +0530, Keerthy wrote:
> On chip temperature sensor driver. The driver monitors the temperature of
> the MPU subsystem of the OMAP4. It sends notifications to the user space if
> the temperature crosses user defined thresholds via kobject_uevent interface.
> The user is allowed to configure the temperature thresholds vis sysfs nodes
> exposed using hwmon interface.
>
> Signed-off-by: Keerthy <j-keerthy@ti.com>
> Cc: Jean Delvare <khali@linux-fr.org>
> Cc: Guenter Roeck <guenter.roeck@ericsson.com>
> Cc: lm-sensors@lm-sensors.org
> ---
> Documentation/hwmon/omap_temp_sensor | 27 +
> drivers/hwmon/Kconfig | 11 +
> drivers/hwmon/Makefile | 1 +
> drivers/hwmon/omap_temp_sensor.c | 918 ++++++++++++++++++++++++++++++++++
> 4 files changed, 957 insertions(+), 0 deletions(-)
> create mode 100644 Documentation/hwmon/omap_temp_sensor
> create mode 100644 drivers/hwmon/omap_temp_sensor.c
>
> diff --git a/Documentation/hwmon/omap_temp_sensor b/Documentation/hwmon/omap_temp_sensor
> new file mode 100644
> index 0000000..e01a6d6
> --- /dev/null
> +++ b/Documentation/hwmon/omap_temp_sensor
> @@ -0,0 +1,27 @@
> +Kernel driver omap_temp_sensor
> +==============================
> +
> +Supported chips:
> + * Texas Instruments OMAP4460
> + Prefix: 'omap_temp_sensor'
> + Datasheet: http://focus.ti.com/docs/prod/folders/print/tmp102.html
> +
> +Author:
> + J Keerthy <j-keerthy@ti.com>
> +
> +Description
> +-----------
> +
> +The Texas Instruments OMAP4 family of chips have a bandgap temperature sensor.
> +The temperature sensor feature is used to convert the temperature of the device
> +into a decimal value coded on 10 bits. An internal ADC is used for conversion.
> +The recommended operating temperatures must be in the range -40 degree Celsius
> +to 123 degree celsius for standard conversion.
> +The thresholds are programmable and upon crossing the thresholds an interrupt
> +is generated. The OMAP temperature sensor has a programmable update rate in
> +milli seconds.
> +(Currently the driver programs a default of 2000 milli seconds).
> +
> +The driver provides the common sysfs-interface for temperatures (see
> +Documentation/hwmon/sysfs-interface under Temperatures).
> +
> diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
> index 5f888f7..9c9cd8b 100644
> --- a/drivers/hwmon/Kconfig
> +++ b/drivers/hwmon/Kconfig
> @@ -323,6 +323,17 @@ config SENSORS_F71805F
> This driver can also be built as a module. If so, the module
> will be called f71805f.
>
> +config SENSORS_OMAP_BANDGAP_TEMP_SENSOR
> + bool "OMAP on-die temperature sensor hwmon driver"
> + depends on HWMON && ARCH_OMAP && OMAP_TEMP_SENSOR
> + help
> + If you say yes here you get support for hardware
> + monitoring features of the OMAP on die temperature
> + sensor.
> +
> + Continuous conversion programmable delay
> + mode is used for temperature conversion.
> +
> config SENSORS_F71882FG
> tristate "Fintek F71882FG and compatibles"
> help
> diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
> index 28061cf..d0f89f5 100644
> --- a/drivers/hwmon/Makefile
> +++ b/drivers/hwmon/Makefile
> @@ -91,6 +91,7 @@ obj-$(CONFIG_SENSORS_MAX6639) += max6639.o
> obj-$(CONFIG_SENSORS_MAX6642) += max6642.o
> obj-$(CONFIG_SENSORS_MAX6650) += max6650.o
> obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o
> +obj-$(CONFIG_SENSORS_OMAP_BANDGAP_TEMP_SENSOR) += omap_temp_sensor.o
> obj-$(CONFIG_SENSORS_PC87360) += pc87360.o
> obj-$(CONFIG_SENSORS_PC87427) += pc87427.o
> obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o
> diff --git a/drivers/hwmon/omap_temp_sensor.c b/drivers/hwmon/omap_temp_sensor.c
> new file mode 100644
> index 0000000..586a361
> --- /dev/null
> +++ b/drivers/hwmon/omap_temp_sensor.c
> @@ -0,0 +1,918 @@
> +/*
> + * OMAP4 Temperature sensor driver file
> + *
> + * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
> + * Author: J Keerthy <j-keerthy@ti.com>
> + * Author: Moiz Sonasath <m-sonasath@ti.com>
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * 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.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
> + * 02110-1301 USA
> + *
> + */
> +
> +#include <linux/interrupt.h>
> +#include <linux/clk.h>
> +#include <linux/io.h>
> +#include <linux/slab.h>
> +#include <linux/init.h>
> +#include <plat/omap_device.h>
> +#include <linux/kernel.h>
> +#include <linux/device.h>
> +#include <linux/jiffies.h>
> +#include <linux/hwmon.h>
> +#include <linux/hwmon-sysfs.h>
> +#include <linux/stddef.h>
> +#include <linux/sysfs.h>
> +#include <linux/err.h>
> +#include <linux/types.h>
> +#include <linux/mutex.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/delay.h>
> +#include <plat/temperature_sensor.h>
> +
> +#define TSHUT_THRESHOLD_HOT 122000 /* 122 deg C */
> +#define TSHUT_THRESHOLD_COLD 100000 /* 100 deg C */
> +#define BGAP_THRESHOLD_T_HOT 73000 /* 73 deg C */
> +#define BGAP_THRESHOLD_T_COLD 71000 /* 71 deg C */
> +#define OMAP_ADC_START_VALUE 530
> +#define OMAP_ADC_END_VALUE 923
> +
> +/*
> + * omap_temp_sensor structure
> + * @hwmon_dev - device pointer
> + * @clock - Clock pointer
> + * @registers - Pointer to structure with register offsets and bitfields
> + * @sensor_mutex - Mutex for sysfs, irq and PM
> + * @irq - MPU Irq number for thermal alert
> + * @phy_base - Physical base of the temp I/O
> + * @clk_rate - Holds current clock rate
> + * @temp_sensor_ctrl - temp sensor control register value
> + * @bg_ctrl - bandgap ctrl register value
> + * @bg_counter - bandgap counter value
> + * @bg_threshold - bandgap threshold register value
> + * @temp_sensor_tshut_threshold - bandgap tshut register value
> + * @is_efuse_valid - Flag to determine if efuse is valid or not
> + * @clk_on - Manages the current clock state
> + */
> +struct omap_temp_sensor {
> + struct device *hwmon_dev;
> + struct clk *clock;
> + struct omap_temp_sensor_registers *registers;
> + struct mutex sensor_mutex; /* Mutex for sysfs, irq and PM */
> + unsigned int irq;
> + void __iomem *phy_base;
> + u32 clk_rate;
> + u32 temp_sensor_ctrl;
> + u32 bg_ctrl;
> + u32 bg_counter;
> + u32 bg_threshold;
> + u32 temp_sensor_tshut_threshold;
> + bool is_efuse_valid;
> + bool clk_on;
> +};
> +
> +/*
> + * Temperature values in milli degree celsius
> + * ADC code values from 530 to 923
> + */
> +static int adc_to_temp[] = {
> + -40000, -40000, -40000, -40000, -39800, -39400, -39000, -38600, -38200,
> + -37800, -37300, -36800, -36400, -36000, -35600, -35200, -34800,
> + -34300, -33800, -33400, -33000, -32600, -32200, -31800, -31300,
> + -30800, -30400, -30000, -29600, -29200, -28700, -28200, -27800,
> + -27400, -27000, -26600, -26200, -25700, -25200, -24800, -24400,
> + -24000, -23600, -23200, -22700, -22200, -21800, -21400, -21000,
> + -20600, -20200, -19700, -19200, -18800, -18400, -18000, -17600,
> + -17200, -16700, -16200, -15800, -15400, -15000, -14600, -14200,
> + -13700, -13200, -12800, -12400, -12000, -11600, -11200, -10700,
> + -10200, -9800, -9400, -9000, -8600, -8200, -7700, -7200, -6800,
> + -6400, -6000, -5600, -5200, -4800, -4300, -3800, -3400, -3000,
> + -2600, -2200, -1800, -1300, -800, -400, 0, 400, 800, 1200, 1600,
> + 2100, 2600, 3000, 3400, 3800, 4200, 4600, 5100, 5600, 6000, 6400,
> + 6800, 7200, 7600, 8000, 8500, 9000, 9400, 9800, 10200, 10600, 11000,
> + 11400, 11900, 12400, 12800, 13200, 13600, 14000, 14400, 14800,
> + 15300, 15800, 16200, 16600, 17000, 17400, 17800, 18200, 18700,
> + 19200, 19600, 20000, 20400, 20800, 21200, 21600, 22100, 22600,
> + 23000, 23400, 23800, 24200, 24600, 25000, 25400, 25900, 26400,
> + 26800, 27200, 27600, 28000, 28400, 28800, 29300, 29800, 30200,
> + 30600, 31000, 31400, 31800, 32200, 32600, 33100, 33600, 34000,
> + 34400, 34800, 35200, 35600, 36000, 36400, 36800, 37300, 37800,
> + 38200, 38600, 39000, 39400, 39800, 40200, 40600, 41100, 41600,
> + 42000, 42400, 42800, 43200, 43600, 44000, 44400, 44800, 45300,
> + 45800, 46200, 46600, 47000, 47400, 47800, 48200, 48600, 49000,
> + 49500, 50000, 50400, 50800, 51200, 51600, 52000, 52400, 52800,
> + 53200, 53700, 54200, 54600, 55000, 55400, 55800, 56200, 56600,
> + 57000, 57400, 57800, 58200, 58700, 59200, 59600, 60000, 60400,
> + 60800, 61200, 61600, 62000, 62400, 62800, 63300, 63800, 64200,
> + 64600, 65000, 65400, 65800, 66200, 66600, 67000, 67400, 67800,
> + 68200, 68700, 69200, 69600, 70000, 70400, 70800, 71200, 71600,
> + 72000, 72400, 72800, 73200, 73600, 74100, 74600, 75000, 75400,
> + 75800, 76200, 76600, 77000, 77400, 77800, 78200, 78600, 79000,
> + 79400, 79800, 80300, 80800, 81200, 81600, 82000, 82400, 82800,
> + 83200, 83600, 84000, 84400, 84800, 85200, 85600, 86000, 86400,
> + 86800, 87300, 87800, 88200, 88600, 89000, 89400, 89800, 90200,
> + 90600, 91000, 91400, 91800, 92200, 92600, 93000, 93400, 93800,
> + 94200, 94600, 95000, 95500, 96000, 96400, 96800, 97200, 97600,
> + 98000, 98400, 98800, 99200, 99600, 100000, 100400, 100800, 101200,
> + 101600, 102000, 102400, 102800, 103200, 103600, 104000, 104400,
> + 104800, 105200, 105600, 106100, 106600, 107000, 107400, 107800,
> + 108200, 108600, 109000, 109400, 109800, 110200, 110600, 111000,
> + 111400, 111800, 112200, 112600, 113000, 113400, 113800, 114200,
> + 114600, 115000, 115400, 115800, 116200, 116600, 117000, 117400,
> + 117800, 118200, 118600, 119000, 119400, 119800, 120200, 120600,
> + 121000, 121400, 121800, 122200, 122600, 123000
> +};
> +
> +static unsigned long omap_temp_sensor_readl(struct omap_temp_sensor
> + *temp_sensor, u32 reg)
> +{
> + return __raw_readl(temp_sensor->phy_base + reg);
> +}
> +
> +static void omap_temp_sensor_writel(struct omap_temp_sensor *temp_sensor,
> + u32 val, u32 reg)
> +{
> + __raw_writel(val, (temp_sensor->phy_base + reg));
> +}
> +
> +static int adc_to_temp_conversion(int adc_val)
> +{
> + return adc_to_temp[adc_val - OMAP_ADC_START_VALUE];
> +}
> +
> +static int temp_to_adc_conversion(long temp)
> +{
> + int i;
> +
> + for (i = 0; i <= OMAP_ADC_END_VALUE - OMAP_ADC_START_VALUE; i++)
> + if (temp < adc_to_temp[i])
> + return OMAP_ADC_START_VALUE + i - 1;
> +
> + return -EINVAL;
> +}
> +
> +static void omap_configure_temp_sensor_thresholds(struct omap_temp_sensor
> + *temp_sensor)
> +{
> + u32 temp, t_hot, t_cold, tshut_hot, tshut_cold;
> +
> + t_hot = temp_to_adc_conversion(BGAP_THRESHOLD_T_HOT);
> + t_cold = temp_to_adc_conversion(BGAP_THRESHOLD_T_COLD);
> +
> + /* Configure the TALERT thresholds */
> + temp = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_threshold);
> + temp |= (t_hot << __ffs(temp_sensor->registers->threshold_thot_mask)) |
> + (t_cold << __ffs(temp_sensor->registers->threshold_tcold_mask));
> + omap_temp_sensor_writel(temp_sensor, temp,
> + temp_sensor->registers->bgap_threshold);
> +
> + tshut_hot = temp_to_adc_conversion(TSHUT_THRESHOLD_HOT);
> + tshut_cold = temp_to_adc_conversion(TSHUT_THRESHOLD_COLD);
> +
> + /* Configure the TSHUT thresholds */
> + temp = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->thsut_threshold);
> + temp |= (tshut_hot << __ffs(temp_sensor->registers->tshut_hot_mask))
> + | (tshut_cold << __ffs(temp_sensor->registers->tshut_hot_mask));
> + omap_temp_sensor_writel(temp_sensor, temp,
> + temp_sensor->registers->thsut_threshold);
> +}
> +
> +static void omap_configure_temp_sensor_counter(struct omap_temp_sensor
> + *temp_sensor, u32 counter)
> +{
> + u32 val;
> +
> + val = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_counter);
> + val &= ~(temp_sensor->registers->counter_mask);
> + val |= (counter << __ffs(temp_sensor->registers->counter_mask));
> + omap_temp_sensor_writel(temp_sensor, val,
> + temp_sensor->registers->bgap_counter);
> +}
> +
> +static void omap_enable_continuous_mode(struct omap_temp_sensor *temp_sensor,
> + bool enable)
> +{
> + u32 val;
> +
> + val = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_mode_ctrl);
> +
> + if (enable)
> + val |= (1 << __ffs(temp_sensor->registers->mode_ctrl_mask));
> + else
> + val &= ~(temp_sensor->registers->mode_ctrl_mask);
> +
> + omap_temp_sensor_writel(temp_sensor, val,
> + temp_sensor->registers->bgap_mode_ctrl);
> +}
> +
> +/* Sysfs hook functions */
> +
> +static ssize_t show_temp_max(struct device *dev,
> + struct device_attribute *devattr, char *buf)
> +{
> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
> + int temp;
> +
> + mutex_lock(&temp_sensor->sensor_mutex);
> +
> + temp = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_threshold);
> + temp = (temp & temp_sensor->registers->threshold_thot_mask)
> + >> __ffs(temp_sensor->registers->threshold_thot_mask);
> +
> + if (temp < OMAP_ADC_START_VALUE || temp > OMAP_ADC_END_VALUE) {
> + dev_err(dev, "invalid value\n");
> + mutex_unlock(&temp_sensor->sensor_mutex);
> + return -EINVAL;
> + }
> +
> + temp = adc_to_temp_conversion(temp);
> + mutex_unlock(&temp_sensor->sensor_mutex);
> +
> + return snprintf(buf, 16, "%d\n", temp);
> +}
> +
> +static ssize_t set_temp_max(struct device *dev,
> + struct device_attribute *devattr,
> + const char *buf, size_t count)
> +{
> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
> + long val;
> + u32 reg_val, t_cold, t_hot, temp;
> +
> + mutex_lock(&temp_sensor->sensor_mutex);
> +
> + if (strict_strtol(buf, 10, &val)) {
> + count = -EINVAL;
> + goto out;
> + }
> +
> + t_hot = temp_to_adc_conversion(val);
> + if ((t_hot < OMAP_ADC_START_VALUE || t_hot > OMAP_ADC_END_VALUE)) {
> + dev_err(dev, "invalid range\n");
> + count = -EINVAL;
> + goto out;
> + }
> +
> + /* obtain the T cold value */
> + t_cold = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_threshold);
> + t_cold = (t_cold & temp_sensor->registers->threshold_tcold_mask) >>
> + __ffs(temp_sensor->registers->threshold_tcold_mask);
> +
> + if (t_hot < t_cold) {
> + dev_err(dev, "Error! T_HOT value lesser than T_COLD\n");
> + count = -EINVAL;
> + goto out;
> + }
> +
> + /* write the new t_hot value */
> + reg_val = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_threshold);
> + reg_val &= ~(temp_sensor->registers->threshold_thot_mask);
> + reg_val |= (t_hot <<
> + __ffs(temp_sensor->registers->threshold_thot_mask));
> + omap_temp_sensor_writel(temp_sensor, reg_val,
> + temp_sensor->registers->bgap_threshold);
> +
> + /* Read the current temperature */
> + temp = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->temp_sensor_ctrl);
> + temp &= (temp_sensor->registers->bgap_dtemp_mask);
> +
> + /*
> + * If user sets the HIGH threshold(t_hot) greater than the current
> + * temperature(temp) unmask the HOT interrupts
> + */
> + if (t_hot > temp) {
> + reg_val = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_mask_ctrl);
> + reg_val &= ~(temp_sensor->registers->mask_cold_mask);
> + reg_val |= temp_sensor->registers->mask_hot_mask;
> + omap_temp_sensor_writel(temp_sensor, reg_val,
> + temp_sensor->registers->bgap_mask_ctrl);
> + }
> +
> + /*
> + * If current temperature is in-between the hot and cold thresholds,
> + * Enable both masks.
> + */
> + if (temp > t_cold && temp < t_hot) {
> + reg_val = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_mask_ctrl);
> + reg_val |= temp_sensor->registers->mask_cold_mask;
> + reg_val |= temp_sensor->registers->mask_hot_mask;
> + omap_temp_sensor_writel(temp_sensor, reg_val,
> + OMAP4460_BGAP_CTRL_OFFSET);
> + }
> + /*
> + * else no need to do anything since HW will immediately compare
> + * the new threshold and generate interrupt accordingly
> + */
> +out:
> + mutex_unlock(&temp_sensor->sensor_mutex);
> + return count;
> +}
> +
> +static ssize_t show_temp_max_hyst(struct device *dev,
> + struct device_attribute *devattr, char *buf)
> +{
> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
> + u32 temp;
> +
> + mutex_lock(&temp_sensor->sensor_mutex);
> +
> + temp = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_threshold);
> + temp = (temp & temp_sensor->registers->threshold_tcold_mask) >>
> + __ffs(temp_sensor->registers->threshold_tcold_mask);
> +
> + if (temp < OMAP_ADC_START_VALUE || temp > OMAP_ADC_END_VALUE) {
> + dev_err(dev, "invalid value\n");
> + mutex_unlock(&temp_sensor->sensor_mutex);
> + return -EINVAL;
> + }
> +
> + temp = adc_to_temp_conversion(temp);
> + mutex_unlock(&temp_sensor->sensor_mutex);
> +
> + return snprintf(buf, 16, "%d\n", temp);
> +}
> +
> +static ssize_t set_temp_max_hyst(struct device *dev,
> + struct device_attribute *devattr,
> + const char *buf, size_t count)
> +{
> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
> + u32 reg_val, t_hot, t_cold, temp;
> + long val;
> +
> + mutex_lock(&temp_sensor->sensor_mutex);
> +
> + if (strict_strtol(buf, 10, &val)) {
> + count = -EINVAL;
> + goto out;
> + }
> +
> + t_cold = temp_to_adc_conversion(val);
> + if (t_cold < OMAP_ADC_START_VALUE || t_cold > OMAP_ADC_END_VALUE) {
> + dev_err(dev, "invalid range");
> + count = -EINVAL;
> + goto out;
> + }
> +
> + /* obtain the T HOT value */
> + t_hot = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_threshold);
> + t_hot = (t_hot & temp_sensor->registers->threshold_thot_mask) >>
> + __ffs(temp_sensor->registers->threshold_thot_mask);
> +
> + if (t_cold > t_hot) {
> + dev_err(dev, "Error! T_COLD value greater than T_HOT\n");
> + count = -EINVAL;
> + goto out;
> + }
> +
> + /* write the new t_cold value */
> + reg_val = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_threshold);
> + reg_val &= ~(temp_sensor->registers->threshold_tcold_mask);
> + reg_val |= (t_cold <<
> + __ffs(temp_sensor->registers->threshold_tcold_mask));
> + omap_temp_sensor_writel(temp_sensor, reg_val,
> + temp_sensor->registers->bgap_threshold);
> +
> + /* Read the current temperature */
> + temp = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->temp_sensor_ctrl);
> + temp &= (temp_sensor->registers->bgap_dtemp_mask);
> +
> + /*
> + * If user sets the LOW threshold(t_cold) lower than the current
> + * temperature(temp) unmask the COLD interrupts
> + */
> + if (t_cold < temp) {
> + reg_val = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_mask_ctrl);
> + reg_val &= ~(temp_sensor->registers->mask_hot_mask);
> + reg_val |= temp_sensor->registers->mask_cold_mask;
> + omap_temp_sensor_writel(temp_sensor, reg_val,
> + temp_sensor->registers->bgap_mask_ctrl);
> + }
> +
> + /*
> + * If current temperature is in-between the hot and cold thresholds,
> + * Enable both masks.
> + */
> + if (temp < t_hot && temp > t_cold) {
> + reg_val = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_mask_ctrl);
> + reg_val |= temp_sensor->registers->mask_cold_mask;
> + reg_val |= temp_sensor->registers->mask_hot_mask;
> + omap_temp_sensor_writel(temp_sensor, reg_val,
> + temp_sensor->registers->bgap_mask_ctrl);
> + }
> +
> + /*
> + * else no need to do anything since HW will immediately compare
> + * the new threshold and generate interrupt accordingly
> + */
> +
> +out:
> + mutex_unlock(&temp_sensor->sensor_mutex);
> + return count;
> +}
> +
> +static ssize_t show_update_rate(struct device *dev,
> + struct device_attribute *devattr, char *buf)
> +{
> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
> + u32 temp = 0, ret = 0;
> +
> + mutex_lock(&temp_sensor->sensor_mutex);
> +
> + if (!temp_sensor->clk_rate) {
> + dev_err(dev, "clk_rate is NULL\n");
> + ret = -EINVAL;
> + goto out;
> + }
> +
> + temp = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_counter);
> + temp = (temp & temp_sensor->registers->counter_mask) >>
> + __ffs(temp_sensor->registers->counter_mask);
> + temp = temp * 1000 / (temp_sensor->clk_rate);
> +
> +out:
> + mutex_unlock(&temp_sensor->sensor_mutex);
> + if (!ret)
> + return sprintf(buf, "%d\n", temp);
> +
> + return ret;
> +}
> +
> +static ssize_t set_update_rate(struct device *dev,
> + struct device_attribute *devattr,
> + const char *buf, size_t count)
> +{
> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
> + u32 reg_val;
> + long val;
> +
> + mutex_lock(&temp_sensor->sensor_mutex);
> +
> + if (strict_strtol(buf, 10, &val)) {
> + count = -EINVAL;
> + goto out;
> + }
> +
> + val *= temp_sensor->clk_rate / 1000;
> + reg_val = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_counter);
> +
> + reg_val &= ~(temp_sensor->registers->counter_mask);
> + reg_val |= val;
> + omap_temp_sensor_writel(temp_sensor, reg_val,
> + temp_sensor->registers->bgap_counter);
> +
> +out:
> + mutex_unlock(&temp_sensor->sensor_mutex);
> + return count;
> +}
> +
> +static int omap_temp_sensor_read_temp(struct device *dev,
> + struct device_attribute *devattr,
> + char *buf)
> +{
> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
> + int temp, ret = 0;
> +
> + mutex_lock(&temp_sensor->sensor_mutex);
> +
> + temp = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->temp_sensor_ctrl);
> + temp &= temp_sensor->registers->bgap_dtemp_mask;
> +
> + if (!temp_sensor->is_efuse_valid)
> + dev_err(dev, "Invalid EFUSE, Non-trimmed BGAP, Temp not accurate\n");
> +
> + /* look up for temperature in the table and return the temperature */
> + if (temp < OMAP_ADC_START_VALUE || temp > OMAP_ADC_END_VALUE) {
> + dev_err(dev, "invalid adc code reported %d", temp);
> + ret = -EINVAL;
> + goto out;
> + }
> +
> + temp = adc_to_temp[temp - OMAP_ADC_START_VALUE];
> +
> +out:
> + mutex_unlock(&temp_sensor->sensor_mutex);
> + if (!ret)
> + return sprintf(buf, "%d\n", temp);
> +
> + return ret;
> +}
> +
> +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, omap_temp_sensor_read_temp,
> + NULL, 0);
> +static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp_max,
> + set_temp_max, 0);
> +static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO, show_temp_max_hyst,
> + set_temp_max_hyst, 0);
> +static SENSOR_DEVICE_ATTR(update_rate, S_IWUSR | S_IRUGO, show_update_rate,
> + set_update_rate, 0);
> +
> +static struct attribute *omap_temp_sensor_attributes[] = {
> + &sensor_dev_attr_temp1_input.dev_attr.attr,
> + &sensor_dev_attr_temp1_max.dev_attr.attr,
> + &sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
> + &sensor_dev_attr_update_rate.dev_attr.attr,
> + NULL
> +};
> +
> +static const struct attribute_group omap_temp_sensor_group = {
> + .attrs = omap_temp_sensor_attributes,
> +};
> +
> +static int omap_temp_sensor_clk_enable(struct omap_temp_sensor *temp_sensor)
> +{
> + u32 ret = 0;
> +
> + if (temp_sensor->clk_on) {
> + dev_err(temp_sensor->hwmon_dev, "clock already on\n");
> + goto out;
> + }
> +
> + ret = pm_runtime_get_sync(temp_sensor->hwmon_dev);
> + if (ret < 0) {
> + dev_err(temp_sensor->hwmon_dev, "get sync failed\n");
> + goto out;
> + }
> +
> + temp_sensor->clk_on = 1;
> +
> +out:
> + return ret;
> +}
> +
> +static int omap_temp_sensor_clk_disable(struct omap_temp_sensor *temp_sensor)
> +{
> + u32 ret = 0;
> +
> + if (!temp_sensor->clk_on) {
> + dev_err(temp_sensor->hwmon_dev, "clock already off\n");
> + goto out;
> + }
> +
> + /* Gate the clock */
> + ret = pm_runtime_put_sync(temp_sensor->hwmon_dev);
> + if (ret < 0) {
> + dev_err(temp_sensor->hwmon_dev, "put sync failed\n");
> + goto out;
> + }
> + temp_sensor->clk_on = 0;
> +
> +out:
> + return ret;
> +}
> +
> +static irqreturn_t omap_talert_irq_handler(int irq, void *data)
> +{
> + struct omap_temp_sensor *temp_sensor;
> + int t_hot, t_cold, temp;
> +
> + temp_sensor = data;
> + mutex_lock(&temp_sensor->sensor_mutex);
> +
> + /* Read the status of t_hot */
> + t_hot = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_status)
> + & temp_sensor->registers->status_hot_mask;
> +
> + /* Read the status of t_cold */
> + t_cold = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_status)
> + & temp_sensor->registers->status_cold_mask;
> +
> + temp = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_mask_ctrl);
> + /*
> + * One TALERT interrupt: Two sources
> + * If the interrupt is due to t_hot then mask t_hot and
> + * and unmask t_cold else mask t_cold and unmask t_hot
> + */
> + if (t_hot) {
> + temp &= ~(temp_sensor->registers->mask_hot_mask);
> + temp |= temp_sensor->registers->mask_cold_mask;
> + } else if (t_cold) {
> + temp &= ~(temp_sensor->registers->mask_cold_mask);
> + temp |= temp_sensor->registers->mask_hot_mask;
> + }
> +
> + omap_temp_sensor_writel(temp_sensor, temp,
> + temp_sensor->registers->bgap_mask_ctrl);
> +
> + /* kobject_uvent to user space telling thermal threshold crossed */
> + kobject_uevent(&temp_sensor->hwmon_dev->kobj, KOBJ_CHANGE);
> +
> + mutex_unlock(&temp_sensor->sensor_mutex);
> +
> + return IRQ_HANDLED;
> +}
> +
> +static int __devinit omap_temp_sensor_probe(struct platform_device *pdev)
> +{
> + struct omap_temp_sensor_pdata *pdata = pdev->dev.platform_data;
> + struct omap_temp_sensor *temp_sensor;
> + struct resource *mem;
> + int ret = 0;
> + int val, clk_rate;
> +
> + if (!pdata) {
> + dev_err(&pdev->dev, "platform data missing\n");
> + return -EINVAL;
> + }
> +
> + temp_sensor = kzalloc(sizeof(*temp_sensor), GFP_KERNEL);
> + if (!temp_sensor)
> + return -ENOMEM;
> +
> + mutex_init(&temp_sensor->sensor_mutex);
> +
> + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + if (!mem) {
> + dev_err(&pdev->dev, "no mem resource\n");
> + ret = -ENOMEM;
> + goto plat_res_err;
> + }
> +
> + temp_sensor->irq = platform_get_irq_byname(pdev, "thermal_alert");
> + if (temp_sensor->irq < 0) {
> + dev_err(&pdev->dev, "get_irq_byname failed\n");
> + ret = temp_sensor->irq;
> + goto plat_res_err;
> + }
> +
> + temp_sensor->phy_base = ioremap(mem->start, resource_size(mem));
> + temp_sensor->clock = NULL;
> + temp_sensor->registers = pdata->registers;
> + temp_sensor->hwmon_dev = &pdev->dev;
> +
> + pm_runtime_enable(&pdev->dev);
> + pm_runtime_irq_safe(&pdev->dev);
> +
> + /*
> + * check if the efuse has a non-zero value if not
> + * it is an untrimmed sample and the temperatures
> + * may not be accurate
> + */
> +
> + if (omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_efuse))
> + temp_sensor->is_efuse_valid = 1;
> +
> + platform_set_drvdata(pdev, temp_sensor);
> + dev_set_drvdata(&pdev->dev, temp_sensor);
> + temp_sensor->clock = clk_get(temp_sensor->hwmon_dev, "fck");
> + if (IS_ERR(temp_sensor->clock)) {
> + ret = PTR_ERR(temp_sensor->clock);
> + dev_err(temp_sensor->hwmon_dev,
> + "unable to get fclk: %d\n", ret);
> + ret = -EINVAL;
> + goto plat_res_err;
> + }
> +
> + ret = omap_temp_sensor_clk_enable(temp_sensor);
> + if (ret) {
> + dev_err(&pdev->dev, "Cannot enable temp sensor\n");
> + goto clken_err;
> + }
> +
> + clk_rate = clk_round_rate(temp_sensor->clock, 2000000);
> + if (clk_rate < 1000000 || clk_rate == 0xffffffff) {
> + dev_err(&pdev->dev, "Error round rate\n");
> + ret = -EINVAL;
> + goto clken_err;
> + }
> +
> + ret = clk_set_rate(temp_sensor->clock, clk_rate);
> + if (ret) {
> + dev_err(&pdev->dev, "Cannot set clock rate\n");
> + goto clken_err;
> + }
> +
> + temp_sensor->clk_rate = clk_rate;
> + omap_enable_continuous_mode(temp_sensor, 1);
> + omap_configure_temp_sensor_thresholds(temp_sensor);
> + /* 1 ms */
> + omap_configure_temp_sensor_counter(temp_sensor, 1);
> +
> + /* Wait till the first conversion is done wait for at least 1ms */
> + usleep_range(1000, 2000);
> +
> + /* Read the temperature once due to hw issue*/
> + omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->temp_sensor_ctrl);
> +
> + /* Set 2 seconds time as default counter */
> + omap_configure_temp_sensor_counter(temp_sensor,
> + temp_sensor->clk_rate * 2);
> +
> + temp_sensor->hwmon_dev = hwmon_device_register(&pdev->dev);
> + if (IS_ERR(temp_sensor->hwmon_dev)) {
> + dev_err(&pdev->dev, "hwmon_device_register failed.\n");
> + ret = PTR_ERR(temp_sensor->hwmon_dev);
> + goto hwmon_reg_err;
> + }
> +
> + ret = sysfs_create_group(&pdev->dev.kobj,
> + &omap_temp_sensor_group);
> + if (ret) {
> + dev_err(&pdev->dev, "could not create sysfs files\n");
> + goto sysfs_create_err;
> + }
> +
> + kobject_uevent(&temp_sensor->hwmon_dev->kobj, KOBJ_ADD);
> +
> + ret = request_threaded_irq(temp_sensor->irq, NULL,
> + omap_talert_irq_handler, IRQF_TRIGGER_RISING | IRQF_ONESHOT,
> + "temp_sensor", temp_sensor);
> + if (ret) {
> + dev_err(&pdev->dev, "Request threaded irq failed.\n");
> + goto req_irq_err;
> + }
> +
> + /* unmask the T_COLD and unmask T_HOT at init */
> + val = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_mask_ctrl);
> + val |= temp_sensor->registers->mask_cold_mask
> + | temp_sensor->registers->mask_hot_mask;
> +
> + omap_temp_sensor_writel(temp_sensor, val,
> + temp_sensor->registers->bgap_mask_ctrl);
> +
> + return 0;
> +
> +req_irq_err:
> + kobject_uevent(&temp_sensor->hwmon_dev->kobj, KOBJ_REMOVE);
> + sysfs_remove_group(&temp_sensor->hwmon_dev->kobj,
> + &omap_temp_sensor_group);
> +sysfs_create_err:
> + hwmon_device_unregister(&pdev->dev);
> +hwmon_reg_err:
> + omap_temp_sensor_clk_disable(temp_sensor);
> +clken_err:
> + clk_put(temp_sensor->clock);
> +plat_res_err:
> + mutex_destroy(&temp_sensor->sensor_mutex);
> + kfree(temp_sensor);
> + return ret;
> +}
> +
> +static int __devexit omap_temp_sensor_remove(struct platform_device *pdev)
> +{
> + struct omap_temp_sensor *temp_sensor = platform_get_drvdata(pdev);
> +
> + hwmon_device_unregister(&pdev->dev);
> + kobject_uevent(&temp_sensor->hwmon_dev->kobj, KOBJ_REMOVE);
> + sysfs_remove_group(&temp_sensor->hwmon_dev->kobj,
> + &omap_temp_sensor_group);
> + omap_temp_sensor_clk_disable(temp_sensor);
> + clk_put(temp_sensor->clock);
> + platform_set_drvdata(pdev, NULL);
> + free_irq(temp_sensor->irq, temp_sensor);
> + mutex_destroy(&temp_sensor->sensor_mutex);
> + kfree(temp_sensor);
> +
> + return 0;
> +}
> +
> +#ifdef CONFIG_PM
> +static void omap_temp_sensor_save_ctxt(struct omap_temp_sensor *temp_sensor)
> +{
> + temp_sensor->temp_sensor_ctrl = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->temp_sensor_ctrl);
> + temp_sensor->bg_ctrl = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_mask_ctrl);
> + temp_sensor->bg_counter = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_counter);
> + temp_sensor->bg_threshold = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_threshold);
> + temp_sensor->temp_sensor_tshut_threshold =
> + omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->thsut_threshold);
> +}
> +
> +static void omap_temp_sensor_restore_ctxt(struct omap_temp_sensor *temp_sensor)
> +{
> + omap_temp_sensor_writel(temp_sensor,
> + temp_sensor->temp_sensor_ctrl,
> + temp_sensor->registers->temp_sensor_ctrl);
> + omap_temp_sensor_writel(temp_sensor,
> + temp_sensor->bg_ctrl,
> + temp_sensor->registers->bgap_mask_ctrl);
> + omap_temp_sensor_writel(temp_sensor,
> + temp_sensor->bg_counter,
> + temp_sensor->registers->bgap_counter);
> + omap_temp_sensor_writel(temp_sensor,
> + temp_sensor->bg_threshold,
> + temp_sensor->registers->bgap_threshold);
> + omap_temp_sensor_writel(temp_sensor,
> + temp_sensor->temp_sensor_tshut_threshold,
> + temp_sensor->registers->thsut_threshold);
> +}
> +
> +static int omap_temp_sensor_suspend(struct device *dev)
> +{
> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
> +
> + omap_temp_sensor_save_ctxt(temp_sensor);
> +
> + return 0;
> +}
> +
> +static int omap_temp_sensor_resume(struct device *dev)
> +{
> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
> +
> + omap_temp_sensor_restore_ctxt(temp_sensor);
> +
> + return 0;
> +}
> +
> +static int omap_temp_sensor_runtime_suspend(struct device *dev)
> +{
> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
> +
> + omap_temp_sensor_save_ctxt(temp_sensor);
> +
> + return 0;
> +}
> +
> +static int omap_temp_sensor_runtime_resume(struct device *dev)
> +{
> + static int context_loss_count;
> + int temp;
> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
> +
> + temp = omap_device_get_context_loss_count(to_platform_device(dev));
> +
> + if (temp != context_loss_count && context_loss_count != 0)
> + omap_temp_sensor_restore_ctxt(temp_sensor);
> +
> + context_loss_count = temp;
> +
> + return 0;
> +}
> +
> +static const struct dev_pm_ops omap_temp_sensor_dev_pm_ops = {
> + .suspend = omap_temp_sensor_suspend,
> + .resume = omap_temp_sensor_resume,
> + .runtime_suspend = omap_temp_sensor_runtime_suspend,
> + .runtime_resume = omap_temp_sensor_runtime_resume,
> +};
this is not enough... you need to populate the other fields. You might
want to use something like:
static const struct dev_pm_ops omap_temp_sensor_dev_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(omap_temp_sensor_suspend,
omap_temp_sensor_resume)
SET_RUNTIME_PM_OPS(omap_temp_sensor_runtime_suspend,
omap_temp_sensor_runtime_resume, omap_temp_sensor_idle)
};
> +
> +#endif
instead of endif, try something like:
dev_pm_ops....
#define DEV_PM_OPS (&omap_temp_sensor_dev_pm_ops)
#else
#define DEV_PM_OPS NULL
#endif
then you can remove the ifdef on the driver structure.
--
balbi
[-- Attachment #1.2: Digital signature --]
[-- Type: application/pgp-signature, Size: 490 bytes --]
[-- Attachment #2: Type: text/plain, Size: 153 bytes --]
_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors
^ permalink raw reply [flat|nested] 35+ messages in thread
* Re: [RFC PATCH 6/6 V2] hwmon: OMAP4: On die temperature sensor driver
2011-08-18 10:52 ` [RFC PATCH 6/6 V2] hwmon: OMAP4: On die temperature sensor driver Keerthy
@ 2011-08-19 2:13 ` Guenter Roeck
2011-08-19 2:13 ` [lm-sensors] [RFC PATCH 6/6 V2] hwmon: OMAP4: On die Guenter Roeck
2011-08-19 5:34 ` [lm-sensors] [RFC PATCH 6/6 V2] hwmon: OMAP4: On die Todd Poynor
2 siblings, 0 replies; 35+ messages in thread
From: Guenter Roeck @ 2011-08-19 2:13 UTC (permalink / raw)
To: Keerthy; +Cc: linux-omap, Jean Delvare, lm-sensors
On Thu, Aug 18, 2011 at 06:52:15AM -0400, Keerthy wrote:
> On chip temperature sensor driver. The driver monitors the temperature of
> the MPU subsystem of the OMAP4. It sends notifications to the user space if
> the temperature crosses user defined thresholds via kobject_uevent interface.
> The user is allowed to configure the temperature thresholds vis sysfs nodes
> exposed using hwmon interface.
>
> Signed-off-by: Keerthy <j-keerthy@ti.com>
> Cc: Jean Delvare <khali@linux-fr.org>
> Cc: Guenter Roeck <guenter.roeck@ericsson.com>
> Cc: lm-sensors@lm-sensors.org
High level review:
- too much and too broad mutex locking. show functions should not need locks at all,
set functions only while data is written into registers and into platform data.
- driver is quite noisy. There should definitely not be any log messages
if a set parameter is wrong. Show functions already return an error value
to the user; a log message indicating the error again just creates noise.
For one boolean set during probe (is_efuse_valid), each subsequent show results
in a log message if it is false. Some errors result in multiple log messages.
- Wrong use of EINVAL throughout the driver (EINVAL is "Invalid Argument")
- excessive ( )
- linear search through a sorted array is very expensive. Consider using a binary search.
- temp_to_adc_conversion return code (error if negative) is not checked properly.
I am sure there are other problems, but those are difficult to find with all the noise
above.
Guenter
> ---
> Documentation/hwmon/omap_temp_sensor | 27 +
> drivers/hwmon/Kconfig | 11 +
> drivers/hwmon/Makefile | 1 +
> drivers/hwmon/omap_temp_sensor.c | 918 ++++++++++++++++++++++++++++++++++
> 4 files changed, 957 insertions(+), 0 deletions(-)
> create mode 100644 Documentation/hwmon/omap_temp_sensor
> create mode 100644 drivers/hwmon/omap_temp_sensor.c
>
> diff --git a/Documentation/hwmon/omap_temp_sensor b/Documentation/hwmon/omap_temp_sensor
> new file mode 100644
> index 0000000..e01a6d6
> --- /dev/null
> +++ b/Documentation/hwmon/omap_temp_sensor
> @@ -0,0 +1,27 @@
> +Kernel driver omap_temp_sensor
> +==============================
> +
> +Supported chips:
> + * Texas Instruments OMAP4460
> + Prefix: 'omap_temp_sensor'
> + Datasheet: http://focus.ti.com/docs/prod/folders/print/tmp102.html
> +
> +Author:
> + J Keerthy <j-keerthy@ti.com>
> +
> +Description
> +-----------
> +
> +The Texas Instruments OMAP4 family of chips have a bandgap temperature sensor.
> +The temperature sensor feature is used to convert the temperature of the device
> +into a decimal value coded on 10 bits. An internal ADC is used for conversion.
> +The recommended operating temperatures must be in the range -40 degree Celsius
> +to 123 degree celsius for standard conversion.
> +The thresholds are programmable and upon crossing the thresholds an interrupt
> +is generated. The OMAP temperature sensor has a programmable update rate in
> +milli seconds.
> +(Currently the driver programs a default of 2000 milli seconds).
> +
> +The driver provides the common sysfs-interface for temperatures (see
> +Documentation/hwmon/sysfs-interface under Temperatures).
> +
> diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
> index 5f888f7..9c9cd8b 100644
> --- a/drivers/hwmon/Kconfig
> +++ b/drivers/hwmon/Kconfig
> @@ -323,6 +323,17 @@ config SENSORS_F71805F
> This driver can also be built as a module. If so, the module
> will be called f71805f.
>
> +config SENSORS_OMAP_BANDGAP_TEMP_SENSOR
> + bool "OMAP on-die temperature sensor hwmon driver"
> + depends on HWMON && ARCH_OMAP && OMAP_TEMP_SENSOR
> + help
> + If you say yes here you get support for hardware
> + monitoring features of the OMAP on die temperature
> + sensor.
> +
> + Continuous conversion programmable delay
> + mode is used for temperature conversion.
> +
> config SENSORS_F71882FG
> tristate "Fintek F71882FG and compatibles"
> help
> diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
> index 28061cf..d0f89f5 100644
> --- a/drivers/hwmon/Makefile
> +++ b/drivers/hwmon/Makefile
> @@ -91,6 +91,7 @@ obj-$(CONFIG_SENSORS_MAX6639) += max6639.o
> obj-$(CONFIG_SENSORS_MAX6642) += max6642.o
> obj-$(CONFIG_SENSORS_MAX6650) += max6650.o
> obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o
> +obj-$(CONFIG_SENSORS_OMAP_BANDGAP_TEMP_SENSOR) += omap_temp_sensor.o
> obj-$(CONFIG_SENSORS_PC87360) += pc87360.o
> obj-$(CONFIG_SENSORS_PC87427) += pc87427.o
> obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o
> diff --git a/drivers/hwmon/omap_temp_sensor.c b/drivers/hwmon/omap_temp_sensor.c
> new file mode 100644
> index 0000000..586a361
> --- /dev/null
> +++ b/drivers/hwmon/omap_temp_sensor.c
> @@ -0,0 +1,918 @@
> +/*
> + * OMAP4 Temperature sensor driver file
> + *
> + * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
> + * Author: J Keerthy <j-keerthy@ti.com>
> + * Author: Moiz Sonasath <m-sonasath@ti.com>
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * 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.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
> + * 02110-1301 USA
> + *
> + */
> +
> +#include <linux/interrupt.h>
> +#include <linux/clk.h>
> +#include <linux/io.h>
> +#include <linux/slab.h>
> +#include <linux/init.h>
> +#include <plat/omap_device.h>
> +#include <linux/kernel.h>
> +#include <linux/device.h>
> +#include <linux/jiffies.h>
> +#include <linux/hwmon.h>
> +#include <linux/hwmon-sysfs.h>
> +#include <linux/stddef.h>
> +#include <linux/sysfs.h>
> +#include <linux/err.h>
> +#include <linux/types.h>
> +#include <linux/mutex.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/delay.h>
> +#include <plat/temperature_sensor.h>
> +
> +#define TSHUT_THRESHOLD_HOT 122000 /* 122 deg C */
> +#define TSHUT_THRESHOLD_COLD 100000 /* 100 deg C */
> +#define BGAP_THRESHOLD_T_HOT 73000 /* 73 deg C */
> +#define BGAP_THRESHOLD_T_COLD 71000 /* 71 deg C */
> +#define OMAP_ADC_START_VALUE 530
> +#define OMAP_ADC_END_VALUE 923
> +
> +/*
> + * omap_temp_sensor structure
> + * @hwmon_dev - device pointer
> + * @clock - Clock pointer
> + * @registers - Pointer to structure with register offsets and bitfields
> + * @sensor_mutex - Mutex for sysfs, irq and PM
> + * @irq - MPU Irq number for thermal alert
> + * @phy_base - Physical base of the temp I/O
> + * @clk_rate - Holds current clock rate
> + * @temp_sensor_ctrl - temp sensor control register value
> + * @bg_ctrl - bandgap ctrl register value
> + * @bg_counter - bandgap counter value
> + * @bg_threshold - bandgap threshold register value
> + * @temp_sensor_tshut_threshold - bandgap tshut register value
> + * @is_efuse_valid - Flag to determine if efuse is valid or not
> + * @clk_on - Manages the current clock state
> + */
> +struct omap_temp_sensor {
> + struct device *hwmon_dev;
> + struct clk *clock;
> + struct omap_temp_sensor_registers *registers;
> + struct mutex sensor_mutex; /* Mutex for sysfs, irq and PM */
> + unsigned int irq;
> + void __iomem *phy_base;
> + u32 clk_rate;
> + u32 temp_sensor_ctrl;
> + u32 bg_ctrl;
> + u32 bg_counter;
> + u32 bg_threshold;
> + u32 temp_sensor_tshut_threshold;
> + bool is_efuse_valid;
> + bool clk_on;
> +};
> +
> +/*
> + * Temperature values in milli degree celsius
> + * ADC code values from 530 to 923
> + */
> +static int adc_to_temp[] = {
> + -40000, -40000, -40000, -40000, -39800, -39400, -39000, -38600, -38200,
> + -37800, -37300, -36800, -36400, -36000, -35600, -35200, -34800,
> + -34300, -33800, -33400, -33000, -32600, -32200, -31800, -31300,
> + -30800, -30400, -30000, -29600, -29200, -28700, -28200, -27800,
> + -27400, -27000, -26600, -26200, -25700, -25200, -24800, -24400,
> + -24000, -23600, -23200, -22700, -22200, -21800, -21400, -21000,
> + -20600, -20200, -19700, -19200, -18800, -18400, -18000, -17600,
> + -17200, -16700, -16200, -15800, -15400, -15000, -14600, -14200,
> + -13700, -13200, -12800, -12400, -12000, -11600, -11200, -10700,
> + -10200, -9800, -9400, -9000, -8600, -8200, -7700, -7200, -6800,
> + -6400, -6000, -5600, -5200, -4800, -4300, -3800, -3400, -3000,
> + -2600, -2200, -1800, -1300, -800, -400, 0, 400, 800, 1200, 1600,
> + 2100, 2600, 3000, 3400, 3800, 4200, 4600, 5100, 5600, 6000, 6400,
> + 6800, 7200, 7600, 8000, 8500, 9000, 9400, 9800, 10200, 10600, 11000,
> + 11400, 11900, 12400, 12800, 13200, 13600, 14000, 14400, 14800,
> + 15300, 15800, 16200, 16600, 17000, 17400, 17800, 18200, 18700,
> + 19200, 19600, 20000, 20400, 20800, 21200, 21600, 22100, 22600,
> + 23000, 23400, 23800, 24200, 24600, 25000, 25400, 25900, 26400,
> + 26800, 27200, 27600, 28000, 28400, 28800, 29300, 29800, 30200,
> + 30600, 31000, 31400, 31800, 32200, 32600, 33100, 33600, 34000,
> + 34400, 34800, 35200, 35600, 36000, 36400, 36800, 37300, 37800,
> + 38200, 38600, 39000, 39400, 39800, 40200, 40600, 41100, 41600,
> + 42000, 42400, 42800, 43200, 43600, 44000, 44400, 44800, 45300,
> + 45800, 46200, 46600, 47000, 47400, 47800, 48200, 48600, 49000,
> + 49500, 50000, 50400, 50800, 51200, 51600, 52000, 52400, 52800,
> + 53200, 53700, 54200, 54600, 55000, 55400, 55800, 56200, 56600,
> + 57000, 57400, 57800, 58200, 58700, 59200, 59600, 60000, 60400,
> + 60800, 61200, 61600, 62000, 62400, 62800, 63300, 63800, 64200,
> + 64600, 65000, 65400, 65800, 66200, 66600, 67000, 67400, 67800,
> + 68200, 68700, 69200, 69600, 70000, 70400, 70800, 71200, 71600,
> + 72000, 72400, 72800, 73200, 73600, 74100, 74600, 75000, 75400,
> + 75800, 76200, 76600, 77000, 77400, 77800, 78200, 78600, 79000,
> + 79400, 79800, 80300, 80800, 81200, 81600, 82000, 82400, 82800,
> + 83200, 83600, 84000, 84400, 84800, 85200, 85600, 86000, 86400,
> + 86800, 87300, 87800, 88200, 88600, 89000, 89400, 89800, 90200,
> + 90600, 91000, 91400, 91800, 92200, 92600, 93000, 93400, 93800,
> + 94200, 94600, 95000, 95500, 96000, 96400, 96800, 97200, 97600,
> + 98000, 98400, 98800, 99200, 99600, 100000, 100400, 100800, 101200,
> + 101600, 102000, 102400, 102800, 103200, 103600, 104000, 104400,
> + 104800, 105200, 105600, 106100, 106600, 107000, 107400, 107800,
> + 108200, 108600, 109000, 109400, 109800, 110200, 110600, 111000,
> + 111400, 111800, 112200, 112600, 113000, 113400, 113800, 114200,
> + 114600, 115000, 115400, 115800, 116200, 116600, 117000, 117400,
> + 117800, 118200, 118600, 119000, 119400, 119800, 120200, 120600,
> + 121000, 121400, 121800, 122200, 122600, 123000
> +};
> +
> +static unsigned long omap_temp_sensor_readl(struct omap_temp_sensor
> + *temp_sensor, u32 reg)
> +{
> + return __raw_readl(temp_sensor->phy_base + reg);
> +}
> +
> +static void omap_temp_sensor_writel(struct omap_temp_sensor *temp_sensor,
> + u32 val, u32 reg)
> +{
> + __raw_writel(val, (temp_sensor->phy_base + reg));
> +}
> +
> +static int adc_to_temp_conversion(int adc_val)
> +{
> + return adc_to_temp[adc_val - OMAP_ADC_START_VALUE];
> +}
> +
> +static int temp_to_adc_conversion(long temp)
> +{
> + int i;
> +
> + for (i = 0; i <= OMAP_ADC_END_VALUE - OMAP_ADC_START_VALUE; i++)
> + if (temp < adc_to_temp[i])
> + return OMAP_ADC_START_VALUE + i - 1;
> +
> + return -EINVAL;
> +}
> +
> +static void omap_configure_temp_sensor_thresholds(struct omap_temp_sensor
> + *temp_sensor)
> +{
> + u32 temp, t_hot, t_cold, tshut_hot, tshut_cold;
> +
> + t_hot = temp_to_adc_conversion(BGAP_THRESHOLD_T_HOT);
> + t_cold = temp_to_adc_conversion(BGAP_THRESHOLD_T_COLD);
> +
> + /* Configure the TALERT thresholds */
> + temp = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_threshold);
> + temp |= (t_hot << __ffs(temp_sensor->registers->threshold_thot_mask)) |
> + (t_cold << __ffs(temp_sensor->registers->threshold_tcold_mask));
> + omap_temp_sensor_writel(temp_sensor, temp,
> + temp_sensor->registers->bgap_threshold);
> +
> + tshut_hot = temp_to_adc_conversion(TSHUT_THRESHOLD_HOT);
> + tshut_cold = temp_to_adc_conversion(TSHUT_THRESHOLD_COLD);
> +
> + /* Configure the TSHUT thresholds */
> + temp = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->thsut_threshold);
> + temp |= (tshut_hot << __ffs(temp_sensor->registers->tshut_hot_mask))
> + | (tshut_cold << __ffs(temp_sensor->registers->tshut_hot_mask));
> + omap_temp_sensor_writel(temp_sensor, temp,
> + temp_sensor->registers->thsut_threshold);
> +}
> +
> +static void omap_configure_temp_sensor_counter(struct omap_temp_sensor
> + *temp_sensor, u32 counter)
> +{
> + u32 val;
> +
> + val = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_counter);
> + val &= ~(temp_sensor->registers->counter_mask);
> + val |= (counter << __ffs(temp_sensor->registers->counter_mask));
> + omap_temp_sensor_writel(temp_sensor, val,
> + temp_sensor->registers->bgap_counter);
> +}
> +
> +static void omap_enable_continuous_mode(struct omap_temp_sensor *temp_sensor,
> + bool enable)
> +{
> + u32 val;
> +
> + val = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_mode_ctrl);
> +
> + if (enable)
> + val |= (1 << __ffs(temp_sensor->registers->mode_ctrl_mask));
> + else
> + val &= ~(temp_sensor->registers->mode_ctrl_mask);
> +
> + omap_temp_sensor_writel(temp_sensor, val,
> + temp_sensor->registers->bgap_mode_ctrl);
> +}
> +
> +/* Sysfs hook functions */
> +
> +static ssize_t show_temp_max(struct device *dev,
> + struct device_attribute *devattr, char *buf)
> +{
> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
> + int temp;
> +
> + mutex_lock(&temp_sensor->sensor_mutex);
> +
> + temp = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_threshold);
> + temp = (temp & temp_sensor->registers->threshold_thot_mask)
> + >> __ffs(temp_sensor->registers->threshold_thot_mask);
> +
> + if (temp < OMAP_ADC_START_VALUE || temp > OMAP_ADC_END_VALUE) {
> + dev_err(dev, "invalid value\n");
> + mutex_unlock(&temp_sensor->sensor_mutex);
> + return -EINVAL;
> + }
> +
> + temp = adc_to_temp_conversion(temp);
> + mutex_unlock(&temp_sensor->sensor_mutex);
> +
> + return snprintf(buf, 16, "%d\n", temp);
> +}
> +
> +static ssize_t set_temp_max(struct device *dev,
> + struct device_attribute *devattr,
> + const char *buf, size_t count)
> +{
> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
> + long val;
> + u32 reg_val, t_cold, t_hot, temp;
> +
> + mutex_lock(&temp_sensor->sensor_mutex);
> +
> + if (strict_strtol(buf, 10, &val)) {
> + count = -EINVAL;
> + goto out;
> + }
> +
> + t_hot = temp_to_adc_conversion(val);
> + if ((t_hot < OMAP_ADC_START_VALUE || t_hot > OMAP_ADC_END_VALUE)) {
> + dev_err(dev, "invalid range\n");
> + count = -EINVAL;
> + goto out;
> + }
> +
> + /* obtain the T cold value */
> + t_cold = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_threshold);
> + t_cold = (t_cold & temp_sensor->registers->threshold_tcold_mask) >>
> + __ffs(temp_sensor->registers->threshold_tcold_mask);
> +
> + if (t_hot < t_cold) {
> + dev_err(dev, "Error! T_HOT value lesser than T_COLD\n");
> + count = -EINVAL;
> + goto out;
> + }
> +
> + /* write the new t_hot value */
> + reg_val = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_threshold);
> + reg_val &= ~(temp_sensor->registers->threshold_thot_mask);
> + reg_val |= (t_hot <<
> + __ffs(temp_sensor->registers->threshold_thot_mask));
> + omap_temp_sensor_writel(temp_sensor, reg_val,
> + temp_sensor->registers->bgap_threshold);
> +
> + /* Read the current temperature */
> + temp = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->temp_sensor_ctrl);
> + temp &= (temp_sensor->registers->bgap_dtemp_mask);
> +
> + /*
> + * If user sets the HIGH threshold(t_hot) greater than the current
> + * temperature(temp) unmask the HOT interrupts
> + */
> + if (t_hot > temp) {
> + reg_val = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_mask_ctrl);
> + reg_val &= ~(temp_sensor->registers->mask_cold_mask);
> + reg_val |= temp_sensor->registers->mask_hot_mask;
> + omap_temp_sensor_writel(temp_sensor, reg_val,
> + temp_sensor->registers->bgap_mask_ctrl);
> + }
> +
> + /*
> + * If current temperature is in-between the hot and cold thresholds,
> + * Enable both masks.
> + */
> + if (temp > t_cold && temp < t_hot) {
> + reg_val = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_mask_ctrl);
> + reg_val |= temp_sensor->registers->mask_cold_mask;
> + reg_val |= temp_sensor->registers->mask_hot_mask;
> + omap_temp_sensor_writel(temp_sensor, reg_val,
> + OMAP4460_BGAP_CTRL_OFFSET);
> + }
> + /*
> + * else no need to do anything since HW will immediately compare
> + * the new threshold and generate interrupt accordingly
> + */
> +out:
> + mutex_unlock(&temp_sensor->sensor_mutex);
> + return count;
> +}
> +
> +static ssize_t show_temp_max_hyst(struct device *dev,
> + struct device_attribute *devattr, char *buf)
> +{
> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
> + u32 temp;
> +
> + mutex_lock(&temp_sensor->sensor_mutex);
> +
> + temp = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_threshold);
> + temp = (temp & temp_sensor->registers->threshold_tcold_mask) >>
> + __ffs(temp_sensor->registers->threshold_tcold_mask);
> +
> + if (temp < OMAP_ADC_START_VALUE || temp > OMAP_ADC_END_VALUE) {
> + dev_err(dev, "invalid value\n");
> + mutex_unlock(&temp_sensor->sensor_mutex);
> + return -EINVAL;
> + }
> +
> + temp = adc_to_temp_conversion(temp);
> + mutex_unlock(&temp_sensor->sensor_mutex);
> +
> + return snprintf(buf, 16, "%d\n", temp);
> +}
> +
> +static ssize_t set_temp_max_hyst(struct device *dev,
> + struct device_attribute *devattr,
> + const char *buf, size_t count)
> +{
> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
> + u32 reg_val, t_hot, t_cold, temp;
> + long val;
> +
> + mutex_lock(&temp_sensor->sensor_mutex);
> +
> + if (strict_strtol(buf, 10, &val)) {
> + count = -EINVAL;
> + goto out;
> + }
> +
> + t_cold = temp_to_adc_conversion(val);
> + if (t_cold < OMAP_ADC_START_VALUE || t_cold > OMAP_ADC_END_VALUE) {
> + dev_err(dev, "invalid range");
> + count = -EINVAL;
> + goto out;
> + }
> +
> + /* obtain the T HOT value */
> + t_hot = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_threshold);
> + t_hot = (t_hot & temp_sensor->registers->threshold_thot_mask) >>
> + __ffs(temp_sensor->registers->threshold_thot_mask);
> +
> + if (t_cold > t_hot) {
> + dev_err(dev, "Error! T_COLD value greater than T_HOT\n");
> + count = -EINVAL;
> + goto out;
> + }
> +
> + /* write the new t_cold value */
> + reg_val = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_threshold);
> + reg_val &= ~(temp_sensor->registers->threshold_tcold_mask);
> + reg_val |= (t_cold <<
> + __ffs(temp_sensor->registers->threshold_tcold_mask));
> + omap_temp_sensor_writel(temp_sensor, reg_val,
> + temp_sensor->registers->bgap_threshold);
> +
> + /* Read the current temperature */
> + temp = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->temp_sensor_ctrl);
> + temp &= (temp_sensor->registers->bgap_dtemp_mask);
> +
> + /*
> + * If user sets the LOW threshold(t_cold) lower than the current
> + * temperature(temp) unmask the COLD interrupts
> + */
> + if (t_cold < temp) {
> + reg_val = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_mask_ctrl);
> + reg_val &= ~(temp_sensor->registers->mask_hot_mask);
> + reg_val |= temp_sensor->registers->mask_cold_mask;
> + omap_temp_sensor_writel(temp_sensor, reg_val,
> + temp_sensor->registers->bgap_mask_ctrl);
> + }
> +
> + /*
> + * If current temperature is in-between the hot and cold thresholds,
> + * Enable both masks.
> + */
> + if (temp < t_hot && temp > t_cold) {
> + reg_val = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_mask_ctrl);
> + reg_val |= temp_sensor->registers->mask_cold_mask;
> + reg_val |= temp_sensor->registers->mask_hot_mask;
> + omap_temp_sensor_writel(temp_sensor, reg_val,
> + temp_sensor->registers->bgap_mask_ctrl);
> + }
> +
> + /*
> + * else no need to do anything since HW will immediately compare
> + * the new threshold and generate interrupt accordingly
> + */
> +
> +out:
> + mutex_unlock(&temp_sensor->sensor_mutex);
> + return count;
> +}
> +
> +static ssize_t show_update_rate(struct device *dev,
> + struct device_attribute *devattr, char *buf)
> +{
> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
> + u32 temp = 0, ret = 0;
> +
> + mutex_lock(&temp_sensor->sensor_mutex);
> +
> + if (!temp_sensor->clk_rate) {
> + dev_err(dev, "clk_rate is NULL\n");
> + ret = -EINVAL;
> + goto out;
> + }
> +
> + temp = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_counter);
> + temp = (temp & temp_sensor->registers->counter_mask) >>
> + __ffs(temp_sensor->registers->counter_mask);
> + temp = temp * 1000 / (temp_sensor->clk_rate);
> +
> +out:
> + mutex_unlock(&temp_sensor->sensor_mutex);
> + if (!ret)
> + return sprintf(buf, "%d\n", temp);
> +
> + return ret;
> +}
> +
> +static ssize_t set_update_rate(struct device *dev,
> + struct device_attribute *devattr,
> + const char *buf, size_t count)
> +{
> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
> + u32 reg_val;
> + long val;
> +
> + mutex_lock(&temp_sensor->sensor_mutex);
> +
> + if (strict_strtol(buf, 10, &val)) {
> + count = -EINVAL;
> + goto out;
> + }
> +
> + val *= temp_sensor->clk_rate / 1000;
> + reg_val = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_counter);
> +
> + reg_val &= ~(temp_sensor->registers->counter_mask);
> + reg_val |= val;
> + omap_temp_sensor_writel(temp_sensor, reg_val,
> + temp_sensor->registers->bgap_counter);
> +
> +out:
> + mutex_unlock(&temp_sensor->sensor_mutex);
> + return count;
> +}
> +
> +static int omap_temp_sensor_read_temp(struct device *dev,
> + struct device_attribute *devattr,
> + char *buf)
> +{
> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
> + int temp, ret = 0;
> +
> + mutex_lock(&temp_sensor->sensor_mutex);
> +
> + temp = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->temp_sensor_ctrl);
> + temp &= temp_sensor->registers->bgap_dtemp_mask;
> +
> + if (!temp_sensor->is_efuse_valid)
> + dev_err(dev, "Invalid EFUSE, Non-trimmed BGAP, Temp not accurate\n");
> +
> + /* look up for temperature in the table and return the temperature */
> + if (temp < OMAP_ADC_START_VALUE || temp > OMAP_ADC_END_VALUE) {
> + dev_err(dev, "invalid adc code reported %d", temp);
> + ret = -EINVAL;
> + goto out;
> + }
> +
> + temp = adc_to_temp[temp - OMAP_ADC_START_VALUE];
> +
> +out:
> + mutex_unlock(&temp_sensor->sensor_mutex);
> + if (!ret)
> + return sprintf(buf, "%d\n", temp);
> +
> + return ret;
> +}
> +
> +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, omap_temp_sensor_read_temp,
> + NULL, 0);
> +static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp_max,
> + set_temp_max, 0);
> +static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO, show_temp_max_hyst,
> + set_temp_max_hyst, 0);
> +static SENSOR_DEVICE_ATTR(update_rate, S_IWUSR | S_IRUGO, show_update_rate,
> + set_update_rate, 0);
> +
> +static struct attribute *omap_temp_sensor_attributes[] = {
> + &sensor_dev_attr_temp1_input.dev_attr.attr,
> + &sensor_dev_attr_temp1_max.dev_attr.attr,
> + &sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
> + &sensor_dev_attr_update_rate.dev_attr.attr,
> + NULL
> +};
> +
> +static const struct attribute_group omap_temp_sensor_group = {
> + .attrs = omap_temp_sensor_attributes,
> +};
> +
> +static int omap_temp_sensor_clk_enable(struct omap_temp_sensor *temp_sensor)
> +{
> + u32 ret = 0;
> +
> + if (temp_sensor->clk_on) {
> + dev_err(temp_sensor->hwmon_dev, "clock already on\n");
> + goto out;
> + }
> +
> + ret = pm_runtime_get_sync(temp_sensor->hwmon_dev);
> + if (ret < 0) {
> + dev_err(temp_sensor->hwmon_dev, "get sync failed\n");
> + goto out;
> + }
> +
> + temp_sensor->clk_on = 1;
> +
> +out:
> + return ret;
> +}
> +
> +static int omap_temp_sensor_clk_disable(struct omap_temp_sensor *temp_sensor)
> +{
> + u32 ret = 0;
> +
> + if (!temp_sensor->clk_on) {
> + dev_err(temp_sensor->hwmon_dev, "clock already off\n");
> + goto out;
> + }
> +
> + /* Gate the clock */
> + ret = pm_runtime_put_sync(temp_sensor->hwmon_dev);
> + if (ret < 0) {
> + dev_err(temp_sensor->hwmon_dev, "put sync failed\n");
> + goto out;
> + }
> + temp_sensor->clk_on = 0;
> +
> +out:
> + return ret;
> +}
> +
> +static irqreturn_t omap_talert_irq_handler(int irq, void *data)
> +{
> + struct omap_temp_sensor *temp_sensor;
> + int t_hot, t_cold, temp;
> +
> + temp_sensor = data;
> + mutex_lock(&temp_sensor->sensor_mutex);
> +
> + /* Read the status of t_hot */
> + t_hot = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_status)
> + & temp_sensor->registers->status_hot_mask;
> +
> + /* Read the status of t_cold */
> + t_cold = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_status)
> + & temp_sensor->registers->status_cold_mask;
> +
> + temp = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_mask_ctrl);
> + /*
> + * One TALERT interrupt: Two sources
> + * If the interrupt is due to t_hot then mask t_hot and
> + * and unmask t_cold else mask t_cold and unmask t_hot
> + */
> + if (t_hot) {
> + temp &= ~(temp_sensor->registers->mask_hot_mask);
> + temp |= temp_sensor->registers->mask_cold_mask;
> + } else if (t_cold) {
> + temp &= ~(temp_sensor->registers->mask_cold_mask);
> + temp |= temp_sensor->registers->mask_hot_mask;
> + }
> +
> + omap_temp_sensor_writel(temp_sensor, temp,
> + temp_sensor->registers->bgap_mask_ctrl);
> +
> + /* kobject_uvent to user space telling thermal threshold crossed */
> + kobject_uevent(&temp_sensor->hwmon_dev->kobj, KOBJ_CHANGE);
> +
> + mutex_unlock(&temp_sensor->sensor_mutex);
> +
> + return IRQ_HANDLED;
> +}
> +
> +static int __devinit omap_temp_sensor_probe(struct platform_device *pdev)
> +{
> + struct omap_temp_sensor_pdata *pdata = pdev->dev.platform_data;
> + struct omap_temp_sensor *temp_sensor;
> + struct resource *mem;
> + int ret = 0;
> + int val, clk_rate;
> +
> + if (!pdata) {
> + dev_err(&pdev->dev, "platform data missing\n");
> + return -EINVAL;
> + }
> +
> + temp_sensor = kzalloc(sizeof(*temp_sensor), GFP_KERNEL);
> + if (!temp_sensor)
> + return -ENOMEM;
> +
> + mutex_init(&temp_sensor->sensor_mutex);
> +
> + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + if (!mem) {
> + dev_err(&pdev->dev, "no mem resource\n");
> + ret = -ENOMEM;
> + goto plat_res_err;
> + }
> +
> + temp_sensor->irq = platform_get_irq_byname(pdev, "thermal_alert");
> + if (temp_sensor->irq < 0) {
> + dev_err(&pdev->dev, "get_irq_byname failed\n");
> + ret = temp_sensor->irq;
> + goto plat_res_err;
> + }
> +
> + temp_sensor->phy_base = ioremap(mem->start, resource_size(mem));
> + temp_sensor->clock = NULL;
> + temp_sensor->registers = pdata->registers;
> + temp_sensor->hwmon_dev = &pdev->dev;
> +
> + pm_runtime_enable(&pdev->dev);
> + pm_runtime_irq_safe(&pdev->dev);
> +
> + /*
> + * check if the efuse has a non-zero value if not
> + * it is an untrimmed sample and the temperatures
> + * may not be accurate
> + */
> +
> + if (omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_efuse))
> + temp_sensor->is_efuse_valid = 1;
> +
> + platform_set_drvdata(pdev, temp_sensor);
> + dev_set_drvdata(&pdev->dev, temp_sensor);
> + temp_sensor->clock = clk_get(temp_sensor->hwmon_dev, "fck");
> + if (IS_ERR(temp_sensor->clock)) {
> + ret = PTR_ERR(temp_sensor->clock);
> + dev_err(temp_sensor->hwmon_dev,
> + "unable to get fclk: %d\n", ret);
> + ret = -EINVAL;
> + goto plat_res_err;
> + }
> +
> + ret = omap_temp_sensor_clk_enable(temp_sensor);
> + if (ret) {
> + dev_err(&pdev->dev, "Cannot enable temp sensor\n");
> + goto clken_err;
> + }
> +
> + clk_rate = clk_round_rate(temp_sensor->clock, 2000000);
> + if (clk_rate < 1000000 || clk_rate == 0xffffffff) {
> + dev_err(&pdev->dev, "Error round rate\n");
> + ret = -EINVAL;
> + goto clken_err;
> + }
> +
> + ret = clk_set_rate(temp_sensor->clock, clk_rate);
> + if (ret) {
> + dev_err(&pdev->dev, "Cannot set clock rate\n");
> + goto clken_err;
> + }
> +
> + temp_sensor->clk_rate = clk_rate;
> + omap_enable_continuous_mode(temp_sensor, 1);
> + omap_configure_temp_sensor_thresholds(temp_sensor);
> + /* 1 ms */
> + omap_configure_temp_sensor_counter(temp_sensor, 1);
> +
> + /* Wait till the first conversion is done wait for at least 1ms */
> + usleep_range(1000, 2000);
> +
> + /* Read the temperature once due to hw issue*/
> + omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->temp_sensor_ctrl);
> +
> + /* Set 2 seconds time as default counter */
> + omap_configure_temp_sensor_counter(temp_sensor,
> + temp_sensor->clk_rate * 2);
> +
> + temp_sensor->hwmon_dev = hwmon_device_register(&pdev->dev);
> + if (IS_ERR(temp_sensor->hwmon_dev)) {
> + dev_err(&pdev->dev, "hwmon_device_register failed.\n");
> + ret = PTR_ERR(temp_sensor->hwmon_dev);
> + goto hwmon_reg_err;
> + }
> +
> + ret = sysfs_create_group(&pdev->dev.kobj,
> + &omap_temp_sensor_group);
> + if (ret) {
> + dev_err(&pdev->dev, "could not create sysfs files\n");
> + goto sysfs_create_err;
> + }
> +
> + kobject_uevent(&temp_sensor->hwmon_dev->kobj, KOBJ_ADD);
> +
> + ret = request_threaded_irq(temp_sensor->irq, NULL,
> + omap_talert_irq_handler, IRQF_TRIGGER_RISING | IRQF_ONESHOT,
> + "temp_sensor", temp_sensor);
> + if (ret) {
> + dev_err(&pdev->dev, "Request threaded irq failed.\n");
> + goto req_irq_err;
> + }
> +
> + /* unmask the T_COLD and unmask T_HOT at init */
> + val = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_mask_ctrl);
> + val |= temp_sensor->registers->mask_cold_mask
> + | temp_sensor->registers->mask_hot_mask;
> +
> + omap_temp_sensor_writel(temp_sensor, val,
> + temp_sensor->registers->bgap_mask_ctrl);
> +
> + return 0;
> +
> +req_irq_err:
> + kobject_uevent(&temp_sensor->hwmon_dev->kobj, KOBJ_REMOVE);
> + sysfs_remove_group(&temp_sensor->hwmon_dev->kobj,
> + &omap_temp_sensor_group);
> +sysfs_create_err:
> + hwmon_device_unregister(&pdev->dev);
> +hwmon_reg_err:
> + omap_temp_sensor_clk_disable(temp_sensor);
> +clken_err:
> + clk_put(temp_sensor->clock);
> +plat_res_err:
> + mutex_destroy(&temp_sensor->sensor_mutex);
> + kfree(temp_sensor);
> + return ret;
> +}
> +
> +static int __devexit omap_temp_sensor_remove(struct platform_device *pdev)
> +{
> + struct omap_temp_sensor *temp_sensor = platform_get_drvdata(pdev);
> +
> + hwmon_device_unregister(&pdev->dev);
> + kobject_uevent(&temp_sensor->hwmon_dev->kobj, KOBJ_REMOVE);
> + sysfs_remove_group(&temp_sensor->hwmon_dev->kobj,
> + &omap_temp_sensor_group);
> + omap_temp_sensor_clk_disable(temp_sensor);
> + clk_put(temp_sensor->clock);
> + platform_set_drvdata(pdev, NULL);
> + free_irq(temp_sensor->irq, temp_sensor);
> + mutex_destroy(&temp_sensor->sensor_mutex);
> + kfree(temp_sensor);
> +
> + return 0;
> +}
> +
> +#ifdef CONFIG_PM
> +static void omap_temp_sensor_save_ctxt(struct omap_temp_sensor *temp_sensor)
> +{
> + temp_sensor->temp_sensor_ctrl = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->temp_sensor_ctrl);
> + temp_sensor->bg_ctrl = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_mask_ctrl);
> + temp_sensor->bg_counter = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_counter);
> + temp_sensor->bg_threshold = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_threshold);
> + temp_sensor->temp_sensor_tshut_threshold =
> + omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->thsut_threshold);
> +}
> +
> +static void omap_temp_sensor_restore_ctxt(struct omap_temp_sensor *temp_sensor)
> +{
> + omap_temp_sensor_writel(temp_sensor,
> + temp_sensor->temp_sensor_ctrl,
> + temp_sensor->registers->temp_sensor_ctrl);
> + omap_temp_sensor_writel(temp_sensor,
> + temp_sensor->bg_ctrl,
> + temp_sensor->registers->bgap_mask_ctrl);
> + omap_temp_sensor_writel(temp_sensor,
> + temp_sensor->bg_counter,
> + temp_sensor->registers->bgap_counter);
> + omap_temp_sensor_writel(temp_sensor,
> + temp_sensor->bg_threshold,
> + temp_sensor->registers->bgap_threshold);
> + omap_temp_sensor_writel(temp_sensor,
> + temp_sensor->temp_sensor_tshut_threshold,
> + temp_sensor->registers->thsut_threshold);
> +}
> +
> +static int omap_temp_sensor_suspend(struct device *dev)
> +{
> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
> +
> + omap_temp_sensor_save_ctxt(temp_sensor);
> +
> + return 0;
> +}
> +
> +static int omap_temp_sensor_resume(struct device *dev)
> +{
> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
> +
> + omap_temp_sensor_restore_ctxt(temp_sensor);
> +
> + return 0;
> +}
> +
> +static int omap_temp_sensor_runtime_suspend(struct device *dev)
> +{
> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
> +
> + omap_temp_sensor_save_ctxt(temp_sensor);
> +
> + return 0;
> +}
> +
> +static int omap_temp_sensor_runtime_resume(struct device *dev)
> +{
> + static int context_loss_count;
> + int temp;
> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
> +
> + temp = omap_device_get_context_loss_count(to_platform_device(dev));
> +
> + if (temp != context_loss_count && context_loss_count != 0)
> + omap_temp_sensor_restore_ctxt(temp_sensor);
> +
> + context_loss_count = temp;
> +
> + return 0;
> +}
> +
> +static const struct dev_pm_ops omap_temp_sensor_dev_pm_ops = {
> + .suspend = omap_temp_sensor_suspend,
> + .resume = omap_temp_sensor_resume,
> + .runtime_suspend = omap_temp_sensor_runtime_suspend,
> + .runtime_resume = omap_temp_sensor_runtime_resume,
> +};
> +
> +#endif
> +
> +static struct platform_driver omap_temp_sensor_driver = {
> + .probe = omap_temp_sensor_probe,
> + .remove = omap_temp_sensor_remove,
> + .driver = {
> + .name = "omap_temp_sensor",
> +#ifdef CONFIG_PM
> + .pm = &omap_temp_sensor_dev_pm_ops,
> +#endif
> + },
> +};
> +
> +int __init omap_temp_sensor_init(void)
> +{
> + return platform_driver_register(&omap_temp_sensor_driver);
> +}
> +module_init(omap_temp_sensor_init);
> +
> +static void __exit omap_temp_sensor_exit(void)
> +{
> + platform_driver_unregister(&omap_temp_sensor_driver);
> +}
> +module_exit(omap_temp_sensor_exit);
> +
> +MODULE_DESCRIPTION("OMAP446X temperature sensor Driver");
> +MODULE_LICENSE("GPL");
> +MODULE_ALIAS("platform:" DRIVER_NAME);
> +MODULE_AUTHOR("J Keerthy <j-keerthy@ti.com>");
> --
> 1.7.0.4
>
^ permalink raw reply [flat|nested] 35+ messages in thread
* Re: [lm-sensors] [RFC PATCH 6/6 V2] hwmon: OMAP4: On die
@ 2011-08-19 2:13 ` Guenter Roeck
0 siblings, 0 replies; 35+ messages in thread
From: Guenter Roeck @ 2011-08-19 2:13 UTC (permalink / raw)
To: Keerthy; +Cc: linux-omap, Jean Delvare, lm-sensors
On Thu, Aug 18, 2011 at 06:52:15AM -0400, Keerthy wrote:
> On chip temperature sensor driver. The driver monitors the temperature of
> the MPU subsystem of the OMAP4. It sends notifications to the user space if
> the temperature crosses user defined thresholds via kobject_uevent interface.
> The user is allowed to configure the temperature thresholds vis sysfs nodes
> exposed using hwmon interface.
>
> Signed-off-by: Keerthy <j-keerthy@ti.com>
> Cc: Jean Delvare <khali@linux-fr.org>
> Cc: Guenter Roeck <guenter.roeck@ericsson.com>
> Cc: lm-sensors@lm-sensors.org
High level review:
- too much and too broad mutex locking. show functions should not need locks at all,
set functions only while data is written into registers and into platform data.
- driver is quite noisy. There should definitely not be any log messages
if a set parameter is wrong. Show functions already return an error value
to the user; a log message indicating the error again just creates noise.
For one boolean set during probe (is_efuse_valid), each subsequent show results
in a log message if it is false. Some errors result in multiple log messages.
- Wrong use of EINVAL throughout the driver (EINVAL is "Invalid Argument")
- excessive ( )
- linear search through a sorted array is very expensive. Consider using a binary search.
- temp_to_adc_conversion return code (error if negative) is not checked properly.
I am sure there are other problems, but those are difficult to find with all the noise
above.
Guenter
> ---
> Documentation/hwmon/omap_temp_sensor | 27 +
> drivers/hwmon/Kconfig | 11 +
> drivers/hwmon/Makefile | 1 +
> drivers/hwmon/omap_temp_sensor.c | 918 ++++++++++++++++++++++++++++++++++
> 4 files changed, 957 insertions(+), 0 deletions(-)
> create mode 100644 Documentation/hwmon/omap_temp_sensor
> create mode 100644 drivers/hwmon/omap_temp_sensor.c
>
> diff --git a/Documentation/hwmon/omap_temp_sensor b/Documentation/hwmon/omap_temp_sensor
> new file mode 100644
> index 0000000..e01a6d6
> --- /dev/null
> +++ b/Documentation/hwmon/omap_temp_sensor
> @@ -0,0 +1,27 @@
> +Kernel driver omap_temp_sensor
> +===============
> +
> +Supported chips:
> + * Texas Instruments OMAP4460
> + Prefix: 'omap_temp_sensor'
> + Datasheet: http://focus.ti.com/docs/prod/folders/print/tmp102.html
> +
> +Author:
> + J Keerthy <j-keerthy@ti.com>
> +
> +Description
> +-----------
> +
> +The Texas Instruments OMAP4 family of chips have a bandgap temperature sensor.
> +The temperature sensor feature is used to convert the temperature of the device
> +into a decimal value coded on 10 bits. An internal ADC is used for conversion.
> +The recommended operating temperatures must be in the range -40 degree Celsius
> +to 123 degree celsius for standard conversion.
> +The thresholds are programmable and upon crossing the thresholds an interrupt
> +is generated. The OMAP temperature sensor has a programmable update rate in
> +milli seconds.
> +(Currently the driver programs a default of 2000 milli seconds).
> +
> +The driver provides the common sysfs-interface for temperatures (see
> +Documentation/hwmon/sysfs-interface under Temperatures).
> +
> diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
> index 5f888f7..9c9cd8b 100644
> --- a/drivers/hwmon/Kconfig
> +++ b/drivers/hwmon/Kconfig
> @@ -323,6 +323,17 @@ config SENSORS_F71805F
> This driver can also be built as a module. If so, the module
> will be called f71805f.
>
> +config SENSORS_OMAP_BANDGAP_TEMP_SENSOR
> + bool "OMAP on-die temperature sensor hwmon driver"
> + depends on HWMON && ARCH_OMAP && OMAP_TEMP_SENSOR
> + help
> + If you say yes here you get support for hardware
> + monitoring features of the OMAP on die temperature
> + sensor.
> +
> + Continuous conversion programmable delay
> + mode is used for temperature conversion.
> +
> config SENSORS_F71882FG
> tristate "Fintek F71882FG and compatibles"
> help
> diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
> index 28061cf..d0f89f5 100644
> --- a/drivers/hwmon/Makefile
> +++ b/drivers/hwmon/Makefile
> @@ -91,6 +91,7 @@ obj-$(CONFIG_SENSORS_MAX6639) += max6639.o
> obj-$(CONFIG_SENSORS_MAX6642) += max6642.o
> obj-$(CONFIG_SENSORS_MAX6650) += max6650.o
> obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o
> +obj-$(CONFIG_SENSORS_OMAP_BANDGAP_TEMP_SENSOR) += omap_temp_sensor.o
> obj-$(CONFIG_SENSORS_PC87360) += pc87360.o
> obj-$(CONFIG_SENSORS_PC87427) += pc87427.o
> obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o
> diff --git a/drivers/hwmon/omap_temp_sensor.c b/drivers/hwmon/omap_temp_sensor.c
> new file mode 100644
> index 0000000..586a361
> --- /dev/null
> +++ b/drivers/hwmon/omap_temp_sensor.c
> @@ -0,0 +1,918 @@
> +/*
> + * OMAP4 Temperature sensor driver file
> + *
> + * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
> + * Author: J Keerthy <j-keerthy@ti.com>
> + * Author: Moiz Sonasath <m-sonasath@ti.com>
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * 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.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
> + * 02110-1301 USA
> + *
> + */
> +
> +#include <linux/interrupt.h>
> +#include <linux/clk.h>
> +#include <linux/io.h>
> +#include <linux/slab.h>
> +#include <linux/init.h>
> +#include <plat/omap_device.h>
> +#include <linux/kernel.h>
> +#include <linux/device.h>
> +#include <linux/jiffies.h>
> +#include <linux/hwmon.h>
> +#include <linux/hwmon-sysfs.h>
> +#include <linux/stddef.h>
> +#include <linux/sysfs.h>
> +#include <linux/err.h>
> +#include <linux/types.h>
> +#include <linux/mutex.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/delay.h>
> +#include <plat/temperature_sensor.h>
> +
> +#define TSHUT_THRESHOLD_HOT 122000 /* 122 deg C */
> +#define TSHUT_THRESHOLD_COLD 100000 /* 100 deg C */
> +#define BGAP_THRESHOLD_T_HOT 73000 /* 73 deg C */
> +#define BGAP_THRESHOLD_T_COLD 71000 /* 71 deg C */
> +#define OMAP_ADC_START_VALUE 530
> +#define OMAP_ADC_END_VALUE 923
> +
> +/*
> + * omap_temp_sensor structure
> + * @hwmon_dev - device pointer
> + * @clock - Clock pointer
> + * @registers - Pointer to structure with register offsets and bitfields
> + * @sensor_mutex - Mutex for sysfs, irq and PM
> + * @irq - MPU Irq number for thermal alert
> + * @phy_base - Physical base of the temp I/O
> + * @clk_rate - Holds current clock rate
> + * @temp_sensor_ctrl - temp sensor control register value
> + * @bg_ctrl - bandgap ctrl register value
> + * @bg_counter - bandgap counter value
> + * @bg_threshold - bandgap threshold register value
> + * @temp_sensor_tshut_threshold - bandgap tshut register value
> + * @is_efuse_valid - Flag to determine if efuse is valid or not
> + * @clk_on - Manages the current clock state
> + */
> +struct omap_temp_sensor {
> + struct device *hwmon_dev;
> + struct clk *clock;
> + struct omap_temp_sensor_registers *registers;
> + struct mutex sensor_mutex; /* Mutex for sysfs, irq and PM */
> + unsigned int irq;
> + void __iomem *phy_base;
> + u32 clk_rate;
> + u32 temp_sensor_ctrl;
> + u32 bg_ctrl;
> + u32 bg_counter;
> + u32 bg_threshold;
> + u32 temp_sensor_tshut_threshold;
> + bool is_efuse_valid;
> + bool clk_on;
> +};
> +
> +/*
> + * Temperature values in milli degree celsius
> + * ADC code values from 530 to 923
> + */
> +static int adc_to_temp[] = {
> + -40000, -40000, -40000, -40000, -39800, -39400, -39000, -38600, -38200,
> + -37800, -37300, -36800, -36400, -36000, -35600, -35200, -34800,
> + -34300, -33800, -33400, -33000, -32600, -32200, -31800, -31300,
> + -30800, -30400, -30000, -29600, -29200, -28700, -28200, -27800,
> + -27400, -27000, -26600, -26200, -25700, -25200, -24800, -24400,
> + -24000, -23600, -23200, -22700, -22200, -21800, -21400, -21000,
> + -20600, -20200, -19700, -19200, -18800, -18400, -18000, -17600,
> + -17200, -16700, -16200, -15800, -15400, -15000, -14600, -14200,
> + -13700, -13200, -12800, -12400, -12000, -11600, -11200, -10700,
> + -10200, -9800, -9400, -9000, -8600, -8200, -7700, -7200, -6800,
> + -6400, -6000, -5600, -5200, -4800, -4300, -3800, -3400, -3000,
> + -2600, -2200, -1800, -1300, -800, -400, 0, 400, 800, 1200, 1600,
> + 2100, 2600, 3000, 3400, 3800, 4200, 4600, 5100, 5600, 6000, 6400,
> + 6800, 7200, 7600, 8000, 8500, 9000, 9400, 9800, 10200, 10600, 11000,
> + 11400, 11900, 12400, 12800, 13200, 13600, 14000, 14400, 14800,
> + 15300, 15800, 16200, 16600, 17000, 17400, 17800, 18200, 18700,
> + 19200, 19600, 20000, 20400, 20800, 21200, 21600, 22100, 22600,
> + 23000, 23400, 23800, 24200, 24600, 25000, 25400, 25900, 26400,
> + 26800, 27200, 27600, 28000, 28400, 28800, 29300, 29800, 30200,
> + 30600, 31000, 31400, 31800, 32200, 32600, 33100, 33600, 34000,
> + 34400, 34800, 35200, 35600, 36000, 36400, 36800, 37300, 37800,
> + 38200, 38600, 39000, 39400, 39800, 40200, 40600, 41100, 41600,
> + 42000, 42400, 42800, 43200, 43600, 44000, 44400, 44800, 45300,
> + 45800, 46200, 46600, 47000, 47400, 47800, 48200, 48600, 49000,
> + 49500, 50000, 50400, 50800, 51200, 51600, 52000, 52400, 52800,
> + 53200, 53700, 54200, 54600, 55000, 55400, 55800, 56200, 56600,
> + 57000, 57400, 57800, 58200, 58700, 59200, 59600, 60000, 60400,
> + 60800, 61200, 61600, 62000, 62400, 62800, 63300, 63800, 64200,
> + 64600, 65000, 65400, 65800, 66200, 66600, 67000, 67400, 67800,
> + 68200, 68700, 69200, 69600, 70000, 70400, 70800, 71200, 71600,
> + 72000, 72400, 72800, 73200, 73600, 74100, 74600, 75000, 75400,
> + 75800, 76200, 76600, 77000, 77400, 77800, 78200, 78600, 79000,
> + 79400, 79800, 80300, 80800, 81200, 81600, 82000, 82400, 82800,
> + 83200, 83600, 84000, 84400, 84800, 85200, 85600, 86000, 86400,
> + 86800, 87300, 87800, 88200, 88600, 89000, 89400, 89800, 90200,
> + 90600, 91000, 91400, 91800, 92200, 92600, 93000, 93400, 93800,
> + 94200, 94600, 95000, 95500, 96000, 96400, 96800, 97200, 97600,
> + 98000, 98400, 98800, 99200, 99600, 100000, 100400, 100800, 101200,
> + 101600, 102000, 102400, 102800, 103200, 103600, 104000, 104400,
> + 104800, 105200, 105600, 106100, 106600, 107000, 107400, 107800,
> + 108200, 108600, 109000, 109400, 109800, 110200, 110600, 111000,
> + 111400, 111800, 112200, 112600, 113000, 113400, 113800, 114200,
> + 114600, 115000, 115400, 115800, 116200, 116600, 117000, 117400,
> + 117800, 118200, 118600, 119000, 119400, 119800, 120200, 120600,
> + 121000, 121400, 121800, 122200, 122600, 123000
> +};
> +
> +static unsigned long omap_temp_sensor_readl(struct omap_temp_sensor
> + *temp_sensor, u32 reg)
> +{
> + return __raw_readl(temp_sensor->phy_base + reg);
> +}
> +
> +static void omap_temp_sensor_writel(struct omap_temp_sensor *temp_sensor,
> + u32 val, u32 reg)
> +{
> + __raw_writel(val, (temp_sensor->phy_base + reg));
> +}
> +
> +static int adc_to_temp_conversion(int adc_val)
> +{
> + return adc_to_temp[adc_val - OMAP_ADC_START_VALUE];
> +}
> +
> +static int temp_to_adc_conversion(long temp)
> +{
> + int i;
> +
> + for (i = 0; i <= OMAP_ADC_END_VALUE - OMAP_ADC_START_VALUE; i++)
> + if (temp < adc_to_temp[i])
> + return OMAP_ADC_START_VALUE + i - 1;
> +
> + return -EINVAL;
> +}
> +
> +static void omap_configure_temp_sensor_thresholds(struct omap_temp_sensor
> + *temp_sensor)
> +{
> + u32 temp, t_hot, t_cold, tshut_hot, tshut_cold;
> +
> + t_hot = temp_to_adc_conversion(BGAP_THRESHOLD_T_HOT);
> + t_cold = temp_to_adc_conversion(BGAP_THRESHOLD_T_COLD);
> +
> + /* Configure the TALERT thresholds */
> + temp = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_threshold);
> + temp |= (t_hot << __ffs(temp_sensor->registers->threshold_thot_mask)) |
> + (t_cold << __ffs(temp_sensor->registers->threshold_tcold_mask));
> + omap_temp_sensor_writel(temp_sensor, temp,
> + temp_sensor->registers->bgap_threshold);
> +
> + tshut_hot = temp_to_adc_conversion(TSHUT_THRESHOLD_HOT);
> + tshut_cold = temp_to_adc_conversion(TSHUT_THRESHOLD_COLD);
> +
> + /* Configure the TSHUT thresholds */
> + temp = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->thsut_threshold);
> + temp |= (tshut_hot << __ffs(temp_sensor->registers->tshut_hot_mask))
> + | (tshut_cold << __ffs(temp_sensor->registers->tshut_hot_mask));
> + omap_temp_sensor_writel(temp_sensor, temp,
> + temp_sensor->registers->thsut_threshold);
> +}
> +
> +static void omap_configure_temp_sensor_counter(struct omap_temp_sensor
> + *temp_sensor, u32 counter)
> +{
> + u32 val;
> +
> + val = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_counter);
> + val &= ~(temp_sensor->registers->counter_mask);
> + val |= (counter << __ffs(temp_sensor->registers->counter_mask));
> + omap_temp_sensor_writel(temp_sensor, val,
> + temp_sensor->registers->bgap_counter);
> +}
> +
> +static void omap_enable_continuous_mode(struct omap_temp_sensor *temp_sensor,
> + bool enable)
> +{
> + u32 val;
> +
> + val = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_mode_ctrl);
> +
> + if (enable)
> + val |= (1 << __ffs(temp_sensor->registers->mode_ctrl_mask));
> + else
> + val &= ~(temp_sensor->registers->mode_ctrl_mask);
> +
> + omap_temp_sensor_writel(temp_sensor, val,
> + temp_sensor->registers->bgap_mode_ctrl);
> +}
> +
> +/* Sysfs hook functions */
> +
> +static ssize_t show_temp_max(struct device *dev,
> + struct device_attribute *devattr, char *buf)
> +{
> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
> + int temp;
> +
> + mutex_lock(&temp_sensor->sensor_mutex);
> +
> + temp = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_threshold);
> + temp = (temp & temp_sensor->registers->threshold_thot_mask)
> + >> __ffs(temp_sensor->registers->threshold_thot_mask);
> +
> + if (temp < OMAP_ADC_START_VALUE || temp > OMAP_ADC_END_VALUE) {
> + dev_err(dev, "invalid value\n");
> + mutex_unlock(&temp_sensor->sensor_mutex);
> + return -EINVAL;
> + }
> +
> + temp = adc_to_temp_conversion(temp);
> + mutex_unlock(&temp_sensor->sensor_mutex);
> +
> + return snprintf(buf, 16, "%d\n", temp);
> +}
> +
> +static ssize_t set_temp_max(struct device *dev,
> + struct device_attribute *devattr,
> + const char *buf, size_t count)
> +{
> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
> + long val;
> + u32 reg_val, t_cold, t_hot, temp;
> +
> + mutex_lock(&temp_sensor->sensor_mutex);
> +
> + if (strict_strtol(buf, 10, &val)) {
> + count = -EINVAL;
> + goto out;
> + }
> +
> + t_hot = temp_to_adc_conversion(val);
> + if ((t_hot < OMAP_ADC_START_VALUE || t_hot > OMAP_ADC_END_VALUE)) {
> + dev_err(dev, "invalid range\n");
> + count = -EINVAL;
> + goto out;
> + }
> +
> + /* obtain the T cold value */
> + t_cold = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_threshold);
> + t_cold = (t_cold & temp_sensor->registers->threshold_tcold_mask) >>
> + __ffs(temp_sensor->registers->threshold_tcold_mask);
> +
> + if (t_hot < t_cold) {
> + dev_err(dev, "Error! T_HOT value lesser than T_COLD\n");
> + count = -EINVAL;
> + goto out;
> + }
> +
> + /* write the new t_hot value */
> + reg_val = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_threshold);
> + reg_val &= ~(temp_sensor->registers->threshold_thot_mask);
> + reg_val |= (t_hot <<
> + __ffs(temp_sensor->registers->threshold_thot_mask));
> + omap_temp_sensor_writel(temp_sensor, reg_val,
> + temp_sensor->registers->bgap_threshold);
> +
> + /* Read the current temperature */
> + temp = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->temp_sensor_ctrl);
> + temp &= (temp_sensor->registers->bgap_dtemp_mask);
> +
> + /*
> + * If user sets the HIGH threshold(t_hot) greater than the current
> + * temperature(temp) unmask the HOT interrupts
> + */
> + if (t_hot > temp) {
> + reg_val = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_mask_ctrl);
> + reg_val &= ~(temp_sensor->registers->mask_cold_mask);
> + reg_val |= temp_sensor->registers->mask_hot_mask;
> + omap_temp_sensor_writel(temp_sensor, reg_val,
> + temp_sensor->registers->bgap_mask_ctrl);
> + }
> +
> + /*
> + * If current temperature is in-between the hot and cold thresholds,
> + * Enable both masks.
> + */
> + if (temp > t_cold && temp < t_hot) {
> + reg_val = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_mask_ctrl);
> + reg_val |= temp_sensor->registers->mask_cold_mask;
> + reg_val |= temp_sensor->registers->mask_hot_mask;
> + omap_temp_sensor_writel(temp_sensor, reg_val,
> + OMAP4460_BGAP_CTRL_OFFSET);
> + }
> + /*
> + * else no need to do anything since HW will immediately compare
> + * the new threshold and generate interrupt accordingly
> + */
> +out:
> + mutex_unlock(&temp_sensor->sensor_mutex);
> + return count;
> +}
> +
> +static ssize_t show_temp_max_hyst(struct device *dev,
> + struct device_attribute *devattr, char *buf)
> +{
> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
> + u32 temp;
> +
> + mutex_lock(&temp_sensor->sensor_mutex);
> +
> + temp = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_threshold);
> + temp = (temp & temp_sensor->registers->threshold_tcold_mask) >>
> + __ffs(temp_sensor->registers->threshold_tcold_mask);
> +
> + if (temp < OMAP_ADC_START_VALUE || temp > OMAP_ADC_END_VALUE) {
> + dev_err(dev, "invalid value\n");
> + mutex_unlock(&temp_sensor->sensor_mutex);
> + return -EINVAL;
> + }
> +
> + temp = adc_to_temp_conversion(temp);
> + mutex_unlock(&temp_sensor->sensor_mutex);
> +
> + return snprintf(buf, 16, "%d\n", temp);
> +}
> +
> +static ssize_t set_temp_max_hyst(struct device *dev,
> + struct device_attribute *devattr,
> + const char *buf, size_t count)
> +{
> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
> + u32 reg_val, t_hot, t_cold, temp;
> + long val;
> +
> + mutex_lock(&temp_sensor->sensor_mutex);
> +
> + if (strict_strtol(buf, 10, &val)) {
> + count = -EINVAL;
> + goto out;
> + }
> +
> + t_cold = temp_to_adc_conversion(val);
> + if (t_cold < OMAP_ADC_START_VALUE || t_cold > OMAP_ADC_END_VALUE) {
> + dev_err(dev, "invalid range");
> + count = -EINVAL;
> + goto out;
> + }
> +
> + /* obtain the T HOT value */
> + t_hot = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_threshold);
> + t_hot = (t_hot & temp_sensor->registers->threshold_thot_mask) >>
> + __ffs(temp_sensor->registers->threshold_thot_mask);
> +
> + if (t_cold > t_hot) {
> + dev_err(dev, "Error! T_COLD value greater than T_HOT\n");
> + count = -EINVAL;
> + goto out;
> + }
> +
> + /* write the new t_cold value */
> + reg_val = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_threshold);
> + reg_val &= ~(temp_sensor->registers->threshold_tcold_mask);
> + reg_val |= (t_cold <<
> + __ffs(temp_sensor->registers->threshold_tcold_mask));
> + omap_temp_sensor_writel(temp_sensor, reg_val,
> + temp_sensor->registers->bgap_threshold);
> +
> + /* Read the current temperature */
> + temp = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->temp_sensor_ctrl);
> + temp &= (temp_sensor->registers->bgap_dtemp_mask);
> +
> + /*
> + * If user sets the LOW threshold(t_cold) lower than the current
> + * temperature(temp) unmask the COLD interrupts
> + */
> + if (t_cold < temp) {
> + reg_val = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_mask_ctrl);
> + reg_val &= ~(temp_sensor->registers->mask_hot_mask);
> + reg_val |= temp_sensor->registers->mask_cold_mask;
> + omap_temp_sensor_writel(temp_sensor, reg_val,
> + temp_sensor->registers->bgap_mask_ctrl);
> + }
> +
> + /*
> + * If current temperature is in-between the hot and cold thresholds,
> + * Enable both masks.
> + */
> + if (temp < t_hot && temp > t_cold) {
> + reg_val = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_mask_ctrl);
> + reg_val |= temp_sensor->registers->mask_cold_mask;
> + reg_val |= temp_sensor->registers->mask_hot_mask;
> + omap_temp_sensor_writel(temp_sensor, reg_val,
> + temp_sensor->registers->bgap_mask_ctrl);
> + }
> +
> + /*
> + * else no need to do anything since HW will immediately compare
> + * the new threshold and generate interrupt accordingly
> + */
> +
> +out:
> + mutex_unlock(&temp_sensor->sensor_mutex);
> + return count;
> +}
> +
> +static ssize_t show_update_rate(struct device *dev,
> + struct device_attribute *devattr, char *buf)
> +{
> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
> + u32 temp = 0, ret = 0;
> +
> + mutex_lock(&temp_sensor->sensor_mutex);
> +
> + if (!temp_sensor->clk_rate) {
> + dev_err(dev, "clk_rate is NULL\n");
> + ret = -EINVAL;
> + goto out;
> + }
> +
> + temp = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_counter);
> + temp = (temp & temp_sensor->registers->counter_mask) >>
> + __ffs(temp_sensor->registers->counter_mask);
> + temp = temp * 1000 / (temp_sensor->clk_rate);
> +
> +out:
> + mutex_unlock(&temp_sensor->sensor_mutex);
> + if (!ret)
> + return sprintf(buf, "%d\n", temp);
> +
> + return ret;
> +}
> +
> +static ssize_t set_update_rate(struct device *dev,
> + struct device_attribute *devattr,
> + const char *buf, size_t count)
> +{
> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
> + u32 reg_val;
> + long val;
> +
> + mutex_lock(&temp_sensor->sensor_mutex);
> +
> + if (strict_strtol(buf, 10, &val)) {
> + count = -EINVAL;
> + goto out;
> + }
> +
> + val *= temp_sensor->clk_rate / 1000;
> + reg_val = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_counter);
> +
> + reg_val &= ~(temp_sensor->registers->counter_mask);
> + reg_val |= val;
> + omap_temp_sensor_writel(temp_sensor, reg_val,
> + temp_sensor->registers->bgap_counter);
> +
> +out:
> + mutex_unlock(&temp_sensor->sensor_mutex);
> + return count;
> +}
> +
> +static int omap_temp_sensor_read_temp(struct device *dev,
> + struct device_attribute *devattr,
> + char *buf)
> +{
> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
> + int temp, ret = 0;
> +
> + mutex_lock(&temp_sensor->sensor_mutex);
> +
> + temp = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->temp_sensor_ctrl);
> + temp &= temp_sensor->registers->bgap_dtemp_mask;
> +
> + if (!temp_sensor->is_efuse_valid)
> + dev_err(dev, "Invalid EFUSE, Non-trimmed BGAP, Temp not accurate\n");
> +
> + /* look up for temperature in the table and return the temperature */
> + if (temp < OMAP_ADC_START_VALUE || temp > OMAP_ADC_END_VALUE) {
> + dev_err(dev, "invalid adc code reported %d", temp);
> + ret = -EINVAL;
> + goto out;
> + }
> +
> + temp = adc_to_temp[temp - OMAP_ADC_START_VALUE];
> +
> +out:
> + mutex_unlock(&temp_sensor->sensor_mutex);
> + if (!ret)
> + return sprintf(buf, "%d\n", temp);
> +
> + return ret;
> +}
> +
> +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, omap_temp_sensor_read_temp,
> + NULL, 0);
> +static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp_max,
> + set_temp_max, 0);
> +static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO, show_temp_max_hyst,
> + set_temp_max_hyst, 0);
> +static SENSOR_DEVICE_ATTR(update_rate, S_IWUSR | S_IRUGO, show_update_rate,
> + set_update_rate, 0);
> +
> +static struct attribute *omap_temp_sensor_attributes[] = {
> + &sensor_dev_attr_temp1_input.dev_attr.attr,
> + &sensor_dev_attr_temp1_max.dev_attr.attr,
> + &sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
> + &sensor_dev_attr_update_rate.dev_attr.attr,
> + NULL
> +};
> +
> +static const struct attribute_group omap_temp_sensor_group = {
> + .attrs = omap_temp_sensor_attributes,
> +};
> +
> +static int omap_temp_sensor_clk_enable(struct omap_temp_sensor *temp_sensor)
> +{
> + u32 ret = 0;
> +
> + if (temp_sensor->clk_on) {
> + dev_err(temp_sensor->hwmon_dev, "clock already on\n");
> + goto out;
> + }
> +
> + ret = pm_runtime_get_sync(temp_sensor->hwmon_dev);
> + if (ret < 0) {
> + dev_err(temp_sensor->hwmon_dev, "get sync failed\n");
> + goto out;
> + }
> +
> + temp_sensor->clk_on = 1;
> +
> +out:
> + return ret;
> +}
> +
> +static int omap_temp_sensor_clk_disable(struct omap_temp_sensor *temp_sensor)
> +{
> + u32 ret = 0;
> +
> + if (!temp_sensor->clk_on) {
> + dev_err(temp_sensor->hwmon_dev, "clock already off\n");
> + goto out;
> + }
> +
> + /* Gate the clock */
> + ret = pm_runtime_put_sync(temp_sensor->hwmon_dev);
> + if (ret < 0) {
> + dev_err(temp_sensor->hwmon_dev, "put sync failed\n");
> + goto out;
> + }
> + temp_sensor->clk_on = 0;
> +
> +out:
> + return ret;
> +}
> +
> +static irqreturn_t omap_talert_irq_handler(int irq, void *data)
> +{
> + struct omap_temp_sensor *temp_sensor;
> + int t_hot, t_cold, temp;
> +
> + temp_sensor = data;
> + mutex_lock(&temp_sensor->sensor_mutex);
> +
> + /* Read the status of t_hot */
> + t_hot = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_status)
> + & temp_sensor->registers->status_hot_mask;
> +
> + /* Read the status of t_cold */
> + t_cold = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_status)
> + & temp_sensor->registers->status_cold_mask;
> +
> + temp = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_mask_ctrl);
> + /*
> + * One TALERT interrupt: Two sources
> + * If the interrupt is due to t_hot then mask t_hot and
> + * and unmask t_cold else mask t_cold and unmask t_hot
> + */
> + if (t_hot) {
> + temp &= ~(temp_sensor->registers->mask_hot_mask);
> + temp |= temp_sensor->registers->mask_cold_mask;
> + } else if (t_cold) {
> + temp &= ~(temp_sensor->registers->mask_cold_mask);
> + temp |= temp_sensor->registers->mask_hot_mask;
> + }
> +
> + omap_temp_sensor_writel(temp_sensor, temp,
> + temp_sensor->registers->bgap_mask_ctrl);
> +
> + /* kobject_uvent to user space telling thermal threshold crossed */
> + kobject_uevent(&temp_sensor->hwmon_dev->kobj, KOBJ_CHANGE);
> +
> + mutex_unlock(&temp_sensor->sensor_mutex);
> +
> + return IRQ_HANDLED;
> +}
> +
> +static int __devinit omap_temp_sensor_probe(struct platform_device *pdev)
> +{
> + struct omap_temp_sensor_pdata *pdata = pdev->dev.platform_data;
> + struct omap_temp_sensor *temp_sensor;
> + struct resource *mem;
> + int ret = 0;
> + int val, clk_rate;
> +
> + if (!pdata) {
> + dev_err(&pdev->dev, "platform data missing\n");
> + return -EINVAL;
> + }
> +
> + temp_sensor = kzalloc(sizeof(*temp_sensor), GFP_KERNEL);
> + if (!temp_sensor)
> + return -ENOMEM;
> +
> + mutex_init(&temp_sensor->sensor_mutex);
> +
> + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + if (!mem) {
> + dev_err(&pdev->dev, "no mem resource\n");
> + ret = -ENOMEM;
> + goto plat_res_err;
> + }
> +
> + temp_sensor->irq = platform_get_irq_byname(pdev, "thermal_alert");
> + if (temp_sensor->irq < 0) {
> + dev_err(&pdev->dev, "get_irq_byname failed\n");
> + ret = temp_sensor->irq;
> + goto plat_res_err;
> + }
> +
> + temp_sensor->phy_base = ioremap(mem->start, resource_size(mem));
> + temp_sensor->clock = NULL;
> + temp_sensor->registers = pdata->registers;
> + temp_sensor->hwmon_dev = &pdev->dev;
> +
> + pm_runtime_enable(&pdev->dev);
> + pm_runtime_irq_safe(&pdev->dev);
> +
> + /*
> + * check if the efuse has a non-zero value if not
> + * it is an untrimmed sample and the temperatures
> + * may not be accurate
> + */
> +
> + if (omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_efuse))
> + temp_sensor->is_efuse_valid = 1;
> +
> + platform_set_drvdata(pdev, temp_sensor);
> + dev_set_drvdata(&pdev->dev, temp_sensor);
> + temp_sensor->clock = clk_get(temp_sensor->hwmon_dev, "fck");
> + if (IS_ERR(temp_sensor->clock)) {
> + ret = PTR_ERR(temp_sensor->clock);
> + dev_err(temp_sensor->hwmon_dev,
> + "unable to get fclk: %d\n", ret);
> + ret = -EINVAL;
> + goto plat_res_err;
> + }
> +
> + ret = omap_temp_sensor_clk_enable(temp_sensor);
> + if (ret) {
> + dev_err(&pdev->dev, "Cannot enable temp sensor\n");
> + goto clken_err;
> + }
> +
> + clk_rate = clk_round_rate(temp_sensor->clock, 2000000);
> + if (clk_rate < 1000000 || clk_rate = 0xffffffff) {
> + dev_err(&pdev->dev, "Error round rate\n");
> + ret = -EINVAL;
> + goto clken_err;
> + }
> +
> + ret = clk_set_rate(temp_sensor->clock, clk_rate);
> + if (ret) {
> + dev_err(&pdev->dev, "Cannot set clock rate\n");
> + goto clken_err;
> + }
> +
> + temp_sensor->clk_rate = clk_rate;
> + omap_enable_continuous_mode(temp_sensor, 1);
> + omap_configure_temp_sensor_thresholds(temp_sensor);
> + /* 1 ms */
> + omap_configure_temp_sensor_counter(temp_sensor, 1);
> +
> + /* Wait till the first conversion is done wait for at least 1ms */
> + usleep_range(1000, 2000);
> +
> + /* Read the temperature once due to hw issue*/
> + omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->temp_sensor_ctrl);
> +
> + /* Set 2 seconds time as default counter */
> + omap_configure_temp_sensor_counter(temp_sensor,
> + temp_sensor->clk_rate * 2);
> +
> + temp_sensor->hwmon_dev = hwmon_device_register(&pdev->dev);
> + if (IS_ERR(temp_sensor->hwmon_dev)) {
> + dev_err(&pdev->dev, "hwmon_device_register failed.\n");
> + ret = PTR_ERR(temp_sensor->hwmon_dev);
> + goto hwmon_reg_err;
> + }
> +
> + ret = sysfs_create_group(&pdev->dev.kobj,
> + &omap_temp_sensor_group);
> + if (ret) {
> + dev_err(&pdev->dev, "could not create sysfs files\n");
> + goto sysfs_create_err;
> + }
> +
> + kobject_uevent(&temp_sensor->hwmon_dev->kobj, KOBJ_ADD);
> +
> + ret = request_threaded_irq(temp_sensor->irq, NULL,
> + omap_talert_irq_handler, IRQF_TRIGGER_RISING | IRQF_ONESHOT,
> + "temp_sensor", temp_sensor);
> + if (ret) {
> + dev_err(&pdev->dev, "Request threaded irq failed.\n");
> + goto req_irq_err;
> + }
> +
> + /* unmask the T_COLD and unmask T_HOT at init */
> + val = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_mask_ctrl);
> + val |= temp_sensor->registers->mask_cold_mask
> + | temp_sensor->registers->mask_hot_mask;
> +
> + omap_temp_sensor_writel(temp_sensor, val,
> + temp_sensor->registers->bgap_mask_ctrl);
> +
> + return 0;
> +
> +req_irq_err:
> + kobject_uevent(&temp_sensor->hwmon_dev->kobj, KOBJ_REMOVE);
> + sysfs_remove_group(&temp_sensor->hwmon_dev->kobj,
> + &omap_temp_sensor_group);
> +sysfs_create_err:
> + hwmon_device_unregister(&pdev->dev);
> +hwmon_reg_err:
> + omap_temp_sensor_clk_disable(temp_sensor);
> +clken_err:
> + clk_put(temp_sensor->clock);
> +plat_res_err:
> + mutex_destroy(&temp_sensor->sensor_mutex);
> + kfree(temp_sensor);
> + return ret;
> +}
> +
> +static int __devexit omap_temp_sensor_remove(struct platform_device *pdev)
> +{
> + struct omap_temp_sensor *temp_sensor = platform_get_drvdata(pdev);
> +
> + hwmon_device_unregister(&pdev->dev);
> + kobject_uevent(&temp_sensor->hwmon_dev->kobj, KOBJ_REMOVE);
> + sysfs_remove_group(&temp_sensor->hwmon_dev->kobj,
> + &omap_temp_sensor_group);
> + omap_temp_sensor_clk_disable(temp_sensor);
> + clk_put(temp_sensor->clock);
> + platform_set_drvdata(pdev, NULL);
> + free_irq(temp_sensor->irq, temp_sensor);
> + mutex_destroy(&temp_sensor->sensor_mutex);
> + kfree(temp_sensor);
> +
> + return 0;
> +}
> +
> +#ifdef CONFIG_PM
> +static void omap_temp_sensor_save_ctxt(struct omap_temp_sensor *temp_sensor)
> +{
> + temp_sensor->temp_sensor_ctrl = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->temp_sensor_ctrl);
> + temp_sensor->bg_ctrl = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_mask_ctrl);
> + temp_sensor->bg_counter = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_counter);
> + temp_sensor->bg_threshold = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_threshold);
> + temp_sensor->temp_sensor_tshut_threshold > + omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->thsut_threshold);
> +}
> +
> +static void omap_temp_sensor_restore_ctxt(struct omap_temp_sensor *temp_sensor)
> +{
> + omap_temp_sensor_writel(temp_sensor,
> + temp_sensor->temp_sensor_ctrl,
> + temp_sensor->registers->temp_sensor_ctrl);
> + omap_temp_sensor_writel(temp_sensor,
> + temp_sensor->bg_ctrl,
> + temp_sensor->registers->bgap_mask_ctrl);
> + omap_temp_sensor_writel(temp_sensor,
> + temp_sensor->bg_counter,
> + temp_sensor->registers->bgap_counter);
> + omap_temp_sensor_writel(temp_sensor,
> + temp_sensor->bg_threshold,
> + temp_sensor->registers->bgap_threshold);
> + omap_temp_sensor_writel(temp_sensor,
> + temp_sensor->temp_sensor_tshut_threshold,
> + temp_sensor->registers->thsut_threshold);
> +}
> +
> +static int omap_temp_sensor_suspend(struct device *dev)
> +{
> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
> +
> + omap_temp_sensor_save_ctxt(temp_sensor);
> +
> + return 0;
> +}
> +
> +static int omap_temp_sensor_resume(struct device *dev)
> +{
> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
> +
> + omap_temp_sensor_restore_ctxt(temp_sensor);
> +
> + return 0;
> +}
> +
> +static int omap_temp_sensor_runtime_suspend(struct device *dev)
> +{
> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
> +
> + omap_temp_sensor_save_ctxt(temp_sensor);
> +
> + return 0;
> +}
> +
> +static int omap_temp_sensor_runtime_resume(struct device *dev)
> +{
> + static int context_loss_count;
> + int temp;
> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
> +
> + temp = omap_device_get_context_loss_count(to_platform_device(dev));
> +
> + if (temp != context_loss_count && context_loss_count != 0)
> + omap_temp_sensor_restore_ctxt(temp_sensor);
> +
> + context_loss_count = temp;
> +
> + return 0;
> +}
> +
> +static const struct dev_pm_ops omap_temp_sensor_dev_pm_ops = {
> + .suspend = omap_temp_sensor_suspend,
> + .resume = omap_temp_sensor_resume,
> + .runtime_suspend = omap_temp_sensor_runtime_suspend,
> + .runtime_resume = omap_temp_sensor_runtime_resume,
> +};
> +
> +#endif
> +
> +static struct platform_driver omap_temp_sensor_driver = {
> + .probe = omap_temp_sensor_probe,
> + .remove = omap_temp_sensor_remove,
> + .driver = {
> + .name = "omap_temp_sensor",
> +#ifdef CONFIG_PM
> + .pm = &omap_temp_sensor_dev_pm_ops,
> +#endif
> + },
> +};
> +
> +int __init omap_temp_sensor_init(void)
> +{
> + return platform_driver_register(&omap_temp_sensor_driver);
> +}
> +module_init(omap_temp_sensor_init);
> +
> +static void __exit omap_temp_sensor_exit(void)
> +{
> + platform_driver_unregister(&omap_temp_sensor_driver);
> +}
> +module_exit(omap_temp_sensor_exit);
> +
> +MODULE_DESCRIPTION("OMAP446X temperature sensor Driver");
> +MODULE_LICENSE("GPL");
> +MODULE_ALIAS("platform:" DRIVER_NAME);
> +MODULE_AUTHOR("J Keerthy <j-keerthy@ti.com>");
> --
> 1.7.0.4
>
_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors
^ permalink raw reply [flat|nested] 35+ messages in thread
* Re: [RFC PATCH 6/6 V2] hwmon: OMAP4: On die temperature sensor driver
2011-08-18 10:52 ` [RFC PATCH 6/6 V2] hwmon: OMAP4: On die temperature sensor driver Keerthy
@ 2011-08-19 5:34 ` Todd Poynor
2011-08-19 2:13 ` [lm-sensors] [RFC PATCH 6/6 V2] hwmon: OMAP4: On die Guenter Roeck
2011-08-19 5:34 ` [lm-sensors] [RFC PATCH 6/6 V2] hwmon: OMAP4: On die Todd Poynor
2 siblings, 0 replies; 35+ messages in thread
From: Todd Poynor @ 2011-08-19 5:34 UTC (permalink / raw)
To: Keerthy; +Cc: linux-omap, Jean Delvare, Guenter Roeck, lm-sensors
On Thu, Aug 18, 2011 at 04:22:15PM +0530, Keerthy wrote:
...
> +static int omap_temp_sensor_clk_enable(struct omap_temp_sensor *temp_sensor)
> +{
> + u32 ret = 0;
> +
> + if (temp_sensor->clk_on) {
> + dev_err(temp_sensor->hwmon_dev, "clock already on\n");
> + goto out;
> + }
> +
> + ret = pm_runtime_get_sync(temp_sensor->hwmon_dev);
> + if (ret < 0) {
> + dev_err(temp_sensor->hwmon_dev, "get sync failed\n");
> + goto out;
> + }
> +
> + temp_sensor->clk_on = 1;
Probably should hold the mutex around this to keep clk_on consistent
with runtime PM state (and in disable method).
...
> +static int __devinit omap_temp_sensor_probe(struct platform_device *pdev)
> +{
> + struct omap_temp_sensor_pdata *pdata = pdev->dev.platform_data;
> + struct omap_temp_sensor *temp_sensor;
> + struct resource *mem;
> + int ret = 0;
> + int val, clk_rate;
> +
> + if (!pdata) {
> + dev_err(&pdev->dev, "platform data missing\n");
> + return -EINVAL;
> + }
> +
> + temp_sensor = kzalloc(sizeof(*temp_sensor), GFP_KERNEL);
> + if (!temp_sensor)
> + return -ENOMEM;
> +
> + mutex_init(&temp_sensor->sensor_mutex);
> +
> + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + if (!mem) {
> + dev_err(&pdev->dev, "no mem resource\n");
> + ret = -ENOMEM;
> + goto plat_res_err;
> + }
> +
> + temp_sensor->irq = platform_get_irq_byname(pdev, "thermal_alert");
> + if (temp_sensor->irq < 0) {
> + dev_err(&pdev->dev, "get_irq_byname failed\n");
> + ret = temp_sensor->irq;
> + goto plat_res_err;
> + }
> +
> + temp_sensor->phy_base = ioremap(mem->start, resource_size(mem));
> + temp_sensor->clock = NULL;
> + temp_sensor->registers = pdata->registers;
> + temp_sensor->hwmon_dev = &pdev->dev;
> +
> + pm_runtime_enable(&pdev->dev);
> + pm_runtime_irq_safe(&pdev->dev);
> +
> + /*
> + * check if the efuse has a non-zero value if not
> + * it is an untrimmed sample and the temperatures
> + * may not be accurate
> + */
> +
> + if (omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_efuse))
> + temp_sensor->is_efuse_valid = 1;
> +
> + platform_set_drvdata(pdev, temp_sensor);
> + dev_set_drvdata(&pdev->dev, temp_sensor);
> + temp_sensor->clock = clk_get(temp_sensor->hwmon_dev, "fck");
> + if (IS_ERR(temp_sensor->clock)) {
> + ret = PTR_ERR(temp_sensor->clock);
> + dev_err(temp_sensor->hwmon_dev,
> + "unable to get fclk: %d\n", ret);
> + ret = -EINVAL;
> + goto plat_res_err;
> + }
> +
> + ret = omap_temp_sensor_clk_enable(temp_sensor);
> + if (ret) {
> + dev_err(&pdev->dev, "Cannot enable temp sensor\n");
> + goto clken_err;
> + }
> +
> + clk_rate = clk_round_rate(temp_sensor->clock, 2000000);
> + if (clk_rate < 1000000 || clk_rate == 0xffffffff) {
> + dev_err(&pdev->dev, "Error round rate\n");
> + ret = -EINVAL;
> + goto clken_err;
> + }
> +
> + ret = clk_set_rate(temp_sensor->clock, clk_rate);
> + if (ret) {
> + dev_err(&pdev->dev, "Cannot set clock rate\n");
> + goto clken_err;
> + }
> +
> + temp_sensor->clk_rate = clk_rate;
> + omap_enable_continuous_mode(temp_sensor, 1);
> + omap_configure_temp_sensor_thresholds(temp_sensor);
> + /* 1 ms */
> + omap_configure_temp_sensor_counter(temp_sensor, 1);
> +
> + /* Wait till the first conversion is done wait for at least 1ms */
> + usleep_range(1000, 2000);
> +
> + /* Read the temperature once due to hw issue*/
> + omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->temp_sensor_ctrl);
> +
> + /* Set 2 seconds time as default counter */
> + omap_configure_temp_sensor_counter(temp_sensor,
> + temp_sensor->clk_rate * 2);
> +
> + temp_sensor->hwmon_dev = hwmon_device_register(&pdev->dev);
> + if (IS_ERR(temp_sensor->hwmon_dev)) {
> + dev_err(&pdev->dev, "hwmon_device_register failed.\n");
> + ret = PTR_ERR(temp_sensor->hwmon_dev);
> + goto hwmon_reg_err;
> + }
> +
> + ret = sysfs_create_group(&pdev->dev.kobj,
> + &omap_temp_sensor_group);
> + if (ret) {
> + dev_err(&pdev->dev, "could not create sysfs files\n");
> + goto sysfs_create_err;
> + }
> +
> + kobject_uevent(&temp_sensor->hwmon_dev->kobj, KOBJ_ADD);
> +
> + ret = request_threaded_irq(temp_sensor->irq, NULL,
> + omap_talert_irq_handler, IRQF_TRIGGER_RISING | IRQF_ONESHOT,
> + "temp_sensor", temp_sensor);
> + if (ret) {
> + dev_err(&pdev->dev, "Request threaded irq failed.\n");
> + goto req_irq_err;
> + }
> +
> + /* unmask the T_COLD and unmask T_HOT at init */
> + val = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_mask_ctrl);
> + val |= temp_sensor->registers->mask_cold_mask
> + | temp_sensor->registers->mask_hot_mask;
> +
> + omap_temp_sensor_writel(temp_sensor, val,
> + temp_sensor->registers->bgap_mask_ctrl);
> +
> + return 0;
> +
> +req_irq_err:
> + kobject_uevent(&temp_sensor->hwmon_dev->kobj, KOBJ_REMOVE);
> + sysfs_remove_group(&temp_sensor->hwmon_dev->kobj,
> + &omap_temp_sensor_group);
> +sysfs_create_err:
> + hwmon_device_unregister(&pdev->dev);
> +hwmon_reg_err:
> + omap_temp_sensor_clk_disable(temp_sensor);
> +clken_err:
> + clk_put(temp_sensor->clock);
> +plat_res_err:
> + mutex_destroy(&temp_sensor->sensor_mutex);
> + kfree(temp_sensor);
Should also:
platform_set_drvdata(pdev, NULL);
dev_set_drvdata(&pdev->dev, NULL);
> + return ret;
> +}
> +
> +static int __devexit omap_temp_sensor_remove(struct platform_device *pdev)
> +{
> + struct omap_temp_sensor *temp_sensor = platform_get_drvdata(pdev);
> +
> + hwmon_device_unregister(&pdev->dev);
> + kobject_uevent(&temp_sensor->hwmon_dev->kobj, KOBJ_REMOVE);
> + sysfs_remove_group(&temp_sensor->hwmon_dev->kobj,
> + &omap_temp_sensor_group);
> + omap_temp_sensor_clk_disable(temp_sensor);
> + clk_put(temp_sensor->clock);
> + platform_set_drvdata(pdev, NULL);
And:
dev_set_drvdata(&pdev->dev, NULL);
> + free_irq(temp_sensor->irq, temp_sensor);
Need to free IRQ before clock is disabled (else ISR may access while
clock stopped, possible L3 interconnect error and ARM imprecise
external abort)?
> + mutex_destroy(&temp_sensor->sensor_mutex);
> + kfree(temp_sensor);
> +
> + return 0;
> +}
Todd
^ permalink raw reply [flat|nested] 35+ messages in thread
* Re: [lm-sensors] [RFC PATCH 6/6 V2] hwmon: OMAP4: On die
@ 2011-08-19 5:34 ` Todd Poynor
0 siblings, 0 replies; 35+ messages in thread
From: Todd Poynor @ 2011-08-19 5:34 UTC (permalink / raw)
To: Keerthy; +Cc: linux-omap, Jean Delvare, Guenter Roeck, lm-sensors
On Thu, Aug 18, 2011 at 04:22:15PM +0530, Keerthy wrote:
...
> +static int omap_temp_sensor_clk_enable(struct omap_temp_sensor *temp_sensor)
> +{
> + u32 ret = 0;
> +
> + if (temp_sensor->clk_on) {
> + dev_err(temp_sensor->hwmon_dev, "clock already on\n");
> + goto out;
> + }
> +
> + ret = pm_runtime_get_sync(temp_sensor->hwmon_dev);
> + if (ret < 0) {
> + dev_err(temp_sensor->hwmon_dev, "get sync failed\n");
> + goto out;
> + }
> +
> + temp_sensor->clk_on = 1;
Probably should hold the mutex around this to keep clk_on consistent
with runtime PM state (and in disable method).
...
> +static int __devinit omap_temp_sensor_probe(struct platform_device *pdev)
> +{
> + struct omap_temp_sensor_pdata *pdata = pdev->dev.platform_data;
> + struct omap_temp_sensor *temp_sensor;
> + struct resource *mem;
> + int ret = 0;
> + int val, clk_rate;
> +
> + if (!pdata) {
> + dev_err(&pdev->dev, "platform data missing\n");
> + return -EINVAL;
> + }
> +
> + temp_sensor = kzalloc(sizeof(*temp_sensor), GFP_KERNEL);
> + if (!temp_sensor)
> + return -ENOMEM;
> +
> + mutex_init(&temp_sensor->sensor_mutex);
> +
> + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + if (!mem) {
> + dev_err(&pdev->dev, "no mem resource\n");
> + ret = -ENOMEM;
> + goto plat_res_err;
> + }
> +
> + temp_sensor->irq = platform_get_irq_byname(pdev, "thermal_alert");
> + if (temp_sensor->irq < 0) {
> + dev_err(&pdev->dev, "get_irq_byname failed\n");
> + ret = temp_sensor->irq;
> + goto plat_res_err;
> + }
> +
> + temp_sensor->phy_base = ioremap(mem->start, resource_size(mem));
> + temp_sensor->clock = NULL;
> + temp_sensor->registers = pdata->registers;
> + temp_sensor->hwmon_dev = &pdev->dev;
> +
> + pm_runtime_enable(&pdev->dev);
> + pm_runtime_irq_safe(&pdev->dev);
> +
> + /*
> + * check if the efuse has a non-zero value if not
> + * it is an untrimmed sample and the temperatures
> + * may not be accurate
> + */
> +
> + if (omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_efuse))
> + temp_sensor->is_efuse_valid = 1;
> +
> + platform_set_drvdata(pdev, temp_sensor);
> + dev_set_drvdata(&pdev->dev, temp_sensor);
> + temp_sensor->clock = clk_get(temp_sensor->hwmon_dev, "fck");
> + if (IS_ERR(temp_sensor->clock)) {
> + ret = PTR_ERR(temp_sensor->clock);
> + dev_err(temp_sensor->hwmon_dev,
> + "unable to get fclk: %d\n", ret);
> + ret = -EINVAL;
> + goto plat_res_err;
> + }
> +
> + ret = omap_temp_sensor_clk_enable(temp_sensor);
> + if (ret) {
> + dev_err(&pdev->dev, "Cannot enable temp sensor\n");
> + goto clken_err;
> + }
> +
> + clk_rate = clk_round_rate(temp_sensor->clock, 2000000);
> + if (clk_rate < 1000000 || clk_rate = 0xffffffff) {
> + dev_err(&pdev->dev, "Error round rate\n");
> + ret = -EINVAL;
> + goto clken_err;
> + }
> +
> + ret = clk_set_rate(temp_sensor->clock, clk_rate);
> + if (ret) {
> + dev_err(&pdev->dev, "Cannot set clock rate\n");
> + goto clken_err;
> + }
> +
> + temp_sensor->clk_rate = clk_rate;
> + omap_enable_continuous_mode(temp_sensor, 1);
> + omap_configure_temp_sensor_thresholds(temp_sensor);
> + /* 1 ms */
> + omap_configure_temp_sensor_counter(temp_sensor, 1);
> +
> + /* Wait till the first conversion is done wait for at least 1ms */
> + usleep_range(1000, 2000);
> +
> + /* Read the temperature once due to hw issue*/
> + omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->temp_sensor_ctrl);
> +
> + /* Set 2 seconds time as default counter */
> + omap_configure_temp_sensor_counter(temp_sensor,
> + temp_sensor->clk_rate * 2);
> +
> + temp_sensor->hwmon_dev = hwmon_device_register(&pdev->dev);
> + if (IS_ERR(temp_sensor->hwmon_dev)) {
> + dev_err(&pdev->dev, "hwmon_device_register failed.\n");
> + ret = PTR_ERR(temp_sensor->hwmon_dev);
> + goto hwmon_reg_err;
> + }
> +
> + ret = sysfs_create_group(&pdev->dev.kobj,
> + &omap_temp_sensor_group);
> + if (ret) {
> + dev_err(&pdev->dev, "could not create sysfs files\n");
> + goto sysfs_create_err;
> + }
> +
> + kobject_uevent(&temp_sensor->hwmon_dev->kobj, KOBJ_ADD);
> +
> + ret = request_threaded_irq(temp_sensor->irq, NULL,
> + omap_talert_irq_handler, IRQF_TRIGGER_RISING | IRQF_ONESHOT,
> + "temp_sensor", temp_sensor);
> + if (ret) {
> + dev_err(&pdev->dev, "Request threaded irq failed.\n");
> + goto req_irq_err;
> + }
> +
> + /* unmask the T_COLD and unmask T_HOT at init */
> + val = omap_temp_sensor_readl(temp_sensor,
> + temp_sensor->registers->bgap_mask_ctrl);
> + val |= temp_sensor->registers->mask_cold_mask
> + | temp_sensor->registers->mask_hot_mask;
> +
> + omap_temp_sensor_writel(temp_sensor, val,
> + temp_sensor->registers->bgap_mask_ctrl);
> +
> + return 0;
> +
> +req_irq_err:
> + kobject_uevent(&temp_sensor->hwmon_dev->kobj, KOBJ_REMOVE);
> + sysfs_remove_group(&temp_sensor->hwmon_dev->kobj,
> + &omap_temp_sensor_group);
> +sysfs_create_err:
> + hwmon_device_unregister(&pdev->dev);
> +hwmon_reg_err:
> + omap_temp_sensor_clk_disable(temp_sensor);
> +clken_err:
> + clk_put(temp_sensor->clock);
> +plat_res_err:
> + mutex_destroy(&temp_sensor->sensor_mutex);
> + kfree(temp_sensor);
Should also:
platform_set_drvdata(pdev, NULL);
dev_set_drvdata(&pdev->dev, NULL);
> + return ret;
> +}
> +
> +static int __devexit omap_temp_sensor_remove(struct platform_device *pdev)
> +{
> + struct omap_temp_sensor *temp_sensor = platform_get_drvdata(pdev);
> +
> + hwmon_device_unregister(&pdev->dev);
> + kobject_uevent(&temp_sensor->hwmon_dev->kobj, KOBJ_REMOVE);
> + sysfs_remove_group(&temp_sensor->hwmon_dev->kobj,
> + &omap_temp_sensor_group);
> + omap_temp_sensor_clk_disable(temp_sensor);
> + clk_put(temp_sensor->clock);
> + platform_set_drvdata(pdev, NULL);
And:
dev_set_drvdata(&pdev->dev, NULL);
> + free_irq(temp_sensor->irq, temp_sensor);
Need to free IRQ before clock is disabled (else ISR may access while
clock stopped, possible L3 interconnect error and ARM imprecise
external abort)?
> + mutex_destroy(&temp_sensor->sensor_mutex);
> + kfree(temp_sensor);
> +
> + return 0;
> +}
Todd
_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors
^ permalink raw reply [flat|nested] 35+ messages in thread
* Re: [RFC PATCH 5/6 V2] OMAP4: Temperature sensor device support
2011-08-18 10:52 ` [RFC PATCH 5/6 V2] OMAP4: Temperature sensor device support Keerthy
@ 2011-08-19 5:47 ` Todd Poynor
0 siblings, 0 replies; 35+ messages in thread
From: Todd Poynor @ 2011-08-19 5:47 UTC (permalink / raw)
To: Keerthy; +Cc: linux-omap, tony
On Thu, Aug 18, 2011 at 04:22:14PM +0530, Keerthy wrote:
...
> +int omap_temp_sensor_device_idle(struct omap_device *od)
> +{
> + struct omap_temp_sensor_registers *registers;
> + struct resource *mem;
> + void __iomem *phy_base;
> + unsigned long timeout;
> + u32 ret = 0, temp;
> +
> + mem = platform_get_resource(&od->pdev, IORESOURCE_MEM, 0);
> + if (!mem) {
> + dev_err(&od->pdev.dev, "no mem resource\n");
> + ret = -EINVAL;
> + goto plat_res_err;
> + }
> +
> + phy_base = ioremap(mem->start, resource_size(mem));
Check NULL return.
> +
> + if (!strcmp(od->hwmods[0]->dev_attr, "mpu"))
> + registers = &omap_mpu_temp_sensor_registers;
> +
> + temp = __raw_readl(phy_base + registers->temp_sensor_ctrl);
Compile warning on possible use of uninitialized value?
Suggest return error if string comparison (ugh) does not match.
> + temp |= registers->bgap_tempsoff_mask;
> +
> + /* BGAP_TEMPSOFF should be set to 1 before gating clock */
> + __raw_writel(temp, phy_base + registers->temp_sensor_ctrl);
> + temp = __raw_readl(phy_base + registers->bgap_status);
> + timeout = jiffies + msecs_to_jiffies(5);
> +
> + /* wait till the clean stop bit is set or till the timeout expires */
> + while (!(temp | registers->status_clean_stop_mask) &&
> + !(time_after(jiffies, timeout))) {
> + temp = __raw_readl(phy_base + registers->bgap_status);
> + usleep_range(500, 2000);
> + }
> +
> + if (time_after(jiffies, timeout))
> + dev_err(&od->pdev.dev, "Clean stop bit not set\n");
> +
> + ret = omap_device_idle_hwmods(od);
> +
> +plat_res_err:
> + return ret;
phy_base not saved anywhere, VM leak. Need iounmap(phy_base).
> +}
> +
> +int omap_temp_sensor_device_activate(struct omap_device *od)
> +{
> + struct omap_temp_sensor_registers *registers;
> + struct resource *mem;
> + void __iomem *phy_base;
> + u32 ret = 0, temp;
> +
> + ret = omap_device_enable_hwmods(od);
> + if (ret < 0)
> + return ret;
> + mem = platform_get_resource(&od->pdev, IORESOURCE_MEM, 0);
> + if (!mem) {
> + dev_err(&od->pdev.dev, "no mem resource\n");
> + return -EINVAL;
> + }
> +
> + phy_base = ioremap(mem->start, resource_size(mem));
Check NULL return.
> +
> + if (!strcmp(od->hwmods[0]->dev_attr, "mpu"))
> + registers = &omap_mpu_temp_sensor_registers;
> +
> + temp = __raw_readl(phy_base + registers->temp_sensor_ctrl);
Error out if not "mpu".
> + temp &= ~(registers->bgap_tempsoff_mask);
> + /* BGAP_TEMPSOFF should be reset to 0 */
> + __raw_writel(temp,
> + phy_base + registers->temp_sensor_ctrl);
> +
> + return 0;
iounmap(phy_base)
> +}
> +
> +static struct omap_device_pm_latency omap_temp_sensor_latency[] = {
> + {
> + .deactivate_func = omap_temp_sensor_device_idle,
> + .activate_func = omap_temp_sensor_device_activate,
> + .flags = OMAP_DEVICE_LATENCY_AUTO_ADJUST,
> + }
> +};
> +
> +static DEFINE_IDR(temp_sensor_device_idr);
> +
> +static int temp_sensor_dev_init(struct omap_hwmod *oh, void *user)
> +{
> + struct omap_temp_sensor_pdata *temp_sensor_pdata;
> + struct omap_device *od;
> + struct omap_temp_sensor_dev_attr *temp_sensor_dev_attr;
> + int ret = 0;
> + int num;
> + struct mutex sensor_mutex; /* sensor mutex */
> +
> + mutex_init(&sensor_mutex);
> +
> + temp_sensor_pdata =
> + kzalloc(sizeof(*temp_sensor_pdata), GFP_KERNEL);
> + if (!temp_sensor_pdata) {
> + dev_err(&oh->od->pdev.dev,
> + "Unable to allocate memory for temp sensor pdata\n");
> + return -ENOMEM;
> + }
> +
> + mutex_lock(&sensor_mutex);
Mutex just init'ed above, this code can't run concurrent with any
other accessors.
> + ret = idr_pre_get(&temp_sensor_device_idr, GFP_KERNEL);
> + if (ret < 0) {
> + mutex_unlock(&sensor_mutex);
> + goto fail_id;
> + }
> + ret = idr_get_new(&temp_sensor_device_idr, temp_sensor_pdata, &num);
> + if (ret < 0) {
> + mutex_unlock(&sensor_mutex);
> + goto fail_id;
> + }
> + mutex_unlock(&sensor_mutex);
> +
> + temp_sensor_dev_attr = oh->dev_attr;
> + if (!strcmp(temp_sensor_dev_attr->name, "mpu"))
> + temp_sensor_pdata->registers = &omap_mpu_temp_sensor_registers;
> +
> + od = omap_device_build("omap_temp_sensor", num,
> + oh, temp_sensor_pdata, sizeof(*temp_sensor_pdata),
> + omap_temp_sensor_latency,
> + ARRAY_SIZE(omap_temp_sensor_latency), 0);
Inconsistent tabbing.
> + if (IS_ERR(od)) {
> + dev_warn(&oh->od->pdev.dev,
> + "Could not build omap_device for %s\n", oh->name);
> + ret = PTR_ERR(od);
> + }
> +
> +fail_id:
> + kfree(temp_sensor_pdata);
> +
> + return ret;
> +}
Todd
^ permalink raw reply [flat|nested] 35+ messages in thread
* Re: [RFC PATCH 6/6 V2] hwmon: OMAP4: On die temperature sensor driver
2011-08-19 2:13 ` [lm-sensors] [RFC PATCH 6/6 V2] hwmon: OMAP4: On die Guenter Roeck
@ 2011-08-19 6:16 ` J, KEERTHY
-1 siblings, 0 replies; 35+ messages in thread
From: J, KEERTHY @ 2011-08-19 6:04 UTC (permalink / raw)
To: Guenter Roeck; +Cc: linux-omap, Jean Delvare, lm-sensors
On Fri, Aug 19, 2011 at 7:43 AM, Guenter Roeck
<guenter.roeck@ericsson.com> wrote:
> On Thu, Aug 18, 2011 at 06:52:15AM -0400, Keerthy wrote:
>> On chip temperature sensor driver. The driver monitors the temperature of
>> the MPU subsystem of the OMAP4. It sends notifications to the user space if
>> the temperature crosses user defined thresholds via kobject_uevent interface.
>> The user is allowed to configure the temperature thresholds vis sysfs nodes
>> exposed using hwmon interface.
>>
>> Signed-off-by: Keerthy <j-keerthy@ti.com>
>> Cc: Jean Delvare <khali@linux-fr.org>
>> Cc: Guenter Roeck <guenter.roeck@ericsson.com>
>> Cc: lm-sensors@lm-sensors.org
>
> High level review:
>
> - too much and too broad mutex locking. show functions should not need locks at all,
> set functions only while data is written into registers and into platform data.
Ok. I will clean this.
> - driver is quite noisy. There should definitely not be any log messages
> if a set parameter is wrong. Show functions already return an error value
> to the user; a log message indicating the error again just creates noise.
> For one boolean set during probe (is_efuse_valid), each subsequent show results
> in a log message if it is false. Some errors result in multiple log messages.
A user tries to set an invalid temperature threshold. The user should
be notified about this. The invalid temperature will not be set. The user
should not be allowed to set an invalid temperature. It is to inform
the user about precisely the problem with the parameter.
In some of the samples the bandgap is not trimmed and hence
temperature reported will be wrong. So every time a user tries to read
he is alerted that the temperatures are not accurate.
> - Wrong use of EINVAL throughout the driver (EINVAL is "Invalid Argument")
Ok. I will correct this.
> - excessive ( )
Ok.
> - linear search through a sorted array is very expensive. Consider using a binary search.
Yes. I will implement binary search.
> - temp_to_adc_conversion return code (error if negative) is not checked properly.
Ok. I will add checks.
>
> I am sure there are other problems, but those are difficult to find with all the noise
> above.
>
> Guenter
>
>> ---
>> Documentation/hwmon/omap_temp_sensor | 27 +
>> drivers/hwmon/Kconfig | 11 +
>> drivers/hwmon/Makefile | 1 +
>> drivers/hwmon/omap_temp_sensor.c | 918 ++++++++++++++++++++++++++++++++++
>> 4 files changed, 957 insertions(+), 0 deletions(-)
>> create mode 100644 Documentation/hwmon/omap_temp_sensor
>> create mode 100644 drivers/hwmon/omap_temp_sensor.c
>>
>> diff --git a/Documentation/hwmon/omap_temp_sensor b/Documentation/hwmon/omap_temp_sensor
>> new file mode 100644
>> index 0000000..e01a6d6
>> --- /dev/null
>> +++ b/Documentation/hwmon/omap_temp_sensor
>> @@ -0,0 +1,27 @@
>> +Kernel driver omap_temp_sensor
>> +==============================
>> +
>> +Supported chips:
>> + * Texas Instruments OMAP4460
>> + Prefix: 'omap_temp_sensor'
>> + Datasheet: http://focus.ti.com/docs/prod/folders/print/tmp102.html
>> +
>> +Author:
>> + J Keerthy <j-keerthy@ti.com>
>> +
>> +Description
>> +-----------
>> +
>> +The Texas Instruments OMAP4 family of chips have a bandgap temperature sensor.
>> +The temperature sensor feature is used to convert the temperature of the device
>> +into a decimal value coded on 10 bits. An internal ADC is used for conversion.
>> +The recommended operating temperatures must be in the range -40 degree Celsius
>> +to 123 degree celsius for standard conversion.
>> +The thresholds are programmable and upon crossing the thresholds an interrupt
>> +is generated. The OMAP temperature sensor has a programmable update rate in
>> +milli seconds.
>> +(Currently the driver programs a default of 2000 milli seconds).
>> +
>> +The driver provides the common sysfs-interface for temperatures (see
>> +Documentation/hwmon/sysfs-interface under Temperatures).
>> +
>> diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
>> index 5f888f7..9c9cd8b 100644
>> --- a/drivers/hwmon/Kconfig
>> +++ b/drivers/hwmon/Kconfig
>> @@ -323,6 +323,17 @@ config SENSORS_F71805F
>> This driver can also be built as a module. If so, the module
>> will be called f71805f.
>>
>> +config SENSORS_OMAP_BANDGAP_TEMP_SENSOR
>> + bool "OMAP on-die temperature sensor hwmon driver"
>> + depends on HWMON && ARCH_OMAP && OMAP_TEMP_SENSOR
>> + help
>> + If you say yes here you get support for hardware
>> + monitoring features of the OMAP on die temperature
>> + sensor.
>> +
>> + Continuous conversion programmable delay
>> + mode is used for temperature conversion.
>> +
>> config SENSORS_F71882FG
>> tristate "Fintek F71882FG and compatibles"
>> help
>> diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
>> index 28061cf..d0f89f5 100644
>> --- a/drivers/hwmon/Makefile
>> +++ b/drivers/hwmon/Makefile
>> @@ -91,6 +91,7 @@ obj-$(CONFIG_SENSORS_MAX6639) += max6639.o
>> obj-$(CONFIG_SENSORS_MAX6642) += max6642.o
>> obj-$(CONFIG_SENSORS_MAX6650) += max6650.o
>> obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o
>> +obj-$(CONFIG_SENSORS_OMAP_BANDGAP_TEMP_SENSOR) += omap_temp_sensor.o
>> obj-$(CONFIG_SENSORS_PC87360) += pc87360.o
>> obj-$(CONFIG_SENSORS_PC87427) += pc87427.o
>> obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o
>> diff --git a/drivers/hwmon/omap_temp_sensor.c b/drivers/hwmon/omap_temp_sensor.c
>> new file mode 100644
>> index 0000000..586a361
>> --- /dev/null
>> +++ b/drivers/hwmon/omap_temp_sensor.c
>> @@ -0,0 +1,918 @@
>> +/*
>> + * OMAP4 Temperature sensor driver file
>> + *
>> + * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
>> + * Author: J Keerthy <j-keerthy@ti.com>
>> + * Author: Moiz Sonasath <m-sonasath@ti.com>
>> + *
>> + * This program is free software; you can redistribute it and/or
>> + * modify it under the terms of the GNU General Public License
>> + * 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.
>> + *
>> + * You should have received a copy of the GNU General Public License
>> + * along with this program; if not, write to the Free Software
>> + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
>> + * 02110-1301 USA
>> + *
>> + */
>> +
>> +#include <linux/interrupt.h>
>> +#include <linux/clk.h>
>> +#include <linux/io.h>
>> +#include <linux/slab.h>
>> +#include <linux/init.h>
>> +#include <plat/omap_device.h>
>> +#include <linux/kernel.h>
>> +#include <linux/device.h>
>> +#include <linux/jiffies.h>
>> +#include <linux/hwmon.h>
>> +#include <linux/hwmon-sysfs.h>
>> +#include <linux/stddef.h>
>> +#include <linux/sysfs.h>
>> +#include <linux/err.h>
>> +#include <linux/types.h>
>> +#include <linux/mutex.h>
>> +#include <linux/pm_runtime.h>
>> +#include <linux/delay.h>
>> +#include <plat/temperature_sensor.h>
>> +
>> +#define TSHUT_THRESHOLD_HOT 122000 /* 122 deg C */
>> +#define TSHUT_THRESHOLD_COLD 100000 /* 100 deg C */
>> +#define BGAP_THRESHOLD_T_HOT 73000 /* 73 deg C */
>> +#define BGAP_THRESHOLD_T_COLD 71000 /* 71 deg C */
>> +#define OMAP_ADC_START_VALUE 530
>> +#define OMAP_ADC_END_VALUE 923
>> +
>> +/*
>> + * omap_temp_sensor structure
>> + * @hwmon_dev - device pointer
>> + * @clock - Clock pointer
>> + * @registers - Pointer to structure with register offsets and bitfields
>> + * @sensor_mutex - Mutex for sysfs, irq and PM
>> + * @irq - MPU Irq number for thermal alert
>> + * @phy_base - Physical base of the temp I/O
>> + * @clk_rate - Holds current clock rate
>> + * @temp_sensor_ctrl - temp sensor control register value
>> + * @bg_ctrl - bandgap ctrl register value
>> + * @bg_counter - bandgap counter value
>> + * @bg_threshold - bandgap threshold register value
>> + * @temp_sensor_tshut_threshold - bandgap tshut register value
>> + * @is_efuse_valid - Flag to determine if efuse is valid or not
>> + * @clk_on - Manages the current clock state
>> + */
>> +struct omap_temp_sensor {
>> + struct device *hwmon_dev;
>> + struct clk *clock;
>> + struct omap_temp_sensor_registers *registers;
>> + struct mutex sensor_mutex; /* Mutex for sysfs, irq and PM */
>> + unsigned int irq;
>> + void __iomem *phy_base;
>> + u32 clk_rate;
>> + u32 temp_sensor_ctrl;
>> + u32 bg_ctrl;
>> + u32 bg_counter;
>> + u32 bg_threshold;
>> + u32 temp_sensor_tshut_threshold;
>> + bool is_efuse_valid;
>> + bool clk_on;
>> +};
>> +
>> +/*
>> + * Temperature values in milli degree celsius
>> + * ADC code values from 530 to 923
>> + */
>> +static int adc_to_temp[] = {
>> + -40000, -40000, -40000, -40000, -39800, -39400, -39000, -38600, -38200,
>> + -37800, -37300, -36800, -36400, -36000, -35600, -35200, -34800,
>> + -34300, -33800, -33400, -33000, -32600, -32200, -31800, -31300,
>> + -30800, -30400, -30000, -29600, -29200, -28700, -28200, -27800,
>> + -27400, -27000, -26600, -26200, -25700, -25200, -24800, -24400,
>> + -24000, -23600, -23200, -22700, -22200, -21800, -21400, -21000,
>> + -20600, -20200, -19700, -19200, -18800, -18400, -18000, -17600,
>> + -17200, -16700, -16200, -15800, -15400, -15000, -14600, -14200,
>> + -13700, -13200, -12800, -12400, -12000, -11600, -11200, -10700,
>> + -10200, -9800, -9400, -9000, -8600, -8200, -7700, -7200, -6800,
>> + -6400, -6000, -5600, -5200, -4800, -4300, -3800, -3400, -3000,
>> + -2600, -2200, -1800, -1300, -800, -400, 0, 400, 800, 1200, 1600,
>> + 2100, 2600, 3000, 3400, 3800, 4200, 4600, 5100, 5600, 6000, 6400,
>> + 6800, 7200, 7600, 8000, 8500, 9000, 9400, 9800, 10200, 10600, 11000,
>> + 11400, 11900, 12400, 12800, 13200, 13600, 14000, 14400, 14800,
>> + 15300, 15800, 16200, 16600, 17000, 17400, 17800, 18200, 18700,
>> + 19200, 19600, 20000, 20400, 20800, 21200, 21600, 22100, 22600,
>> + 23000, 23400, 23800, 24200, 24600, 25000, 25400, 25900, 26400,
>> + 26800, 27200, 27600, 28000, 28400, 28800, 29300, 29800, 30200,
>> + 30600, 31000, 31400, 31800, 32200, 32600, 33100, 33600, 34000,
>> + 34400, 34800, 35200, 35600, 36000, 36400, 36800, 37300, 37800,
>> + 38200, 38600, 39000, 39400, 39800, 40200, 40600, 41100, 41600,
>> + 42000, 42400, 42800, 43200, 43600, 44000, 44400, 44800, 45300,
>> + 45800, 46200, 46600, 47000, 47400, 47800, 48200, 48600, 49000,
>> + 49500, 50000, 50400, 50800, 51200, 51600, 52000, 52400, 52800,
>> + 53200, 53700, 54200, 54600, 55000, 55400, 55800, 56200, 56600,
>> + 57000, 57400, 57800, 58200, 58700, 59200, 59600, 60000, 60400,
>> + 60800, 61200, 61600, 62000, 62400, 62800, 63300, 63800, 64200,
>> + 64600, 65000, 65400, 65800, 66200, 66600, 67000, 67400, 67800,
>> + 68200, 68700, 69200, 69600, 70000, 70400, 70800, 71200, 71600,
>> + 72000, 72400, 72800, 73200, 73600, 74100, 74600, 75000, 75400,
>> + 75800, 76200, 76600, 77000, 77400, 77800, 78200, 78600, 79000,
>> + 79400, 79800, 80300, 80800, 81200, 81600, 82000, 82400, 82800,
>> + 83200, 83600, 84000, 84400, 84800, 85200, 85600, 86000, 86400,
>> + 86800, 87300, 87800, 88200, 88600, 89000, 89400, 89800, 90200,
>> + 90600, 91000, 91400, 91800, 92200, 92600, 93000, 93400, 93800,
>> + 94200, 94600, 95000, 95500, 96000, 96400, 96800, 97200, 97600,
>> + 98000, 98400, 98800, 99200, 99600, 100000, 100400, 100800, 101200,
>> + 101600, 102000, 102400, 102800, 103200, 103600, 104000, 104400,
>> + 104800, 105200, 105600, 106100, 106600, 107000, 107400, 107800,
>> + 108200, 108600, 109000, 109400, 109800, 110200, 110600, 111000,
>> + 111400, 111800, 112200, 112600, 113000, 113400, 113800, 114200,
>> + 114600, 115000, 115400, 115800, 116200, 116600, 117000, 117400,
>> + 117800, 118200, 118600, 119000, 119400, 119800, 120200, 120600,
>> + 121000, 121400, 121800, 122200, 122600, 123000
>> +};
>> +
>> +static unsigned long omap_temp_sensor_readl(struct omap_temp_sensor
>> + *temp_sensor, u32 reg)
>> +{
>> + return __raw_readl(temp_sensor->phy_base + reg);
>> +}
>> +
>> +static void omap_temp_sensor_writel(struct omap_temp_sensor *temp_sensor,
>> + u32 val, u32 reg)
>> +{
>> + __raw_writel(val, (temp_sensor->phy_base + reg));
>> +}
>> +
>> +static int adc_to_temp_conversion(int adc_val)
>> +{
>> + return adc_to_temp[adc_val - OMAP_ADC_START_VALUE];
>> +}
>> +
>> +static int temp_to_adc_conversion(long temp)
>> +{
>> + int i;
>> +
>> + for (i = 0; i <= OMAP_ADC_END_VALUE - OMAP_ADC_START_VALUE; i++)
>> + if (temp < adc_to_temp[i])
>> + return OMAP_ADC_START_VALUE + i - 1;
>> +
>> + return -EINVAL;
>> +}
>> +
>> +static void omap_configure_temp_sensor_thresholds(struct omap_temp_sensor
>> + *temp_sensor)
>> +{
>> + u32 temp, t_hot, t_cold, tshut_hot, tshut_cold;
>> +
>> + t_hot = temp_to_adc_conversion(BGAP_THRESHOLD_T_HOT);
>> + t_cold = temp_to_adc_conversion(BGAP_THRESHOLD_T_COLD);
>> +
>> + /* Configure the TALERT thresholds */
>> + temp = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_threshold);
>> + temp |= (t_hot << __ffs(temp_sensor->registers->threshold_thot_mask)) |
>> + (t_cold << __ffs(temp_sensor->registers->threshold_tcold_mask));
>> + omap_temp_sensor_writel(temp_sensor, temp,
>> + temp_sensor->registers->bgap_threshold);
>> +
>> + tshut_hot = temp_to_adc_conversion(TSHUT_THRESHOLD_HOT);
>> + tshut_cold = temp_to_adc_conversion(TSHUT_THRESHOLD_COLD);
>> +
>> + /* Configure the TSHUT thresholds */
>> + temp = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->thsut_threshold);
>> + temp |= (tshut_hot << __ffs(temp_sensor->registers->tshut_hot_mask))
>> + | (tshut_cold << __ffs(temp_sensor->registers->tshut_hot_mask));
>> + omap_temp_sensor_writel(temp_sensor, temp,
>> + temp_sensor->registers->thsut_threshold);
>> +}
>> +
>> +static void omap_configure_temp_sensor_counter(struct omap_temp_sensor
>> + *temp_sensor, u32 counter)
>> +{
>> + u32 val;
>> +
>> + val = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_counter);
>> + val &= ~(temp_sensor->registers->counter_mask);
>> + val |= (counter << __ffs(temp_sensor->registers->counter_mask));
>> + omap_temp_sensor_writel(temp_sensor, val,
>> + temp_sensor->registers->bgap_counter);
>> +}
>> +
>> +static void omap_enable_continuous_mode(struct omap_temp_sensor *temp_sensor,
>> + bool enable)
>> +{
>> + u32 val;
>> +
>> + val = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_mode_ctrl);
>> +
>> + if (enable)
>> + val |= (1 << __ffs(temp_sensor->registers->mode_ctrl_mask));
>> + else
>> + val &= ~(temp_sensor->registers->mode_ctrl_mask);
>> +
>> + omap_temp_sensor_writel(temp_sensor, val,
>> + temp_sensor->registers->bgap_mode_ctrl);
>> +}
>> +
>> +/* Sysfs hook functions */
>> +
>> +static ssize_t show_temp_max(struct device *dev,
>> + struct device_attribute *devattr, char *buf)
>> +{
>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>> + int temp;
>> +
>> + mutex_lock(&temp_sensor->sensor_mutex);
>> +
>> + temp = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_threshold);
>> + temp = (temp & temp_sensor->registers->threshold_thot_mask)
>> + >> __ffs(temp_sensor->registers->threshold_thot_mask);
>> +
>> + if (temp < OMAP_ADC_START_VALUE || temp > OMAP_ADC_END_VALUE) {
>> + dev_err(dev, "invalid value\n");
>> + mutex_unlock(&temp_sensor->sensor_mutex);
>> + return -EINVAL;
>> + }
>> +
>> + temp = adc_to_temp_conversion(temp);
>> + mutex_unlock(&temp_sensor->sensor_mutex);
>> +
>> + return snprintf(buf, 16, "%d\n", temp);
>> +}
>> +
>> +static ssize_t set_temp_max(struct device *dev,
>> + struct device_attribute *devattr,
>> + const char *buf, size_t count)
>> +{
>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>> + long val;
>> + u32 reg_val, t_cold, t_hot, temp;
>> +
>> + mutex_lock(&temp_sensor->sensor_mutex);
>> +
>> + if (strict_strtol(buf, 10, &val)) {
>> + count = -EINVAL;
>> + goto out;
>> + }
>> +
>> + t_hot = temp_to_adc_conversion(val);
>> + if ((t_hot < OMAP_ADC_START_VALUE || t_hot > OMAP_ADC_END_VALUE)) {
>> + dev_err(dev, "invalid range\n");
>> + count = -EINVAL;
>> + goto out;
>> + }
>> +
>> + /* obtain the T cold value */
>> + t_cold = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_threshold);
>> + t_cold = (t_cold & temp_sensor->registers->threshold_tcold_mask) >>
>> + __ffs(temp_sensor->registers->threshold_tcold_mask);
>> +
>> + if (t_hot < t_cold) {
>> + dev_err(dev, "Error! T_HOT value lesser than T_COLD\n");
>> + count = -EINVAL;
>> + goto out;
>> + }
>> +
>> + /* write the new t_hot value */
>> + reg_val = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_threshold);
>> + reg_val &= ~(temp_sensor->registers->threshold_thot_mask);
>> + reg_val |= (t_hot <<
>> + __ffs(temp_sensor->registers->threshold_thot_mask));
>> + omap_temp_sensor_writel(temp_sensor, reg_val,
>> + temp_sensor->registers->bgap_threshold);
>> +
>> + /* Read the current temperature */
>> + temp = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->temp_sensor_ctrl);
>> + temp &= (temp_sensor->registers->bgap_dtemp_mask);
>> +
>> + /*
>> + * If user sets the HIGH threshold(t_hot) greater than the current
>> + * temperature(temp) unmask the HOT interrupts
>> + */
>> + if (t_hot > temp) {
>> + reg_val = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_mask_ctrl);
>> + reg_val &= ~(temp_sensor->registers->mask_cold_mask);
>> + reg_val |= temp_sensor->registers->mask_hot_mask;
>> + omap_temp_sensor_writel(temp_sensor, reg_val,
>> + temp_sensor->registers->bgap_mask_ctrl);
>> + }
>> +
>> + /*
>> + * If current temperature is in-between the hot and cold thresholds,
>> + * Enable both masks.
>> + */
>> + if (temp > t_cold && temp < t_hot) {
>> + reg_val = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_mask_ctrl);
>> + reg_val |= temp_sensor->registers->mask_cold_mask;
>> + reg_val |= temp_sensor->registers->mask_hot_mask;
>> + omap_temp_sensor_writel(temp_sensor, reg_val,
>> + OMAP4460_BGAP_CTRL_OFFSET);
>> + }
>> + /*
>> + * else no need to do anything since HW will immediately compare
>> + * the new threshold and generate interrupt accordingly
>> + */
>> +out:
>> + mutex_unlock(&temp_sensor->sensor_mutex);
>> + return count;
>> +}
>> +
>> +static ssize_t show_temp_max_hyst(struct device *dev,
>> + struct device_attribute *devattr, char *buf)
>> +{
>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>> + u32 temp;
>> +
>> + mutex_lock(&temp_sensor->sensor_mutex);
>> +
>> + temp = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_threshold);
>> + temp = (temp & temp_sensor->registers->threshold_tcold_mask) >>
>> + __ffs(temp_sensor->registers->threshold_tcold_mask);
>> +
>> + if (temp < OMAP_ADC_START_VALUE || temp > OMAP_ADC_END_VALUE) {
>> + dev_err(dev, "invalid value\n");
>> + mutex_unlock(&temp_sensor->sensor_mutex);
>> + return -EINVAL;
>> + }
>> +
>> + temp = adc_to_temp_conversion(temp);
>> + mutex_unlock(&temp_sensor->sensor_mutex);
>> +
>> + return snprintf(buf, 16, "%d\n", temp);
>> +}
>> +
>> +static ssize_t set_temp_max_hyst(struct device *dev,
>> + struct device_attribute *devattr,
>> + const char *buf, size_t count)
>> +{
>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>> + u32 reg_val, t_hot, t_cold, temp;
>> + long val;
>> +
>> + mutex_lock(&temp_sensor->sensor_mutex);
>> +
>> + if (strict_strtol(buf, 10, &val)) {
>> + count = -EINVAL;
>> + goto out;
>> + }
>> +
>> + t_cold = temp_to_adc_conversion(val);
>> + if (t_cold < OMAP_ADC_START_VALUE || t_cold > OMAP_ADC_END_VALUE) {
>> + dev_err(dev, "invalid range");
>> + count = -EINVAL;
>> + goto out;
>> + }
>> +
>> + /* obtain the T HOT value */
>> + t_hot = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_threshold);
>> + t_hot = (t_hot & temp_sensor->registers->threshold_thot_mask) >>
>> + __ffs(temp_sensor->registers->threshold_thot_mask);
>> +
>> + if (t_cold > t_hot) {
>> + dev_err(dev, "Error! T_COLD value greater than T_HOT\n");
>> + count = -EINVAL;
>> + goto out;
>> + }
>> +
>> + /* write the new t_cold value */
>> + reg_val = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_threshold);
>> + reg_val &= ~(temp_sensor->registers->threshold_tcold_mask);
>> + reg_val |= (t_cold <<
>> + __ffs(temp_sensor->registers->threshold_tcold_mask));
>> + omap_temp_sensor_writel(temp_sensor, reg_val,
>> + temp_sensor->registers->bgap_threshold);
>> +
>> + /* Read the current temperature */
>> + temp = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->temp_sensor_ctrl);
>> + temp &= (temp_sensor->registers->bgap_dtemp_mask);
>> +
>> + /*
>> + * If user sets the LOW threshold(t_cold) lower than the current
>> + * temperature(temp) unmask the COLD interrupts
>> + */
>> + if (t_cold < temp) {
>> + reg_val = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_mask_ctrl);
>> + reg_val &= ~(temp_sensor->registers->mask_hot_mask);
>> + reg_val |= temp_sensor->registers->mask_cold_mask;
>> + omap_temp_sensor_writel(temp_sensor, reg_val,
>> + temp_sensor->registers->bgap_mask_ctrl);
>> + }
>> +
>> + /*
>> + * If current temperature is in-between the hot and cold thresholds,
>> + * Enable both masks.
>> + */
>> + if (temp < t_hot && temp > t_cold) {
>> + reg_val = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_mask_ctrl);
>> + reg_val |= temp_sensor->registers->mask_cold_mask;
>> + reg_val |= temp_sensor->registers->mask_hot_mask;
>> + omap_temp_sensor_writel(temp_sensor, reg_val,
>> + temp_sensor->registers->bgap_mask_ctrl);
>> + }
>> +
>> + /*
>> + * else no need to do anything since HW will immediately compare
>> + * the new threshold and generate interrupt accordingly
>> + */
>> +
>> +out:
>> + mutex_unlock(&temp_sensor->sensor_mutex);
>> + return count;
>> +}
>> +
>> +static ssize_t show_update_rate(struct device *dev,
>> + struct device_attribute *devattr, char *buf)
>> +{
>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>> + u32 temp = 0, ret = 0;
>> +
>> + mutex_lock(&temp_sensor->sensor_mutex);
>> +
>> + if (!temp_sensor->clk_rate) {
>> + dev_err(dev, "clk_rate is NULL\n");
>> + ret = -EINVAL;
>> + goto out;
>> + }
>> +
>> + temp = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_counter);
>> + temp = (temp & temp_sensor->registers->counter_mask) >>
>> + __ffs(temp_sensor->registers->counter_mask);
>> + temp = temp * 1000 / (temp_sensor->clk_rate);
>> +
>> +out:
>> + mutex_unlock(&temp_sensor->sensor_mutex);
>> + if (!ret)
>> + return sprintf(buf, "%d\n", temp);
>> +
>> + return ret;
>> +}
>> +
>> +static ssize_t set_update_rate(struct device *dev,
>> + struct device_attribute *devattr,
>> + const char *buf, size_t count)
>> +{
>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>> + u32 reg_val;
>> + long val;
>> +
>> + mutex_lock(&temp_sensor->sensor_mutex);
>> +
>> + if (strict_strtol(buf, 10, &val)) {
>> + count = -EINVAL;
>> + goto out;
>> + }
>> +
>> + val *= temp_sensor->clk_rate / 1000;
>> + reg_val = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_counter);
>> +
>> + reg_val &= ~(temp_sensor->registers->counter_mask);
>> + reg_val |= val;
>> + omap_temp_sensor_writel(temp_sensor, reg_val,
>> + temp_sensor->registers->bgap_counter);
>> +
>> +out:
>> + mutex_unlock(&temp_sensor->sensor_mutex);
>> + return count;
>> +}
>> +
>> +static int omap_temp_sensor_read_temp(struct device *dev,
>> + struct device_attribute *devattr,
>> + char *buf)
>> +{
>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>> + int temp, ret = 0;
>> +
>> + mutex_lock(&temp_sensor->sensor_mutex);
>> +
>> + temp = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->temp_sensor_ctrl);
>> + temp &= temp_sensor->registers->bgap_dtemp_mask;
>> +
>> + if (!temp_sensor->is_efuse_valid)
>> + dev_err(dev, "Invalid EFUSE, Non-trimmed BGAP, Temp not accurate\n");
>> +
>> + /* look up for temperature in the table and return the temperature */
>> + if (temp < OMAP_ADC_START_VALUE || temp > OMAP_ADC_END_VALUE) {
>> + dev_err(dev, "invalid adc code reported %d", temp);
>> + ret = -EINVAL;
>> + goto out;
>> + }
>> +
>> + temp = adc_to_temp[temp - OMAP_ADC_START_VALUE];
>> +
>> +out:
>> + mutex_unlock(&temp_sensor->sensor_mutex);
>> + if (!ret)
>> + return sprintf(buf, "%d\n", temp);
>> +
>> + return ret;
>> +}
>> +
>> +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, omap_temp_sensor_read_temp,
>> + NULL, 0);
>> +static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp_max,
>> + set_temp_max, 0);
>> +static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO, show_temp_max_hyst,
>> + set_temp_max_hyst, 0);
>> +static SENSOR_DEVICE_ATTR(update_rate, S_IWUSR | S_IRUGO, show_update_rate,
>> + set_update_rate, 0);
>> +
>> +static struct attribute *omap_temp_sensor_attributes[] = {
>> + &sensor_dev_attr_temp1_input.dev_attr.attr,
>> + &sensor_dev_attr_temp1_max.dev_attr.attr,
>> + &sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
>> + &sensor_dev_attr_update_rate.dev_attr.attr,
>> + NULL
>> +};
>> +
>> +static const struct attribute_group omap_temp_sensor_group = {
>> + .attrs = omap_temp_sensor_attributes,
>> +};
>> +
>> +static int omap_temp_sensor_clk_enable(struct omap_temp_sensor *temp_sensor)
>> +{
>> + u32 ret = 0;
>> +
>> + if (temp_sensor->clk_on) {
>> + dev_err(temp_sensor->hwmon_dev, "clock already on\n");
>> + goto out;
>> + }
>> +
>> + ret = pm_runtime_get_sync(temp_sensor->hwmon_dev);
>> + if (ret < 0) {
>> + dev_err(temp_sensor->hwmon_dev, "get sync failed\n");
>> + goto out;
>> + }
>> +
>> + temp_sensor->clk_on = 1;
>> +
>> +out:
>> + return ret;
>> +}
>> +
>> +static int omap_temp_sensor_clk_disable(struct omap_temp_sensor *temp_sensor)
>> +{
>> + u32 ret = 0;
>> +
>> + if (!temp_sensor->clk_on) {
>> + dev_err(temp_sensor->hwmon_dev, "clock already off\n");
>> + goto out;
>> + }
>> +
>> + /* Gate the clock */
>> + ret = pm_runtime_put_sync(temp_sensor->hwmon_dev);
>> + if (ret < 0) {
>> + dev_err(temp_sensor->hwmon_dev, "put sync failed\n");
>> + goto out;
>> + }
>> + temp_sensor->clk_on = 0;
>> +
>> +out:
>> + return ret;
>> +}
>> +
>> +static irqreturn_t omap_talert_irq_handler(int irq, void *data)
>> +{
>> + struct omap_temp_sensor *temp_sensor;
>> + int t_hot, t_cold, temp;
>> +
>> + temp_sensor = data;
>> + mutex_lock(&temp_sensor->sensor_mutex);
>> +
>> + /* Read the status of t_hot */
>> + t_hot = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_status)
>> + & temp_sensor->registers->status_hot_mask;
>> +
>> + /* Read the status of t_cold */
>> + t_cold = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_status)
>> + & temp_sensor->registers->status_cold_mask;
>> +
>> + temp = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_mask_ctrl);
>> + /*
>> + * One TALERT interrupt: Two sources
>> + * If the interrupt is due to t_hot then mask t_hot and
>> + * and unmask t_cold else mask t_cold and unmask t_hot
>> + */
>> + if (t_hot) {
>> + temp &= ~(temp_sensor->registers->mask_hot_mask);
>> + temp |= temp_sensor->registers->mask_cold_mask;
>> + } else if (t_cold) {
>> + temp &= ~(temp_sensor->registers->mask_cold_mask);
>> + temp |= temp_sensor->registers->mask_hot_mask;
>> + }
>> +
>> + omap_temp_sensor_writel(temp_sensor, temp,
>> + temp_sensor->registers->bgap_mask_ctrl);
>> +
>> + /* kobject_uvent to user space telling thermal threshold crossed */
>> + kobject_uevent(&temp_sensor->hwmon_dev->kobj, KOBJ_CHANGE);
>> +
>> + mutex_unlock(&temp_sensor->sensor_mutex);
>> +
>> + return IRQ_HANDLED;
>> +}
>> +
>> +static int __devinit omap_temp_sensor_probe(struct platform_device *pdev)
>> +{
>> + struct omap_temp_sensor_pdata *pdata = pdev->dev.platform_data;
>> + struct omap_temp_sensor *temp_sensor;
>> + struct resource *mem;
>> + int ret = 0;
>> + int val, clk_rate;
>> +
>> + if (!pdata) {
>> + dev_err(&pdev->dev, "platform data missing\n");
>> + return -EINVAL;
>> + }
>> +
>> + temp_sensor = kzalloc(sizeof(*temp_sensor), GFP_KERNEL);
>> + if (!temp_sensor)
>> + return -ENOMEM;
>> +
>> + mutex_init(&temp_sensor->sensor_mutex);
>> +
>> + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> + if (!mem) {
>> + dev_err(&pdev->dev, "no mem resource\n");
>> + ret = -ENOMEM;
>> + goto plat_res_err;
>> + }
>> +
>> + temp_sensor->irq = platform_get_irq_byname(pdev, "thermal_alert");
>> + if (temp_sensor->irq < 0) {
>> + dev_err(&pdev->dev, "get_irq_byname failed\n");
>> + ret = temp_sensor->irq;
>> + goto plat_res_err;
>> + }
>> +
>> + temp_sensor->phy_base = ioremap(mem->start, resource_size(mem));
>> + temp_sensor->clock = NULL;
>> + temp_sensor->registers = pdata->registers;
>> + temp_sensor->hwmon_dev = &pdev->dev;
>> +
>> + pm_runtime_enable(&pdev->dev);
>> + pm_runtime_irq_safe(&pdev->dev);
>> +
>> + /*
>> + * check if the efuse has a non-zero value if not
>> + * it is an untrimmed sample and the temperatures
>> + * may not be accurate
>> + */
>> +
>> + if (omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_efuse))
>> + temp_sensor->is_efuse_valid = 1;
>> +
>> + platform_set_drvdata(pdev, temp_sensor);
>> + dev_set_drvdata(&pdev->dev, temp_sensor);
>> + temp_sensor->clock = clk_get(temp_sensor->hwmon_dev, "fck");
>> + if (IS_ERR(temp_sensor->clock)) {
>> + ret = PTR_ERR(temp_sensor->clock);
>> + dev_err(temp_sensor->hwmon_dev,
>> + "unable to get fclk: %d\n", ret);
>> + ret = -EINVAL;
>> + goto plat_res_err;
>> + }
>> +
>> + ret = omap_temp_sensor_clk_enable(temp_sensor);
>> + if (ret) {
>> + dev_err(&pdev->dev, "Cannot enable temp sensor\n");
>> + goto clken_err;
>> + }
>> +
>> + clk_rate = clk_round_rate(temp_sensor->clock, 2000000);
>> + if (clk_rate < 1000000 || clk_rate == 0xffffffff) {
>> + dev_err(&pdev->dev, "Error round rate\n");
>> + ret = -EINVAL;
>> + goto clken_err;
>> + }
>> +
>> + ret = clk_set_rate(temp_sensor->clock, clk_rate);
>> + if (ret) {
>> + dev_err(&pdev->dev, "Cannot set clock rate\n");
>> + goto clken_err;
>> + }
>> +
>> + temp_sensor->clk_rate = clk_rate;
>> + omap_enable_continuous_mode(temp_sensor, 1);
>> + omap_configure_temp_sensor_thresholds(temp_sensor);
>> + /* 1 ms */
>> + omap_configure_temp_sensor_counter(temp_sensor, 1);
>> +
>> + /* Wait till the first conversion is done wait for at least 1ms */
>> + usleep_range(1000, 2000);
>> +
>> + /* Read the temperature once due to hw issue*/
>> + omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->temp_sensor_ctrl);
>> +
>> + /* Set 2 seconds time as default counter */
>> + omap_configure_temp_sensor_counter(temp_sensor,
>> + temp_sensor->clk_rate * 2);
>> +
>> + temp_sensor->hwmon_dev = hwmon_device_register(&pdev->dev);
>> + if (IS_ERR(temp_sensor->hwmon_dev)) {
>> + dev_err(&pdev->dev, "hwmon_device_register failed.\n");
>> + ret = PTR_ERR(temp_sensor->hwmon_dev);
>> + goto hwmon_reg_err;
>> + }
>> +
>> + ret = sysfs_create_group(&pdev->dev.kobj,
>> + &omap_temp_sensor_group);
>> + if (ret) {
>> + dev_err(&pdev->dev, "could not create sysfs files\n");
>> + goto sysfs_create_err;
>> + }
>> +
>> + kobject_uevent(&temp_sensor->hwmon_dev->kobj, KOBJ_ADD);
>> +
>> + ret = request_threaded_irq(temp_sensor->irq, NULL,
>> + omap_talert_irq_handler, IRQF_TRIGGER_RISING | IRQF_ONESHOT,
>> + "temp_sensor", temp_sensor);
>> + if (ret) {
>> + dev_err(&pdev->dev, "Request threaded irq failed.\n");
>> + goto req_irq_err;
>> + }
>> +
>> + /* unmask the T_COLD and unmask T_HOT at init */
>> + val = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_mask_ctrl);
>> + val |= temp_sensor->registers->mask_cold_mask
>> + | temp_sensor->registers->mask_hot_mask;
>> +
>> + omap_temp_sensor_writel(temp_sensor, val,
>> + temp_sensor->registers->bgap_mask_ctrl);
>> +
>> + return 0;
>> +
>> +req_irq_err:
>> + kobject_uevent(&temp_sensor->hwmon_dev->kobj, KOBJ_REMOVE);
>> + sysfs_remove_group(&temp_sensor->hwmon_dev->kobj,
>> + &omap_temp_sensor_group);
>> +sysfs_create_err:
>> + hwmon_device_unregister(&pdev->dev);
>> +hwmon_reg_err:
>> + omap_temp_sensor_clk_disable(temp_sensor);
>> +clken_err:
>> + clk_put(temp_sensor->clock);
>> +plat_res_err:
>> + mutex_destroy(&temp_sensor->sensor_mutex);
>> + kfree(temp_sensor);
>> + return ret;
>> +}
>> +
>> +static int __devexit omap_temp_sensor_remove(struct platform_device *pdev)
>> +{
>> + struct omap_temp_sensor *temp_sensor = platform_get_drvdata(pdev);
>> +
>> + hwmon_device_unregister(&pdev->dev);
>> + kobject_uevent(&temp_sensor->hwmon_dev->kobj, KOBJ_REMOVE);
>> + sysfs_remove_group(&temp_sensor->hwmon_dev->kobj,
>> + &omap_temp_sensor_group);
>> + omap_temp_sensor_clk_disable(temp_sensor);
>> + clk_put(temp_sensor->clock);
>> + platform_set_drvdata(pdev, NULL);
>> + free_irq(temp_sensor->irq, temp_sensor);
>> + mutex_destroy(&temp_sensor->sensor_mutex);
>> + kfree(temp_sensor);
>> +
>> + return 0;
>> +}
>> +
>> +#ifdef CONFIG_PM
>> +static void omap_temp_sensor_save_ctxt(struct omap_temp_sensor *temp_sensor)
>> +{
>> + temp_sensor->temp_sensor_ctrl = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->temp_sensor_ctrl);
>> + temp_sensor->bg_ctrl = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_mask_ctrl);
>> + temp_sensor->bg_counter = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_counter);
>> + temp_sensor->bg_threshold = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_threshold);
>> + temp_sensor->temp_sensor_tshut_threshold =
>> + omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->thsut_threshold);
>> +}
>> +
>> +static void omap_temp_sensor_restore_ctxt(struct omap_temp_sensor *temp_sensor)
>> +{
>> + omap_temp_sensor_writel(temp_sensor,
>> + temp_sensor->temp_sensor_ctrl,
>> + temp_sensor->registers->temp_sensor_ctrl);
>> + omap_temp_sensor_writel(temp_sensor,
>> + temp_sensor->bg_ctrl,
>> + temp_sensor->registers->bgap_mask_ctrl);
>> + omap_temp_sensor_writel(temp_sensor,
>> + temp_sensor->bg_counter,
>> + temp_sensor->registers->bgap_counter);
>> + omap_temp_sensor_writel(temp_sensor,
>> + temp_sensor->bg_threshold,
>> + temp_sensor->registers->bgap_threshold);
>> + omap_temp_sensor_writel(temp_sensor,
>> + temp_sensor->temp_sensor_tshut_threshold,
>> + temp_sensor->registers->thsut_threshold);
>> +}
>> +
>> +static int omap_temp_sensor_suspend(struct device *dev)
>> +{
>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>> +
>> + omap_temp_sensor_save_ctxt(temp_sensor);
>> +
>> + return 0;
>> +}
>> +
>> +static int omap_temp_sensor_resume(struct device *dev)
>> +{
>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>> +
>> + omap_temp_sensor_restore_ctxt(temp_sensor);
>> +
>> + return 0;
>> +}
>> +
>> +static int omap_temp_sensor_runtime_suspend(struct device *dev)
>> +{
>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>> +
>> + omap_temp_sensor_save_ctxt(temp_sensor);
>> +
>> + return 0;
>> +}
>> +
>> +static int omap_temp_sensor_runtime_resume(struct device *dev)
>> +{
>> + static int context_loss_count;
>> + int temp;
>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>> +
>> + temp = omap_device_get_context_loss_count(to_platform_device(dev));
>> +
>> + if (temp != context_loss_count && context_loss_count != 0)
>> + omap_temp_sensor_restore_ctxt(temp_sensor);
>> +
>> + context_loss_count = temp;
>> +
>> + return 0;
>> +}
>> +
>> +static const struct dev_pm_ops omap_temp_sensor_dev_pm_ops = {
>> + .suspend = omap_temp_sensor_suspend,
>> + .resume = omap_temp_sensor_resume,
>> + .runtime_suspend = omap_temp_sensor_runtime_suspend,
>> + .runtime_resume = omap_temp_sensor_runtime_resume,
>> +};
>> +
>> +#endif
>> +
>> +static struct platform_driver omap_temp_sensor_driver = {
>> + .probe = omap_temp_sensor_probe,
>> + .remove = omap_temp_sensor_remove,
>> + .driver = {
>> + .name = "omap_temp_sensor",
>> +#ifdef CONFIG_PM
>> + .pm = &omap_temp_sensor_dev_pm_ops,
>> +#endif
>> + },
>> +};
>> +
>> +int __init omap_temp_sensor_init(void)
>> +{
>> + return platform_driver_register(&omap_temp_sensor_driver);
>> +}
>> +module_init(omap_temp_sensor_init);
>> +
>> +static void __exit omap_temp_sensor_exit(void)
>> +{
>> + platform_driver_unregister(&omap_temp_sensor_driver);
>> +}
>> +module_exit(omap_temp_sensor_exit);
>> +
>> +MODULE_DESCRIPTION("OMAP446X temperature sensor Driver");
>> +MODULE_LICENSE("GPL");
>> +MODULE_ALIAS("platform:" DRIVER_NAME);
>> +MODULE_AUTHOR("J Keerthy <j-keerthy@ti.com>");
>> --
>> 1.7.0.4
>>
>
--
Regards and Thanks,
Keerthy
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 35+ messages in thread
* Re: [lm-sensors] [RFC PATCH 6/6 V2] hwmon: OMAP4: On die
@ 2011-08-19 6:16 ` J, KEERTHY
0 siblings, 0 replies; 35+ messages in thread
From: J, KEERTHY @ 2011-08-19 6:16 UTC (permalink / raw)
To: Guenter Roeck; +Cc: linux-omap, Jean Delvare, lm-sensors
On Fri, Aug 19, 2011 at 7:43 AM, Guenter Roeck
<guenter.roeck@ericsson.com> wrote:
> On Thu, Aug 18, 2011 at 06:52:15AM -0400, Keerthy wrote:
>> On chip temperature sensor driver. The driver monitors the temperature of
>> the MPU subsystem of the OMAP4. It sends notifications to the user space if
>> the temperature crosses user defined thresholds via kobject_uevent interface.
>> The user is allowed to configure the temperature thresholds vis sysfs nodes
>> exposed using hwmon interface.
>>
>> Signed-off-by: Keerthy <j-keerthy@ti.com>
>> Cc: Jean Delvare <khali@linux-fr.org>
>> Cc: Guenter Roeck <guenter.roeck@ericsson.com>
>> Cc: lm-sensors@lm-sensors.org
>
> High level review:
>
> - too much and too broad mutex locking. show functions should not need locks at all,
> set functions only while data is written into registers and into platform data.
Ok. I will clean this.
> - driver is quite noisy. There should definitely not be any log messages
> if a set parameter is wrong. Show functions already return an error value
> to the user; a log message indicating the error again just creates noise.
> For one boolean set during probe (is_efuse_valid), each subsequent show results
> in a log message if it is false. Some errors result in multiple log messages.
A user tries to set an invalid temperature threshold. The user should
be notified about this. The invalid temperature will not be set. The user
should not be allowed to set an invalid temperature. It is to inform
the user about precisely the problem with the parameter.
In some of the samples the bandgap is not trimmed and hence
temperature reported will be wrong. So every time a user tries to read
he is alerted that the temperatures are not accurate.
> - Wrong use of EINVAL throughout the driver (EINVAL is "Invalid Argument")
Ok. I will correct this.
> - excessive ( )
Ok.
> - linear search through a sorted array is very expensive. Consider using a binary search.
Yes. I will implement binary search.
> - temp_to_adc_conversion return code (error if negative) is not checked properly.
Ok. I will add checks.
>
> I am sure there are other problems, but those are difficult to find with all the noise
> above.
>
> Guenter
>
>> ---
>> Documentation/hwmon/omap_temp_sensor | 27 +
>> drivers/hwmon/Kconfig | 11 +
>> drivers/hwmon/Makefile | 1 +
>> drivers/hwmon/omap_temp_sensor.c | 918 ++++++++++++++++++++++++++++++++++
>> 4 files changed, 957 insertions(+), 0 deletions(-)
>> create mode 100644 Documentation/hwmon/omap_temp_sensor
>> create mode 100644 drivers/hwmon/omap_temp_sensor.c
>>
>> diff --git a/Documentation/hwmon/omap_temp_sensor b/Documentation/hwmon/omap_temp_sensor
>> new file mode 100644
>> index 0000000..e01a6d6
>> --- /dev/null
>> +++ b/Documentation/hwmon/omap_temp_sensor
>> @@ -0,0 +1,27 @@
>> +Kernel driver omap_temp_sensor
>> +===============
>> +
>> +Supported chips:
>> + * Texas Instruments OMAP4460
>> + Prefix: 'omap_temp_sensor'
>> + Datasheet: http://focus.ti.com/docs/prod/folders/print/tmp102.html
>> +
>> +Author:
>> + J Keerthy <j-keerthy@ti.com>
>> +
>> +Description
>> +-----------
>> +
>> +The Texas Instruments OMAP4 family of chips have a bandgap temperature sensor.
>> +The temperature sensor feature is used to convert the temperature of the device
>> +into a decimal value coded on 10 bits. An internal ADC is used for conversion.
>> +The recommended operating temperatures must be in the range -40 degree Celsius
>> +to 123 degree celsius for standard conversion.
>> +The thresholds are programmable and upon crossing the thresholds an interrupt
>> +is generated. The OMAP temperature sensor has a programmable update rate in
>> +milli seconds.
>> +(Currently the driver programs a default of 2000 milli seconds).
>> +
>> +The driver provides the common sysfs-interface for temperatures (see
>> +Documentation/hwmon/sysfs-interface under Temperatures).
>> +
>> diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
>> index 5f888f7..9c9cd8b 100644
>> --- a/drivers/hwmon/Kconfig
>> +++ b/drivers/hwmon/Kconfig
>> @@ -323,6 +323,17 @@ config SENSORS_F71805F
>> This driver can also be built as a module. If so, the module
>> will be called f71805f.
>>
>> +config SENSORS_OMAP_BANDGAP_TEMP_SENSOR
>> + bool "OMAP on-die temperature sensor hwmon driver"
>> + depends on HWMON && ARCH_OMAP && OMAP_TEMP_SENSOR
>> + help
>> + If you say yes here you get support for hardware
>> + monitoring features of the OMAP on die temperature
>> + sensor.
>> +
>> + Continuous conversion programmable delay
>> + mode is used for temperature conversion.
>> +
>> config SENSORS_F71882FG
>> tristate "Fintek F71882FG and compatibles"
>> help
>> diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
>> index 28061cf..d0f89f5 100644
>> --- a/drivers/hwmon/Makefile
>> +++ b/drivers/hwmon/Makefile
>> @@ -91,6 +91,7 @@ obj-$(CONFIG_SENSORS_MAX6639) += max6639.o
>> obj-$(CONFIG_SENSORS_MAX6642) += max6642.o
>> obj-$(CONFIG_SENSORS_MAX6650) += max6650.o
>> obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o
>> +obj-$(CONFIG_SENSORS_OMAP_BANDGAP_TEMP_SENSOR) += omap_temp_sensor.o
>> obj-$(CONFIG_SENSORS_PC87360) += pc87360.o
>> obj-$(CONFIG_SENSORS_PC87427) += pc87427.o
>> obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o
>> diff --git a/drivers/hwmon/omap_temp_sensor.c b/drivers/hwmon/omap_temp_sensor.c
>> new file mode 100644
>> index 0000000..586a361
>> --- /dev/null
>> +++ b/drivers/hwmon/omap_temp_sensor.c
>> @@ -0,0 +1,918 @@
>> +/*
>> + * OMAP4 Temperature sensor driver file
>> + *
>> + * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
>> + * Author: J Keerthy <j-keerthy@ti.com>
>> + * Author: Moiz Sonasath <m-sonasath@ti.com>
>> + *
>> + * This program is free software; you can redistribute it and/or
>> + * modify it under the terms of the GNU General Public License
>> + * 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.
>> + *
>> + * You should have received a copy of the GNU General Public License
>> + * along with this program; if not, write to the Free Software
>> + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
>> + * 02110-1301 USA
>> + *
>> + */
>> +
>> +#include <linux/interrupt.h>
>> +#include <linux/clk.h>
>> +#include <linux/io.h>
>> +#include <linux/slab.h>
>> +#include <linux/init.h>
>> +#include <plat/omap_device.h>
>> +#include <linux/kernel.h>
>> +#include <linux/device.h>
>> +#include <linux/jiffies.h>
>> +#include <linux/hwmon.h>
>> +#include <linux/hwmon-sysfs.h>
>> +#include <linux/stddef.h>
>> +#include <linux/sysfs.h>
>> +#include <linux/err.h>
>> +#include <linux/types.h>
>> +#include <linux/mutex.h>
>> +#include <linux/pm_runtime.h>
>> +#include <linux/delay.h>
>> +#include <plat/temperature_sensor.h>
>> +
>> +#define TSHUT_THRESHOLD_HOT 122000 /* 122 deg C */
>> +#define TSHUT_THRESHOLD_COLD 100000 /* 100 deg C */
>> +#define BGAP_THRESHOLD_T_HOT 73000 /* 73 deg C */
>> +#define BGAP_THRESHOLD_T_COLD 71000 /* 71 deg C */
>> +#define OMAP_ADC_START_VALUE 530
>> +#define OMAP_ADC_END_VALUE 923
>> +
>> +/*
>> + * omap_temp_sensor structure
>> + * @hwmon_dev - device pointer
>> + * @clock - Clock pointer
>> + * @registers - Pointer to structure with register offsets and bitfields
>> + * @sensor_mutex - Mutex for sysfs, irq and PM
>> + * @irq - MPU Irq number for thermal alert
>> + * @phy_base - Physical base of the temp I/O
>> + * @clk_rate - Holds current clock rate
>> + * @temp_sensor_ctrl - temp sensor control register value
>> + * @bg_ctrl - bandgap ctrl register value
>> + * @bg_counter - bandgap counter value
>> + * @bg_threshold - bandgap threshold register value
>> + * @temp_sensor_tshut_threshold - bandgap tshut register value
>> + * @is_efuse_valid - Flag to determine if efuse is valid or not
>> + * @clk_on - Manages the current clock state
>> + */
>> +struct omap_temp_sensor {
>> + struct device *hwmon_dev;
>> + struct clk *clock;
>> + struct omap_temp_sensor_registers *registers;
>> + struct mutex sensor_mutex; /* Mutex for sysfs, irq and PM */
>> + unsigned int irq;
>> + void __iomem *phy_base;
>> + u32 clk_rate;
>> + u32 temp_sensor_ctrl;
>> + u32 bg_ctrl;
>> + u32 bg_counter;
>> + u32 bg_threshold;
>> + u32 temp_sensor_tshut_threshold;
>> + bool is_efuse_valid;
>> + bool clk_on;
>> +};
>> +
>> +/*
>> + * Temperature values in milli degree celsius
>> + * ADC code values from 530 to 923
>> + */
>> +static int adc_to_temp[] = {
>> + -40000, -40000, -40000, -40000, -39800, -39400, -39000, -38600, -38200,
>> + -37800, -37300, -36800, -36400, -36000, -35600, -35200, -34800,
>> + -34300, -33800, -33400, -33000, -32600, -32200, -31800, -31300,
>> + -30800, -30400, -30000, -29600, -29200, -28700, -28200, -27800,
>> + -27400, -27000, -26600, -26200, -25700, -25200, -24800, -24400,
>> + -24000, -23600, -23200, -22700, -22200, -21800, -21400, -21000,
>> + -20600, -20200, -19700, -19200, -18800, -18400, -18000, -17600,
>> + -17200, -16700, -16200, -15800, -15400, -15000, -14600, -14200,
>> + -13700, -13200, -12800, -12400, -12000, -11600, -11200, -10700,
>> + -10200, -9800, -9400, -9000, -8600, -8200, -7700, -7200, -6800,
>> + -6400, -6000, -5600, -5200, -4800, -4300, -3800, -3400, -3000,
>> + -2600, -2200, -1800, -1300, -800, -400, 0, 400, 800, 1200, 1600,
>> + 2100, 2600, 3000, 3400, 3800, 4200, 4600, 5100, 5600, 6000, 6400,
>> + 6800, 7200, 7600, 8000, 8500, 9000, 9400, 9800, 10200, 10600, 11000,
>> + 11400, 11900, 12400, 12800, 13200, 13600, 14000, 14400, 14800,
>> + 15300, 15800, 16200, 16600, 17000, 17400, 17800, 18200, 18700,
>> + 19200, 19600, 20000, 20400, 20800, 21200, 21600, 22100, 22600,
>> + 23000, 23400, 23800, 24200, 24600, 25000, 25400, 25900, 26400,
>> + 26800, 27200, 27600, 28000, 28400, 28800, 29300, 29800, 30200,
>> + 30600, 31000, 31400, 31800, 32200, 32600, 33100, 33600, 34000,
>> + 34400, 34800, 35200, 35600, 36000, 36400, 36800, 37300, 37800,
>> + 38200, 38600, 39000, 39400, 39800, 40200, 40600, 41100, 41600,
>> + 42000, 42400, 42800, 43200, 43600, 44000, 44400, 44800, 45300,
>> + 45800, 46200, 46600, 47000, 47400, 47800, 48200, 48600, 49000,
>> + 49500, 50000, 50400, 50800, 51200, 51600, 52000, 52400, 52800,
>> + 53200, 53700, 54200, 54600, 55000, 55400, 55800, 56200, 56600,
>> + 57000, 57400, 57800, 58200, 58700, 59200, 59600, 60000, 60400,
>> + 60800, 61200, 61600, 62000, 62400, 62800, 63300, 63800, 64200,
>> + 64600, 65000, 65400, 65800, 66200, 66600, 67000, 67400, 67800,
>> + 68200, 68700, 69200, 69600, 70000, 70400, 70800, 71200, 71600,
>> + 72000, 72400, 72800, 73200, 73600, 74100, 74600, 75000, 75400,
>> + 75800, 76200, 76600, 77000, 77400, 77800, 78200, 78600, 79000,
>> + 79400, 79800, 80300, 80800, 81200, 81600, 82000, 82400, 82800,
>> + 83200, 83600, 84000, 84400, 84800, 85200, 85600, 86000, 86400,
>> + 86800, 87300, 87800, 88200, 88600, 89000, 89400, 89800, 90200,
>> + 90600, 91000, 91400, 91800, 92200, 92600, 93000, 93400, 93800,
>> + 94200, 94600, 95000, 95500, 96000, 96400, 96800, 97200, 97600,
>> + 98000, 98400, 98800, 99200, 99600, 100000, 100400, 100800, 101200,
>> + 101600, 102000, 102400, 102800, 103200, 103600, 104000, 104400,
>> + 104800, 105200, 105600, 106100, 106600, 107000, 107400, 107800,
>> + 108200, 108600, 109000, 109400, 109800, 110200, 110600, 111000,
>> + 111400, 111800, 112200, 112600, 113000, 113400, 113800, 114200,
>> + 114600, 115000, 115400, 115800, 116200, 116600, 117000, 117400,
>> + 117800, 118200, 118600, 119000, 119400, 119800, 120200, 120600,
>> + 121000, 121400, 121800, 122200, 122600, 123000
>> +};
>> +
>> +static unsigned long omap_temp_sensor_readl(struct omap_temp_sensor
>> + *temp_sensor, u32 reg)
>> +{
>> + return __raw_readl(temp_sensor->phy_base + reg);
>> +}
>> +
>> +static void omap_temp_sensor_writel(struct omap_temp_sensor *temp_sensor,
>> + u32 val, u32 reg)
>> +{
>> + __raw_writel(val, (temp_sensor->phy_base + reg));
>> +}
>> +
>> +static int adc_to_temp_conversion(int adc_val)
>> +{
>> + return adc_to_temp[adc_val - OMAP_ADC_START_VALUE];
>> +}
>> +
>> +static int temp_to_adc_conversion(long temp)
>> +{
>> + int i;
>> +
>> + for (i = 0; i <= OMAP_ADC_END_VALUE - OMAP_ADC_START_VALUE; i++)
>> + if (temp < adc_to_temp[i])
>> + return OMAP_ADC_START_VALUE + i - 1;
>> +
>> + return -EINVAL;
>> +}
>> +
>> +static void omap_configure_temp_sensor_thresholds(struct omap_temp_sensor
>> + *temp_sensor)
>> +{
>> + u32 temp, t_hot, t_cold, tshut_hot, tshut_cold;
>> +
>> + t_hot = temp_to_adc_conversion(BGAP_THRESHOLD_T_HOT);
>> + t_cold = temp_to_adc_conversion(BGAP_THRESHOLD_T_COLD);
>> +
>> + /* Configure the TALERT thresholds */
>> + temp = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_threshold);
>> + temp |= (t_hot << __ffs(temp_sensor->registers->threshold_thot_mask)) |
>> + (t_cold << __ffs(temp_sensor->registers->threshold_tcold_mask));
>> + omap_temp_sensor_writel(temp_sensor, temp,
>> + temp_sensor->registers->bgap_threshold);
>> +
>> + tshut_hot = temp_to_adc_conversion(TSHUT_THRESHOLD_HOT);
>> + tshut_cold = temp_to_adc_conversion(TSHUT_THRESHOLD_COLD);
>> +
>> + /* Configure the TSHUT thresholds */
>> + temp = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->thsut_threshold);
>> + temp |= (tshut_hot << __ffs(temp_sensor->registers->tshut_hot_mask))
>> + | (tshut_cold << __ffs(temp_sensor->registers->tshut_hot_mask));
>> + omap_temp_sensor_writel(temp_sensor, temp,
>> + temp_sensor->registers->thsut_threshold);
>> +}
>> +
>> +static void omap_configure_temp_sensor_counter(struct omap_temp_sensor
>> + *temp_sensor, u32 counter)
>> +{
>> + u32 val;
>> +
>> + val = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_counter);
>> + val &= ~(temp_sensor->registers->counter_mask);
>> + val |= (counter << __ffs(temp_sensor->registers->counter_mask));
>> + omap_temp_sensor_writel(temp_sensor, val,
>> + temp_sensor->registers->bgap_counter);
>> +}
>> +
>> +static void omap_enable_continuous_mode(struct omap_temp_sensor *temp_sensor,
>> + bool enable)
>> +{
>> + u32 val;
>> +
>> + val = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_mode_ctrl);
>> +
>> + if (enable)
>> + val |= (1 << __ffs(temp_sensor->registers->mode_ctrl_mask));
>> + else
>> + val &= ~(temp_sensor->registers->mode_ctrl_mask);
>> +
>> + omap_temp_sensor_writel(temp_sensor, val,
>> + temp_sensor->registers->bgap_mode_ctrl);
>> +}
>> +
>> +/* Sysfs hook functions */
>> +
>> +static ssize_t show_temp_max(struct device *dev,
>> + struct device_attribute *devattr, char *buf)
>> +{
>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>> + int temp;
>> +
>> + mutex_lock(&temp_sensor->sensor_mutex);
>> +
>> + temp = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_threshold);
>> + temp = (temp & temp_sensor->registers->threshold_thot_mask)
>> + >> __ffs(temp_sensor->registers->threshold_thot_mask);
>> +
>> + if (temp < OMAP_ADC_START_VALUE || temp > OMAP_ADC_END_VALUE) {
>> + dev_err(dev, "invalid value\n");
>> + mutex_unlock(&temp_sensor->sensor_mutex);
>> + return -EINVAL;
>> + }
>> +
>> + temp = adc_to_temp_conversion(temp);
>> + mutex_unlock(&temp_sensor->sensor_mutex);
>> +
>> + return snprintf(buf, 16, "%d\n", temp);
>> +}
>> +
>> +static ssize_t set_temp_max(struct device *dev,
>> + struct device_attribute *devattr,
>> + const char *buf, size_t count)
>> +{
>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>> + long val;
>> + u32 reg_val, t_cold, t_hot, temp;
>> +
>> + mutex_lock(&temp_sensor->sensor_mutex);
>> +
>> + if (strict_strtol(buf, 10, &val)) {
>> + count = -EINVAL;
>> + goto out;
>> + }
>> +
>> + t_hot = temp_to_adc_conversion(val);
>> + if ((t_hot < OMAP_ADC_START_VALUE || t_hot > OMAP_ADC_END_VALUE)) {
>> + dev_err(dev, "invalid range\n");
>> + count = -EINVAL;
>> + goto out;
>> + }
>> +
>> + /* obtain the T cold value */
>> + t_cold = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_threshold);
>> + t_cold = (t_cold & temp_sensor->registers->threshold_tcold_mask) >>
>> + __ffs(temp_sensor->registers->threshold_tcold_mask);
>> +
>> + if (t_hot < t_cold) {
>> + dev_err(dev, "Error! T_HOT value lesser than T_COLD\n");
>> + count = -EINVAL;
>> + goto out;
>> + }
>> +
>> + /* write the new t_hot value */
>> + reg_val = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_threshold);
>> + reg_val &= ~(temp_sensor->registers->threshold_thot_mask);
>> + reg_val |= (t_hot <<
>> + __ffs(temp_sensor->registers->threshold_thot_mask));
>> + omap_temp_sensor_writel(temp_sensor, reg_val,
>> + temp_sensor->registers->bgap_threshold);
>> +
>> + /* Read the current temperature */
>> + temp = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->temp_sensor_ctrl);
>> + temp &= (temp_sensor->registers->bgap_dtemp_mask);
>> +
>> + /*
>> + * If user sets the HIGH threshold(t_hot) greater than the current
>> + * temperature(temp) unmask the HOT interrupts
>> + */
>> + if (t_hot > temp) {
>> + reg_val = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_mask_ctrl);
>> + reg_val &= ~(temp_sensor->registers->mask_cold_mask);
>> + reg_val |= temp_sensor->registers->mask_hot_mask;
>> + omap_temp_sensor_writel(temp_sensor, reg_val,
>> + temp_sensor->registers->bgap_mask_ctrl);
>> + }
>> +
>> + /*
>> + * If current temperature is in-between the hot and cold thresholds,
>> + * Enable both masks.
>> + */
>> + if (temp > t_cold && temp < t_hot) {
>> + reg_val = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_mask_ctrl);
>> + reg_val |= temp_sensor->registers->mask_cold_mask;
>> + reg_val |= temp_sensor->registers->mask_hot_mask;
>> + omap_temp_sensor_writel(temp_sensor, reg_val,
>> + OMAP4460_BGAP_CTRL_OFFSET);
>> + }
>> + /*
>> + * else no need to do anything since HW will immediately compare
>> + * the new threshold and generate interrupt accordingly
>> + */
>> +out:
>> + mutex_unlock(&temp_sensor->sensor_mutex);
>> + return count;
>> +}
>> +
>> +static ssize_t show_temp_max_hyst(struct device *dev,
>> + struct device_attribute *devattr, char *buf)
>> +{
>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>> + u32 temp;
>> +
>> + mutex_lock(&temp_sensor->sensor_mutex);
>> +
>> + temp = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_threshold);
>> + temp = (temp & temp_sensor->registers->threshold_tcold_mask) >>
>> + __ffs(temp_sensor->registers->threshold_tcold_mask);
>> +
>> + if (temp < OMAP_ADC_START_VALUE || temp > OMAP_ADC_END_VALUE) {
>> + dev_err(dev, "invalid value\n");
>> + mutex_unlock(&temp_sensor->sensor_mutex);
>> + return -EINVAL;
>> + }
>> +
>> + temp = adc_to_temp_conversion(temp);
>> + mutex_unlock(&temp_sensor->sensor_mutex);
>> +
>> + return snprintf(buf, 16, "%d\n", temp);
>> +}
>> +
>> +static ssize_t set_temp_max_hyst(struct device *dev,
>> + struct device_attribute *devattr,
>> + const char *buf, size_t count)
>> +{
>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>> + u32 reg_val, t_hot, t_cold, temp;
>> + long val;
>> +
>> + mutex_lock(&temp_sensor->sensor_mutex);
>> +
>> + if (strict_strtol(buf, 10, &val)) {
>> + count = -EINVAL;
>> + goto out;
>> + }
>> +
>> + t_cold = temp_to_adc_conversion(val);
>> + if (t_cold < OMAP_ADC_START_VALUE || t_cold > OMAP_ADC_END_VALUE) {
>> + dev_err(dev, "invalid range");
>> + count = -EINVAL;
>> + goto out;
>> + }
>> +
>> + /* obtain the T HOT value */
>> + t_hot = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_threshold);
>> + t_hot = (t_hot & temp_sensor->registers->threshold_thot_mask) >>
>> + __ffs(temp_sensor->registers->threshold_thot_mask);
>> +
>> + if (t_cold > t_hot) {
>> + dev_err(dev, "Error! T_COLD value greater than T_HOT\n");
>> + count = -EINVAL;
>> + goto out;
>> + }
>> +
>> + /* write the new t_cold value */
>> + reg_val = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_threshold);
>> + reg_val &= ~(temp_sensor->registers->threshold_tcold_mask);
>> + reg_val |= (t_cold <<
>> + __ffs(temp_sensor->registers->threshold_tcold_mask));
>> + omap_temp_sensor_writel(temp_sensor, reg_val,
>> + temp_sensor->registers->bgap_threshold);
>> +
>> + /* Read the current temperature */
>> + temp = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->temp_sensor_ctrl);
>> + temp &= (temp_sensor->registers->bgap_dtemp_mask);
>> +
>> + /*
>> + * If user sets the LOW threshold(t_cold) lower than the current
>> + * temperature(temp) unmask the COLD interrupts
>> + */
>> + if (t_cold < temp) {
>> + reg_val = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_mask_ctrl);
>> + reg_val &= ~(temp_sensor->registers->mask_hot_mask);
>> + reg_val |= temp_sensor->registers->mask_cold_mask;
>> + omap_temp_sensor_writel(temp_sensor, reg_val,
>> + temp_sensor->registers->bgap_mask_ctrl);
>> + }
>> +
>> + /*
>> + * If current temperature is in-between the hot and cold thresholds,
>> + * Enable both masks.
>> + */
>> + if (temp < t_hot && temp > t_cold) {
>> + reg_val = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_mask_ctrl);
>> + reg_val |= temp_sensor->registers->mask_cold_mask;
>> + reg_val |= temp_sensor->registers->mask_hot_mask;
>> + omap_temp_sensor_writel(temp_sensor, reg_val,
>> + temp_sensor->registers->bgap_mask_ctrl);
>> + }
>> +
>> + /*
>> + * else no need to do anything since HW will immediately compare
>> + * the new threshold and generate interrupt accordingly
>> + */
>> +
>> +out:
>> + mutex_unlock(&temp_sensor->sensor_mutex);
>> + return count;
>> +}
>> +
>> +static ssize_t show_update_rate(struct device *dev,
>> + struct device_attribute *devattr, char *buf)
>> +{
>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>> + u32 temp = 0, ret = 0;
>> +
>> + mutex_lock(&temp_sensor->sensor_mutex);
>> +
>> + if (!temp_sensor->clk_rate) {
>> + dev_err(dev, "clk_rate is NULL\n");
>> + ret = -EINVAL;
>> + goto out;
>> + }
>> +
>> + temp = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_counter);
>> + temp = (temp & temp_sensor->registers->counter_mask) >>
>> + __ffs(temp_sensor->registers->counter_mask);
>> + temp = temp * 1000 / (temp_sensor->clk_rate);
>> +
>> +out:
>> + mutex_unlock(&temp_sensor->sensor_mutex);
>> + if (!ret)
>> + return sprintf(buf, "%d\n", temp);
>> +
>> + return ret;
>> +}
>> +
>> +static ssize_t set_update_rate(struct device *dev,
>> + struct device_attribute *devattr,
>> + const char *buf, size_t count)
>> +{
>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>> + u32 reg_val;
>> + long val;
>> +
>> + mutex_lock(&temp_sensor->sensor_mutex);
>> +
>> + if (strict_strtol(buf, 10, &val)) {
>> + count = -EINVAL;
>> + goto out;
>> + }
>> +
>> + val *= temp_sensor->clk_rate / 1000;
>> + reg_val = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_counter);
>> +
>> + reg_val &= ~(temp_sensor->registers->counter_mask);
>> + reg_val |= val;
>> + omap_temp_sensor_writel(temp_sensor, reg_val,
>> + temp_sensor->registers->bgap_counter);
>> +
>> +out:
>> + mutex_unlock(&temp_sensor->sensor_mutex);
>> + return count;
>> +}
>> +
>> +static int omap_temp_sensor_read_temp(struct device *dev,
>> + struct device_attribute *devattr,
>> + char *buf)
>> +{
>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>> + int temp, ret = 0;
>> +
>> + mutex_lock(&temp_sensor->sensor_mutex);
>> +
>> + temp = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->temp_sensor_ctrl);
>> + temp &= temp_sensor->registers->bgap_dtemp_mask;
>> +
>> + if (!temp_sensor->is_efuse_valid)
>> + dev_err(dev, "Invalid EFUSE, Non-trimmed BGAP, Temp not accurate\n");
>> +
>> + /* look up for temperature in the table and return the temperature */
>> + if (temp < OMAP_ADC_START_VALUE || temp > OMAP_ADC_END_VALUE) {
>> + dev_err(dev, "invalid adc code reported %d", temp);
>> + ret = -EINVAL;
>> + goto out;
>> + }
>> +
>> + temp = adc_to_temp[temp - OMAP_ADC_START_VALUE];
>> +
>> +out:
>> + mutex_unlock(&temp_sensor->sensor_mutex);
>> + if (!ret)
>> + return sprintf(buf, "%d\n", temp);
>> +
>> + return ret;
>> +}
>> +
>> +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, omap_temp_sensor_read_temp,
>> + NULL, 0);
>> +static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp_max,
>> + set_temp_max, 0);
>> +static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO, show_temp_max_hyst,
>> + set_temp_max_hyst, 0);
>> +static SENSOR_DEVICE_ATTR(update_rate, S_IWUSR | S_IRUGO, show_update_rate,
>> + set_update_rate, 0);
>> +
>> +static struct attribute *omap_temp_sensor_attributes[] = {
>> + &sensor_dev_attr_temp1_input.dev_attr.attr,
>> + &sensor_dev_attr_temp1_max.dev_attr.attr,
>> + &sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
>> + &sensor_dev_attr_update_rate.dev_attr.attr,
>> + NULL
>> +};
>> +
>> +static const struct attribute_group omap_temp_sensor_group = {
>> + .attrs = omap_temp_sensor_attributes,
>> +};
>> +
>> +static int omap_temp_sensor_clk_enable(struct omap_temp_sensor *temp_sensor)
>> +{
>> + u32 ret = 0;
>> +
>> + if (temp_sensor->clk_on) {
>> + dev_err(temp_sensor->hwmon_dev, "clock already on\n");
>> + goto out;
>> + }
>> +
>> + ret = pm_runtime_get_sync(temp_sensor->hwmon_dev);
>> + if (ret < 0) {
>> + dev_err(temp_sensor->hwmon_dev, "get sync failed\n");
>> + goto out;
>> + }
>> +
>> + temp_sensor->clk_on = 1;
>> +
>> +out:
>> + return ret;
>> +}
>> +
>> +static int omap_temp_sensor_clk_disable(struct omap_temp_sensor *temp_sensor)
>> +{
>> + u32 ret = 0;
>> +
>> + if (!temp_sensor->clk_on) {
>> + dev_err(temp_sensor->hwmon_dev, "clock already off\n");
>> + goto out;
>> + }
>> +
>> + /* Gate the clock */
>> + ret = pm_runtime_put_sync(temp_sensor->hwmon_dev);
>> + if (ret < 0) {
>> + dev_err(temp_sensor->hwmon_dev, "put sync failed\n");
>> + goto out;
>> + }
>> + temp_sensor->clk_on = 0;
>> +
>> +out:
>> + return ret;
>> +}
>> +
>> +static irqreturn_t omap_talert_irq_handler(int irq, void *data)
>> +{
>> + struct omap_temp_sensor *temp_sensor;
>> + int t_hot, t_cold, temp;
>> +
>> + temp_sensor = data;
>> + mutex_lock(&temp_sensor->sensor_mutex);
>> +
>> + /* Read the status of t_hot */
>> + t_hot = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_status)
>> + & temp_sensor->registers->status_hot_mask;
>> +
>> + /* Read the status of t_cold */
>> + t_cold = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_status)
>> + & temp_sensor->registers->status_cold_mask;
>> +
>> + temp = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_mask_ctrl);
>> + /*
>> + * One TALERT interrupt: Two sources
>> + * If the interrupt is due to t_hot then mask t_hot and
>> + * and unmask t_cold else mask t_cold and unmask t_hot
>> + */
>> + if (t_hot) {
>> + temp &= ~(temp_sensor->registers->mask_hot_mask);
>> + temp |= temp_sensor->registers->mask_cold_mask;
>> + } else if (t_cold) {
>> + temp &= ~(temp_sensor->registers->mask_cold_mask);
>> + temp |= temp_sensor->registers->mask_hot_mask;
>> + }
>> +
>> + omap_temp_sensor_writel(temp_sensor, temp,
>> + temp_sensor->registers->bgap_mask_ctrl);
>> +
>> + /* kobject_uvent to user space telling thermal threshold crossed */
>> + kobject_uevent(&temp_sensor->hwmon_dev->kobj, KOBJ_CHANGE);
>> +
>> + mutex_unlock(&temp_sensor->sensor_mutex);
>> +
>> + return IRQ_HANDLED;
>> +}
>> +
>> +static int __devinit omap_temp_sensor_probe(struct platform_device *pdev)
>> +{
>> + struct omap_temp_sensor_pdata *pdata = pdev->dev.platform_data;
>> + struct omap_temp_sensor *temp_sensor;
>> + struct resource *mem;
>> + int ret = 0;
>> + int val, clk_rate;
>> +
>> + if (!pdata) {
>> + dev_err(&pdev->dev, "platform data missing\n");
>> + return -EINVAL;
>> + }
>> +
>> + temp_sensor = kzalloc(sizeof(*temp_sensor), GFP_KERNEL);
>> + if (!temp_sensor)
>> + return -ENOMEM;
>> +
>> + mutex_init(&temp_sensor->sensor_mutex);
>> +
>> + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> + if (!mem) {
>> + dev_err(&pdev->dev, "no mem resource\n");
>> + ret = -ENOMEM;
>> + goto plat_res_err;
>> + }
>> +
>> + temp_sensor->irq = platform_get_irq_byname(pdev, "thermal_alert");
>> + if (temp_sensor->irq < 0) {
>> + dev_err(&pdev->dev, "get_irq_byname failed\n");
>> + ret = temp_sensor->irq;
>> + goto plat_res_err;
>> + }
>> +
>> + temp_sensor->phy_base = ioremap(mem->start, resource_size(mem));
>> + temp_sensor->clock = NULL;
>> + temp_sensor->registers = pdata->registers;
>> + temp_sensor->hwmon_dev = &pdev->dev;
>> +
>> + pm_runtime_enable(&pdev->dev);
>> + pm_runtime_irq_safe(&pdev->dev);
>> +
>> + /*
>> + * check if the efuse has a non-zero value if not
>> + * it is an untrimmed sample and the temperatures
>> + * may not be accurate
>> + */
>> +
>> + if (omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_efuse))
>> + temp_sensor->is_efuse_valid = 1;
>> +
>> + platform_set_drvdata(pdev, temp_sensor);
>> + dev_set_drvdata(&pdev->dev, temp_sensor);
>> + temp_sensor->clock = clk_get(temp_sensor->hwmon_dev, "fck");
>> + if (IS_ERR(temp_sensor->clock)) {
>> + ret = PTR_ERR(temp_sensor->clock);
>> + dev_err(temp_sensor->hwmon_dev,
>> + "unable to get fclk: %d\n", ret);
>> + ret = -EINVAL;
>> + goto plat_res_err;
>> + }
>> +
>> + ret = omap_temp_sensor_clk_enable(temp_sensor);
>> + if (ret) {
>> + dev_err(&pdev->dev, "Cannot enable temp sensor\n");
>> + goto clken_err;
>> + }
>> +
>> + clk_rate = clk_round_rate(temp_sensor->clock, 2000000);
>> + if (clk_rate < 1000000 || clk_rate = 0xffffffff) {
>> + dev_err(&pdev->dev, "Error round rate\n");
>> + ret = -EINVAL;
>> + goto clken_err;
>> + }
>> +
>> + ret = clk_set_rate(temp_sensor->clock, clk_rate);
>> + if (ret) {
>> + dev_err(&pdev->dev, "Cannot set clock rate\n");
>> + goto clken_err;
>> + }
>> +
>> + temp_sensor->clk_rate = clk_rate;
>> + omap_enable_continuous_mode(temp_sensor, 1);
>> + omap_configure_temp_sensor_thresholds(temp_sensor);
>> + /* 1 ms */
>> + omap_configure_temp_sensor_counter(temp_sensor, 1);
>> +
>> + /* Wait till the first conversion is done wait for at least 1ms */
>> + usleep_range(1000, 2000);
>> +
>> + /* Read the temperature once due to hw issue*/
>> + omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->temp_sensor_ctrl);
>> +
>> + /* Set 2 seconds time as default counter */
>> + omap_configure_temp_sensor_counter(temp_sensor,
>> + temp_sensor->clk_rate * 2);
>> +
>> + temp_sensor->hwmon_dev = hwmon_device_register(&pdev->dev);
>> + if (IS_ERR(temp_sensor->hwmon_dev)) {
>> + dev_err(&pdev->dev, "hwmon_device_register failed.\n");
>> + ret = PTR_ERR(temp_sensor->hwmon_dev);
>> + goto hwmon_reg_err;
>> + }
>> +
>> + ret = sysfs_create_group(&pdev->dev.kobj,
>> + &omap_temp_sensor_group);
>> + if (ret) {
>> + dev_err(&pdev->dev, "could not create sysfs files\n");
>> + goto sysfs_create_err;
>> + }
>> +
>> + kobject_uevent(&temp_sensor->hwmon_dev->kobj, KOBJ_ADD);
>> +
>> + ret = request_threaded_irq(temp_sensor->irq, NULL,
>> + omap_talert_irq_handler, IRQF_TRIGGER_RISING | IRQF_ONESHOT,
>> + "temp_sensor", temp_sensor);
>> + if (ret) {
>> + dev_err(&pdev->dev, "Request threaded irq failed.\n");
>> + goto req_irq_err;
>> + }
>> +
>> + /* unmask the T_COLD and unmask T_HOT at init */
>> + val = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_mask_ctrl);
>> + val |= temp_sensor->registers->mask_cold_mask
>> + | temp_sensor->registers->mask_hot_mask;
>> +
>> + omap_temp_sensor_writel(temp_sensor, val,
>> + temp_sensor->registers->bgap_mask_ctrl);
>> +
>> + return 0;
>> +
>> +req_irq_err:
>> + kobject_uevent(&temp_sensor->hwmon_dev->kobj, KOBJ_REMOVE);
>> + sysfs_remove_group(&temp_sensor->hwmon_dev->kobj,
>> + &omap_temp_sensor_group);
>> +sysfs_create_err:
>> + hwmon_device_unregister(&pdev->dev);
>> +hwmon_reg_err:
>> + omap_temp_sensor_clk_disable(temp_sensor);
>> +clken_err:
>> + clk_put(temp_sensor->clock);
>> +plat_res_err:
>> + mutex_destroy(&temp_sensor->sensor_mutex);
>> + kfree(temp_sensor);
>> + return ret;
>> +}
>> +
>> +static int __devexit omap_temp_sensor_remove(struct platform_device *pdev)
>> +{
>> + struct omap_temp_sensor *temp_sensor = platform_get_drvdata(pdev);
>> +
>> + hwmon_device_unregister(&pdev->dev);
>> + kobject_uevent(&temp_sensor->hwmon_dev->kobj, KOBJ_REMOVE);
>> + sysfs_remove_group(&temp_sensor->hwmon_dev->kobj,
>> + &omap_temp_sensor_group);
>> + omap_temp_sensor_clk_disable(temp_sensor);
>> + clk_put(temp_sensor->clock);
>> + platform_set_drvdata(pdev, NULL);
>> + free_irq(temp_sensor->irq, temp_sensor);
>> + mutex_destroy(&temp_sensor->sensor_mutex);
>> + kfree(temp_sensor);
>> +
>> + return 0;
>> +}
>> +
>> +#ifdef CONFIG_PM
>> +static void omap_temp_sensor_save_ctxt(struct omap_temp_sensor *temp_sensor)
>> +{
>> + temp_sensor->temp_sensor_ctrl = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->temp_sensor_ctrl);
>> + temp_sensor->bg_ctrl = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_mask_ctrl);
>> + temp_sensor->bg_counter = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_counter);
>> + temp_sensor->bg_threshold = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_threshold);
>> + temp_sensor->temp_sensor_tshut_threshold >> + omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->thsut_threshold);
>> +}
>> +
>> +static void omap_temp_sensor_restore_ctxt(struct omap_temp_sensor *temp_sensor)
>> +{
>> + omap_temp_sensor_writel(temp_sensor,
>> + temp_sensor->temp_sensor_ctrl,
>> + temp_sensor->registers->temp_sensor_ctrl);
>> + omap_temp_sensor_writel(temp_sensor,
>> + temp_sensor->bg_ctrl,
>> + temp_sensor->registers->bgap_mask_ctrl);
>> + omap_temp_sensor_writel(temp_sensor,
>> + temp_sensor->bg_counter,
>> + temp_sensor->registers->bgap_counter);
>> + omap_temp_sensor_writel(temp_sensor,
>> + temp_sensor->bg_threshold,
>> + temp_sensor->registers->bgap_threshold);
>> + omap_temp_sensor_writel(temp_sensor,
>> + temp_sensor->temp_sensor_tshut_threshold,
>> + temp_sensor->registers->thsut_threshold);
>> +}
>> +
>> +static int omap_temp_sensor_suspend(struct device *dev)
>> +{
>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>> +
>> + omap_temp_sensor_save_ctxt(temp_sensor);
>> +
>> + return 0;
>> +}
>> +
>> +static int omap_temp_sensor_resume(struct device *dev)
>> +{
>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>> +
>> + omap_temp_sensor_restore_ctxt(temp_sensor);
>> +
>> + return 0;
>> +}
>> +
>> +static int omap_temp_sensor_runtime_suspend(struct device *dev)
>> +{
>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>> +
>> + omap_temp_sensor_save_ctxt(temp_sensor);
>> +
>> + return 0;
>> +}
>> +
>> +static int omap_temp_sensor_runtime_resume(struct device *dev)
>> +{
>> + static int context_loss_count;
>> + int temp;
>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>> +
>> + temp = omap_device_get_context_loss_count(to_platform_device(dev));
>> +
>> + if (temp != context_loss_count && context_loss_count != 0)
>> + omap_temp_sensor_restore_ctxt(temp_sensor);
>> +
>> + context_loss_count = temp;
>> +
>> + return 0;
>> +}
>> +
>> +static const struct dev_pm_ops omap_temp_sensor_dev_pm_ops = {
>> + .suspend = omap_temp_sensor_suspend,
>> + .resume = omap_temp_sensor_resume,
>> + .runtime_suspend = omap_temp_sensor_runtime_suspend,
>> + .runtime_resume = omap_temp_sensor_runtime_resume,
>> +};
>> +
>> +#endif
>> +
>> +static struct platform_driver omap_temp_sensor_driver = {
>> + .probe = omap_temp_sensor_probe,
>> + .remove = omap_temp_sensor_remove,
>> + .driver = {
>> + .name = "omap_temp_sensor",
>> +#ifdef CONFIG_PM
>> + .pm = &omap_temp_sensor_dev_pm_ops,
>> +#endif
>> + },
>> +};
>> +
>> +int __init omap_temp_sensor_init(void)
>> +{
>> + return platform_driver_register(&omap_temp_sensor_driver);
>> +}
>> +module_init(omap_temp_sensor_init);
>> +
>> +static void __exit omap_temp_sensor_exit(void)
>> +{
>> + platform_driver_unregister(&omap_temp_sensor_driver);
>> +}
>> +module_exit(omap_temp_sensor_exit);
>> +
>> +MODULE_DESCRIPTION("OMAP446X temperature sensor Driver");
>> +MODULE_LICENSE("GPL");
>> +MODULE_ALIAS("platform:" DRIVER_NAME);
>> +MODULE_AUTHOR("J Keerthy <j-keerthy@ti.com>");
>> --
>> 1.7.0.4
>>
>
--
Regards and Thanks,
Keerthy
_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors
^ permalink raw reply [flat|nested] 35+ messages in thread
* Re: [RFC PATCH 6/6 V2] hwmon: OMAP4: On die temperature sensor driver
2011-08-19 6:16 ` [lm-sensors] [RFC PATCH 6/6 V2] hwmon: OMAP4: On die J, KEERTHY
@ 2011-08-19 6:17 ` Guenter Roeck
-1 siblings, 0 replies; 35+ messages in thread
From: Guenter Roeck @ 2011-08-19 6:17 UTC (permalink / raw)
To: J, KEERTHY; +Cc: linux-omap, Jean Delvare, lm-sensors
On Fri, Aug 19, 2011 at 02:04:58AM -0400, J, KEERTHY wrote:
> On Fri, Aug 19, 2011 at 7:43 AM, Guenter Roeck
> <guenter.roeck@ericsson.com> wrote:
> > On Thu, Aug 18, 2011 at 06:52:15AM -0400, Keerthy wrote:
> >> On chip temperature sensor driver. The driver monitors the temperature of
> >> the MPU subsystem of the OMAP4. It sends notifications to the user space if
> >> the temperature crosses user defined thresholds via kobject_uevent interface.
> >> The user is allowed to configure the temperature thresholds vis sysfs nodes
> >> exposed using hwmon interface.
> >>
> >> Signed-off-by: Keerthy <j-keerthy@ti.com>
> >> Cc: Jean Delvare <khali@linux-fr.org>
> >> Cc: Guenter Roeck <guenter.roeck@ericsson.com>
> >> Cc: lm-sensors@lm-sensors.org
> >
> > High level review:
> >
> > - too much and too broad mutex locking. show functions should not need locks at all,
> > set functions only while data is written into registers and into platform data.
>
> Ok. I will clean this.
>
> > - driver is quite noisy. There should definitely not be any log messages
> > if a set parameter is wrong. Show functions already return an error value
> > to the user; a log message indicating the error again just creates noise.
> > For one boolean set during probe (is_efuse_valid), each subsequent show results
> > in a log message if it is false. Some errors result in multiple log messages.
>
> A user tries to set an invalid temperature threshold. The user should
> be notified about this. The invalid temperature will not be set. The user
> should not be allowed to set an invalid temperature. It is to inform
> the user about precisely the problem with the parameter.
>
User is notified with -EINVAL. Unless on the console, which is unlikely,
the user will likely not notice a message in the kernel log.
> In some of the samples the bandgap is not trimmed and hence
> temperature reported will be wrong. So every time a user tries to read
> he is alerted that the temperatures are not accurate.
>
In the kernel log ? Sorry, that doesn't make sense. You alert the system administrator,
not the user.
Guenter
^ permalink raw reply [flat|nested] 35+ messages in thread
* Re: [lm-sensors] [RFC PATCH 6/6 V2] hwmon: OMAP4: On die
@ 2011-08-19 6:17 ` Guenter Roeck
0 siblings, 0 replies; 35+ messages in thread
From: Guenter Roeck @ 2011-08-19 6:17 UTC (permalink / raw)
To: J, KEERTHY; +Cc: linux-omap, Jean Delvare, lm-sensors
On Fri, Aug 19, 2011 at 02:04:58AM -0400, J, KEERTHY wrote:
> On Fri, Aug 19, 2011 at 7:43 AM, Guenter Roeck
> <guenter.roeck@ericsson.com> wrote:
> > On Thu, Aug 18, 2011 at 06:52:15AM -0400, Keerthy wrote:
> >> On chip temperature sensor driver. The driver monitors the temperature of
> >> the MPU subsystem of the OMAP4. It sends notifications to the user space if
> >> the temperature crosses user defined thresholds via kobject_uevent interface.
> >> The user is allowed to configure the temperature thresholds vis sysfs nodes
> >> exposed using hwmon interface.
> >>
> >> Signed-off-by: Keerthy <j-keerthy@ti.com>
> >> Cc: Jean Delvare <khali@linux-fr.org>
> >> Cc: Guenter Roeck <guenter.roeck@ericsson.com>
> >> Cc: lm-sensors@lm-sensors.org
> >
> > High level review:
> >
> > - too much and too broad mutex locking. show functions should not need locks at all,
> > set functions only while data is written into registers and into platform data.
>
> Ok. I will clean this.
>
> > - driver is quite noisy. There should definitely not be any log messages
> > if a set parameter is wrong. Show functions already return an error value
> > to the user; a log message indicating the error again just creates noise.
> > For one boolean set during probe (is_efuse_valid), each subsequent show results
> > in a log message if it is false. Some errors result in multiple log messages.
>
> A user tries to set an invalid temperature threshold. The user should
> be notified about this. The invalid temperature will not be set. The user
> should not be allowed to set an invalid temperature. It is to inform
> the user about precisely the problem with the parameter.
>
User is notified with -EINVAL. Unless on the console, which is unlikely,
the user will likely not notice a message in the kernel log.
> In some of the samples the bandgap is not trimmed and hence
> temperature reported will be wrong. So every time a user tries to read
> he is alerted that the temperatures are not accurate.
>
In the kernel log ? Sorry, that doesn't make sense. You alert the system administrator,
not the user.
Guenter
_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors
^ permalink raw reply [flat|nested] 35+ messages in thread
* Re: [RFC PATCH 6/6 V2] hwmon: OMAP4: On die temperature sensor driver
2011-08-19 6:16 ` [lm-sensors] [RFC PATCH 6/6 V2] hwmon: OMAP4: On die J, KEERTHY
@ 2011-08-19 9:16 ` J, KEERTHY
-1 siblings, 0 replies; 35+ messages in thread
From: J, KEERTHY @ 2011-08-19 9:04 UTC (permalink / raw)
To: Guenter Roeck; +Cc: linux-omap, Jean Delvare, lm-sensors
On Fri, Aug 19, 2011 at 11:34 AM, J, KEERTHY <j-keerthy@ti.com> wrote:
> On Fri, Aug 19, 2011 at 7:43 AM, Guenter Roeck
> <guenter.roeck@ericsson.com> wrote:
>> On Thu, Aug 18, 2011 at 06:52:15AM -0400, Keerthy wrote:
>>> On chip temperature sensor driver. The driver monitors the temperature of
>>> the MPU subsystem of the OMAP4. It sends notifications to the user space if
>>> the temperature crosses user defined thresholds via kobject_uevent interface.
>>> The user is allowed to configure the temperature thresholds vis sysfs nodes
>>> exposed using hwmon interface.
>>>
>>> Signed-off-by: Keerthy <j-keerthy@ti.com>
>>> Cc: Jean Delvare <khali@linux-fr.org>
>>> Cc: Guenter Roeck <guenter.roeck@ericsson.com>
>>> Cc: lm-sensors@lm-sensors.org
>>
>> High level review:
>>
>> - too much and too broad mutex locking. show functions should not need locks at all,
>> set functions only while data is written into registers and into platform data.
>
> Ok. I will clean this.
>
>> - driver is quite noisy. There should definitely not be any log messages
>> if a set parameter is wrong. Show functions already return an error value
>> to the user; a log message indicating the error again just creates noise.
>> For one boolean set during probe (is_efuse_valid), each subsequent show results
>> in a log message if it is false. Some errors result in multiple log messages.
>
> A user tries to set an invalid temperature threshold. The user should
> be notified about this. The invalid temperature will not be set. The user
> should not be allowed to set an invalid temperature. It is to inform
> the user about precisely the problem with the parameter.
>
> In some of the samples the bandgap is not trimmed and hence
> temperature reported will be wrong. So every time a user tries to read
> he is alerted that the temperatures are not accurate.
>
>> - Wrong use of EINVAL throughout the driver (EINVAL is "Invalid Argument")
>
> Ok. I will correct this.
When the temperature is out of range can i return -EDOM?
Any suggestions?
>
>> - excessive ( )
>
> Ok.
>
>> - linear search through a sorted array is very expensive. Consider using a binary search.
>
> Yes. I will implement binary search.
>
>> - temp_to_adc_conversion return code (error if negative) is not checked properly.
>
> Ok. I will add checks.
>
>>
>> I am sure there are other problems, but those are difficult to find with all the noise
>> above.
>>
>> Guenter
>>
>>> ---
>>> Documentation/hwmon/omap_temp_sensor | 27 +
>>> drivers/hwmon/Kconfig | 11 +
>>> drivers/hwmon/Makefile | 1 +
>>> drivers/hwmon/omap_temp_sensor.c | 918 ++++++++++++++++++++++++++++++++++
>>> 4 files changed, 957 insertions(+), 0 deletions(-)
>>> create mode 100644 Documentation/hwmon/omap_temp_sensor
>>> create mode 100644 drivers/hwmon/omap_temp_sensor.c
>>>
>>> diff --git a/Documentation/hwmon/omap_temp_sensor b/Documentation/hwmon/omap_temp_sensor
>>> new file mode 100644
>>> index 0000000..e01a6d6
>>> --- /dev/null
>>> +++ b/Documentation/hwmon/omap_temp_sensor
>>> @@ -0,0 +1,27 @@
>>> +Kernel driver omap_temp_sensor
>>> +==============================
>>> +
>>> +Supported chips:
>>> + * Texas Instruments OMAP4460
>>> + Prefix: 'omap_temp_sensor'
>>> + Datasheet: http://focus.ti.com/docs/prod/folders/print/tmp102.html
>>> +
>>> +Author:
>>> + J Keerthy <j-keerthy@ti.com>
>>> +
>>> +Description
>>> +-----------
>>> +
>>> +The Texas Instruments OMAP4 family of chips have a bandgap temperature sensor.
>>> +The temperature sensor feature is used to convert the temperature of the device
>>> +into a decimal value coded on 10 bits. An internal ADC is used for conversion.
>>> +The recommended operating temperatures must be in the range -40 degree Celsius
>>> +to 123 degree celsius for standard conversion.
>>> +The thresholds are programmable and upon crossing the thresholds an interrupt
>>> +is generated. The OMAP temperature sensor has a programmable update rate in
>>> +milli seconds.
>>> +(Currently the driver programs a default of 2000 milli seconds).
>>> +
>>> +The driver provides the common sysfs-interface for temperatures (see
>>> +Documentation/hwmon/sysfs-interface under Temperatures).
>>> +
>>> diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
>>> index 5f888f7..9c9cd8b 100644
>>> --- a/drivers/hwmon/Kconfig
>>> +++ b/drivers/hwmon/Kconfig
>>> @@ -323,6 +323,17 @@ config SENSORS_F71805F
>>> This driver can also be built as a module. If so, the module
>>> will be called f71805f.
>>>
>>> +config SENSORS_OMAP_BANDGAP_TEMP_SENSOR
>>> + bool "OMAP on-die temperature sensor hwmon driver"
>>> + depends on HWMON && ARCH_OMAP && OMAP_TEMP_SENSOR
>>> + help
>>> + If you say yes here you get support for hardware
>>> + monitoring features of the OMAP on die temperature
>>> + sensor.
>>> +
>>> + Continuous conversion programmable delay
>>> + mode is used for temperature conversion.
>>> +
>>> config SENSORS_F71882FG
>>> tristate "Fintek F71882FG and compatibles"
>>> help
>>> diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
>>> index 28061cf..d0f89f5 100644
>>> --- a/drivers/hwmon/Makefile
>>> +++ b/drivers/hwmon/Makefile
>>> @@ -91,6 +91,7 @@ obj-$(CONFIG_SENSORS_MAX6639) += max6639.o
>>> obj-$(CONFIG_SENSORS_MAX6642) += max6642.o
>>> obj-$(CONFIG_SENSORS_MAX6650) += max6650.o
>>> obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o
>>> +obj-$(CONFIG_SENSORS_OMAP_BANDGAP_TEMP_SENSOR) += omap_temp_sensor.o
>>> obj-$(CONFIG_SENSORS_PC87360) += pc87360.o
>>> obj-$(CONFIG_SENSORS_PC87427) += pc87427.o
>>> obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o
>>> diff --git a/drivers/hwmon/omap_temp_sensor.c b/drivers/hwmon/omap_temp_sensor.c
>>> new file mode 100644
>>> index 0000000..586a361
>>> --- /dev/null
>>> +++ b/drivers/hwmon/omap_temp_sensor.c
>>> @@ -0,0 +1,918 @@
>>> +/*
>>> + * OMAP4 Temperature sensor driver file
>>> + *
>>> + * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
>>> + * Author: J Keerthy <j-keerthy@ti.com>
>>> + * Author: Moiz Sonasath <m-sonasath@ti.com>
>>> + *
>>> + * This program is free software; you can redistribute it and/or
>>> + * modify it under the terms of the GNU General Public License
>>> + * 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.
>>> + *
>>> + * You should have received a copy of the GNU General Public License
>>> + * along with this program; if not, write to the Free Software
>>> + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
>>> + * 02110-1301 USA
>>> + *
>>> + */
>>> +
>>> +#include <linux/interrupt.h>
>>> +#include <linux/clk.h>
>>> +#include <linux/io.h>
>>> +#include <linux/slab.h>
>>> +#include <linux/init.h>
>>> +#include <plat/omap_device.h>
>>> +#include <linux/kernel.h>
>>> +#include <linux/device.h>
>>> +#include <linux/jiffies.h>
>>> +#include <linux/hwmon.h>
>>> +#include <linux/hwmon-sysfs.h>
>>> +#include <linux/stddef.h>
>>> +#include <linux/sysfs.h>
>>> +#include <linux/err.h>
>>> +#include <linux/types.h>
>>> +#include <linux/mutex.h>
>>> +#include <linux/pm_runtime.h>
>>> +#include <linux/delay.h>
>>> +#include <plat/temperature_sensor.h>
>>> +
>>> +#define TSHUT_THRESHOLD_HOT 122000 /* 122 deg C */
>>> +#define TSHUT_THRESHOLD_COLD 100000 /* 100 deg C */
>>> +#define BGAP_THRESHOLD_T_HOT 73000 /* 73 deg C */
>>> +#define BGAP_THRESHOLD_T_COLD 71000 /* 71 deg C */
>>> +#define OMAP_ADC_START_VALUE 530
>>> +#define OMAP_ADC_END_VALUE 923
>>> +
>>> +/*
>>> + * omap_temp_sensor structure
>>> + * @hwmon_dev - device pointer
>>> + * @clock - Clock pointer
>>> + * @registers - Pointer to structure with register offsets and bitfields
>>> + * @sensor_mutex - Mutex for sysfs, irq and PM
>>> + * @irq - MPU Irq number for thermal alert
>>> + * @phy_base - Physical base of the temp I/O
>>> + * @clk_rate - Holds current clock rate
>>> + * @temp_sensor_ctrl - temp sensor control register value
>>> + * @bg_ctrl - bandgap ctrl register value
>>> + * @bg_counter - bandgap counter value
>>> + * @bg_threshold - bandgap threshold register value
>>> + * @temp_sensor_tshut_threshold - bandgap tshut register value
>>> + * @is_efuse_valid - Flag to determine if efuse is valid or not
>>> + * @clk_on - Manages the current clock state
>>> + */
>>> +struct omap_temp_sensor {
>>> + struct device *hwmon_dev;
>>> + struct clk *clock;
>>> + struct omap_temp_sensor_registers *registers;
>>> + struct mutex sensor_mutex; /* Mutex for sysfs, irq and PM */
>>> + unsigned int irq;
>>> + void __iomem *phy_base;
>>> + u32 clk_rate;
>>> + u32 temp_sensor_ctrl;
>>> + u32 bg_ctrl;
>>> + u32 bg_counter;
>>> + u32 bg_threshold;
>>> + u32 temp_sensor_tshut_threshold;
>>> + bool is_efuse_valid;
>>> + bool clk_on;
>>> +};
>>> +
>>> +/*
>>> + * Temperature values in milli degree celsius
>>> + * ADC code values from 530 to 923
>>> + */
>>> +static int adc_to_temp[] = {
>>> + -40000, -40000, -40000, -40000, -39800, -39400, -39000, -38600, -38200,
>>> + -37800, -37300, -36800, -36400, -36000, -35600, -35200, -34800,
>>> + -34300, -33800, -33400, -33000, -32600, -32200, -31800, -31300,
>>> + -30800, -30400, -30000, -29600, -29200, -28700, -28200, -27800,
>>> + -27400, -27000, -26600, -26200, -25700, -25200, -24800, -24400,
>>> + -24000, -23600, -23200, -22700, -22200, -21800, -21400, -21000,
>>> + -20600, -20200, -19700, -19200, -18800, -18400, -18000, -17600,
>>> + -17200, -16700, -16200, -15800, -15400, -15000, -14600, -14200,
>>> + -13700, -13200, -12800, -12400, -12000, -11600, -11200, -10700,
>>> + -10200, -9800, -9400, -9000, -8600, -8200, -7700, -7200, -6800,
>>> + -6400, -6000, -5600, -5200, -4800, -4300, -3800, -3400, -3000,
>>> + -2600, -2200, -1800, -1300, -800, -400, 0, 400, 800, 1200, 1600,
>>> + 2100, 2600, 3000, 3400, 3800, 4200, 4600, 5100, 5600, 6000, 6400,
>>> + 6800, 7200, 7600, 8000, 8500, 9000, 9400, 9800, 10200, 10600, 11000,
>>> + 11400, 11900, 12400, 12800, 13200, 13600, 14000, 14400, 14800,
>>> + 15300, 15800, 16200, 16600, 17000, 17400, 17800, 18200, 18700,
>>> + 19200, 19600, 20000, 20400, 20800, 21200, 21600, 22100, 22600,
>>> + 23000, 23400, 23800, 24200, 24600, 25000, 25400, 25900, 26400,
>>> + 26800, 27200, 27600, 28000, 28400, 28800, 29300, 29800, 30200,
>>> + 30600, 31000, 31400, 31800, 32200, 32600, 33100, 33600, 34000,
>>> + 34400, 34800, 35200, 35600, 36000, 36400, 36800, 37300, 37800,
>>> + 38200, 38600, 39000, 39400, 39800, 40200, 40600, 41100, 41600,
>>> + 42000, 42400, 42800, 43200, 43600, 44000, 44400, 44800, 45300,
>>> + 45800, 46200, 46600, 47000, 47400, 47800, 48200, 48600, 49000,
>>> + 49500, 50000, 50400, 50800, 51200, 51600, 52000, 52400, 52800,
>>> + 53200, 53700, 54200, 54600, 55000, 55400, 55800, 56200, 56600,
>>> + 57000, 57400, 57800, 58200, 58700, 59200, 59600, 60000, 60400,
>>> + 60800, 61200, 61600, 62000, 62400, 62800, 63300, 63800, 64200,
>>> + 64600, 65000, 65400, 65800, 66200, 66600, 67000, 67400, 67800,
>>> + 68200, 68700, 69200, 69600, 70000, 70400, 70800, 71200, 71600,
>>> + 72000, 72400, 72800, 73200, 73600, 74100, 74600, 75000, 75400,
>>> + 75800, 76200, 76600, 77000, 77400, 77800, 78200, 78600, 79000,
>>> + 79400, 79800, 80300, 80800, 81200, 81600, 82000, 82400, 82800,
>>> + 83200, 83600, 84000, 84400, 84800, 85200, 85600, 86000, 86400,
>>> + 86800, 87300, 87800, 88200, 88600, 89000, 89400, 89800, 90200,
>>> + 90600, 91000, 91400, 91800, 92200, 92600, 93000, 93400, 93800,
>>> + 94200, 94600, 95000, 95500, 96000, 96400, 96800, 97200, 97600,
>>> + 98000, 98400, 98800, 99200, 99600, 100000, 100400, 100800, 101200,
>>> + 101600, 102000, 102400, 102800, 103200, 103600, 104000, 104400,
>>> + 104800, 105200, 105600, 106100, 106600, 107000, 107400, 107800,
>>> + 108200, 108600, 109000, 109400, 109800, 110200, 110600, 111000,
>>> + 111400, 111800, 112200, 112600, 113000, 113400, 113800, 114200,
>>> + 114600, 115000, 115400, 115800, 116200, 116600, 117000, 117400,
>>> + 117800, 118200, 118600, 119000, 119400, 119800, 120200, 120600,
>>> + 121000, 121400, 121800, 122200, 122600, 123000
>>> +};
>>> +
>>> +static unsigned long omap_temp_sensor_readl(struct omap_temp_sensor
>>> + *temp_sensor, u32 reg)
>>> +{
>>> + return __raw_readl(temp_sensor->phy_base + reg);
>>> +}
>>> +
>>> +static void omap_temp_sensor_writel(struct omap_temp_sensor *temp_sensor,
>>> + u32 val, u32 reg)
>>> +{
>>> + __raw_writel(val, (temp_sensor->phy_base + reg));
>>> +}
>>> +
>>> +static int adc_to_temp_conversion(int adc_val)
>>> +{
>>> + return adc_to_temp[adc_val - OMAP_ADC_START_VALUE];
>>> +}
>>> +
>>> +static int temp_to_adc_conversion(long temp)
>>> +{
>>> + int i;
>>> +
>>> + for (i = 0; i <= OMAP_ADC_END_VALUE - OMAP_ADC_START_VALUE; i++)
>>> + if (temp < adc_to_temp[i])
>>> + return OMAP_ADC_START_VALUE + i - 1;
>>> +
>>> + return -EINVAL;
>>> +}
>>> +
>>> +static void omap_configure_temp_sensor_thresholds(struct omap_temp_sensor
>>> + *temp_sensor)
>>> +{
>>> + u32 temp, t_hot, t_cold, tshut_hot, tshut_cold;
>>> +
>>> + t_hot = temp_to_adc_conversion(BGAP_THRESHOLD_T_HOT);
>>> + t_cold = temp_to_adc_conversion(BGAP_THRESHOLD_T_COLD);
>>> +
>>> + /* Configure the TALERT thresholds */
>>> + temp = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_threshold);
>>> + temp |= (t_hot << __ffs(temp_sensor->registers->threshold_thot_mask)) |
>>> + (t_cold << __ffs(temp_sensor->registers->threshold_tcold_mask));
>>> + omap_temp_sensor_writel(temp_sensor, temp,
>>> + temp_sensor->registers->bgap_threshold);
>>> +
>>> + tshut_hot = temp_to_adc_conversion(TSHUT_THRESHOLD_HOT);
>>> + tshut_cold = temp_to_adc_conversion(TSHUT_THRESHOLD_COLD);
>>> +
>>> + /* Configure the TSHUT thresholds */
>>> + temp = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->thsut_threshold);
>>> + temp |= (tshut_hot << __ffs(temp_sensor->registers->tshut_hot_mask))
>>> + | (tshut_cold << __ffs(temp_sensor->registers->tshut_hot_mask));
>>> + omap_temp_sensor_writel(temp_sensor, temp,
>>> + temp_sensor->registers->thsut_threshold);
>>> +}
>>> +
>>> +static void omap_configure_temp_sensor_counter(struct omap_temp_sensor
>>> + *temp_sensor, u32 counter)
>>> +{
>>> + u32 val;
>>> +
>>> + val = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_counter);
>>> + val &= ~(temp_sensor->registers->counter_mask);
>>> + val |= (counter << __ffs(temp_sensor->registers->counter_mask));
>>> + omap_temp_sensor_writel(temp_sensor, val,
>>> + temp_sensor->registers->bgap_counter);
>>> +}
>>> +
>>> +static void omap_enable_continuous_mode(struct omap_temp_sensor *temp_sensor,
>>> + bool enable)
>>> +{
>>> + u32 val;
>>> +
>>> + val = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_mode_ctrl);
>>> +
>>> + if (enable)
>>> + val |= (1 << __ffs(temp_sensor->registers->mode_ctrl_mask));
>>> + else
>>> + val &= ~(temp_sensor->registers->mode_ctrl_mask);
>>> +
>>> + omap_temp_sensor_writel(temp_sensor, val,
>>> + temp_sensor->registers->bgap_mode_ctrl);
>>> +}
>>> +
>>> +/* Sysfs hook functions */
>>> +
>>> +static ssize_t show_temp_max(struct device *dev,
>>> + struct device_attribute *devattr, char *buf)
>>> +{
>>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>>> + int temp;
>>> +
>>> + mutex_lock(&temp_sensor->sensor_mutex);
>>> +
>>> + temp = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_threshold);
>>> + temp = (temp & temp_sensor->registers->threshold_thot_mask)
>>> + >> __ffs(temp_sensor->registers->threshold_thot_mask);
>>> +
>>> + if (temp < OMAP_ADC_START_VALUE || temp > OMAP_ADC_END_VALUE) {
>>> + dev_err(dev, "invalid value\n");
>>> + mutex_unlock(&temp_sensor->sensor_mutex);
>>> + return -EINVAL;
>>> + }
>>> +
>>> + temp = adc_to_temp_conversion(temp);
>>> + mutex_unlock(&temp_sensor->sensor_mutex);
>>> +
>>> + return snprintf(buf, 16, "%d\n", temp);
>>> +}
>>> +
>>> +static ssize_t set_temp_max(struct device *dev,
>>> + struct device_attribute *devattr,
>>> + const char *buf, size_t count)
>>> +{
>>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>>> + long val;
>>> + u32 reg_val, t_cold, t_hot, temp;
>>> +
>>> + mutex_lock(&temp_sensor->sensor_mutex);
>>> +
>>> + if (strict_strtol(buf, 10, &val)) {
>>> + count = -EINVAL;
>>> + goto out;
>>> + }
>>> +
>>> + t_hot = temp_to_adc_conversion(val);
>>> + if ((t_hot < OMAP_ADC_START_VALUE || t_hot > OMAP_ADC_END_VALUE)) {
>>> + dev_err(dev, "invalid range\n");
>>> + count = -EINVAL;
>>> + goto out;
>>> + }
>>> +
>>> + /* obtain the T cold value */
>>> + t_cold = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_threshold);
>>> + t_cold = (t_cold & temp_sensor->registers->threshold_tcold_mask) >>
>>> + __ffs(temp_sensor->registers->threshold_tcold_mask);
>>> +
>>> + if (t_hot < t_cold) {
>>> + dev_err(dev, "Error! T_HOT value lesser than T_COLD\n");
>>> + count = -EINVAL;
>>> + goto out;
>>> + }
>>> +
>>> + /* write the new t_hot value */
>>> + reg_val = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_threshold);
>>> + reg_val &= ~(temp_sensor->registers->threshold_thot_mask);
>>> + reg_val |= (t_hot <<
>>> + __ffs(temp_sensor->registers->threshold_thot_mask));
>>> + omap_temp_sensor_writel(temp_sensor, reg_val,
>>> + temp_sensor->registers->bgap_threshold);
>>> +
>>> + /* Read the current temperature */
>>> + temp = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->temp_sensor_ctrl);
>>> + temp &= (temp_sensor->registers->bgap_dtemp_mask);
>>> +
>>> + /*
>>> + * If user sets the HIGH threshold(t_hot) greater than the current
>>> + * temperature(temp) unmask the HOT interrupts
>>> + */
>>> + if (t_hot > temp) {
>>> + reg_val = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_mask_ctrl);
>>> + reg_val &= ~(temp_sensor->registers->mask_cold_mask);
>>> + reg_val |= temp_sensor->registers->mask_hot_mask;
>>> + omap_temp_sensor_writel(temp_sensor, reg_val,
>>> + temp_sensor->registers->bgap_mask_ctrl);
>>> + }
>>> +
>>> + /*
>>> + * If current temperature is in-between the hot and cold thresholds,
>>> + * Enable both masks.
>>> + */
>>> + if (temp > t_cold && temp < t_hot) {
>>> + reg_val = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_mask_ctrl);
>>> + reg_val |= temp_sensor->registers->mask_cold_mask;
>>> + reg_val |= temp_sensor->registers->mask_hot_mask;
>>> + omap_temp_sensor_writel(temp_sensor, reg_val,
>>> + OMAP4460_BGAP_CTRL_OFFSET);
>>> + }
>>> + /*
>>> + * else no need to do anything since HW will immediately compare
>>> + * the new threshold and generate interrupt accordingly
>>> + */
>>> +out:
>>> + mutex_unlock(&temp_sensor->sensor_mutex);
>>> + return count;
>>> +}
>>> +
>>> +static ssize_t show_temp_max_hyst(struct device *dev,
>>> + struct device_attribute *devattr, char *buf)
>>> +{
>>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>>> + u32 temp;
>>> +
>>> + mutex_lock(&temp_sensor->sensor_mutex);
>>> +
>>> + temp = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_threshold);
>>> + temp = (temp & temp_sensor->registers->threshold_tcold_mask) >>
>>> + __ffs(temp_sensor->registers->threshold_tcold_mask);
>>> +
>>> + if (temp < OMAP_ADC_START_VALUE || temp > OMAP_ADC_END_VALUE) {
>>> + dev_err(dev, "invalid value\n");
>>> + mutex_unlock(&temp_sensor->sensor_mutex);
>>> + return -EINVAL;
>>> + }
>>> +
>>> + temp = adc_to_temp_conversion(temp);
>>> + mutex_unlock(&temp_sensor->sensor_mutex);
>>> +
>>> + return snprintf(buf, 16, "%d\n", temp);
>>> +}
>>> +
>>> +static ssize_t set_temp_max_hyst(struct device *dev,
>>> + struct device_attribute *devattr,
>>> + const char *buf, size_t count)
>>> +{
>>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>>> + u32 reg_val, t_hot, t_cold, temp;
>>> + long val;
>>> +
>>> + mutex_lock(&temp_sensor->sensor_mutex);
>>> +
>>> + if (strict_strtol(buf, 10, &val)) {
>>> + count = -EINVAL;
>>> + goto out;
>>> + }
>>> +
>>> + t_cold = temp_to_adc_conversion(val);
>>> + if (t_cold < OMAP_ADC_START_VALUE || t_cold > OMAP_ADC_END_VALUE) {
>>> + dev_err(dev, "invalid range");
>>> + count = -EINVAL;
>>> + goto out;
>>> + }
>>> +
>>> + /* obtain the T HOT value */
>>> + t_hot = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_threshold);
>>> + t_hot = (t_hot & temp_sensor->registers->threshold_thot_mask) >>
>>> + __ffs(temp_sensor->registers->threshold_thot_mask);
>>> +
>>> + if (t_cold > t_hot) {
>>> + dev_err(dev, "Error! T_COLD value greater than T_HOT\n");
>>> + count = -EINVAL;
>>> + goto out;
>>> + }
>>> +
>>> + /* write the new t_cold value */
>>> + reg_val = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_threshold);
>>> + reg_val &= ~(temp_sensor->registers->threshold_tcold_mask);
>>> + reg_val |= (t_cold <<
>>> + __ffs(temp_sensor->registers->threshold_tcold_mask));
>>> + omap_temp_sensor_writel(temp_sensor, reg_val,
>>> + temp_sensor->registers->bgap_threshold);
>>> +
>>> + /* Read the current temperature */
>>> + temp = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->temp_sensor_ctrl);
>>> + temp &= (temp_sensor->registers->bgap_dtemp_mask);
>>> +
>>> + /*
>>> + * If user sets the LOW threshold(t_cold) lower than the current
>>> + * temperature(temp) unmask the COLD interrupts
>>> + */
>>> + if (t_cold < temp) {
>>> + reg_val = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_mask_ctrl);
>>> + reg_val &= ~(temp_sensor->registers->mask_hot_mask);
>>> + reg_val |= temp_sensor->registers->mask_cold_mask;
>>> + omap_temp_sensor_writel(temp_sensor, reg_val,
>>> + temp_sensor->registers->bgap_mask_ctrl);
>>> + }
>>> +
>>> + /*
>>> + * If current temperature is in-between the hot and cold thresholds,
>>> + * Enable both masks.
>>> + */
>>> + if (temp < t_hot && temp > t_cold) {
>>> + reg_val = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_mask_ctrl);
>>> + reg_val |= temp_sensor->registers->mask_cold_mask;
>>> + reg_val |= temp_sensor->registers->mask_hot_mask;
>>> + omap_temp_sensor_writel(temp_sensor, reg_val,
>>> + temp_sensor->registers->bgap_mask_ctrl);
>>> + }
>>> +
>>> + /*
>>> + * else no need to do anything since HW will immediately compare
>>> + * the new threshold and generate interrupt accordingly
>>> + */
>>> +
>>> +out:
>>> + mutex_unlock(&temp_sensor->sensor_mutex);
>>> + return count;
>>> +}
>>> +
>>> +static ssize_t show_update_rate(struct device *dev,
>>> + struct device_attribute *devattr, char *buf)
>>> +{
>>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>>> + u32 temp = 0, ret = 0;
>>> +
>>> + mutex_lock(&temp_sensor->sensor_mutex);
>>> +
>>> + if (!temp_sensor->clk_rate) {
>>> + dev_err(dev, "clk_rate is NULL\n");
>>> + ret = -EINVAL;
>>> + goto out;
>>> + }
>>> +
>>> + temp = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_counter);
>>> + temp = (temp & temp_sensor->registers->counter_mask) >>
>>> + __ffs(temp_sensor->registers->counter_mask);
>>> + temp = temp * 1000 / (temp_sensor->clk_rate);
>>> +
>>> +out:
>>> + mutex_unlock(&temp_sensor->sensor_mutex);
>>> + if (!ret)
>>> + return sprintf(buf, "%d\n", temp);
>>> +
>>> + return ret;
>>> +}
>>> +
>>> +static ssize_t set_update_rate(struct device *dev,
>>> + struct device_attribute *devattr,
>>> + const char *buf, size_t count)
>>> +{
>>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>>> + u32 reg_val;
>>> + long val;
>>> +
>>> + mutex_lock(&temp_sensor->sensor_mutex);
>>> +
>>> + if (strict_strtol(buf, 10, &val)) {
>>> + count = -EINVAL;
>>> + goto out;
>>> + }
>>> +
>>> + val *= temp_sensor->clk_rate / 1000;
>>> + reg_val = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_counter);
>>> +
>>> + reg_val &= ~(temp_sensor->registers->counter_mask);
>>> + reg_val |= val;
>>> + omap_temp_sensor_writel(temp_sensor, reg_val,
>>> + temp_sensor->registers->bgap_counter);
>>> +
>>> +out:
>>> + mutex_unlock(&temp_sensor->sensor_mutex);
>>> + return count;
>>> +}
>>> +
>>> +static int omap_temp_sensor_read_temp(struct device *dev,
>>> + struct device_attribute *devattr,
>>> + char *buf)
>>> +{
>>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>>> + int temp, ret = 0;
>>> +
>>> + mutex_lock(&temp_sensor->sensor_mutex);
>>> +
>>> + temp = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->temp_sensor_ctrl);
>>> + temp &= temp_sensor->registers->bgap_dtemp_mask;
>>> +
>>> + if (!temp_sensor->is_efuse_valid)
>>> + dev_err(dev, "Invalid EFUSE, Non-trimmed BGAP, Temp not accurate\n");
>>> +
>>> + /* look up for temperature in the table and return the temperature */
>>> + if (temp < OMAP_ADC_START_VALUE || temp > OMAP_ADC_END_VALUE) {
>>> + dev_err(dev, "invalid adc code reported %d", temp);
>>> + ret = -EINVAL;
>>> + goto out;
>>> + }
>>> +
>>> + temp = adc_to_temp[temp - OMAP_ADC_START_VALUE];
>>> +
>>> +out:
>>> + mutex_unlock(&temp_sensor->sensor_mutex);
>>> + if (!ret)
>>> + return sprintf(buf, "%d\n", temp);
>>> +
>>> + return ret;
>>> +}
>>> +
>>> +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, omap_temp_sensor_read_temp,
>>> + NULL, 0);
>>> +static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp_max,
>>> + set_temp_max, 0);
>>> +static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO, show_temp_max_hyst,
>>> + set_temp_max_hyst, 0);
>>> +static SENSOR_DEVICE_ATTR(update_rate, S_IWUSR | S_IRUGO, show_update_rate,
>>> + set_update_rate, 0);
>>> +
>>> +static struct attribute *omap_temp_sensor_attributes[] = {
>>> + &sensor_dev_attr_temp1_input.dev_attr.attr,
>>> + &sensor_dev_attr_temp1_max.dev_attr.attr,
>>> + &sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
>>> + &sensor_dev_attr_update_rate.dev_attr.attr,
>>> + NULL
>>> +};
>>> +
>>> +static const struct attribute_group omap_temp_sensor_group = {
>>> + .attrs = omap_temp_sensor_attributes,
>>> +};
>>> +
>>> +static int omap_temp_sensor_clk_enable(struct omap_temp_sensor *temp_sensor)
>>> +{
>>> + u32 ret = 0;
>>> +
>>> + if (temp_sensor->clk_on) {
>>> + dev_err(temp_sensor->hwmon_dev, "clock already on\n");
>>> + goto out;
>>> + }
>>> +
>>> + ret = pm_runtime_get_sync(temp_sensor->hwmon_dev);
>>> + if (ret < 0) {
>>> + dev_err(temp_sensor->hwmon_dev, "get sync failed\n");
>>> + goto out;
>>> + }
>>> +
>>> + temp_sensor->clk_on = 1;
>>> +
>>> +out:
>>> + return ret;
>>> +}
>>> +
>>> +static int omap_temp_sensor_clk_disable(struct omap_temp_sensor *temp_sensor)
>>> +{
>>> + u32 ret = 0;
>>> +
>>> + if (!temp_sensor->clk_on) {
>>> + dev_err(temp_sensor->hwmon_dev, "clock already off\n");
>>> + goto out;
>>> + }
>>> +
>>> + /* Gate the clock */
>>> + ret = pm_runtime_put_sync(temp_sensor->hwmon_dev);
>>> + if (ret < 0) {
>>> + dev_err(temp_sensor->hwmon_dev, "put sync failed\n");
>>> + goto out;
>>> + }
>>> + temp_sensor->clk_on = 0;
>>> +
>>> +out:
>>> + return ret;
>>> +}
>>> +
>>> +static irqreturn_t omap_talert_irq_handler(int irq, void *data)
>>> +{
>>> + struct omap_temp_sensor *temp_sensor;
>>> + int t_hot, t_cold, temp;
>>> +
>>> + temp_sensor = data;
>>> + mutex_lock(&temp_sensor->sensor_mutex);
>>> +
>>> + /* Read the status of t_hot */
>>> + t_hot = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_status)
>>> + & temp_sensor->registers->status_hot_mask;
>>> +
>>> + /* Read the status of t_cold */
>>> + t_cold = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_status)
>>> + & temp_sensor->registers->status_cold_mask;
>>> +
>>> + temp = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_mask_ctrl);
>>> + /*
>>> + * One TALERT interrupt: Two sources
>>> + * If the interrupt is due to t_hot then mask t_hot and
>>> + * and unmask t_cold else mask t_cold and unmask t_hot
>>> + */
>>> + if (t_hot) {
>>> + temp &= ~(temp_sensor->registers->mask_hot_mask);
>>> + temp |= temp_sensor->registers->mask_cold_mask;
>>> + } else if (t_cold) {
>>> + temp &= ~(temp_sensor->registers->mask_cold_mask);
>>> + temp |= temp_sensor->registers->mask_hot_mask;
>>> + }
>>> +
>>> + omap_temp_sensor_writel(temp_sensor, temp,
>>> + temp_sensor->registers->bgap_mask_ctrl);
>>> +
>>> + /* kobject_uvent to user space telling thermal threshold crossed */
>>> + kobject_uevent(&temp_sensor->hwmon_dev->kobj, KOBJ_CHANGE);
>>> +
>>> + mutex_unlock(&temp_sensor->sensor_mutex);
>>> +
>>> + return IRQ_HANDLED;
>>> +}
>>> +
>>> +static int __devinit omap_temp_sensor_probe(struct platform_device *pdev)
>>> +{
>>> + struct omap_temp_sensor_pdata *pdata = pdev->dev.platform_data;
>>> + struct omap_temp_sensor *temp_sensor;
>>> + struct resource *mem;
>>> + int ret = 0;
>>> + int val, clk_rate;
>>> +
>>> + if (!pdata) {
>>> + dev_err(&pdev->dev, "platform data missing\n");
>>> + return -EINVAL;
>>> + }
>>> +
>>> + temp_sensor = kzalloc(sizeof(*temp_sensor), GFP_KERNEL);
>>> + if (!temp_sensor)
>>> + return -ENOMEM;
>>> +
>>> + mutex_init(&temp_sensor->sensor_mutex);
>>> +
>>> + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>>> + if (!mem) {
>>> + dev_err(&pdev->dev, "no mem resource\n");
>>> + ret = -ENOMEM;
>>> + goto plat_res_err;
>>> + }
>>> +
>>> + temp_sensor->irq = platform_get_irq_byname(pdev, "thermal_alert");
>>> + if (temp_sensor->irq < 0) {
>>> + dev_err(&pdev->dev, "get_irq_byname failed\n");
>>> + ret = temp_sensor->irq;
>>> + goto plat_res_err;
>>> + }
>>> +
>>> + temp_sensor->phy_base = ioremap(mem->start, resource_size(mem));
>>> + temp_sensor->clock = NULL;
>>> + temp_sensor->registers = pdata->registers;
>>> + temp_sensor->hwmon_dev = &pdev->dev;
>>> +
>>> + pm_runtime_enable(&pdev->dev);
>>> + pm_runtime_irq_safe(&pdev->dev);
>>> +
>>> + /*
>>> + * check if the efuse has a non-zero value if not
>>> + * it is an untrimmed sample and the temperatures
>>> + * may not be accurate
>>> + */
>>> +
>>> + if (omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_efuse))
>>> + temp_sensor->is_efuse_valid = 1;
>>> +
>>> + platform_set_drvdata(pdev, temp_sensor);
>>> + dev_set_drvdata(&pdev->dev, temp_sensor);
>>> + temp_sensor->clock = clk_get(temp_sensor->hwmon_dev, "fck");
>>> + if (IS_ERR(temp_sensor->clock)) {
>>> + ret = PTR_ERR(temp_sensor->clock);
>>> + dev_err(temp_sensor->hwmon_dev,
>>> + "unable to get fclk: %d\n", ret);
>>> + ret = -EINVAL;
>>> + goto plat_res_err;
>>> + }
>>> +
>>> + ret = omap_temp_sensor_clk_enable(temp_sensor);
>>> + if (ret) {
>>> + dev_err(&pdev->dev, "Cannot enable temp sensor\n");
>>> + goto clken_err;
>>> + }
>>> +
>>> + clk_rate = clk_round_rate(temp_sensor->clock, 2000000);
>>> + if (clk_rate < 1000000 || clk_rate == 0xffffffff) {
>>> + dev_err(&pdev->dev, "Error round rate\n");
>>> + ret = -EINVAL;
>>> + goto clken_err;
>>> + }
>>> +
>>> + ret = clk_set_rate(temp_sensor->clock, clk_rate);
>>> + if (ret) {
>>> + dev_err(&pdev->dev, "Cannot set clock rate\n");
>>> + goto clken_err;
>>> + }
>>> +
>>> + temp_sensor->clk_rate = clk_rate;
>>> + omap_enable_continuous_mode(temp_sensor, 1);
>>> + omap_configure_temp_sensor_thresholds(temp_sensor);
>>> + /* 1 ms */
>>> + omap_configure_temp_sensor_counter(temp_sensor, 1);
>>> +
>>> + /* Wait till the first conversion is done wait for at least 1ms */
>>> + usleep_range(1000, 2000);
>>> +
>>> + /* Read the temperature once due to hw issue*/
>>> + omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->temp_sensor_ctrl);
>>> +
>>> + /* Set 2 seconds time as default counter */
>>> + omap_configure_temp_sensor_counter(temp_sensor,
>>> + temp_sensor->clk_rate * 2);
>>> +
>>> + temp_sensor->hwmon_dev = hwmon_device_register(&pdev->dev);
>>> + if (IS_ERR(temp_sensor->hwmon_dev)) {
>>> + dev_err(&pdev->dev, "hwmon_device_register failed.\n");
>>> + ret = PTR_ERR(temp_sensor->hwmon_dev);
>>> + goto hwmon_reg_err;
>>> + }
>>> +
>>> + ret = sysfs_create_group(&pdev->dev.kobj,
>>> + &omap_temp_sensor_group);
>>> + if (ret) {
>>> + dev_err(&pdev->dev, "could not create sysfs files\n");
>>> + goto sysfs_create_err;
>>> + }
>>> +
>>> + kobject_uevent(&temp_sensor->hwmon_dev->kobj, KOBJ_ADD);
>>> +
>>> + ret = request_threaded_irq(temp_sensor->irq, NULL,
>>> + omap_talert_irq_handler, IRQF_TRIGGER_RISING | IRQF_ONESHOT,
>>> + "temp_sensor", temp_sensor);
>>> + if (ret) {
>>> + dev_err(&pdev->dev, "Request threaded irq failed.\n");
>>> + goto req_irq_err;
>>> + }
>>> +
>>> + /* unmask the T_COLD and unmask T_HOT at init */
>>> + val = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_mask_ctrl);
>>> + val |= temp_sensor->registers->mask_cold_mask
>>> + | temp_sensor->registers->mask_hot_mask;
>>> +
>>> + omap_temp_sensor_writel(temp_sensor, val,
>>> + temp_sensor->registers->bgap_mask_ctrl);
>>> +
>>> + return 0;
>>> +
>>> +req_irq_err:
>>> + kobject_uevent(&temp_sensor->hwmon_dev->kobj, KOBJ_REMOVE);
>>> + sysfs_remove_group(&temp_sensor->hwmon_dev->kobj,
>>> + &omap_temp_sensor_group);
>>> +sysfs_create_err:
>>> + hwmon_device_unregister(&pdev->dev);
>>> +hwmon_reg_err:
>>> + omap_temp_sensor_clk_disable(temp_sensor);
>>> +clken_err:
>>> + clk_put(temp_sensor->clock);
>>> +plat_res_err:
>>> + mutex_destroy(&temp_sensor->sensor_mutex);
>>> + kfree(temp_sensor);
>>> + return ret;
>>> +}
>>> +
>>> +static int __devexit omap_temp_sensor_remove(struct platform_device *pdev)
>>> +{
>>> + struct omap_temp_sensor *temp_sensor = platform_get_drvdata(pdev);
>>> +
>>> + hwmon_device_unregister(&pdev->dev);
>>> + kobject_uevent(&temp_sensor->hwmon_dev->kobj, KOBJ_REMOVE);
>>> + sysfs_remove_group(&temp_sensor->hwmon_dev->kobj,
>>> + &omap_temp_sensor_group);
>>> + omap_temp_sensor_clk_disable(temp_sensor);
>>> + clk_put(temp_sensor->clock);
>>> + platform_set_drvdata(pdev, NULL);
>>> + free_irq(temp_sensor->irq, temp_sensor);
>>> + mutex_destroy(&temp_sensor->sensor_mutex);
>>> + kfree(temp_sensor);
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +#ifdef CONFIG_PM
>>> +static void omap_temp_sensor_save_ctxt(struct omap_temp_sensor *temp_sensor)
>>> +{
>>> + temp_sensor->temp_sensor_ctrl = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->temp_sensor_ctrl);
>>> + temp_sensor->bg_ctrl = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_mask_ctrl);
>>> + temp_sensor->bg_counter = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_counter);
>>> + temp_sensor->bg_threshold = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_threshold);
>>> + temp_sensor->temp_sensor_tshut_threshold =
>>> + omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->thsut_threshold);
>>> +}
>>> +
>>> +static void omap_temp_sensor_restore_ctxt(struct omap_temp_sensor *temp_sensor)
>>> +{
>>> + omap_temp_sensor_writel(temp_sensor,
>>> + temp_sensor->temp_sensor_ctrl,
>>> + temp_sensor->registers->temp_sensor_ctrl);
>>> + omap_temp_sensor_writel(temp_sensor,
>>> + temp_sensor->bg_ctrl,
>>> + temp_sensor->registers->bgap_mask_ctrl);
>>> + omap_temp_sensor_writel(temp_sensor,
>>> + temp_sensor->bg_counter,
>>> + temp_sensor->registers->bgap_counter);
>>> + omap_temp_sensor_writel(temp_sensor,
>>> + temp_sensor->bg_threshold,
>>> + temp_sensor->registers->bgap_threshold);
>>> + omap_temp_sensor_writel(temp_sensor,
>>> + temp_sensor->temp_sensor_tshut_threshold,
>>> + temp_sensor->registers->thsut_threshold);
>>> +}
>>> +
>>> +static int omap_temp_sensor_suspend(struct device *dev)
>>> +{
>>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>>> +
>>> + omap_temp_sensor_save_ctxt(temp_sensor);
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static int omap_temp_sensor_resume(struct device *dev)
>>> +{
>>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>>> +
>>> + omap_temp_sensor_restore_ctxt(temp_sensor);
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static int omap_temp_sensor_runtime_suspend(struct device *dev)
>>> +{
>>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>>> +
>>> + omap_temp_sensor_save_ctxt(temp_sensor);
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static int omap_temp_sensor_runtime_resume(struct device *dev)
>>> +{
>>> + static int context_loss_count;
>>> + int temp;
>>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>>> +
>>> + temp = omap_device_get_context_loss_count(to_platform_device(dev));
>>> +
>>> + if (temp != context_loss_count && context_loss_count != 0)
>>> + omap_temp_sensor_restore_ctxt(temp_sensor);
>>> +
>>> + context_loss_count = temp;
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static const struct dev_pm_ops omap_temp_sensor_dev_pm_ops = {
>>> + .suspend = omap_temp_sensor_suspend,
>>> + .resume = omap_temp_sensor_resume,
>>> + .runtime_suspend = omap_temp_sensor_runtime_suspend,
>>> + .runtime_resume = omap_temp_sensor_runtime_resume,
>>> +};
>>> +
>>> +#endif
>>> +
>>> +static struct platform_driver omap_temp_sensor_driver = {
>>> + .probe = omap_temp_sensor_probe,
>>> + .remove = omap_temp_sensor_remove,
>>> + .driver = {
>>> + .name = "omap_temp_sensor",
>>> +#ifdef CONFIG_PM
>>> + .pm = &omap_temp_sensor_dev_pm_ops,
>>> +#endif
>>> + },
>>> +};
>>> +
>>> +int __init omap_temp_sensor_init(void)
>>> +{
>>> + return platform_driver_register(&omap_temp_sensor_driver);
>>> +}
>>> +module_init(omap_temp_sensor_init);
>>> +
>>> +static void __exit omap_temp_sensor_exit(void)
>>> +{
>>> + platform_driver_unregister(&omap_temp_sensor_driver);
>>> +}
>>> +module_exit(omap_temp_sensor_exit);
>>> +
>>> +MODULE_DESCRIPTION("OMAP446X temperature sensor Driver");
>>> +MODULE_LICENSE("GPL");
>>> +MODULE_ALIAS("platform:" DRIVER_NAME);
>>> +MODULE_AUTHOR("J Keerthy <j-keerthy@ti.com>");
>>> --
>>> 1.7.0.4
>>>
>>
>
>
>
> --
> Regards and Thanks,
> Keerthy
>
--
Regards and Thanks,
Keerthy
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 35+ messages in thread
* Re: [lm-sensors] [RFC PATCH 6/6 V2] hwmon: OMAP4: On die
@ 2011-08-19 9:16 ` J, KEERTHY
0 siblings, 0 replies; 35+ messages in thread
From: J, KEERTHY @ 2011-08-19 9:16 UTC (permalink / raw)
To: Guenter Roeck; +Cc: linux-omap, Jean Delvare, lm-sensors
On Fri, Aug 19, 2011 at 11:34 AM, J, KEERTHY <j-keerthy@ti.com> wrote:
> On Fri, Aug 19, 2011 at 7:43 AM, Guenter Roeck
> <guenter.roeck@ericsson.com> wrote:
>> On Thu, Aug 18, 2011 at 06:52:15AM -0400, Keerthy wrote:
>>> On chip temperature sensor driver. The driver monitors the temperature of
>>> the MPU subsystem of the OMAP4. It sends notifications to the user space if
>>> the temperature crosses user defined thresholds via kobject_uevent interface.
>>> The user is allowed to configure the temperature thresholds vis sysfs nodes
>>> exposed using hwmon interface.
>>>
>>> Signed-off-by: Keerthy <j-keerthy@ti.com>
>>> Cc: Jean Delvare <khali@linux-fr.org>
>>> Cc: Guenter Roeck <guenter.roeck@ericsson.com>
>>> Cc: lm-sensors@lm-sensors.org
>>
>> High level review:
>>
>> - too much and too broad mutex locking. show functions should not need locks at all,
>> set functions only while data is written into registers and into platform data.
>
> Ok. I will clean this.
>
>> - driver is quite noisy. There should definitely not be any log messages
>> if a set parameter is wrong. Show functions already return an error value
>> to the user; a log message indicating the error again just creates noise.
>> For one boolean set during probe (is_efuse_valid), each subsequent show results
>> in a log message if it is false. Some errors result in multiple log messages.
>
> A user tries to set an invalid temperature threshold. The user should
> be notified about this. The invalid temperature will not be set. The user
> should not be allowed to set an invalid temperature. It is to inform
> the user about precisely the problem with the parameter.
>
> In some of the samples the bandgap is not trimmed and hence
> temperature reported will be wrong. So every time a user tries to read
> he is alerted that the temperatures are not accurate.
>
>> - Wrong use of EINVAL throughout the driver (EINVAL is "Invalid Argument")
>
> Ok. I will correct this.
When the temperature is out of range can i return -EDOM?
Any suggestions?
>
>> - excessive ( )
>
> Ok.
>
>> - linear search through a sorted array is very expensive. Consider using a binary search.
>
> Yes. I will implement binary search.
>
>> - temp_to_adc_conversion return code (error if negative) is not checked properly.
>
> Ok. I will add checks.
>
>>
>> I am sure there are other problems, but those are difficult to find with all the noise
>> above.
>>
>> Guenter
>>
>>> ---
>>> Documentation/hwmon/omap_temp_sensor | 27 +
>>> drivers/hwmon/Kconfig | 11 +
>>> drivers/hwmon/Makefile | 1 +
>>> drivers/hwmon/omap_temp_sensor.c | 918 ++++++++++++++++++++++++++++++++++
>>> 4 files changed, 957 insertions(+), 0 deletions(-)
>>> create mode 100644 Documentation/hwmon/omap_temp_sensor
>>> create mode 100644 drivers/hwmon/omap_temp_sensor.c
>>>
>>> diff --git a/Documentation/hwmon/omap_temp_sensor b/Documentation/hwmon/omap_temp_sensor
>>> new file mode 100644
>>> index 0000000..e01a6d6
>>> --- /dev/null
>>> +++ b/Documentation/hwmon/omap_temp_sensor
>>> @@ -0,0 +1,27 @@
>>> +Kernel driver omap_temp_sensor
>>> +===============
>>> +
>>> +Supported chips:
>>> + * Texas Instruments OMAP4460
>>> + Prefix: 'omap_temp_sensor'
>>> + Datasheet: http://focus.ti.com/docs/prod/folders/print/tmp102.html
>>> +
>>> +Author:
>>> + J Keerthy <j-keerthy@ti.com>
>>> +
>>> +Description
>>> +-----------
>>> +
>>> +The Texas Instruments OMAP4 family of chips have a bandgap temperature sensor.
>>> +The temperature sensor feature is used to convert the temperature of the device
>>> +into a decimal value coded on 10 bits. An internal ADC is used for conversion.
>>> +The recommended operating temperatures must be in the range -40 degree Celsius
>>> +to 123 degree celsius for standard conversion.
>>> +The thresholds are programmable and upon crossing the thresholds an interrupt
>>> +is generated. The OMAP temperature sensor has a programmable update rate in
>>> +milli seconds.
>>> +(Currently the driver programs a default of 2000 milli seconds).
>>> +
>>> +The driver provides the common sysfs-interface for temperatures (see
>>> +Documentation/hwmon/sysfs-interface under Temperatures).
>>> +
>>> diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
>>> index 5f888f7..9c9cd8b 100644
>>> --- a/drivers/hwmon/Kconfig
>>> +++ b/drivers/hwmon/Kconfig
>>> @@ -323,6 +323,17 @@ config SENSORS_F71805F
>>> This driver can also be built as a module. If so, the module
>>> will be called f71805f.
>>>
>>> +config SENSORS_OMAP_BANDGAP_TEMP_SENSOR
>>> + bool "OMAP on-die temperature sensor hwmon driver"
>>> + depends on HWMON && ARCH_OMAP && OMAP_TEMP_SENSOR
>>> + help
>>> + If you say yes here you get support for hardware
>>> + monitoring features of the OMAP on die temperature
>>> + sensor.
>>> +
>>> + Continuous conversion programmable delay
>>> + mode is used for temperature conversion.
>>> +
>>> config SENSORS_F71882FG
>>> tristate "Fintek F71882FG and compatibles"
>>> help
>>> diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
>>> index 28061cf..d0f89f5 100644
>>> --- a/drivers/hwmon/Makefile
>>> +++ b/drivers/hwmon/Makefile
>>> @@ -91,6 +91,7 @@ obj-$(CONFIG_SENSORS_MAX6639) += max6639.o
>>> obj-$(CONFIG_SENSORS_MAX6642) += max6642.o
>>> obj-$(CONFIG_SENSORS_MAX6650) += max6650.o
>>> obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o
>>> +obj-$(CONFIG_SENSORS_OMAP_BANDGAP_TEMP_SENSOR) += omap_temp_sensor.o
>>> obj-$(CONFIG_SENSORS_PC87360) += pc87360.o
>>> obj-$(CONFIG_SENSORS_PC87427) += pc87427.o
>>> obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o
>>> diff --git a/drivers/hwmon/omap_temp_sensor.c b/drivers/hwmon/omap_temp_sensor.c
>>> new file mode 100644
>>> index 0000000..586a361
>>> --- /dev/null
>>> +++ b/drivers/hwmon/omap_temp_sensor.c
>>> @@ -0,0 +1,918 @@
>>> +/*
>>> + * OMAP4 Temperature sensor driver file
>>> + *
>>> + * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
>>> + * Author: J Keerthy <j-keerthy@ti.com>
>>> + * Author: Moiz Sonasath <m-sonasath@ti.com>
>>> + *
>>> + * This program is free software; you can redistribute it and/or
>>> + * modify it under the terms of the GNU General Public License
>>> + * 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.
>>> + *
>>> + * You should have received a copy of the GNU General Public License
>>> + * along with this program; if not, write to the Free Software
>>> + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
>>> + * 02110-1301 USA
>>> + *
>>> + */
>>> +
>>> +#include <linux/interrupt.h>
>>> +#include <linux/clk.h>
>>> +#include <linux/io.h>
>>> +#include <linux/slab.h>
>>> +#include <linux/init.h>
>>> +#include <plat/omap_device.h>
>>> +#include <linux/kernel.h>
>>> +#include <linux/device.h>
>>> +#include <linux/jiffies.h>
>>> +#include <linux/hwmon.h>
>>> +#include <linux/hwmon-sysfs.h>
>>> +#include <linux/stddef.h>
>>> +#include <linux/sysfs.h>
>>> +#include <linux/err.h>
>>> +#include <linux/types.h>
>>> +#include <linux/mutex.h>
>>> +#include <linux/pm_runtime.h>
>>> +#include <linux/delay.h>
>>> +#include <plat/temperature_sensor.h>
>>> +
>>> +#define TSHUT_THRESHOLD_HOT 122000 /* 122 deg C */
>>> +#define TSHUT_THRESHOLD_COLD 100000 /* 100 deg C */
>>> +#define BGAP_THRESHOLD_T_HOT 73000 /* 73 deg C */
>>> +#define BGAP_THRESHOLD_T_COLD 71000 /* 71 deg C */
>>> +#define OMAP_ADC_START_VALUE 530
>>> +#define OMAP_ADC_END_VALUE 923
>>> +
>>> +/*
>>> + * omap_temp_sensor structure
>>> + * @hwmon_dev - device pointer
>>> + * @clock - Clock pointer
>>> + * @registers - Pointer to structure with register offsets and bitfields
>>> + * @sensor_mutex - Mutex for sysfs, irq and PM
>>> + * @irq - MPU Irq number for thermal alert
>>> + * @phy_base - Physical base of the temp I/O
>>> + * @clk_rate - Holds current clock rate
>>> + * @temp_sensor_ctrl - temp sensor control register value
>>> + * @bg_ctrl - bandgap ctrl register value
>>> + * @bg_counter - bandgap counter value
>>> + * @bg_threshold - bandgap threshold register value
>>> + * @temp_sensor_tshut_threshold - bandgap tshut register value
>>> + * @is_efuse_valid - Flag to determine if efuse is valid or not
>>> + * @clk_on - Manages the current clock state
>>> + */
>>> +struct omap_temp_sensor {
>>> + struct device *hwmon_dev;
>>> + struct clk *clock;
>>> + struct omap_temp_sensor_registers *registers;
>>> + struct mutex sensor_mutex; /* Mutex for sysfs, irq and PM */
>>> + unsigned int irq;
>>> + void __iomem *phy_base;
>>> + u32 clk_rate;
>>> + u32 temp_sensor_ctrl;
>>> + u32 bg_ctrl;
>>> + u32 bg_counter;
>>> + u32 bg_threshold;
>>> + u32 temp_sensor_tshut_threshold;
>>> + bool is_efuse_valid;
>>> + bool clk_on;
>>> +};
>>> +
>>> +/*
>>> + * Temperature values in milli degree celsius
>>> + * ADC code values from 530 to 923
>>> + */
>>> +static int adc_to_temp[] = {
>>> + -40000, -40000, -40000, -40000, -39800, -39400, -39000, -38600, -38200,
>>> + -37800, -37300, -36800, -36400, -36000, -35600, -35200, -34800,
>>> + -34300, -33800, -33400, -33000, -32600, -32200, -31800, -31300,
>>> + -30800, -30400, -30000, -29600, -29200, -28700, -28200, -27800,
>>> + -27400, -27000, -26600, -26200, -25700, -25200, -24800, -24400,
>>> + -24000, -23600, -23200, -22700, -22200, -21800, -21400, -21000,
>>> + -20600, -20200, -19700, -19200, -18800, -18400, -18000, -17600,
>>> + -17200, -16700, -16200, -15800, -15400, -15000, -14600, -14200,
>>> + -13700, -13200, -12800, -12400, -12000, -11600, -11200, -10700,
>>> + -10200, -9800, -9400, -9000, -8600, -8200, -7700, -7200, -6800,
>>> + -6400, -6000, -5600, -5200, -4800, -4300, -3800, -3400, -3000,
>>> + -2600, -2200, -1800, -1300, -800, -400, 0, 400, 800, 1200, 1600,
>>> + 2100, 2600, 3000, 3400, 3800, 4200, 4600, 5100, 5600, 6000, 6400,
>>> + 6800, 7200, 7600, 8000, 8500, 9000, 9400, 9800, 10200, 10600, 11000,
>>> + 11400, 11900, 12400, 12800, 13200, 13600, 14000, 14400, 14800,
>>> + 15300, 15800, 16200, 16600, 17000, 17400, 17800, 18200, 18700,
>>> + 19200, 19600, 20000, 20400, 20800, 21200, 21600, 22100, 22600,
>>> + 23000, 23400, 23800, 24200, 24600, 25000, 25400, 25900, 26400,
>>> + 26800, 27200, 27600, 28000, 28400, 28800, 29300, 29800, 30200,
>>> + 30600, 31000, 31400, 31800, 32200, 32600, 33100, 33600, 34000,
>>> + 34400, 34800, 35200, 35600, 36000, 36400, 36800, 37300, 37800,
>>> + 38200, 38600, 39000, 39400, 39800, 40200, 40600, 41100, 41600,
>>> + 42000, 42400, 42800, 43200, 43600, 44000, 44400, 44800, 45300,
>>> + 45800, 46200, 46600, 47000, 47400, 47800, 48200, 48600, 49000,
>>> + 49500, 50000, 50400, 50800, 51200, 51600, 52000, 52400, 52800,
>>> + 53200, 53700, 54200, 54600, 55000, 55400, 55800, 56200, 56600,
>>> + 57000, 57400, 57800, 58200, 58700, 59200, 59600, 60000, 60400,
>>> + 60800, 61200, 61600, 62000, 62400, 62800, 63300, 63800, 64200,
>>> + 64600, 65000, 65400, 65800, 66200, 66600, 67000, 67400, 67800,
>>> + 68200, 68700, 69200, 69600, 70000, 70400, 70800, 71200, 71600,
>>> + 72000, 72400, 72800, 73200, 73600, 74100, 74600, 75000, 75400,
>>> + 75800, 76200, 76600, 77000, 77400, 77800, 78200, 78600, 79000,
>>> + 79400, 79800, 80300, 80800, 81200, 81600, 82000, 82400, 82800,
>>> + 83200, 83600, 84000, 84400, 84800, 85200, 85600, 86000, 86400,
>>> + 86800, 87300, 87800, 88200, 88600, 89000, 89400, 89800, 90200,
>>> + 90600, 91000, 91400, 91800, 92200, 92600, 93000, 93400, 93800,
>>> + 94200, 94600, 95000, 95500, 96000, 96400, 96800, 97200, 97600,
>>> + 98000, 98400, 98800, 99200, 99600, 100000, 100400, 100800, 101200,
>>> + 101600, 102000, 102400, 102800, 103200, 103600, 104000, 104400,
>>> + 104800, 105200, 105600, 106100, 106600, 107000, 107400, 107800,
>>> + 108200, 108600, 109000, 109400, 109800, 110200, 110600, 111000,
>>> + 111400, 111800, 112200, 112600, 113000, 113400, 113800, 114200,
>>> + 114600, 115000, 115400, 115800, 116200, 116600, 117000, 117400,
>>> + 117800, 118200, 118600, 119000, 119400, 119800, 120200, 120600,
>>> + 121000, 121400, 121800, 122200, 122600, 123000
>>> +};
>>> +
>>> +static unsigned long omap_temp_sensor_readl(struct omap_temp_sensor
>>> + *temp_sensor, u32 reg)
>>> +{
>>> + return __raw_readl(temp_sensor->phy_base + reg);
>>> +}
>>> +
>>> +static void omap_temp_sensor_writel(struct omap_temp_sensor *temp_sensor,
>>> + u32 val, u32 reg)
>>> +{
>>> + __raw_writel(val, (temp_sensor->phy_base + reg));
>>> +}
>>> +
>>> +static int adc_to_temp_conversion(int adc_val)
>>> +{
>>> + return adc_to_temp[adc_val - OMAP_ADC_START_VALUE];
>>> +}
>>> +
>>> +static int temp_to_adc_conversion(long temp)
>>> +{
>>> + int i;
>>> +
>>> + for (i = 0; i <= OMAP_ADC_END_VALUE - OMAP_ADC_START_VALUE; i++)
>>> + if (temp < adc_to_temp[i])
>>> + return OMAP_ADC_START_VALUE + i - 1;
>>> +
>>> + return -EINVAL;
>>> +}
>>> +
>>> +static void omap_configure_temp_sensor_thresholds(struct omap_temp_sensor
>>> + *temp_sensor)
>>> +{
>>> + u32 temp, t_hot, t_cold, tshut_hot, tshut_cold;
>>> +
>>> + t_hot = temp_to_adc_conversion(BGAP_THRESHOLD_T_HOT);
>>> + t_cold = temp_to_adc_conversion(BGAP_THRESHOLD_T_COLD);
>>> +
>>> + /* Configure the TALERT thresholds */
>>> + temp = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_threshold);
>>> + temp |= (t_hot << __ffs(temp_sensor->registers->threshold_thot_mask)) |
>>> + (t_cold << __ffs(temp_sensor->registers->threshold_tcold_mask));
>>> + omap_temp_sensor_writel(temp_sensor, temp,
>>> + temp_sensor->registers->bgap_threshold);
>>> +
>>> + tshut_hot = temp_to_adc_conversion(TSHUT_THRESHOLD_HOT);
>>> + tshut_cold = temp_to_adc_conversion(TSHUT_THRESHOLD_COLD);
>>> +
>>> + /* Configure the TSHUT thresholds */
>>> + temp = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->thsut_threshold);
>>> + temp |= (tshut_hot << __ffs(temp_sensor->registers->tshut_hot_mask))
>>> + | (tshut_cold << __ffs(temp_sensor->registers->tshut_hot_mask));
>>> + omap_temp_sensor_writel(temp_sensor, temp,
>>> + temp_sensor->registers->thsut_threshold);
>>> +}
>>> +
>>> +static void omap_configure_temp_sensor_counter(struct omap_temp_sensor
>>> + *temp_sensor, u32 counter)
>>> +{
>>> + u32 val;
>>> +
>>> + val = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_counter);
>>> + val &= ~(temp_sensor->registers->counter_mask);
>>> + val |= (counter << __ffs(temp_sensor->registers->counter_mask));
>>> + omap_temp_sensor_writel(temp_sensor, val,
>>> + temp_sensor->registers->bgap_counter);
>>> +}
>>> +
>>> +static void omap_enable_continuous_mode(struct omap_temp_sensor *temp_sensor,
>>> + bool enable)
>>> +{
>>> + u32 val;
>>> +
>>> + val = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_mode_ctrl);
>>> +
>>> + if (enable)
>>> + val |= (1 << __ffs(temp_sensor->registers->mode_ctrl_mask));
>>> + else
>>> + val &= ~(temp_sensor->registers->mode_ctrl_mask);
>>> +
>>> + omap_temp_sensor_writel(temp_sensor, val,
>>> + temp_sensor->registers->bgap_mode_ctrl);
>>> +}
>>> +
>>> +/* Sysfs hook functions */
>>> +
>>> +static ssize_t show_temp_max(struct device *dev,
>>> + struct device_attribute *devattr, char *buf)
>>> +{
>>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>>> + int temp;
>>> +
>>> + mutex_lock(&temp_sensor->sensor_mutex);
>>> +
>>> + temp = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_threshold);
>>> + temp = (temp & temp_sensor->registers->threshold_thot_mask)
>>> + >> __ffs(temp_sensor->registers->threshold_thot_mask);
>>> +
>>> + if (temp < OMAP_ADC_START_VALUE || temp > OMAP_ADC_END_VALUE) {
>>> + dev_err(dev, "invalid value\n");
>>> + mutex_unlock(&temp_sensor->sensor_mutex);
>>> + return -EINVAL;
>>> + }
>>> +
>>> + temp = adc_to_temp_conversion(temp);
>>> + mutex_unlock(&temp_sensor->sensor_mutex);
>>> +
>>> + return snprintf(buf, 16, "%d\n", temp);
>>> +}
>>> +
>>> +static ssize_t set_temp_max(struct device *dev,
>>> + struct device_attribute *devattr,
>>> + const char *buf, size_t count)
>>> +{
>>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>>> + long val;
>>> + u32 reg_val, t_cold, t_hot, temp;
>>> +
>>> + mutex_lock(&temp_sensor->sensor_mutex);
>>> +
>>> + if (strict_strtol(buf, 10, &val)) {
>>> + count = -EINVAL;
>>> + goto out;
>>> + }
>>> +
>>> + t_hot = temp_to_adc_conversion(val);
>>> + if ((t_hot < OMAP_ADC_START_VALUE || t_hot > OMAP_ADC_END_VALUE)) {
>>> + dev_err(dev, "invalid range\n");
>>> + count = -EINVAL;
>>> + goto out;
>>> + }
>>> +
>>> + /* obtain the T cold value */
>>> + t_cold = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_threshold);
>>> + t_cold = (t_cold & temp_sensor->registers->threshold_tcold_mask) >>
>>> + __ffs(temp_sensor->registers->threshold_tcold_mask);
>>> +
>>> + if (t_hot < t_cold) {
>>> + dev_err(dev, "Error! T_HOT value lesser than T_COLD\n");
>>> + count = -EINVAL;
>>> + goto out;
>>> + }
>>> +
>>> + /* write the new t_hot value */
>>> + reg_val = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_threshold);
>>> + reg_val &= ~(temp_sensor->registers->threshold_thot_mask);
>>> + reg_val |= (t_hot <<
>>> + __ffs(temp_sensor->registers->threshold_thot_mask));
>>> + omap_temp_sensor_writel(temp_sensor, reg_val,
>>> + temp_sensor->registers->bgap_threshold);
>>> +
>>> + /* Read the current temperature */
>>> + temp = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->temp_sensor_ctrl);
>>> + temp &= (temp_sensor->registers->bgap_dtemp_mask);
>>> +
>>> + /*
>>> + * If user sets the HIGH threshold(t_hot) greater than the current
>>> + * temperature(temp) unmask the HOT interrupts
>>> + */
>>> + if (t_hot > temp) {
>>> + reg_val = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_mask_ctrl);
>>> + reg_val &= ~(temp_sensor->registers->mask_cold_mask);
>>> + reg_val |= temp_sensor->registers->mask_hot_mask;
>>> + omap_temp_sensor_writel(temp_sensor, reg_val,
>>> + temp_sensor->registers->bgap_mask_ctrl);
>>> + }
>>> +
>>> + /*
>>> + * If current temperature is in-between the hot and cold thresholds,
>>> + * Enable both masks.
>>> + */
>>> + if (temp > t_cold && temp < t_hot) {
>>> + reg_val = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_mask_ctrl);
>>> + reg_val |= temp_sensor->registers->mask_cold_mask;
>>> + reg_val |= temp_sensor->registers->mask_hot_mask;
>>> + omap_temp_sensor_writel(temp_sensor, reg_val,
>>> + OMAP4460_BGAP_CTRL_OFFSET);
>>> + }
>>> + /*
>>> + * else no need to do anything since HW will immediately compare
>>> + * the new threshold and generate interrupt accordingly
>>> + */
>>> +out:
>>> + mutex_unlock(&temp_sensor->sensor_mutex);
>>> + return count;
>>> +}
>>> +
>>> +static ssize_t show_temp_max_hyst(struct device *dev,
>>> + struct device_attribute *devattr, char *buf)
>>> +{
>>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>>> + u32 temp;
>>> +
>>> + mutex_lock(&temp_sensor->sensor_mutex);
>>> +
>>> + temp = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_threshold);
>>> + temp = (temp & temp_sensor->registers->threshold_tcold_mask) >>
>>> + __ffs(temp_sensor->registers->threshold_tcold_mask);
>>> +
>>> + if (temp < OMAP_ADC_START_VALUE || temp > OMAP_ADC_END_VALUE) {
>>> + dev_err(dev, "invalid value\n");
>>> + mutex_unlock(&temp_sensor->sensor_mutex);
>>> + return -EINVAL;
>>> + }
>>> +
>>> + temp = adc_to_temp_conversion(temp);
>>> + mutex_unlock(&temp_sensor->sensor_mutex);
>>> +
>>> + return snprintf(buf, 16, "%d\n", temp);
>>> +}
>>> +
>>> +static ssize_t set_temp_max_hyst(struct device *dev,
>>> + struct device_attribute *devattr,
>>> + const char *buf, size_t count)
>>> +{
>>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>>> + u32 reg_val, t_hot, t_cold, temp;
>>> + long val;
>>> +
>>> + mutex_lock(&temp_sensor->sensor_mutex);
>>> +
>>> + if (strict_strtol(buf, 10, &val)) {
>>> + count = -EINVAL;
>>> + goto out;
>>> + }
>>> +
>>> + t_cold = temp_to_adc_conversion(val);
>>> + if (t_cold < OMAP_ADC_START_VALUE || t_cold > OMAP_ADC_END_VALUE) {
>>> + dev_err(dev, "invalid range");
>>> + count = -EINVAL;
>>> + goto out;
>>> + }
>>> +
>>> + /* obtain the T HOT value */
>>> + t_hot = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_threshold);
>>> + t_hot = (t_hot & temp_sensor->registers->threshold_thot_mask) >>
>>> + __ffs(temp_sensor->registers->threshold_thot_mask);
>>> +
>>> + if (t_cold > t_hot) {
>>> + dev_err(dev, "Error! T_COLD value greater than T_HOT\n");
>>> + count = -EINVAL;
>>> + goto out;
>>> + }
>>> +
>>> + /* write the new t_cold value */
>>> + reg_val = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_threshold);
>>> + reg_val &= ~(temp_sensor->registers->threshold_tcold_mask);
>>> + reg_val |= (t_cold <<
>>> + __ffs(temp_sensor->registers->threshold_tcold_mask));
>>> + omap_temp_sensor_writel(temp_sensor, reg_val,
>>> + temp_sensor->registers->bgap_threshold);
>>> +
>>> + /* Read the current temperature */
>>> + temp = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->temp_sensor_ctrl);
>>> + temp &= (temp_sensor->registers->bgap_dtemp_mask);
>>> +
>>> + /*
>>> + * If user sets the LOW threshold(t_cold) lower than the current
>>> + * temperature(temp) unmask the COLD interrupts
>>> + */
>>> + if (t_cold < temp) {
>>> + reg_val = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_mask_ctrl);
>>> + reg_val &= ~(temp_sensor->registers->mask_hot_mask);
>>> + reg_val |= temp_sensor->registers->mask_cold_mask;
>>> + omap_temp_sensor_writel(temp_sensor, reg_val,
>>> + temp_sensor->registers->bgap_mask_ctrl);
>>> + }
>>> +
>>> + /*
>>> + * If current temperature is in-between the hot and cold thresholds,
>>> + * Enable both masks.
>>> + */
>>> + if (temp < t_hot && temp > t_cold) {
>>> + reg_val = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_mask_ctrl);
>>> + reg_val |= temp_sensor->registers->mask_cold_mask;
>>> + reg_val |= temp_sensor->registers->mask_hot_mask;
>>> + omap_temp_sensor_writel(temp_sensor, reg_val,
>>> + temp_sensor->registers->bgap_mask_ctrl);
>>> + }
>>> +
>>> + /*
>>> + * else no need to do anything since HW will immediately compare
>>> + * the new threshold and generate interrupt accordingly
>>> + */
>>> +
>>> +out:
>>> + mutex_unlock(&temp_sensor->sensor_mutex);
>>> + return count;
>>> +}
>>> +
>>> +static ssize_t show_update_rate(struct device *dev,
>>> + struct device_attribute *devattr, char *buf)
>>> +{
>>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>>> + u32 temp = 0, ret = 0;
>>> +
>>> + mutex_lock(&temp_sensor->sensor_mutex);
>>> +
>>> + if (!temp_sensor->clk_rate) {
>>> + dev_err(dev, "clk_rate is NULL\n");
>>> + ret = -EINVAL;
>>> + goto out;
>>> + }
>>> +
>>> + temp = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_counter);
>>> + temp = (temp & temp_sensor->registers->counter_mask) >>
>>> + __ffs(temp_sensor->registers->counter_mask);
>>> + temp = temp * 1000 / (temp_sensor->clk_rate);
>>> +
>>> +out:
>>> + mutex_unlock(&temp_sensor->sensor_mutex);
>>> + if (!ret)
>>> + return sprintf(buf, "%d\n", temp);
>>> +
>>> + return ret;
>>> +}
>>> +
>>> +static ssize_t set_update_rate(struct device *dev,
>>> + struct device_attribute *devattr,
>>> + const char *buf, size_t count)
>>> +{
>>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>>> + u32 reg_val;
>>> + long val;
>>> +
>>> + mutex_lock(&temp_sensor->sensor_mutex);
>>> +
>>> + if (strict_strtol(buf, 10, &val)) {
>>> + count = -EINVAL;
>>> + goto out;
>>> + }
>>> +
>>> + val *= temp_sensor->clk_rate / 1000;
>>> + reg_val = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_counter);
>>> +
>>> + reg_val &= ~(temp_sensor->registers->counter_mask);
>>> + reg_val |= val;
>>> + omap_temp_sensor_writel(temp_sensor, reg_val,
>>> + temp_sensor->registers->bgap_counter);
>>> +
>>> +out:
>>> + mutex_unlock(&temp_sensor->sensor_mutex);
>>> + return count;
>>> +}
>>> +
>>> +static int omap_temp_sensor_read_temp(struct device *dev,
>>> + struct device_attribute *devattr,
>>> + char *buf)
>>> +{
>>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>>> + int temp, ret = 0;
>>> +
>>> + mutex_lock(&temp_sensor->sensor_mutex);
>>> +
>>> + temp = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->temp_sensor_ctrl);
>>> + temp &= temp_sensor->registers->bgap_dtemp_mask;
>>> +
>>> + if (!temp_sensor->is_efuse_valid)
>>> + dev_err(dev, "Invalid EFUSE, Non-trimmed BGAP, Temp not accurate\n");
>>> +
>>> + /* look up for temperature in the table and return the temperature */
>>> + if (temp < OMAP_ADC_START_VALUE || temp > OMAP_ADC_END_VALUE) {
>>> + dev_err(dev, "invalid adc code reported %d", temp);
>>> + ret = -EINVAL;
>>> + goto out;
>>> + }
>>> +
>>> + temp = adc_to_temp[temp - OMAP_ADC_START_VALUE];
>>> +
>>> +out:
>>> + mutex_unlock(&temp_sensor->sensor_mutex);
>>> + if (!ret)
>>> + return sprintf(buf, "%d\n", temp);
>>> +
>>> + return ret;
>>> +}
>>> +
>>> +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, omap_temp_sensor_read_temp,
>>> + NULL, 0);
>>> +static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp_max,
>>> + set_temp_max, 0);
>>> +static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO, show_temp_max_hyst,
>>> + set_temp_max_hyst, 0);
>>> +static SENSOR_DEVICE_ATTR(update_rate, S_IWUSR | S_IRUGO, show_update_rate,
>>> + set_update_rate, 0);
>>> +
>>> +static struct attribute *omap_temp_sensor_attributes[] = {
>>> + &sensor_dev_attr_temp1_input.dev_attr.attr,
>>> + &sensor_dev_attr_temp1_max.dev_attr.attr,
>>> + &sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
>>> + &sensor_dev_attr_update_rate.dev_attr.attr,
>>> + NULL
>>> +};
>>> +
>>> +static const struct attribute_group omap_temp_sensor_group = {
>>> + .attrs = omap_temp_sensor_attributes,
>>> +};
>>> +
>>> +static int omap_temp_sensor_clk_enable(struct omap_temp_sensor *temp_sensor)
>>> +{
>>> + u32 ret = 0;
>>> +
>>> + if (temp_sensor->clk_on) {
>>> + dev_err(temp_sensor->hwmon_dev, "clock already on\n");
>>> + goto out;
>>> + }
>>> +
>>> + ret = pm_runtime_get_sync(temp_sensor->hwmon_dev);
>>> + if (ret < 0) {
>>> + dev_err(temp_sensor->hwmon_dev, "get sync failed\n");
>>> + goto out;
>>> + }
>>> +
>>> + temp_sensor->clk_on = 1;
>>> +
>>> +out:
>>> + return ret;
>>> +}
>>> +
>>> +static int omap_temp_sensor_clk_disable(struct omap_temp_sensor *temp_sensor)
>>> +{
>>> + u32 ret = 0;
>>> +
>>> + if (!temp_sensor->clk_on) {
>>> + dev_err(temp_sensor->hwmon_dev, "clock already off\n");
>>> + goto out;
>>> + }
>>> +
>>> + /* Gate the clock */
>>> + ret = pm_runtime_put_sync(temp_sensor->hwmon_dev);
>>> + if (ret < 0) {
>>> + dev_err(temp_sensor->hwmon_dev, "put sync failed\n");
>>> + goto out;
>>> + }
>>> + temp_sensor->clk_on = 0;
>>> +
>>> +out:
>>> + return ret;
>>> +}
>>> +
>>> +static irqreturn_t omap_talert_irq_handler(int irq, void *data)
>>> +{
>>> + struct omap_temp_sensor *temp_sensor;
>>> + int t_hot, t_cold, temp;
>>> +
>>> + temp_sensor = data;
>>> + mutex_lock(&temp_sensor->sensor_mutex);
>>> +
>>> + /* Read the status of t_hot */
>>> + t_hot = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_status)
>>> + & temp_sensor->registers->status_hot_mask;
>>> +
>>> + /* Read the status of t_cold */
>>> + t_cold = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_status)
>>> + & temp_sensor->registers->status_cold_mask;
>>> +
>>> + temp = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_mask_ctrl);
>>> + /*
>>> + * One TALERT interrupt: Two sources
>>> + * If the interrupt is due to t_hot then mask t_hot and
>>> + * and unmask t_cold else mask t_cold and unmask t_hot
>>> + */
>>> + if (t_hot) {
>>> + temp &= ~(temp_sensor->registers->mask_hot_mask);
>>> + temp |= temp_sensor->registers->mask_cold_mask;
>>> + } else if (t_cold) {
>>> + temp &= ~(temp_sensor->registers->mask_cold_mask);
>>> + temp |= temp_sensor->registers->mask_hot_mask;
>>> + }
>>> +
>>> + omap_temp_sensor_writel(temp_sensor, temp,
>>> + temp_sensor->registers->bgap_mask_ctrl);
>>> +
>>> + /* kobject_uvent to user space telling thermal threshold crossed */
>>> + kobject_uevent(&temp_sensor->hwmon_dev->kobj, KOBJ_CHANGE);
>>> +
>>> + mutex_unlock(&temp_sensor->sensor_mutex);
>>> +
>>> + return IRQ_HANDLED;
>>> +}
>>> +
>>> +static int __devinit omap_temp_sensor_probe(struct platform_device *pdev)
>>> +{
>>> + struct omap_temp_sensor_pdata *pdata = pdev->dev.platform_data;
>>> + struct omap_temp_sensor *temp_sensor;
>>> + struct resource *mem;
>>> + int ret = 0;
>>> + int val, clk_rate;
>>> +
>>> + if (!pdata) {
>>> + dev_err(&pdev->dev, "platform data missing\n");
>>> + return -EINVAL;
>>> + }
>>> +
>>> + temp_sensor = kzalloc(sizeof(*temp_sensor), GFP_KERNEL);
>>> + if (!temp_sensor)
>>> + return -ENOMEM;
>>> +
>>> + mutex_init(&temp_sensor->sensor_mutex);
>>> +
>>> + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>>> + if (!mem) {
>>> + dev_err(&pdev->dev, "no mem resource\n");
>>> + ret = -ENOMEM;
>>> + goto plat_res_err;
>>> + }
>>> +
>>> + temp_sensor->irq = platform_get_irq_byname(pdev, "thermal_alert");
>>> + if (temp_sensor->irq < 0) {
>>> + dev_err(&pdev->dev, "get_irq_byname failed\n");
>>> + ret = temp_sensor->irq;
>>> + goto plat_res_err;
>>> + }
>>> +
>>> + temp_sensor->phy_base = ioremap(mem->start, resource_size(mem));
>>> + temp_sensor->clock = NULL;
>>> + temp_sensor->registers = pdata->registers;
>>> + temp_sensor->hwmon_dev = &pdev->dev;
>>> +
>>> + pm_runtime_enable(&pdev->dev);
>>> + pm_runtime_irq_safe(&pdev->dev);
>>> +
>>> + /*
>>> + * check if the efuse has a non-zero value if not
>>> + * it is an untrimmed sample and the temperatures
>>> + * may not be accurate
>>> + */
>>> +
>>> + if (omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_efuse))
>>> + temp_sensor->is_efuse_valid = 1;
>>> +
>>> + platform_set_drvdata(pdev, temp_sensor);
>>> + dev_set_drvdata(&pdev->dev, temp_sensor);
>>> + temp_sensor->clock = clk_get(temp_sensor->hwmon_dev, "fck");
>>> + if (IS_ERR(temp_sensor->clock)) {
>>> + ret = PTR_ERR(temp_sensor->clock);
>>> + dev_err(temp_sensor->hwmon_dev,
>>> + "unable to get fclk: %d\n", ret);
>>> + ret = -EINVAL;
>>> + goto plat_res_err;
>>> + }
>>> +
>>> + ret = omap_temp_sensor_clk_enable(temp_sensor);
>>> + if (ret) {
>>> + dev_err(&pdev->dev, "Cannot enable temp sensor\n");
>>> + goto clken_err;
>>> + }
>>> +
>>> + clk_rate = clk_round_rate(temp_sensor->clock, 2000000);
>>> + if (clk_rate < 1000000 || clk_rate = 0xffffffff) {
>>> + dev_err(&pdev->dev, "Error round rate\n");
>>> + ret = -EINVAL;
>>> + goto clken_err;
>>> + }
>>> +
>>> + ret = clk_set_rate(temp_sensor->clock, clk_rate);
>>> + if (ret) {
>>> + dev_err(&pdev->dev, "Cannot set clock rate\n");
>>> + goto clken_err;
>>> + }
>>> +
>>> + temp_sensor->clk_rate = clk_rate;
>>> + omap_enable_continuous_mode(temp_sensor, 1);
>>> + omap_configure_temp_sensor_thresholds(temp_sensor);
>>> + /* 1 ms */
>>> + omap_configure_temp_sensor_counter(temp_sensor, 1);
>>> +
>>> + /* Wait till the first conversion is done wait for at least 1ms */
>>> + usleep_range(1000, 2000);
>>> +
>>> + /* Read the temperature once due to hw issue*/
>>> + omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->temp_sensor_ctrl);
>>> +
>>> + /* Set 2 seconds time as default counter */
>>> + omap_configure_temp_sensor_counter(temp_sensor,
>>> + temp_sensor->clk_rate * 2);
>>> +
>>> + temp_sensor->hwmon_dev = hwmon_device_register(&pdev->dev);
>>> + if (IS_ERR(temp_sensor->hwmon_dev)) {
>>> + dev_err(&pdev->dev, "hwmon_device_register failed.\n");
>>> + ret = PTR_ERR(temp_sensor->hwmon_dev);
>>> + goto hwmon_reg_err;
>>> + }
>>> +
>>> + ret = sysfs_create_group(&pdev->dev.kobj,
>>> + &omap_temp_sensor_group);
>>> + if (ret) {
>>> + dev_err(&pdev->dev, "could not create sysfs files\n");
>>> + goto sysfs_create_err;
>>> + }
>>> +
>>> + kobject_uevent(&temp_sensor->hwmon_dev->kobj, KOBJ_ADD);
>>> +
>>> + ret = request_threaded_irq(temp_sensor->irq, NULL,
>>> + omap_talert_irq_handler, IRQF_TRIGGER_RISING | IRQF_ONESHOT,
>>> + "temp_sensor", temp_sensor);
>>> + if (ret) {
>>> + dev_err(&pdev->dev, "Request threaded irq failed.\n");
>>> + goto req_irq_err;
>>> + }
>>> +
>>> + /* unmask the T_COLD and unmask T_HOT at init */
>>> + val = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_mask_ctrl);
>>> + val |= temp_sensor->registers->mask_cold_mask
>>> + | temp_sensor->registers->mask_hot_mask;
>>> +
>>> + omap_temp_sensor_writel(temp_sensor, val,
>>> + temp_sensor->registers->bgap_mask_ctrl);
>>> +
>>> + return 0;
>>> +
>>> +req_irq_err:
>>> + kobject_uevent(&temp_sensor->hwmon_dev->kobj, KOBJ_REMOVE);
>>> + sysfs_remove_group(&temp_sensor->hwmon_dev->kobj,
>>> + &omap_temp_sensor_group);
>>> +sysfs_create_err:
>>> + hwmon_device_unregister(&pdev->dev);
>>> +hwmon_reg_err:
>>> + omap_temp_sensor_clk_disable(temp_sensor);
>>> +clken_err:
>>> + clk_put(temp_sensor->clock);
>>> +plat_res_err:
>>> + mutex_destroy(&temp_sensor->sensor_mutex);
>>> + kfree(temp_sensor);
>>> + return ret;
>>> +}
>>> +
>>> +static int __devexit omap_temp_sensor_remove(struct platform_device *pdev)
>>> +{
>>> + struct omap_temp_sensor *temp_sensor = platform_get_drvdata(pdev);
>>> +
>>> + hwmon_device_unregister(&pdev->dev);
>>> + kobject_uevent(&temp_sensor->hwmon_dev->kobj, KOBJ_REMOVE);
>>> + sysfs_remove_group(&temp_sensor->hwmon_dev->kobj,
>>> + &omap_temp_sensor_group);
>>> + omap_temp_sensor_clk_disable(temp_sensor);
>>> + clk_put(temp_sensor->clock);
>>> + platform_set_drvdata(pdev, NULL);
>>> + free_irq(temp_sensor->irq, temp_sensor);
>>> + mutex_destroy(&temp_sensor->sensor_mutex);
>>> + kfree(temp_sensor);
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +#ifdef CONFIG_PM
>>> +static void omap_temp_sensor_save_ctxt(struct omap_temp_sensor *temp_sensor)
>>> +{
>>> + temp_sensor->temp_sensor_ctrl = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->temp_sensor_ctrl);
>>> + temp_sensor->bg_ctrl = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_mask_ctrl);
>>> + temp_sensor->bg_counter = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_counter);
>>> + temp_sensor->bg_threshold = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_threshold);
>>> + temp_sensor->temp_sensor_tshut_threshold >>> + omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->thsut_threshold);
>>> +}
>>> +
>>> +static void omap_temp_sensor_restore_ctxt(struct omap_temp_sensor *temp_sensor)
>>> +{
>>> + omap_temp_sensor_writel(temp_sensor,
>>> + temp_sensor->temp_sensor_ctrl,
>>> + temp_sensor->registers->temp_sensor_ctrl);
>>> + omap_temp_sensor_writel(temp_sensor,
>>> + temp_sensor->bg_ctrl,
>>> + temp_sensor->registers->bgap_mask_ctrl);
>>> + omap_temp_sensor_writel(temp_sensor,
>>> + temp_sensor->bg_counter,
>>> + temp_sensor->registers->bgap_counter);
>>> + omap_temp_sensor_writel(temp_sensor,
>>> + temp_sensor->bg_threshold,
>>> + temp_sensor->registers->bgap_threshold);
>>> + omap_temp_sensor_writel(temp_sensor,
>>> + temp_sensor->temp_sensor_tshut_threshold,
>>> + temp_sensor->registers->thsut_threshold);
>>> +}
>>> +
>>> +static int omap_temp_sensor_suspend(struct device *dev)
>>> +{
>>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>>> +
>>> + omap_temp_sensor_save_ctxt(temp_sensor);
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static int omap_temp_sensor_resume(struct device *dev)
>>> +{
>>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>>> +
>>> + omap_temp_sensor_restore_ctxt(temp_sensor);
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static int omap_temp_sensor_runtime_suspend(struct device *dev)
>>> +{
>>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>>> +
>>> + omap_temp_sensor_save_ctxt(temp_sensor);
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static int omap_temp_sensor_runtime_resume(struct device *dev)
>>> +{
>>> + static int context_loss_count;
>>> + int temp;
>>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>>> +
>>> + temp = omap_device_get_context_loss_count(to_platform_device(dev));
>>> +
>>> + if (temp != context_loss_count && context_loss_count != 0)
>>> + omap_temp_sensor_restore_ctxt(temp_sensor);
>>> +
>>> + context_loss_count = temp;
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static const struct dev_pm_ops omap_temp_sensor_dev_pm_ops = {
>>> + .suspend = omap_temp_sensor_suspend,
>>> + .resume = omap_temp_sensor_resume,
>>> + .runtime_suspend = omap_temp_sensor_runtime_suspend,
>>> + .runtime_resume = omap_temp_sensor_runtime_resume,
>>> +};
>>> +
>>> +#endif
>>> +
>>> +static struct platform_driver omap_temp_sensor_driver = {
>>> + .probe = omap_temp_sensor_probe,
>>> + .remove = omap_temp_sensor_remove,
>>> + .driver = {
>>> + .name = "omap_temp_sensor",
>>> +#ifdef CONFIG_PM
>>> + .pm = &omap_temp_sensor_dev_pm_ops,
>>> +#endif
>>> + },
>>> +};
>>> +
>>> +int __init omap_temp_sensor_init(void)
>>> +{
>>> + return platform_driver_register(&omap_temp_sensor_driver);
>>> +}
>>> +module_init(omap_temp_sensor_init);
>>> +
>>> +static void __exit omap_temp_sensor_exit(void)
>>> +{
>>> + platform_driver_unregister(&omap_temp_sensor_driver);
>>> +}
>>> +module_exit(omap_temp_sensor_exit);
>>> +
>>> +MODULE_DESCRIPTION("OMAP446X temperature sensor Driver");
>>> +MODULE_LICENSE("GPL");
>>> +MODULE_ALIAS("platform:" DRIVER_NAME);
>>> +MODULE_AUTHOR("J Keerthy <j-keerthy@ti.com>");
>>> --
>>> 1.7.0.4
>>>
>>
>
>
>
> --
> Regards and Thanks,
> Keerthy
>
--
Regards and Thanks,
Keerthy
_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors
^ permalink raw reply [flat|nested] 35+ messages in thread
* Re: [RFC PATCH 6/6 V2] hwmon: OMAP4: On die temperature sensor driver
2011-08-19 6:16 ` [lm-sensors] [RFC PATCH 6/6 V2] hwmon: OMAP4: On die J, KEERTHY
@ 2011-08-19 12:55 ` J, KEERTHY
-1 siblings, 0 replies; 35+ messages in thread
From: J, KEERTHY @ 2011-08-19 12:53 UTC (permalink / raw)
To: Guenter Roeck; +Cc: linux-omap, Jean Delvare, lm-sensors
On Fri, Aug 19, 2011 at 11:34 AM, J, KEERTHY <j-keerthy@ti.com> wrote:
> On Fri, Aug 19, 2011 at 7:43 AM, Guenter Roeck
> <guenter.roeck@ericsson.com> wrote:
>> On Thu, Aug 18, 2011 at 06:52:15AM -0400, Keerthy wrote:
>>> On chip temperature sensor driver. The driver monitors the temperature of
>>> the MPU subsystem of the OMAP4. It sends notifications to the user space if
>>> the temperature crosses user defined thresholds via kobject_uevent interface.
>>> The user is allowed to configure the temperature thresholds vis sysfs nodes
>>> exposed using hwmon interface.
>>>
>>> Signed-off-by: Keerthy <j-keerthy@ti.com>
>>> Cc: Jean Delvare <khali@linux-fr.org>
>>> Cc: Guenter Roeck <guenter.roeck@ericsson.com>
>>> Cc: lm-sensors@lm-sensors.org
>>
>> High level review:
>>
>> - too much and too broad mutex locking. show functions should not need locks at all,
>> set functions only while data is written into registers and into platform data.
>
> Ok. I will clean this.
Even the show function might read an inconsistent value without
a mutex lock if the registers were written using set function was
preempted. There is a necessity for mutex locking even in show function.
>
>> - driver is quite noisy. There should definitely not be any log messages
>> if a set parameter is wrong. Show functions already return an error value
>> to the user; a log message indicating the error again just creates noise.
>> For one boolean set during probe (is_efuse_valid), each subsequent show results
>> in a log message if it is false. Some errors result in multiple log messages.
>
> A user tries to set an invalid temperature threshold. The user should
> be notified about this. The invalid temperature will not be set. The user
> should not be allowed to set an invalid temperature. It is to inform
> the user about precisely the problem with the parameter.
>
> In some of the samples the bandgap is not trimmed and hence
> temperature reported will be wrong. So every time a user tries to read
> he is alerted that the temperatures are not accurate.
>
>> - Wrong use of EINVAL throughout the driver (EINVAL is "Invalid Argument")
>
> Ok. I will correct this.
>
>> - excessive ( )
>
> Ok.
>
>> - linear search through a sorted array is very expensive. Consider using a binary search.
>
> Yes. I will implement binary search.
>
>> - temp_to_adc_conversion return code (error if negative) is not checked properly.
>
> Ok. I will add checks.
>
>>
>> I am sure there are other problems, but those are difficult to find with all the noise
>> above.
>>
>> Guenter
>>
>>> ---
>>> Documentation/hwmon/omap_temp_sensor | 27 +
>>> drivers/hwmon/Kconfig | 11 +
>>> drivers/hwmon/Makefile | 1 +
>>> drivers/hwmon/omap_temp_sensor.c | 918 ++++++++++++++++++++++++++++++++++
>>> 4 files changed, 957 insertions(+), 0 deletions(-)
>>> create mode 100644 Documentation/hwmon/omap_temp_sensor
>>> create mode 100644 drivers/hwmon/omap_temp_sensor.c
>>>
>>> diff --git a/Documentation/hwmon/omap_temp_sensor b/Documentation/hwmon/omap_temp_sensor
>>> new file mode 100644
>>> index 0000000..e01a6d6
>>> --- /dev/null
>>> +++ b/Documentation/hwmon/omap_temp_sensor
>>> @@ -0,0 +1,27 @@
>>> +Kernel driver omap_temp_sensor
>>> +==============================
>>> +
>>> +Supported chips:
>>> + * Texas Instruments OMAP4460
>>> + Prefix: 'omap_temp_sensor'
>>> + Datasheet: http://focus.ti.com/docs/prod/folders/print/tmp102.html
>>> +
>>> +Author:
>>> + J Keerthy <j-keerthy@ti.com>
>>> +
>>> +Description
>>> +-----------
>>> +
>>> +The Texas Instruments OMAP4 family of chips have a bandgap temperature sensor.
>>> +The temperature sensor feature is used to convert the temperature of the device
>>> +into a decimal value coded on 10 bits. An internal ADC is used for conversion.
>>> +The recommended operating temperatures must be in the range -40 degree Celsius
>>> +to 123 degree celsius for standard conversion.
>>> +The thresholds are programmable and upon crossing the thresholds an interrupt
>>> +is generated. The OMAP temperature sensor has a programmable update rate in
>>> +milli seconds.
>>> +(Currently the driver programs a default of 2000 milli seconds).
>>> +
>>> +The driver provides the common sysfs-interface for temperatures (see
>>> +Documentation/hwmon/sysfs-interface under Temperatures).
>>> +
>>> diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
>>> index 5f888f7..9c9cd8b 100644
>>> --- a/drivers/hwmon/Kconfig
>>> +++ b/drivers/hwmon/Kconfig
>>> @@ -323,6 +323,17 @@ config SENSORS_F71805F
>>> This driver can also be built as a module. If so, the module
>>> will be called f71805f.
>>>
>>> +config SENSORS_OMAP_BANDGAP_TEMP_SENSOR
>>> + bool "OMAP on-die temperature sensor hwmon driver"
>>> + depends on HWMON && ARCH_OMAP && OMAP_TEMP_SENSOR
>>> + help
>>> + If you say yes here you get support for hardware
>>> + monitoring features of the OMAP on die temperature
>>> + sensor.
>>> +
>>> + Continuous conversion programmable delay
>>> + mode is used for temperature conversion.
>>> +
>>> config SENSORS_F71882FG
>>> tristate "Fintek F71882FG and compatibles"
>>> help
>>> diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
>>> index 28061cf..d0f89f5 100644
>>> --- a/drivers/hwmon/Makefile
>>> +++ b/drivers/hwmon/Makefile
>>> @@ -91,6 +91,7 @@ obj-$(CONFIG_SENSORS_MAX6639) += max6639.o
>>> obj-$(CONFIG_SENSORS_MAX6642) += max6642.o
>>> obj-$(CONFIG_SENSORS_MAX6650) += max6650.o
>>> obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o
>>> +obj-$(CONFIG_SENSORS_OMAP_BANDGAP_TEMP_SENSOR) += omap_temp_sensor.o
>>> obj-$(CONFIG_SENSORS_PC87360) += pc87360.o
>>> obj-$(CONFIG_SENSORS_PC87427) += pc87427.o
>>> obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o
>>> diff --git a/drivers/hwmon/omap_temp_sensor.c b/drivers/hwmon/omap_temp_sensor.c
>>> new file mode 100644
>>> index 0000000..586a361
>>> --- /dev/null
>>> +++ b/drivers/hwmon/omap_temp_sensor.c
>>> @@ -0,0 +1,918 @@
>>> +/*
>>> + * OMAP4 Temperature sensor driver file
>>> + *
>>> + * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
>>> + * Author: J Keerthy <j-keerthy@ti.com>
>>> + * Author: Moiz Sonasath <m-sonasath@ti.com>
>>> + *
>>> + * This program is free software; you can redistribute it and/or
>>> + * modify it under the terms of the GNU General Public License
>>> + * 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.
>>> + *
>>> + * You should have received a copy of the GNU General Public License
>>> + * along with this program; if not, write to the Free Software
>>> + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
>>> + * 02110-1301 USA
>>> + *
>>> + */
>>> +
>>> +#include <linux/interrupt.h>
>>> +#include <linux/clk.h>
>>> +#include <linux/io.h>
>>> +#include <linux/slab.h>
>>> +#include <linux/init.h>
>>> +#include <plat/omap_device.h>
>>> +#include <linux/kernel.h>
>>> +#include <linux/device.h>
>>> +#include <linux/jiffies.h>
>>> +#include <linux/hwmon.h>
>>> +#include <linux/hwmon-sysfs.h>
>>> +#include <linux/stddef.h>
>>> +#include <linux/sysfs.h>
>>> +#include <linux/err.h>
>>> +#include <linux/types.h>
>>> +#include <linux/mutex.h>
>>> +#include <linux/pm_runtime.h>
>>> +#include <linux/delay.h>
>>> +#include <plat/temperature_sensor.h>
>>> +
>>> +#define TSHUT_THRESHOLD_HOT 122000 /* 122 deg C */
>>> +#define TSHUT_THRESHOLD_COLD 100000 /* 100 deg C */
>>> +#define BGAP_THRESHOLD_T_HOT 73000 /* 73 deg C */
>>> +#define BGAP_THRESHOLD_T_COLD 71000 /* 71 deg C */
>>> +#define OMAP_ADC_START_VALUE 530
>>> +#define OMAP_ADC_END_VALUE 923
>>> +
>>> +/*
>>> + * omap_temp_sensor structure
>>> + * @hwmon_dev - device pointer
>>> + * @clock - Clock pointer
>>> + * @registers - Pointer to structure with register offsets and bitfields
>>> + * @sensor_mutex - Mutex for sysfs, irq and PM
>>> + * @irq - MPU Irq number for thermal alert
>>> + * @phy_base - Physical base of the temp I/O
>>> + * @clk_rate - Holds current clock rate
>>> + * @temp_sensor_ctrl - temp sensor control register value
>>> + * @bg_ctrl - bandgap ctrl register value
>>> + * @bg_counter - bandgap counter value
>>> + * @bg_threshold - bandgap threshold register value
>>> + * @temp_sensor_tshut_threshold - bandgap tshut register value
>>> + * @is_efuse_valid - Flag to determine if efuse is valid or not
>>> + * @clk_on - Manages the current clock state
>>> + */
>>> +struct omap_temp_sensor {
>>> + struct device *hwmon_dev;
>>> + struct clk *clock;
>>> + struct omap_temp_sensor_registers *registers;
>>> + struct mutex sensor_mutex; /* Mutex for sysfs, irq and PM */
>>> + unsigned int irq;
>>> + void __iomem *phy_base;
>>> + u32 clk_rate;
>>> + u32 temp_sensor_ctrl;
>>> + u32 bg_ctrl;
>>> + u32 bg_counter;
>>> + u32 bg_threshold;
>>> + u32 temp_sensor_tshut_threshold;
>>> + bool is_efuse_valid;
>>> + bool clk_on;
>>> +};
>>> +
>>> +/*
>>> + * Temperature values in milli degree celsius
>>> + * ADC code values from 530 to 923
>>> + */
>>> +static int adc_to_temp[] = {
>>> + -40000, -40000, -40000, -40000, -39800, -39400, -39000, -38600, -38200,
>>> + -37800, -37300, -36800, -36400, -36000, -35600, -35200, -34800,
>>> + -34300, -33800, -33400, -33000, -32600, -32200, -31800, -31300,
>>> + -30800, -30400, -30000, -29600, -29200, -28700, -28200, -27800,
>>> + -27400, -27000, -26600, -26200, -25700, -25200, -24800, -24400,
>>> + -24000, -23600, -23200, -22700, -22200, -21800, -21400, -21000,
>>> + -20600, -20200, -19700, -19200, -18800, -18400, -18000, -17600,
>>> + -17200, -16700, -16200, -15800, -15400, -15000, -14600, -14200,
>>> + -13700, -13200, -12800, -12400, -12000, -11600, -11200, -10700,
>>> + -10200, -9800, -9400, -9000, -8600, -8200, -7700, -7200, -6800,
>>> + -6400, -6000, -5600, -5200, -4800, -4300, -3800, -3400, -3000,
>>> + -2600, -2200, -1800, -1300, -800, -400, 0, 400, 800, 1200, 1600,
>>> + 2100, 2600, 3000, 3400, 3800, 4200, 4600, 5100, 5600, 6000, 6400,
>>> + 6800, 7200, 7600, 8000, 8500, 9000, 9400, 9800, 10200, 10600, 11000,
>>> + 11400, 11900, 12400, 12800, 13200, 13600, 14000, 14400, 14800,
>>> + 15300, 15800, 16200, 16600, 17000, 17400, 17800, 18200, 18700,
>>> + 19200, 19600, 20000, 20400, 20800, 21200, 21600, 22100, 22600,
>>> + 23000, 23400, 23800, 24200, 24600, 25000, 25400, 25900, 26400,
>>> + 26800, 27200, 27600, 28000, 28400, 28800, 29300, 29800, 30200,
>>> + 30600, 31000, 31400, 31800, 32200, 32600, 33100, 33600, 34000,
>>> + 34400, 34800, 35200, 35600, 36000, 36400, 36800, 37300, 37800,
>>> + 38200, 38600, 39000, 39400, 39800, 40200, 40600, 41100, 41600,
>>> + 42000, 42400, 42800, 43200, 43600, 44000, 44400, 44800, 45300,
>>> + 45800, 46200, 46600, 47000, 47400, 47800, 48200, 48600, 49000,
>>> + 49500, 50000, 50400, 50800, 51200, 51600, 52000, 52400, 52800,
>>> + 53200, 53700, 54200, 54600, 55000, 55400, 55800, 56200, 56600,
>>> + 57000, 57400, 57800, 58200, 58700, 59200, 59600, 60000, 60400,
>>> + 60800, 61200, 61600, 62000, 62400, 62800, 63300, 63800, 64200,
>>> + 64600, 65000, 65400, 65800, 66200, 66600, 67000, 67400, 67800,
>>> + 68200, 68700, 69200, 69600, 70000, 70400, 70800, 71200, 71600,
>>> + 72000, 72400, 72800, 73200, 73600, 74100, 74600, 75000, 75400,
>>> + 75800, 76200, 76600, 77000, 77400, 77800, 78200, 78600, 79000,
>>> + 79400, 79800, 80300, 80800, 81200, 81600, 82000, 82400, 82800,
>>> + 83200, 83600, 84000, 84400, 84800, 85200, 85600, 86000, 86400,
>>> + 86800, 87300, 87800, 88200, 88600, 89000, 89400, 89800, 90200,
>>> + 90600, 91000, 91400, 91800, 92200, 92600, 93000, 93400, 93800,
>>> + 94200, 94600, 95000, 95500, 96000, 96400, 96800, 97200, 97600,
>>> + 98000, 98400, 98800, 99200, 99600, 100000, 100400, 100800, 101200,
>>> + 101600, 102000, 102400, 102800, 103200, 103600, 104000, 104400,
>>> + 104800, 105200, 105600, 106100, 106600, 107000, 107400, 107800,
>>> + 108200, 108600, 109000, 109400, 109800, 110200, 110600, 111000,
>>> + 111400, 111800, 112200, 112600, 113000, 113400, 113800, 114200,
>>> + 114600, 115000, 115400, 115800, 116200, 116600, 117000, 117400,
>>> + 117800, 118200, 118600, 119000, 119400, 119800, 120200, 120600,
>>> + 121000, 121400, 121800, 122200, 122600, 123000
>>> +};
>>> +
>>> +static unsigned long omap_temp_sensor_readl(struct omap_temp_sensor
>>> + *temp_sensor, u32 reg)
>>> +{
>>> + return __raw_readl(temp_sensor->phy_base + reg);
>>> +}
>>> +
>>> +static void omap_temp_sensor_writel(struct omap_temp_sensor *temp_sensor,
>>> + u32 val, u32 reg)
>>> +{
>>> + __raw_writel(val, (temp_sensor->phy_base + reg));
>>> +}
>>> +
>>> +static int adc_to_temp_conversion(int adc_val)
>>> +{
>>> + return adc_to_temp[adc_val - OMAP_ADC_START_VALUE];
>>> +}
>>> +
>>> +static int temp_to_adc_conversion(long temp)
>>> +{
>>> + int i;
>>> +
>>> + for (i = 0; i <= OMAP_ADC_END_VALUE - OMAP_ADC_START_VALUE; i++)
>>> + if (temp < adc_to_temp[i])
>>> + return OMAP_ADC_START_VALUE + i - 1;
>>> +
>>> + return -EINVAL;
>>> +}
>>> +
>>> +static void omap_configure_temp_sensor_thresholds(struct omap_temp_sensor
>>> + *temp_sensor)
>>> +{
>>> + u32 temp, t_hot, t_cold, tshut_hot, tshut_cold;
>>> +
>>> + t_hot = temp_to_adc_conversion(BGAP_THRESHOLD_T_HOT);
>>> + t_cold = temp_to_adc_conversion(BGAP_THRESHOLD_T_COLD);
>>> +
>>> + /* Configure the TALERT thresholds */
>>> + temp = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_threshold);
>>> + temp |= (t_hot << __ffs(temp_sensor->registers->threshold_thot_mask)) |
>>> + (t_cold << __ffs(temp_sensor->registers->threshold_tcold_mask));
>>> + omap_temp_sensor_writel(temp_sensor, temp,
>>> + temp_sensor->registers->bgap_threshold);
>>> +
>>> + tshut_hot = temp_to_adc_conversion(TSHUT_THRESHOLD_HOT);
>>> + tshut_cold = temp_to_adc_conversion(TSHUT_THRESHOLD_COLD);
>>> +
>>> + /* Configure the TSHUT thresholds */
>>> + temp = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->thsut_threshold);
>>> + temp |= (tshut_hot << __ffs(temp_sensor->registers->tshut_hot_mask))
>>> + | (tshut_cold << __ffs(temp_sensor->registers->tshut_hot_mask));
>>> + omap_temp_sensor_writel(temp_sensor, temp,
>>> + temp_sensor->registers->thsut_threshold);
>>> +}
>>> +
>>> +static void omap_configure_temp_sensor_counter(struct omap_temp_sensor
>>> + *temp_sensor, u32 counter)
>>> +{
>>> + u32 val;
>>> +
>>> + val = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_counter);
>>> + val &= ~(temp_sensor->registers->counter_mask);
>>> + val |= (counter << __ffs(temp_sensor->registers->counter_mask));
>>> + omap_temp_sensor_writel(temp_sensor, val,
>>> + temp_sensor->registers->bgap_counter);
>>> +}
>>> +
>>> +static void omap_enable_continuous_mode(struct omap_temp_sensor *temp_sensor,
>>> + bool enable)
>>> +{
>>> + u32 val;
>>> +
>>> + val = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_mode_ctrl);
>>> +
>>> + if (enable)
>>> + val |= (1 << __ffs(temp_sensor->registers->mode_ctrl_mask));
>>> + else
>>> + val &= ~(temp_sensor->registers->mode_ctrl_mask);
>>> +
>>> + omap_temp_sensor_writel(temp_sensor, val,
>>> + temp_sensor->registers->bgap_mode_ctrl);
>>> +}
>>> +
>>> +/* Sysfs hook functions */
>>> +
>>> +static ssize_t show_temp_max(struct device *dev,
>>> + struct device_attribute *devattr, char *buf)
>>> +{
>>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>>> + int temp;
>>> +
>>> + mutex_lock(&temp_sensor->sensor_mutex);
>>> +
>>> + temp = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_threshold);
>>> + temp = (temp & temp_sensor->registers->threshold_thot_mask)
>>> + >> __ffs(temp_sensor->registers->threshold_thot_mask);
>>> +
>>> + if (temp < OMAP_ADC_START_VALUE || temp > OMAP_ADC_END_VALUE) {
>>> + dev_err(dev, "invalid value\n");
>>> + mutex_unlock(&temp_sensor->sensor_mutex);
>>> + return -EINVAL;
>>> + }
>>> +
>>> + temp = adc_to_temp_conversion(temp);
>>> + mutex_unlock(&temp_sensor->sensor_mutex);
>>> +
>>> + return snprintf(buf, 16, "%d\n", temp);
>>> +}
>>> +
>>> +static ssize_t set_temp_max(struct device *dev,
>>> + struct device_attribute *devattr,
>>> + const char *buf, size_t count)
>>> +{
>>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>>> + long val;
>>> + u32 reg_val, t_cold, t_hot, temp;
>>> +
>>> + mutex_lock(&temp_sensor->sensor_mutex);
>>> +
>>> + if (strict_strtol(buf, 10, &val)) {
>>> + count = -EINVAL;
>>> + goto out;
>>> + }
>>> +
>>> + t_hot = temp_to_adc_conversion(val);
>>> + if ((t_hot < OMAP_ADC_START_VALUE || t_hot > OMAP_ADC_END_VALUE)) {
>>> + dev_err(dev, "invalid range\n");
>>> + count = -EINVAL;
>>> + goto out;
>>> + }
>>> +
>>> + /* obtain the T cold value */
>>> + t_cold = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_threshold);
>>> + t_cold = (t_cold & temp_sensor->registers->threshold_tcold_mask) >>
>>> + __ffs(temp_sensor->registers->threshold_tcold_mask);
>>> +
>>> + if (t_hot < t_cold) {
>>> + dev_err(dev, "Error! T_HOT value lesser than T_COLD\n");
>>> + count = -EINVAL;
>>> + goto out;
>>> + }
>>> +
>>> + /* write the new t_hot value */
>>> + reg_val = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_threshold);
>>> + reg_val &= ~(temp_sensor->registers->threshold_thot_mask);
>>> + reg_val |= (t_hot <<
>>> + __ffs(temp_sensor->registers->threshold_thot_mask));
>>> + omap_temp_sensor_writel(temp_sensor, reg_val,
>>> + temp_sensor->registers->bgap_threshold);
>>> +
>>> + /* Read the current temperature */
>>> + temp = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->temp_sensor_ctrl);
>>> + temp &= (temp_sensor->registers->bgap_dtemp_mask);
>>> +
>>> + /*
>>> + * If user sets the HIGH threshold(t_hot) greater than the current
>>> + * temperature(temp) unmask the HOT interrupts
>>> + */
>>> + if (t_hot > temp) {
>>> + reg_val = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_mask_ctrl);
>>> + reg_val &= ~(temp_sensor->registers->mask_cold_mask);
>>> + reg_val |= temp_sensor->registers->mask_hot_mask;
>>> + omap_temp_sensor_writel(temp_sensor, reg_val,
>>> + temp_sensor->registers->bgap_mask_ctrl);
>>> + }
>>> +
>>> + /*
>>> + * If current temperature is in-between the hot and cold thresholds,
>>> + * Enable both masks.
>>> + */
>>> + if (temp > t_cold && temp < t_hot) {
>>> + reg_val = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_mask_ctrl);
>>> + reg_val |= temp_sensor->registers->mask_cold_mask;
>>> + reg_val |= temp_sensor->registers->mask_hot_mask;
>>> + omap_temp_sensor_writel(temp_sensor, reg_val,
>>> + OMAP4460_BGAP_CTRL_OFFSET);
>>> + }
>>> + /*
>>> + * else no need to do anything since HW will immediately compare
>>> + * the new threshold and generate interrupt accordingly
>>> + */
>>> +out:
>>> + mutex_unlock(&temp_sensor->sensor_mutex);
>>> + return count;
>>> +}
>>> +
>>> +static ssize_t show_temp_max_hyst(struct device *dev,
>>> + struct device_attribute *devattr, char *buf)
>>> +{
>>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>>> + u32 temp;
>>> +
>>> + mutex_lock(&temp_sensor->sensor_mutex);
>>> +
>>> + temp = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_threshold);
>>> + temp = (temp & temp_sensor->registers->threshold_tcold_mask) >>
>>> + __ffs(temp_sensor->registers->threshold_tcold_mask);
>>> +
>>> + if (temp < OMAP_ADC_START_VALUE || temp > OMAP_ADC_END_VALUE) {
>>> + dev_err(dev, "invalid value\n");
>>> + mutex_unlock(&temp_sensor->sensor_mutex);
>>> + return -EINVAL;
>>> + }
>>> +
>>> + temp = adc_to_temp_conversion(temp);
>>> + mutex_unlock(&temp_sensor->sensor_mutex);
>>> +
>>> + return snprintf(buf, 16, "%d\n", temp);
>>> +}
>>> +
>>> +static ssize_t set_temp_max_hyst(struct device *dev,
>>> + struct device_attribute *devattr,
>>> + const char *buf, size_t count)
>>> +{
>>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>>> + u32 reg_val, t_hot, t_cold, temp;
>>> + long val;
>>> +
>>> + mutex_lock(&temp_sensor->sensor_mutex);
>>> +
>>> + if (strict_strtol(buf, 10, &val)) {
>>> + count = -EINVAL;
>>> + goto out;
>>> + }
>>> +
>>> + t_cold = temp_to_adc_conversion(val);
>>> + if (t_cold < OMAP_ADC_START_VALUE || t_cold > OMAP_ADC_END_VALUE) {
>>> + dev_err(dev, "invalid range");
>>> + count = -EINVAL;
>>> + goto out;
>>> + }
>>> +
>>> + /* obtain the T HOT value */
>>> + t_hot = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_threshold);
>>> + t_hot = (t_hot & temp_sensor->registers->threshold_thot_mask) >>
>>> + __ffs(temp_sensor->registers->threshold_thot_mask);
>>> +
>>> + if (t_cold > t_hot) {
>>> + dev_err(dev, "Error! T_COLD value greater than T_HOT\n");
>>> + count = -EINVAL;
>>> + goto out;
>>> + }
>>> +
>>> + /* write the new t_cold value */
>>> + reg_val = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_threshold);
>>> + reg_val &= ~(temp_sensor->registers->threshold_tcold_mask);
>>> + reg_val |= (t_cold <<
>>> + __ffs(temp_sensor->registers->threshold_tcold_mask));
>>> + omap_temp_sensor_writel(temp_sensor, reg_val,
>>> + temp_sensor->registers->bgap_threshold);
>>> +
>>> + /* Read the current temperature */
>>> + temp = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->temp_sensor_ctrl);
>>> + temp &= (temp_sensor->registers->bgap_dtemp_mask);
>>> +
>>> + /*
>>> + * If user sets the LOW threshold(t_cold) lower than the current
>>> + * temperature(temp) unmask the COLD interrupts
>>> + */
>>> + if (t_cold < temp) {
>>> + reg_val = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_mask_ctrl);
>>> + reg_val &= ~(temp_sensor->registers->mask_hot_mask);
>>> + reg_val |= temp_sensor->registers->mask_cold_mask;
>>> + omap_temp_sensor_writel(temp_sensor, reg_val,
>>> + temp_sensor->registers->bgap_mask_ctrl);
>>> + }
>>> +
>>> + /*
>>> + * If current temperature is in-between the hot and cold thresholds,
>>> + * Enable both masks.
>>> + */
>>> + if (temp < t_hot && temp > t_cold) {
>>> + reg_val = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_mask_ctrl);
>>> + reg_val |= temp_sensor->registers->mask_cold_mask;
>>> + reg_val |= temp_sensor->registers->mask_hot_mask;
>>> + omap_temp_sensor_writel(temp_sensor, reg_val,
>>> + temp_sensor->registers->bgap_mask_ctrl);
>>> + }
>>> +
>>> + /*
>>> + * else no need to do anything since HW will immediately compare
>>> + * the new threshold and generate interrupt accordingly
>>> + */
>>> +
>>> +out:
>>> + mutex_unlock(&temp_sensor->sensor_mutex);
>>> + return count;
>>> +}
>>> +
>>> +static ssize_t show_update_rate(struct device *dev,
>>> + struct device_attribute *devattr, char *buf)
>>> +{
>>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>>> + u32 temp = 0, ret = 0;
>>> +
>>> + mutex_lock(&temp_sensor->sensor_mutex);
>>> +
>>> + if (!temp_sensor->clk_rate) {
>>> + dev_err(dev, "clk_rate is NULL\n");
>>> + ret = -EINVAL;
>>> + goto out;
>>> + }
>>> +
>>> + temp = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_counter);
>>> + temp = (temp & temp_sensor->registers->counter_mask) >>
>>> + __ffs(temp_sensor->registers->counter_mask);
>>> + temp = temp * 1000 / (temp_sensor->clk_rate);
>>> +
>>> +out:
>>> + mutex_unlock(&temp_sensor->sensor_mutex);
>>> + if (!ret)
>>> + return sprintf(buf, "%d\n", temp);
>>> +
>>> + return ret;
>>> +}
>>> +
>>> +static ssize_t set_update_rate(struct device *dev,
>>> + struct device_attribute *devattr,
>>> + const char *buf, size_t count)
>>> +{
>>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>>> + u32 reg_val;
>>> + long val;
>>> +
>>> + mutex_lock(&temp_sensor->sensor_mutex);
>>> +
>>> + if (strict_strtol(buf, 10, &val)) {
>>> + count = -EINVAL;
>>> + goto out;
>>> + }
>>> +
>>> + val *= temp_sensor->clk_rate / 1000;
>>> + reg_val = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_counter);
>>> +
>>> + reg_val &= ~(temp_sensor->registers->counter_mask);
>>> + reg_val |= val;
>>> + omap_temp_sensor_writel(temp_sensor, reg_val,
>>> + temp_sensor->registers->bgap_counter);
>>> +
>>> +out:
>>> + mutex_unlock(&temp_sensor->sensor_mutex);
>>> + return count;
>>> +}
>>> +
>>> +static int omap_temp_sensor_read_temp(struct device *dev,
>>> + struct device_attribute *devattr,
>>> + char *buf)
>>> +{
>>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>>> + int temp, ret = 0;
>>> +
>>> + mutex_lock(&temp_sensor->sensor_mutex);
>>> +
>>> + temp = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->temp_sensor_ctrl);
>>> + temp &= temp_sensor->registers->bgap_dtemp_mask;
>>> +
>>> + if (!temp_sensor->is_efuse_valid)
>>> + dev_err(dev, "Invalid EFUSE, Non-trimmed BGAP, Temp not accurate\n");
>>> +
>>> + /* look up for temperature in the table and return the temperature */
>>> + if (temp < OMAP_ADC_START_VALUE || temp > OMAP_ADC_END_VALUE) {
>>> + dev_err(dev, "invalid adc code reported %d", temp);
>>> + ret = -EINVAL;
>>> + goto out;
>>> + }
>>> +
>>> + temp = adc_to_temp[temp - OMAP_ADC_START_VALUE];
>>> +
>>> +out:
>>> + mutex_unlock(&temp_sensor->sensor_mutex);
>>> + if (!ret)
>>> + return sprintf(buf, "%d\n", temp);
>>> +
>>> + return ret;
>>> +}
>>> +
>>> +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, omap_temp_sensor_read_temp,
>>> + NULL, 0);
>>> +static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp_max,
>>> + set_temp_max, 0);
>>> +static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO, show_temp_max_hyst,
>>> + set_temp_max_hyst, 0);
>>> +static SENSOR_DEVICE_ATTR(update_rate, S_IWUSR | S_IRUGO, show_update_rate,
>>> + set_update_rate, 0);
>>> +
>>> +static struct attribute *omap_temp_sensor_attributes[] = {
>>> + &sensor_dev_attr_temp1_input.dev_attr.attr,
>>> + &sensor_dev_attr_temp1_max.dev_attr.attr,
>>> + &sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
>>> + &sensor_dev_attr_update_rate.dev_attr.attr,
>>> + NULL
>>> +};
>>> +
>>> +static const struct attribute_group omap_temp_sensor_group = {
>>> + .attrs = omap_temp_sensor_attributes,
>>> +};
>>> +
>>> +static int omap_temp_sensor_clk_enable(struct omap_temp_sensor *temp_sensor)
>>> +{
>>> + u32 ret = 0;
>>> +
>>> + if (temp_sensor->clk_on) {
>>> + dev_err(temp_sensor->hwmon_dev, "clock already on\n");
>>> + goto out;
>>> + }
>>> +
>>> + ret = pm_runtime_get_sync(temp_sensor->hwmon_dev);
>>> + if (ret < 0) {
>>> + dev_err(temp_sensor->hwmon_dev, "get sync failed\n");
>>> + goto out;
>>> + }
>>> +
>>> + temp_sensor->clk_on = 1;
>>> +
>>> +out:
>>> + return ret;
>>> +}
>>> +
>>> +static int omap_temp_sensor_clk_disable(struct omap_temp_sensor *temp_sensor)
>>> +{
>>> + u32 ret = 0;
>>> +
>>> + if (!temp_sensor->clk_on) {
>>> + dev_err(temp_sensor->hwmon_dev, "clock already off\n");
>>> + goto out;
>>> + }
>>> +
>>> + /* Gate the clock */
>>> + ret = pm_runtime_put_sync(temp_sensor->hwmon_dev);
>>> + if (ret < 0) {
>>> + dev_err(temp_sensor->hwmon_dev, "put sync failed\n");
>>> + goto out;
>>> + }
>>> + temp_sensor->clk_on = 0;
>>> +
>>> +out:
>>> + return ret;
>>> +}
>>> +
>>> +static irqreturn_t omap_talert_irq_handler(int irq, void *data)
>>> +{
>>> + struct omap_temp_sensor *temp_sensor;
>>> + int t_hot, t_cold, temp;
>>> +
>>> + temp_sensor = data;
>>> + mutex_lock(&temp_sensor->sensor_mutex);
>>> +
>>> + /* Read the status of t_hot */
>>> + t_hot = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_status)
>>> + & temp_sensor->registers->status_hot_mask;
>>> +
>>> + /* Read the status of t_cold */
>>> + t_cold = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_status)
>>> + & temp_sensor->registers->status_cold_mask;
>>> +
>>> + temp = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_mask_ctrl);
>>> + /*
>>> + * One TALERT interrupt: Two sources
>>> + * If the interrupt is due to t_hot then mask t_hot and
>>> + * and unmask t_cold else mask t_cold and unmask t_hot
>>> + */
>>> + if (t_hot) {
>>> + temp &= ~(temp_sensor->registers->mask_hot_mask);
>>> + temp |= temp_sensor->registers->mask_cold_mask;
>>> + } else if (t_cold) {
>>> + temp &= ~(temp_sensor->registers->mask_cold_mask);
>>> + temp |= temp_sensor->registers->mask_hot_mask;
>>> + }
>>> +
>>> + omap_temp_sensor_writel(temp_sensor, temp,
>>> + temp_sensor->registers->bgap_mask_ctrl);
>>> +
>>> + /* kobject_uvent to user space telling thermal threshold crossed */
>>> + kobject_uevent(&temp_sensor->hwmon_dev->kobj, KOBJ_CHANGE);
>>> +
>>> + mutex_unlock(&temp_sensor->sensor_mutex);
>>> +
>>> + return IRQ_HANDLED;
>>> +}
>>> +
>>> +static int __devinit omap_temp_sensor_probe(struct platform_device *pdev)
>>> +{
>>> + struct omap_temp_sensor_pdata *pdata = pdev->dev.platform_data;
>>> + struct omap_temp_sensor *temp_sensor;
>>> + struct resource *mem;
>>> + int ret = 0;
>>> + int val, clk_rate;
>>> +
>>> + if (!pdata) {
>>> + dev_err(&pdev->dev, "platform data missing\n");
>>> + return -EINVAL;
>>> + }
>>> +
>>> + temp_sensor = kzalloc(sizeof(*temp_sensor), GFP_KERNEL);
>>> + if (!temp_sensor)
>>> + return -ENOMEM;
>>> +
>>> + mutex_init(&temp_sensor->sensor_mutex);
>>> +
>>> + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>>> + if (!mem) {
>>> + dev_err(&pdev->dev, "no mem resource\n");
>>> + ret = -ENOMEM;
>>> + goto plat_res_err;
>>> + }
>>> +
>>> + temp_sensor->irq = platform_get_irq_byname(pdev, "thermal_alert");
>>> + if (temp_sensor->irq < 0) {
>>> + dev_err(&pdev->dev, "get_irq_byname failed\n");
>>> + ret = temp_sensor->irq;
>>> + goto plat_res_err;
>>> + }
>>> +
>>> + temp_sensor->phy_base = ioremap(mem->start, resource_size(mem));
>>> + temp_sensor->clock = NULL;
>>> + temp_sensor->registers = pdata->registers;
>>> + temp_sensor->hwmon_dev = &pdev->dev;
>>> +
>>> + pm_runtime_enable(&pdev->dev);
>>> + pm_runtime_irq_safe(&pdev->dev);
>>> +
>>> + /*
>>> + * check if the efuse has a non-zero value if not
>>> + * it is an untrimmed sample and the temperatures
>>> + * may not be accurate
>>> + */
>>> +
>>> + if (omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_efuse))
>>> + temp_sensor->is_efuse_valid = 1;
>>> +
>>> + platform_set_drvdata(pdev, temp_sensor);
>>> + dev_set_drvdata(&pdev->dev, temp_sensor);
>>> + temp_sensor->clock = clk_get(temp_sensor->hwmon_dev, "fck");
>>> + if (IS_ERR(temp_sensor->clock)) {
>>> + ret = PTR_ERR(temp_sensor->clock);
>>> + dev_err(temp_sensor->hwmon_dev,
>>> + "unable to get fclk: %d\n", ret);
>>> + ret = -EINVAL;
>>> + goto plat_res_err;
>>> + }
>>> +
>>> + ret = omap_temp_sensor_clk_enable(temp_sensor);
>>> + if (ret) {
>>> + dev_err(&pdev->dev, "Cannot enable temp sensor\n");
>>> + goto clken_err;
>>> + }
>>> +
>>> + clk_rate = clk_round_rate(temp_sensor->clock, 2000000);
>>> + if (clk_rate < 1000000 || clk_rate == 0xffffffff) {
>>> + dev_err(&pdev->dev, "Error round rate\n");
>>> + ret = -EINVAL;
>>> + goto clken_err;
>>> + }
>>> +
>>> + ret = clk_set_rate(temp_sensor->clock, clk_rate);
>>> + if (ret) {
>>> + dev_err(&pdev->dev, "Cannot set clock rate\n");
>>> + goto clken_err;
>>> + }
>>> +
>>> + temp_sensor->clk_rate = clk_rate;
>>> + omap_enable_continuous_mode(temp_sensor, 1);
>>> + omap_configure_temp_sensor_thresholds(temp_sensor);
>>> + /* 1 ms */
>>> + omap_configure_temp_sensor_counter(temp_sensor, 1);
>>> +
>>> + /* Wait till the first conversion is done wait for at least 1ms */
>>> + usleep_range(1000, 2000);
>>> +
>>> + /* Read the temperature once due to hw issue*/
>>> + omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->temp_sensor_ctrl);
>>> +
>>> + /* Set 2 seconds time as default counter */
>>> + omap_configure_temp_sensor_counter(temp_sensor,
>>> + temp_sensor->clk_rate * 2);
>>> +
>>> + temp_sensor->hwmon_dev = hwmon_device_register(&pdev->dev);
>>> + if (IS_ERR(temp_sensor->hwmon_dev)) {
>>> + dev_err(&pdev->dev, "hwmon_device_register failed.\n");
>>> + ret = PTR_ERR(temp_sensor->hwmon_dev);
>>> + goto hwmon_reg_err;
>>> + }
>>> +
>>> + ret = sysfs_create_group(&pdev->dev.kobj,
>>> + &omap_temp_sensor_group);
>>> + if (ret) {
>>> + dev_err(&pdev->dev, "could not create sysfs files\n");
>>> + goto sysfs_create_err;
>>> + }
>>> +
>>> + kobject_uevent(&temp_sensor->hwmon_dev->kobj, KOBJ_ADD);
>>> +
>>> + ret = request_threaded_irq(temp_sensor->irq, NULL,
>>> + omap_talert_irq_handler, IRQF_TRIGGER_RISING | IRQF_ONESHOT,
>>> + "temp_sensor", temp_sensor);
>>> + if (ret) {
>>> + dev_err(&pdev->dev, "Request threaded irq failed.\n");
>>> + goto req_irq_err;
>>> + }
>>> +
>>> + /* unmask the T_COLD and unmask T_HOT at init */
>>> + val = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_mask_ctrl);
>>> + val |= temp_sensor->registers->mask_cold_mask
>>> + | temp_sensor->registers->mask_hot_mask;
>>> +
>>> + omap_temp_sensor_writel(temp_sensor, val,
>>> + temp_sensor->registers->bgap_mask_ctrl);
>>> +
>>> + return 0;
>>> +
>>> +req_irq_err:
>>> + kobject_uevent(&temp_sensor->hwmon_dev->kobj, KOBJ_REMOVE);
>>> + sysfs_remove_group(&temp_sensor->hwmon_dev->kobj,
>>> + &omap_temp_sensor_group);
>>> +sysfs_create_err:
>>> + hwmon_device_unregister(&pdev->dev);
>>> +hwmon_reg_err:
>>> + omap_temp_sensor_clk_disable(temp_sensor);
>>> +clken_err:
>>> + clk_put(temp_sensor->clock);
>>> +plat_res_err:
>>> + mutex_destroy(&temp_sensor->sensor_mutex);
>>> + kfree(temp_sensor);
>>> + return ret;
>>> +}
>>> +
>>> +static int __devexit omap_temp_sensor_remove(struct platform_device *pdev)
>>> +{
>>> + struct omap_temp_sensor *temp_sensor = platform_get_drvdata(pdev);
>>> +
>>> + hwmon_device_unregister(&pdev->dev);
>>> + kobject_uevent(&temp_sensor->hwmon_dev->kobj, KOBJ_REMOVE);
>>> + sysfs_remove_group(&temp_sensor->hwmon_dev->kobj,
>>> + &omap_temp_sensor_group);
>>> + omap_temp_sensor_clk_disable(temp_sensor);
>>> + clk_put(temp_sensor->clock);
>>> + platform_set_drvdata(pdev, NULL);
>>> + free_irq(temp_sensor->irq, temp_sensor);
>>> + mutex_destroy(&temp_sensor->sensor_mutex);
>>> + kfree(temp_sensor);
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +#ifdef CONFIG_PM
>>> +static void omap_temp_sensor_save_ctxt(struct omap_temp_sensor *temp_sensor)
>>> +{
>>> + temp_sensor->temp_sensor_ctrl = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->temp_sensor_ctrl);
>>> + temp_sensor->bg_ctrl = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_mask_ctrl);
>>> + temp_sensor->bg_counter = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_counter);
>>> + temp_sensor->bg_threshold = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_threshold);
>>> + temp_sensor->temp_sensor_tshut_threshold =
>>> + omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->thsut_threshold);
>>> +}
>>> +
>>> +static void omap_temp_sensor_restore_ctxt(struct omap_temp_sensor *temp_sensor)
>>> +{
>>> + omap_temp_sensor_writel(temp_sensor,
>>> + temp_sensor->temp_sensor_ctrl,
>>> + temp_sensor->registers->temp_sensor_ctrl);
>>> + omap_temp_sensor_writel(temp_sensor,
>>> + temp_sensor->bg_ctrl,
>>> + temp_sensor->registers->bgap_mask_ctrl);
>>> + omap_temp_sensor_writel(temp_sensor,
>>> + temp_sensor->bg_counter,
>>> + temp_sensor->registers->bgap_counter);
>>> + omap_temp_sensor_writel(temp_sensor,
>>> + temp_sensor->bg_threshold,
>>> + temp_sensor->registers->bgap_threshold);
>>> + omap_temp_sensor_writel(temp_sensor,
>>> + temp_sensor->temp_sensor_tshut_threshold,
>>> + temp_sensor->registers->thsut_threshold);
>>> +}
>>> +
>>> +static int omap_temp_sensor_suspend(struct device *dev)
>>> +{
>>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>>> +
>>> + omap_temp_sensor_save_ctxt(temp_sensor);
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static int omap_temp_sensor_resume(struct device *dev)
>>> +{
>>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>>> +
>>> + omap_temp_sensor_restore_ctxt(temp_sensor);
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static int omap_temp_sensor_runtime_suspend(struct device *dev)
>>> +{
>>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>>> +
>>> + omap_temp_sensor_save_ctxt(temp_sensor);
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static int omap_temp_sensor_runtime_resume(struct device *dev)
>>> +{
>>> + static int context_loss_count;
>>> + int temp;
>>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>>> +
>>> + temp = omap_device_get_context_loss_count(to_platform_device(dev));
>>> +
>>> + if (temp != context_loss_count && context_loss_count != 0)
>>> + omap_temp_sensor_restore_ctxt(temp_sensor);
>>> +
>>> + context_loss_count = temp;
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static const struct dev_pm_ops omap_temp_sensor_dev_pm_ops = {
>>> + .suspend = omap_temp_sensor_suspend,
>>> + .resume = omap_temp_sensor_resume,
>>> + .runtime_suspend = omap_temp_sensor_runtime_suspend,
>>> + .runtime_resume = omap_temp_sensor_runtime_resume,
>>> +};
>>> +
>>> +#endif
>>> +
>>> +static struct platform_driver omap_temp_sensor_driver = {
>>> + .probe = omap_temp_sensor_probe,
>>> + .remove = omap_temp_sensor_remove,
>>> + .driver = {
>>> + .name = "omap_temp_sensor",
>>> +#ifdef CONFIG_PM
>>> + .pm = &omap_temp_sensor_dev_pm_ops,
>>> +#endif
>>> + },
>>> +};
>>> +
>>> +int __init omap_temp_sensor_init(void)
>>> +{
>>> + return platform_driver_register(&omap_temp_sensor_driver);
>>> +}
>>> +module_init(omap_temp_sensor_init);
>>> +
>>> +static void __exit omap_temp_sensor_exit(void)
>>> +{
>>> + platform_driver_unregister(&omap_temp_sensor_driver);
>>> +}
>>> +module_exit(omap_temp_sensor_exit);
>>> +
>>> +MODULE_DESCRIPTION("OMAP446X temperature sensor Driver");
>>> +MODULE_LICENSE("GPL");
>>> +MODULE_ALIAS("platform:" DRIVER_NAME);
>>> +MODULE_AUTHOR("J Keerthy <j-keerthy@ti.com>");
>>> --
>>> 1.7.0.4
>>>
>>
>
>
>
> --
> Regards and Thanks,
> Keerthy
>
--
Regards and Thanks,
Keerthy
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 35+ messages in thread
* Re: [lm-sensors] [RFC PATCH 6/6 V2] hwmon: OMAP4: On die
@ 2011-08-19 12:55 ` J, KEERTHY
0 siblings, 0 replies; 35+ messages in thread
From: J, KEERTHY @ 2011-08-19 12:55 UTC (permalink / raw)
To: Guenter Roeck; +Cc: linux-omap, Jean Delvare, lm-sensors
On Fri, Aug 19, 2011 at 11:34 AM, J, KEERTHY <j-keerthy@ti.com> wrote:
> On Fri, Aug 19, 2011 at 7:43 AM, Guenter Roeck
> <guenter.roeck@ericsson.com> wrote:
>> On Thu, Aug 18, 2011 at 06:52:15AM -0400, Keerthy wrote:
>>> On chip temperature sensor driver. The driver monitors the temperature of
>>> the MPU subsystem of the OMAP4. It sends notifications to the user space if
>>> the temperature crosses user defined thresholds via kobject_uevent interface.
>>> The user is allowed to configure the temperature thresholds vis sysfs nodes
>>> exposed using hwmon interface.
>>>
>>> Signed-off-by: Keerthy <j-keerthy@ti.com>
>>> Cc: Jean Delvare <khali@linux-fr.org>
>>> Cc: Guenter Roeck <guenter.roeck@ericsson.com>
>>> Cc: lm-sensors@lm-sensors.org
>>
>> High level review:
>>
>> - too much and too broad mutex locking. show functions should not need locks at all,
>> set functions only while data is written into registers and into platform data.
>
> Ok. I will clean this.
Even the show function might read an inconsistent value without
a mutex lock if the registers were written using set function was
preempted. There is a necessity for mutex locking even in show function.
>
>> - driver is quite noisy. There should definitely not be any log messages
>> if a set parameter is wrong. Show functions already return an error value
>> to the user; a log message indicating the error again just creates noise.
>> For one boolean set during probe (is_efuse_valid), each subsequent show results
>> in a log message if it is false. Some errors result in multiple log messages.
>
> A user tries to set an invalid temperature threshold. The user should
> be notified about this. The invalid temperature will not be set. The user
> should not be allowed to set an invalid temperature. It is to inform
> the user about precisely the problem with the parameter.
>
> In some of the samples the bandgap is not trimmed and hence
> temperature reported will be wrong. So every time a user tries to read
> he is alerted that the temperatures are not accurate.
>
>> - Wrong use of EINVAL throughout the driver (EINVAL is "Invalid Argument")
>
> Ok. I will correct this.
>
>> - excessive ( )
>
> Ok.
>
>> - linear search through a sorted array is very expensive. Consider using a binary search.
>
> Yes. I will implement binary search.
>
>> - temp_to_adc_conversion return code (error if negative) is not checked properly.
>
> Ok. I will add checks.
>
>>
>> I am sure there are other problems, but those are difficult to find with all the noise
>> above.
>>
>> Guenter
>>
>>> ---
>>> Documentation/hwmon/omap_temp_sensor | 27 +
>>> drivers/hwmon/Kconfig | 11 +
>>> drivers/hwmon/Makefile | 1 +
>>> drivers/hwmon/omap_temp_sensor.c | 918 ++++++++++++++++++++++++++++++++++
>>> 4 files changed, 957 insertions(+), 0 deletions(-)
>>> create mode 100644 Documentation/hwmon/omap_temp_sensor
>>> create mode 100644 drivers/hwmon/omap_temp_sensor.c
>>>
>>> diff --git a/Documentation/hwmon/omap_temp_sensor b/Documentation/hwmon/omap_temp_sensor
>>> new file mode 100644
>>> index 0000000..e01a6d6
>>> --- /dev/null
>>> +++ b/Documentation/hwmon/omap_temp_sensor
>>> @@ -0,0 +1,27 @@
>>> +Kernel driver omap_temp_sensor
>>> +===============
>>> +
>>> +Supported chips:
>>> + * Texas Instruments OMAP4460
>>> + Prefix: 'omap_temp_sensor'
>>> + Datasheet: http://focus.ti.com/docs/prod/folders/print/tmp102.html
>>> +
>>> +Author:
>>> + J Keerthy <j-keerthy@ti.com>
>>> +
>>> +Description
>>> +-----------
>>> +
>>> +The Texas Instruments OMAP4 family of chips have a bandgap temperature sensor.
>>> +The temperature sensor feature is used to convert the temperature of the device
>>> +into a decimal value coded on 10 bits. An internal ADC is used for conversion.
>>> +The recommended operating temperatures must be in the range -40 degree Celsius
>>> +to 123 degree celsius for standard conversion.
>>> +The thresholds are programmable and upon crossing the thresholds an interrupt
>>> +is generated. The OMAP temperature sensor has a programmable update rate in
>>> +milli seconds.
>>> +(Currently the driver programs a default of 2000 milli seconds).
>>> +
>>> +The driver provides the common sysfs-interface for temperatures (see
>>> +Documentation/hwmon/sysfs-interface under Temperatures).
>>> +
>>> diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
>>> index 5f888f7..9c9cd8b 100644
>>> --- a/drivers/hwmon/Kconfig
>>> +++ b/drivers/hwmon/Kconfig
>>> @@ -323,6 +323,17 @@ config SENSORS_F71805F
>>> This driver can also be built as a module. If so, the module
>>> will be called f71805f.
>>>
>>> +config SENSORS_OMAP_BANDGAP_TEMP_SENSOR
>>> + bool "OMAP on-die temperature sensor hwmon driver"
>>> + depends on HWMON && ARCH_OMAP && OMAP_TEMP_SENSOR
>>> + help
>>> + If you say yes here you get support for hardware
>>> + monitoring features of the OMAP on die temperature
>>> + sensor.
>>> +
>>> + Continuous conversion programmable delay
>>> + mode is used for temperature conversion.
>>> +
>>> config SENSORS_F71882FG
>>> tristate "Fintek F71882FG and compatibles"
>>> help
>>> diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
>>> index 28061cf..d0f89f5 100644
>>> --- a/drivers/hwmon/Makefile
>>> +++ b/drivers/hwmon/Makefile
>>> @@ -91,6 +91,7 @@ obj-$(CONFIG_SENSORS_MAX6639) += max6639.o
>>> obj-$(CONFIG_SENSORS_MAX6642) += max6642.o
>>> obj-$(CONFIG_SENSORS_MAX6650) += max6650.o
>>> obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o
>>> +obj-$(CONFIG_SENSORS_OMAP_BANDGAP_TEMP_SENSOR) += omap_temp_sensor.o
>>> obj-$(CONFIG_SENSORS_PC87360) += pc87360.o
>>> obj-$(CONFIG_SENSORS_PC87427) += pc87427.o
>>> obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o
>>> diff --git a/drivers/hwmon/omap_temp_sensor.c b/drivers/hwmon/omap_temp_sensor.c
>>> new file mode 100644
>>> index 0000000..586a361
>>> --- /dev/null
>>> +++ b/drivers/hwmon/omap_temp_sensor.c
>>> @@ -0,0 +1,918 @@
>>> +/*
>>> + * OMAP4 Temperature sensor driver file
>>> + *
>>> + * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
>>> + * Author: J Keerthy <j-keerthy@ti.com>
>>> + * Author: Moiz Sonasath <m-sonasath@ti.com>
>>> + *
>>> + * This program is free software; you can redistribute it and/or
>>> + * modify it under the terms of the GNU General Public License
>>> + * 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.
>>> + *
>>> + * You should have received a copy of the GNU General Public License
>>> + * along with this program; if not, write to the Free Software
>>> + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
>>> + * 02110-1301 USA
>>> + *
>>> + */
>>> +
>>> +#include <linux/interrupt.h>
>>> +#include <linux/clk.h>
>>> +#include <linux/io.h>
>>> +#include <linux/slab.h>
>>> +#include <linux/init.h>
>>> +#include <plat/omap_device.h>
>>> +#include <linux/kernel.h>
>>> +#include <linux/device.h>
>>> +#include <linux/jiffies.h>
>>> +#include <linux/hwmon.h>
>>> +#include <linux/hwmon-sysfs.h>
>>> +#include <linux/stddef.h>
>>> +#include <linux/sysfs.h>
>>> +#include <linux/err.h>
>>> +#include <linux/types.h>
>>> +#include <linux/mutex.h>
>>> +#include <linux/pm_runtime.h>
>>> +#include <linux/delay.h>
>>> +#include <plat/temperature_sensor.h>
>>> +
>>> +#define TSHUT_THRESHOLD_HOT 122000 /* 122 deg C */
>>> +#define TSHUT_THRESHOLD_COLD 100000 /* 100 deg C */
>>> +#define BGAP_THRESHOLD_T_HOT 73000 /* 73 deg C */
>>> +#define BGAP_THRESHOLD_T_COLD 71000 /* 71 deg C */
>>> +#define OMAP_ADC_START_VALUE 530
>>> +#define OMAP_ADC_END_VALUE 923
>>> +
>>> +/*
>>> + * omap_temp_sensor structure
>>> + * @hwmon_dev - device pointer
>>> + * @clock - Clock pointer
>>> + * @registers - Pointer to structure with register offsets and bitfields
>>> + * @sensor_mutex - Mutex for sysfs, irq and PM
>>> + * @irq - MPU Irq number for thermal alert
>>> + * @phy_base - Physical base of the temp I/O
>>> + * @clk_rate - Holds current clock rate
>>> + * @temp_sensor_ctrl - temp sensor control register value
>>> + * @bg_ctrl - bandgap ctrl register value
>>> + * @bg_counter - bandgap counter value
>>> + * @bg_threshold - bandgap threshold register value
>>> + * @temp_sensor_tshut_threshold - bandgap tshut register value
>>> + * @is_efuse_valid - Flag to determine if efuse is valid or not
>>> + * @clk_on - Manages the current clock state
>>> + */
>>> +struct omap_temp_sensor {
>>> + struct device *hwmon_dev;
>>> + struct clk *clock;
>>> + struct omap_temp_sensor_registers *registers;
>>> + struct mutex sensor_mutex; /* Mutex for sysfs, irq and PM */
>>> + unsigned int irq;
>>> + void __iomem *phy_base;
>>> + u32 clk_rate;
>>> + u32 temp_sensor_ctrl;
>>> + u32 bg_ctrl;
>>> + u32 bg_counter;
>>> + u32 bg_threshold;
>>> + u32 temp_sensor_tshut_threshold;
>>> + bool is_efuse_valid;
>>> + bool clk_on;
>>> +};
>>> +
>>> +/*
>>> + * Temperature values in milli degree celsius
>>> + * ADC code values from 530 to 923
>>> + */
>>> +static int adc_to_temp[] = {
>>> + -40000, -40000, -40000, -40000, -39800, -39400, -39000, -38600, -38200,
>>> + -37800, -37300, -36800, -36400, -36000, -35600, -35200, -34800,
>>> + -34300, -33800, -33400, -33000, -32600, -32200, -31800, -31300,
>>> + -30800, -30400, -30000, -29600, -29200, -28700, -28200, -27800,
>>> + -27400, -27000, -26600, -26200, -25700, -25200, -24800, -24400,
>>> + -24000, -23600, -23200, -22700, -22200, -21800, -21400, -21000,
>>> + -20600, -20200, -19700, -19200, -18800, -18400, -18000, -17600,
>>> + -17200, -16700, -16200, -15800, -15400, -15000, -14600, -14200,
>>> + -13700, -13200, -12800, -12400, -12000, -11600, -11200, -10700,
>>> + -10200, -9800, -9400, -9000, -8600, -8200, -7700, -7200, -6800,
>>> + -6400, -6000, -5600, -5200, -4800, -4300, -3800, -3400, -3000,
>>> + -2600, -2200, -1800, -1300, -800, -400, 0, 400, 800, 1200, 1600,
>>> + 2100, 2600, 3000, 3400, 3800, 4200, 4600, 5100, 5600, 6000, 6400,
>>> + 6800, 7200, 7600, 8000, 8500, 9000, 9400, 9800, 10200, 10600, 11000,
>>> + 11400, 11900, 12400, 12800, 13200, 13600, 14000, 14400, 14800,
>>> + 15300, 15800, 16200, 16600, 17000, 17400, 17800, 18200, 18700,
>>> + 19200, 19600, 20000, 20400, 20800, 21200, 21600, 22100, 22600,
>>> + 23000, 23400, 23800, 24200, 24600, 25000, 25400, 25900, 26400,
>>> + 26800, 27200, 27600, 28000, 28400, 28800, 29300, 29800, 30200,
>>> + 30600, 31000, 31400, 31800, 32200, 32600, 33100, 33600, 34000,
>>> + 34400, 34800, 35200, 35600, 36000, 36400, 36800, 37300, 37800,
>>> + 38200, 38600, 39000, 39400, 39800, 40200, 40600, 41100, 41600,
>>> + 42000, 42400, 42800, 43200, 43600, 44000, 44400, 44800, 45300,
>>> + 45800, 46200, 46600, 47000, 47400, 47800, 48200, 48600, 49000,
>>> + 49500, 50000, 50400, 50800, 51200, 51600, 52000, 52400, 52800,
>>> + 53200, 53700, 54200, 54600, 55000, 55400, 55800, 56200, 56600,
>>> + 57000, 57400, 57800, 58200, 58700, 59200, 59600, 60000, 60400,
>>> + 60800, 61200, 61600, 62000, 62400, 62800, 63300, 63800, 64200,
>>> + 64600, 65000, 65400, 65800, 66200, 66600, 67000, 67400, 67800,
>>> + 68200, 68700, 69200, 69600, 70000, 70400, 70800, 71200, 71600,
>>> + 72000, 72400, 72800, 73200, 73600, 74100, 74600, 75000, 75400,
>>> + 75800, 76200, 76600, 77000, 77400, 77800, 78200, 78600, 79000,
>>> + 79400, 79800, 80300, 80800, 81200, 81600, 82000, 82400, 82800,
>>> + 83200, 83600, 84000, 84400, 84800, 85200, 85600, 86000, 86400,
>>> + 86800, 87300, 87800, 88200, 88600, 89000, 89400, 89800, 90200,
>>> + 90600, 91000, 91400, 91800, 92200, 92600, 93000, 93400, 93800,
>>> + 94200, 94600, 95000, 95500, 96000, 96400, 96800, 97200, 97600,
>>> + 98000, 98400, 98800, 99200, 99600, 100000, 100400, 100800, 101200,
>>> + 101600, 102000, 102400, 102800, 103200, 103600, 104000, 104400,
>>> + 104800, 105200, 105600, 106100, 106600, 107000, 107400, 107800,
>>> + 108200, 108600, 109000, 109400, 109800, 110200, 110600, 111000,
>>> + 111400, 111800, 112200, 112600, 113000, 113400, 113800, 114200,
>>> + 114600, 115000, 115400, 115800, 116200, 116600, 117000, 117400,
>>> + 117800, 118200, 118600, 119000, 119400, 119800, 120200, 120600,
>>> + 121000, 121400, 121800, 122200, 122600, 123000
>>> +};
>>> +
>>> +static unsigned long omap_temp_sensor_readl(struct omap_temp_sensor
>>> + *temp_sensor, u32 reg)
>>> +{
>>> + return __raw_readl(temp_sensor->phy_base + reg);
>>> +}
>>> +
>>> +static void omap_temp_sensor_writel(struct omap_temp_sensor *temp_sensor,
>>> + u32 val, u32 reg)
>>> +{
>>> + __raw_writel(val, (temp_sensor->phy_base + reg));
>>> +}
>>> +
>>> +static int adc_to_temp_conversion(int adc_val)
>>> +{
>>> + return adc_to_temp[adc_val - OMAP_ADC_START_VALUE];
>>> +}
>>> +
>>> +static int temp_to_adc_conversion(long temp)
>>> +{
>>> + int i;
>>> +
>>> + for (i = 0; i <= OMAP_ADC_END_VALUE - OMAP_ADC_START_VALUE; i++)
>>> + if (temp < adc_to_temp[i])
>>> + return OMAP_ADC_START_VALUE + i - 1;
>>> +
>>> + return -EINVAL;
>>> +}
>>> +
>>> +static void omap_configure_temp_sensor_thresholds(struct omap_temp_sensor
>>> + *temp_sensor)
>>> +{
>>> + u32 temp, t_hot, t_cold, tshut_hot, tshut_cold;
>>> +
>>> + t_hot = temp_to_adc_conversion(BGAP_THRESHOLD_T_HOT);
>>> + t_cold = temp_to_adc_conversion(BGAP_THRESHOLD_T_COLD);
>>> +
>>> + /* Configure the TALERT thresholds */
>>> + temp = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_threshold);
>>> + temp |= (t_hot << __ffs(temp_sensor->registers->threshold_thot_mask)) |
>>> + (t_cold << __ffs(temp_sensor->registers->threshold_tcold_mask));
>>> + omap_temp_sensor_writel(temp_sensor, temp,
>>> + temp_sensor->registers->bgap_threshold);
>>> +
>>> + tshut_hot = temp_to_adc_conversion(TSHUT_THRESHOLD_HOT);
>>> + tshut_cold = temp_to_adc_conversion(TSHUT_THRESHOLD_COLD);
>>> +
>>> + /* Configure the TSHUT thresholds */
>>> + temp = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->thsut_threshold);
>>> + temp |= (tshut_hot << __ffs(temp_sensor->registers->tshut_hot_mask))
>>> + | (tshut_cold << __ffs(temp_sensor->registers->tshut_hot_mask));
>>> + omap_temp_sensor_writel(temp_sensor, temp,
>>> + temp_sensor->registers->thsut_threshold);
>>> +}
>>> +
>>> +static void omap_configure_temp_sensor_counter(struct omap_temp_sensor
>>> + *temp_sensor, u32 counter)
>>> +{
>>> + u32 val;
>>> +
>>> + val = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_counter);
>>> + val &= ~(temp_sensor->registers->counter_mask);
>>> + val |= (counter << __ffs(temp_sensor->registers->counter_mask));
>>> + omap_temp_sensor_writel(temp_sensor, val,
>>> + temp_sensor->registers->bgap_counter);
>>> +}
>>> +
>>> +static void omap_enable_continuous_mode(struct omap_temp_sensor *temp_sensor,
>>> + bool enable)
>>> +{
>>> + u32 val;
>>> +
>>> + val = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_mode_ctrl);
>>> +
>>> + if (enable)
>>> + val |= (1 << __ffs(temp_sensor->registers->mode_ctrl_mask));
>>> + else
>>> + val &= ~(temp_sensor->registers->mode_ctrl_mask);
>>> +
>>> + omap_temp_sensor_writel(temp_sensor, val,
>>> + temp_sensor->registers->bgap_mode_ctrl);
>>> +}
>>> +
>>> +/* Sysfs hook functions */
>>> +
>>> +static ssize_t show_temp_max(struct device *dev,
>>> + struct device_attribute *devattr, char *buf)
>>> +{
>>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>>> + int temp;
>>> +
>>> + mutex_lock(&temp_sensor->sensor_mutex);
>>> +
>>> + temp = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_threshold);
>>> + temp = (temp & temp_sensor->registers->threshold_thot_mask)
>>> + >> __ffs(temp_sensor->registers->threshold_thot_mask);
>>> +
>>> + if (temp < OMAP_ADC_START_VALUE || temp > OMAP_ADC_END_VALUE) {
>>> + dev_err(dev, "invalid value\n");
>>> + mutex_unlock(&temp_sensor->sensor_mutex);
>>> + return -EINVAL;
>>> + }
>>> +
>>> + temp = adc_to_temp_conversion(temp);
>>> + mutex_unlock(&temp_sensor->sensor_mutex);
>>> +
>>> + return snprintf(buf, 16, "%d\n", temp);
>>> +}
>>> +
>>> +static ssize_t set_temp_max(struct device *dev,
>>> + struct device_attribute *devattr,
>>> + const char *buf, size_t count)
>>> +{
>>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>>> + long val;
>>> + u32 reg_val, t_cold, t_hot, temp;
>>> +
>>> + mutex_lock(&temp_sensor->sensor_mutex);
>>> +
>>> + if (strict_strtol(buf, 10, &val)) {
>>> + count = -EINVAL;
>>> + goto out;
>>> + }
>>> +
>>> + t_hot = temp_to_adc_conversion(val);
>>> + if ((t_hot < OMAP_ADC_START_VALUE || t_hot > OMAP_ADC_END_VALUE)) {
>>> + dev_err(dev, "invalid range\n");
>>> + count = -EINVAL;
>>> + goto out;
>>> + }
>>> +
>>> + /* obtain the T cold value */
>>> + t_cold = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_threshold);
>>> + t_cold = (t_cold & temp_sensor->registers->threshold_tcold_mask) >>
>>> + __ffs(temp_sensor->registers->threshold_tcold_mask);
>>> +
>>> + if (t_hot < t_cold) {
>>> + dev_err(dev, "Error! T_HOT value lesser than T_COLD\n");
>>> + count = -EINVAL;
>>> + goto out;
>>> + }
>>> +
>>> + /* write the new t_hot value */
>>> + reg_val = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_threshold);
>>> + reg_val &= ~(temp_sensor->registers->threshold_thot_mask);
>>> + reg_val |= (t_hot <<
>>> + __ffs(temp_sensor->registers->threshold_thot_mask));
>>> + omap_temp_sensor_writel(temp_sensor, reg_val,
>>> + temp_sensor->registers->bgap_threshold);
>>> +
>>> + /* Read the current temperature */
>>> + temp = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->temp_sensor_ctrl);
>>> + temp &= (temp_sensor->registers->bgap_dtemp_mask);
>>> +
>>> + /*
>>> + * If user sets the HIGH threshold(t_hot) greater than the current
>>> + * temperature(temp) unmask the HOT interrupts
>>> + */
>>> + if (t_hot > temp) {
>>> + reg_val = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_mask_ctrl);
>>> + reg_val &= ~(temp_sensor->registers->mask_cold_mask);
>>> + reg_val |= temp_sensor->registers->mask_hot_mask;
>>> + omap_temp_sensor_writel(temp_sensor, reg_val,
>>> + temp_sensor->registers->bgap_mask_ctrl);
>>> + }
>>> +
>>> + /*
>>> + * If current temperature is in-between the hot and cold thresholds,
>>> + * Enable both masks.
>>> + */
>>> + if (temp > t_cold && temp < t_hot) {
>>> + reg_val = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_mask_ctrl);
>>> + reg_val |= temp_sensor->registers->mask_cold_mask;
>>> + reg_val |= temp_sensor->registers->mask_hot_mask;
>>> + omap_temp_sensor_writel(temp_sensor, reg_val,
>>> + OMAP4460_BGAP_CTRL_OFFSET);
>>> + }
>>> + /*
>>> + * else no need to do anything since HW will immediately compare
>>> + * the new threshold and generate interrupt accordingly
>>> + */
>>> +out:
>>> + mutex_unlock(&temp_sensor->sensor_mutex);
>>> + return count;
>>> +}
>>> +
>>> +static ssize_t show_temp_max_hyst(struct device *dev,
>>> + struct device_attribute *devattr, char *buf)
>>> +{
>>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>>> + u32 temp;
>>> +
>>> + mutex_lock(&temp_sensor->sensor_mutex);
>>> +
>>> + temp = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_threshold);
>>> + temp = (temp & temp_sensor->registers->threshold_tcold_mask) >>
>>> + __ffs(temp_sensor->registers->threshold_tcold_mask);
>>> +
>>> + if (temp < OMAP_ADC_START_VALUE || temp > OMAP_ADC_END_VALUE) {
>>> + dev_err(dev, "invalid value\n");
>>> + mutex_unlock(&temp_sensor->sensor_mutex);
>>> + return -EINVAL;
>>> + }
>>> +
>>> + temp = adc_to_temp_conversion(temp);
>>> + mutex_unlock(&temp_sensor->sensor_mutex);
>>> +
>>> + return snprintf(buf, 16, "%d\n", temp);
>>> +}
>>> +
>>> +static ssize_t set_temp_max_hyst(struct device *dev,
>>> + struct device_attribute *devattr,
>>> + const char *buf, size_t count)
>>> +{
>>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>>> + u32 reg_val, t_hot, t_cold, temp;
>>> + long val;
>>> +
>>> + mutex_lock(&temp_sensor->sensor_mutex);
>>> +
>>> + if (strict_strtol(buf, 10, &val)) {
>>> + count = -EINVAL;
>>> + goto out;
>>> + }
>>> +
>>> + t_cold = temp_to_adc_conversion(val);
>>> + if (t_cold < OMAP_ADC_START_VALUE || t_cold > OMAP_ADC_END_VALUE) {
>>> + dev_err(dev, "invalid range");
>>> + count = -EINVAL;
>>> + goto out;
>>> + }
>>> +
>>> + /* obtain the T HOT value */
>>> + t_hot = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_threshold);
>>> + t_hot = (t_hot & temp_sensor->registers->threshold_thot_mask) >>
>>> + __ffs(temp_sensor->registers->threshold_thot_mask);
>>> +
>>> + if (t_cold > t_hot) {
>>> + dev_err(dev, "Error! T_COLD value greater than T_HOT\n");
>>> + count = -EINVAL;
>>> + goto out;
>>> + }
>>> +
>>> + /* write the new t_cold value */
>>> + reg_val = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_threshold);
>>> + reg_val &= ~(temp_sensor->registers->threshold_tcold_mask);
>>> + reg_val |= (t_cold <<
>>> + __ffs(temp_sensor->registers->threshold_tcold_mask));
>>> + omap_temp_sensor_writel(temp_sensor, reg_val,
>>> + temp_sensor->registers->bgap_threshold);
>>> +
>>> + /* Read the current temperature */
>>> + temp = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->temp_sensor_ctrl);
>>> + temp &= (temp_sensor->registers->bgap_dtemp_mask);
>>> +
>>> + /*
>>> + * If user sets the LOW threshold(t_cold) lower than the current
>>> + * temperature(temp) unmask the COLD interrupts
>>> + */
>>> + if (t_cold < temp) {
>>> + reg_val = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_mask_ctrl);
>>> + reg_val &= ~(temp_sensor->registers->mask_hot_mask);
>>> + reg_val |= temp_sensor->registers->mask_cold_mask;
>>> + omap_temp_sensor_writel(temp_sensor, reg_val,
>>> + temp_sensor->registers->bgap_mask_ctrl);
>>> + }
>>> +
>>> + /*
>>> + * If current temperature is in-between the hot and cold thresholds,
>>> + * Enable both masks.
>>> + */
>>> + if (temp < t_hot && temp > t_cold) {
>>> + reg_val = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_mask_ctrl);
>>> + reg_val |= temp_sensor->registers->mask_cold_mask;
>>> + reg_val |= temp_sensor->registers->mask_hot_mask;
>>> + omap_temp_sensor_writel(temp_sensor, reg_val,
>>> + temp_sensor->registers->bgap_mask_ctrl);
>>> + }
>>> +
>>> + /*
>>> + * else no need to do anything since HW will immediately compare
>>> + * the new threshold and generate interrupt accordingly
>>> + */
>>> +
>>> +out:
>>> + mutex_unlock(&temp_sensor->sensor_mutex);
>>> + return count;
>>> +}
>>> +
>>> +static ssize_t show_update_rate(struct device *dev,
>>> + struct device_attribute *devattr, char *buf)
>>> +{
>>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>>> + u32 temp = 0, ret = 0;
>>> +
>>> + mutex_lock(&temp_sensor->sensor_mutex);
>>> +
>>> + if (!temp_sensor->clk_rate) {
>>> + dev_err(dev, "clk_rate is NULL\n");
>>> + ret = -EINVAL;
>>> + goto out;
>>> + }
>>> +
>>> + temp = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_counter);
>>> + temp = (temp & temp_sensor->registers->counter_mask) >>
>>> + __ffs(temp_sensor->registers->counter_mask);
>>> + temp = temp * 1000 / (temp_sensor->clk_rate);
>>> +
>>> +out:
>>> + mutex_unlock(&temp_sensor->sensor_mutex);
>>> + if (!ret)
>>> + return sprintf(buf, "%d\n", temp);
>>> +
>>> + return ret;
>>> +}
>>> +
>>> +static ssize_t set_update_rate(struct device *dev,
>>> + struct device_attribute *devattr,
>>> + const char *buf, size_t count)
>>> +{
>>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>>> + u32 reg_val;
>>> + long val;
>>> +
>>> + mutex_lock(&temp_sensor->sensor_mutex);
>>> +
>>> + if (strict_strtol(buf, 10, &val)) {
>>> + count = -EINVAL;
>>> + goto out;
>>> + }
>>> +
>>> + val *= temp_sensor->clk_rate / 1000;
>>> + reg_val = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_counter);
>>> +
>>> + reg_val &= ~(temp_sensor->registers->counter_mask);
>>> + reg_val |= val;
>>> + omap_temp_sensor_writel(temp_sensor, reg_val,
>>> + temp_sensor->registers->bgap_counter);
>>> +
>>> +out:
>>> + mutex_unlock(&temp_sensor->sensor_mutex);
>>> + return count;
>>> +}
>>> +
>>> +static int omap_temp_sensor_read_temp(struct device *dev,
>>> + struct device_attribute *devattr,
>>> + char *buf)
>>> +{
>>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>>> + int temp, ret = 0;
>>> +
>>> + mutex_lock(&temp_sensor->sensor_mutex);
>>> +
>>> + temp = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->temp_sensor_ctrl);
>>> + temp &= temp_sensor->registers->bgap_dtemp_mask;
>>> +
>>> + if (!temp_sensor->is_efuse_valid)
>>> + dev_err(dev, "Invalid EFUSE, Non-trimmed BGAP, Temp not accurate\n");
>>> +
>>> + /* look up for temperature in the table and return the temperature */
>>> + if (temp < OMAP_ADC_START_VALUE || temp > OMAP_ADC_END_VALUE) {
>>> + dev_err(dev, "invalid adc code reported %d", temp);
>>> + ret = -EINVAL;
>>> + goto out;
>>> + }
>>> +
>>> + temp = adc_to_temp[temp - OMAP_ADC_START_VALUE];
>>> +
>>> +out:
>>> + mutex_unlock(&temp_sensor->sensor_mutex);
>>> + if (!ret)
>>> + return sprintf(buf, "%d\n", temp);
>>> +
>>> + return ret;
>>> +}
>>> +
>>> +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, omap_temp_sensor_read_temp,
>>> + NULL, 0);
>>> +static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp_max,
>>> + set_temp_max, 0);
>>> +static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO, show_temp_max_hyst,
>>> + set_temp_max_hyst, 0);
>>> +static SENSOR_DEVICE_ATTR(update_rate, S_IWUSR | S_IRUGO, show_update_rate,
>>> + set_update_rate, 0);
>>> +
>>> +static struct attribute *omap_temp_sensor_attributes[] = {
>>> + &sensor_dev_attr_temp1_input.dev_attr.attr,
>>> + &sensor_dev_attr_temp1_max.dev_attr.attr,
>>> + &sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
>>> + &sensor_dev_attr_update_rate.dev_attr.attr,
>>> + NULL
>>> +};
>>> +
>>> +static const struct attribute_group omap_temp_sensor_group = {
>>> + .attrs = omap_temp_sensor_attributes,
>>> +};
>>> +
>>> +static int omap_temp_sensor_clk_enable(struct omap_temp_sensor *temp_sensor)
>>> +{
>>> + u32 ret = 0;
>>> +
>>> + if (temp_sensor->clk_on) {
>>> + dev_err(temp_sensor->hwmon_dev, "clock already on\n");
>>> + goto out;
>>> + }
>>> +
>>> + ret = pm_runtime_get_sync(temp_sensor->hwmon_dev);
>>> + if (ret < 0) {
>>> + dev_err(temp_sensor->hwmon_dev, "get sync failed\n");
>>> + goto out;
>>> + }
>>> +
>>> + temp_sensor->clk_on = 1;
>>> +
>>> +out:
>>> + return ret;
>>> +}
>>> +
>>> +static int omap_temp_sensor_clk_disable(struct omap_temp_sensor *temp_sensor)
>>> +{
>>> + u32 ret = 0;
>>> +
>>> + if (!temp_sensor->clk_on) {
>>> + dev_err(temp_sensor->hwmon_dev, "clock already off\n");
>>> + goto out;
>>> + }
>>> +
>>> + /* Gate the clock */
>>> + ret = pm_runtime_put_sync(temp_sensor->hwmon_dev);
>>> + if (ret < 0) {
>>> + dev_err(temp_sensor->hwmon_dev, "put sync failed\n");
>>> + goto out;
>>> + }
>>> + temp_sensor->clk_on = 0;
>>> +
>>> +out:
>>> + return ret;
>>> +}
>>> +
>>> +static irqreturn_t omap_talert_irq_handler(int irq, void *data)
>>> +{
>>> + struct omap_temp_sensor *temp_sensor;
>>> + int t_hot, t_cold, temp;
>>> +
>>> + temp_sensor = data;
>>> + mutex_lock(&temp_sensor->sensor_mutex);
>>> +
>>> + /* Read the status of t_hot */
>>> + t_hot = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_status)
>>> + & temp_sensor->registers->status_hot_mask;
>>> +
>>> + /* Read the status of t_cold */
>>> + t_cold = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_status)
>>> + & temp_sensor->registers->status_cold_mask;
>>> +
>>> + temp = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_mask_ctrl);
>>> + /*
>>> + * One TALERT interrupt: Two sources
>>> + * If the interrupt is due to t_hot then mask t_hot and
>>> + * and unmask t_cold else mask t_cold and unmask t_hot
>>> + */
>>> + if (t_hot) {
>>> + temp &= ~(temp_sensor->registers->mask_hot_mask);
>>> + temp |= temp_sensor->registers->mask_cold_mask;
>>> + } else if (t_cold) {
>>> + temp &= ~(temp_sensor->registers->mask_cold_mask);
>>> + temp |= temp_sensor->registers->mask_hot_mask;
>>> + }
>>> +
>>> + omap_temp_sensor_writel(temp_sensor, temp,
>>> + temp_sensor->registers->bgap_mask_ctrl);
>>> +
>>> + /* kobject_uvent to user space telling thermal threshold crossed */
>>> + kobject_uevent(&temp_sensor->hwmon_dev->kobj, KOBJ_CHANGE);
>>> +
>>> + mutex_unlock(&temp_sensor->sensor_mutex);
>>> +
>>> + return IRQ_HANDLED;
>>> +}
>>> +
>>> +static int __devinit omap_temp_sensor_probe(struct platform_device *pdev)
>>> +{
>>> + struct omap_temp_sensor_pdata *pdata = pdev->dev.platform_data;
>>> + struct omap_temp_sensor *temp_sensor;
>>> + struct resource *mem;
>>> + int ret = 0;
>>> + int val, clk_rate;
>>> +
>>> + if (!pdata) {
>>> + dev_err(&pdev->dev, "platform data missing\n");
>>> + return -EINVAL;
>>> + }
>>> +
>>> + temp_sensor = kzalloc(sizeof(*temp_sensor), GFP_KERNEL);
>>> + if (!temp_sensor)
>>> + return -ENOMEM;
>>> +
>>> + mutex_init(&temp_sensor->sensor_mutex);
>>> +
>>> + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>>> + if (!mem) {
>>> + dev_err(&pdev->dev, "no mem resource\n");
>>> + ret = -ENOMEM;
>>> + goto plat_res_err;
>>> + }
>>> +
>>> + temp_sensor->irq = platform_get_irq_byname(pdev, "thermal_alert");
>>> + if (temp_sensor->irq < 0) {
>>> + dev_err(&pdev->dev, "get_irq_byname failed\n");
>>> + ret = temp_sensor->irq;
>>> + goto plat_res_err;
>>> + }
>>> +
>>> + temp_sensor->phy_base = ioremap(mem->start, resource_size(mem));
>>> + temp_sensor->clock = NULL;
>>> + temp_sensor->registers = pdata->registers;
>>> + temp_sensor->hwmon_dev = &pdev->dev;
>>> +
>>> + pm_runtime_enable(&pdev->dev);
>>> + pm_runtime_irq_safe(&pdev->dev);
>>> +
>>> + /*
>>> + * check if the efuse has a non-zero value if not
>>> + * it is an untrimmed sample and the temperatures
>>> + * may not be accurate
>>> + */
>>> +
>>> + if (omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_efuse))
>>> + temp_sensor->is_efuse_valid = 1;
>>> +
>>> + platform_set_drvdata(pdev, temp_sensor);
>>> + dev_set_drvdata(&pdev->dev, temp_sensor);
>>> + temp_sensor->clock = clk_get(temp_sensor->hwmon_dev, "fck");
>>> + if (IS_ERR(temp_sensor->clock)) {
>>> + ret = PTR_ERR(temp_sensor->clock);
>>> + dev_err(temp_sensor->hwmon_dev,
>>> + "unable to get fclk: %d\n", ret);
>>> + ret = -EINVAL;
>>> + goto plat_res_err;
>>> + }
>>> +
>>> + ret = omap_temp_sensor_clk_enable(temp_sensor);
>>> + if (ret) {
>>> + dev_err(&pdev->dev, "Cannot enable temp sensor\n");
>>> + goto clken_err;
>>> + }
>>> +
>>> + clk_rate = clk_round_rate(temp_sensor->clock, 2000000);
>>> + if (clk_rate < 1000000 || clk_rate = 0xffffffff) {
>>> + dev_err(&pdev->dev, "Error round rate\n");
>>> + ret = -EINVAL;
>>> + goto clken_err;
>>> + }
>>> +
>>> + ret = clk_set_rate(temp_sensor->clock, clk_rate);
>>> + if (ret) {
>>> + dev_err(&pdev->dev, "Cannot set clock rate\n");
>>> + goto clken_err;
>>> + }
>>> +
>>> + temp_sensor->clk_rate = clk_rate;
>>> + omap_enable_continuous_mode(temp_sensor, 1);
>>> + omap_configure_temp_sensor_thresholds(temp_sensor);
>>> + /* 1 ms */
>>> + omap_configure_temp_sensor_counter(temp_sensor, 1);
>>> +
>>> + /* Wait till the first conversion is done wait for at least 1ms */
>>> + usleep_range(1000, 2000);
>>> +
>>> + /* Read the temperature once due to hw issue*/
>>> + omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->temp_sensor_ctrl);
>>> +
>>> + /* Set 2 seconds time as default counter */
>>> + omap_configure_temp_sensor_counter(temp_sensor,
>>> + temp_sensor->clk_rate * 2);
>>> +
>>> + temp_sensor->hwmon_dev = hwmon_device_register(&pdev->dev);
>>> + if (IS_ERR(temp_sensor->hwmon_dev)) {
>>> + dev_err(&pdev->dev, "hwmon_device_register failed.\n");
>>> + ret = PTR_ERR(temp_sensor->hwmon_dev);
>>> + goto hwmon_reg_err;
>>> + }
>>> +
>>> + ret = sysfs_create_group(&pdev->dev.kobj,
>>> + &omap_temp_sensor_group);
>>> + if (ret) {
>>> + dev_err(&pdev->dev, "could not create sysfs files\n");
>>> + goto sysfs_create_err;
>>> + }
>>> +
>>> + kobject_uevent(&temp_sensor->hwmon_dev->kobj, KOBJ_ADD);
>>> +
>>> + ret = request_threaded_irq(temp_sensor->irq, NULL,
>>> + omap_talert_irq_handler, IRQF_TRIGGER_RISING | IRQF_ONESHOT,
>>> + "temp_sensor", temp_sensor);
>>> + if (ret) {
>>> + dev_err(&pdev->dev, "Request threaded irq failed.\n");
>>> + goto req_irq_err;
>>> + }
>>> +
>>> + /* unmask the T_COLD and unmask T_HOT at init */
>>> + val = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_mask_ctrl);
>>> + val |= temp_sensor->registers->mask_cold_mask
>>> + | temp_sensor->registers->mask_hot_mask;
>>> +
>>> + omap_temp_sensor_writel(temp_sensor, val,
>>> + temp_sensor->registers->bgap_mask_ctrl);
>>> +
>>> + return 0;
>>> +
>>> +req_irq_err:
>>> + kobject_uevent(&temp_sensor->hwmon_dev->kobj, KOBJ_REMOVE);
>>> + sysfs_remove_group(&temp_sensor->hwmon_dev->kobj,
>>> + &omap_temp_sensor_group);
>>> +sysfs_create_err:
>>> + hwmon_device_unregister(&pdev->dev);
>>> +hwmon_reg_err:
>>> + omap_temp_sensor_clk_disable(temp_sensor);
>>> +clken_err:
>>> + clk_put(temp_sensor->clock);
>>> +plat_res_err:
>>> + mutex_destroy(&temp_sensor->sensor_mutex);
>>> + kfree(temp_sensor);
>>> + return ret;
>>> +}
>>> +
>>> +static int __devexit omap_temp_sensor_remove(struct platform_device *pdev)
>>> +{
>>> + struct omap_temp_sensor *temp_sensor = platform_get_drvdata(pdev);
>>> +
>>> + hwmon_device_unregister(&pdev->dev);
>>> + kobject_uevent(&temp_sensor->hwmon_dev->kobj, KOBJ_REMOVE);
>>> + sysfs_remove_group(&temp_sensor->hwmon_dev->kobj,
>>> + &omap_temp_sensor_group);
>>> + omap_temp_sensor_clk_disable(temp_sensor);
>>> + clk_put(temp_sensor->clock);
>>> + platform_set_drvdata(pdev, NULL);
>>> + free_irq(temp_sensor->irq, temp_sensor);
>>> + mutex_destroy(&temp_sensor->sensor_mutex);
>>> + kfree(temp_sensor);
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +#ifdef CONFIG_PM
>>> +static void omap_temp_sensor_save_ctxt(struct omap_temp_sensor *temp_sensor)
>>> +{
>>> + temp_sensor->temp_sensor_ctrl = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->temp_sensor_ctrl);
>>> + temp_sensor->bg_ctrl = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_mask_ctrl);
>>> + temp_sensor->bg_counter = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_counter);
>>> + temp_sensor->bg_threshold = omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->bgap_threshold);
>>> + temp_sensor->temp_sensor_tshut_threshold >>> + omap_temp_sensor_readl(temp_sensor,
>>> + temp_sensor->registers->thsut_threshold);
>>> +}
>>> +
>>> +static void omap_temp_sensor_restore_ctxt(struct omap_temp_sensor *temp_sensor)
>>> +{
>>> + omap_temp_sensor_writel(temp_sensor,
>>> + temp_sensor->temp_sensor_ctrl,
>>> + temp_sensor->registers->temp_sensor_ctrl);
>>> + omap_temp_sensor_writel(temp_sensor,
>>> + temp_sensor->bg_ctrl,
>>> + temp_sensor->registers->bgap_mask_ctrl);
>>> + omap_temp_sensor_writel(temp_sensor,
>>> + temp_sensor->bg_counter,
>>> + temp_sensor->registers->bgap_counter);
>>> + omap_temp_sensor_writel(temp_sensor,
>>> + temp_sensor->bg_threshold,
>>> + temp_sensor->registers->bgap_threshold);
>>> + omap_temp_sensor_writel(temp_sensor,
>>> + temp_sensor->temp_sensor_tshut_threshold,
>>> + temp_sensor->registers->thsut_threshold);
>>> +}
>>> +
>>> +static int omap_temp_sensor_suspend(struct device *dev)
>>> +{
>>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>>> +
>>> + omap_temp_sensor_save_ctxt(temp_sensor);
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static int omap_temp_sensor_resume(struct device *dev)
>>> +{
>>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>>> +
>>> + omap_temp_sensor_restore_ctxt(temp_sensor);
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static int omap_temp_sensor_runtime_suspend(struct device *dev)
>>> +{
>>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>>> +
>>> + omap_temp_sensor_save_ctxt(temp_sensor);
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static int omap_temp_sensor_runtime_resume(struct device *dev)
>>> +{
>>> + static int context_loss_count;
>>> + int temp;
>>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>>> +
>>> + temp = omap_device_get_context_loss_count(to_platform_device(dev));
>>> +
>>> + if (temp != context_loss_count && context_loss_count != 0)
>>> + omap_temp_sensor_restore_ctxt(temp_sensor);
>>> +
>>> + context_loss_count = temp;
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static const struct dev_pm_ops omap_temp_sensor_dev_pm_ops = {
>>> + .suspend = omap_temp_sensor_suspend,
>>> + .resume = omap_temp_sensor_resume,
>>> + .runtime_suspend = omap_temp_sensor_runtime_suspend,
>>> + .runtime_resume = omap_temp_sensor_runtime_resume,
>>> +};
>>> +
>>> +#endif
>>> +
>>> +static struct platform_driver omap_temp_sensor_driver = {
>>> + .probe = omap_temp_sensor_probe,
>>> + .remove = omap_temp_sensor_remove,
>>> + .driver = {
>>> + .name = "omap_temp_sensor",
>>> +#ifdef CONFIG_PM
>>> + .pm = &omap_temp_sensor_dev_pm_ops,
>>> +#endif
>>> + },
>>> +};
>>> +
>>> +int __init omap_temp_sensor_init(void)
>>> +{
>>> + return platform_driver_register(&omap_temp_sensor_driver);
>>> +}
>>> +module_init(omap_temp_sensor_init);
>>> +
>>> +static void __exit omap_temp_sensor_exit(void)
>>> +{
>>> + platform_driver_unregister(&omap_temp_sensor_driver);
>>> +}
>>> +module_exit(omap_temp_sensor_exit);
>>> +
>>> +MODULE_DESCRIPTION("OMAP446X temperature sensor Driver");
>>> +MODULE_LICENSE("GPL");
>>> +MODULE_ALIAS("platform:" DRIVER_NAME);
>>> +MODULE_AUTHOR("J Keerthy <j-keerthy@ti.com>");
>>> --
>>> 1.7.0.4
>>>
>>
>
>
>
> --
> Regards and Thanks,
> Keerthy
>
--
Regards and Thanks,
Keerthy
_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors
^ permalink raw reply [flat|nested] 35+ messages in thread
* Re: [RFC PATCH 6/6 V2] hwmon: OMAP4: On die temperature sensor driver
2011-08-19 6:17 ` [lm-sensors] [RFC PATCH 6/6 V2] hwmon: OMAP4: On die Guenter Roeck
@ 2011-08-19 13:13 ` J, KEERTHY
-1 siblings, 0 replies; 35+ messages in thread
From: J, KEERTHY @ 2011-08-19 13:01 UTC (permalink / raw)
To: Guenter Roeck; +Cc: linux-omap, Jean Delvare, lm-sensors
On Fri, Aug 19, 2011 at 11:47 AM, Guenter Roeck
<guenter.roeck@ericsson.com> wrote:
> On Fri, Aug 19, 2011 at 02:04:58AM -0400, J, KEERTHY wrote:
>> On Fri, Aug 19, 2011 at 7:43 AM, Guenter Roeck
>> <guenter.roeck@ericsson.com> wrote:
>> > On Thu, Aug 18, 2011 at 06:52:15AM -0400, Keerthy wrote:
>> >> On chip temperature sensor driver. The driver monitors the temperature of
>> >> the MPU subsystem of the OMAP4. It sends notifications to the user space if
>> >> the temperature crosses user defined thresholds via kobject_uevent interface.
>> >> The user is allowed to configure the temperature thresholds vis sysfs nodes
>> >> exposed using hwmon interface.
>> >>
>> >> Signed-off-by: Keerthy <j-keerthy@ti.com>
>> >> Cc: Jean Delvare <khali@linux-fr.org>
>> >> Cc: Guenter Roeck <guenter.roeck@ericsson.com>
>> >> Cc: lm-sensors@lm-sensors.org
>> >
>> > High level review:
>> >
>> > - too much and too broad mutex locking. show functions should not need locks at all,
>> > set functions only while data is written into registers and into platform data.
>>
>> Ok. I will clean this.
>>
>> > - driver is quite noisy. There should definitely not be any log messages
>> > if a set parameter is wrong. Show functions already return an error value
>> > to the user; a log message indicating the error again just creates noise.
>> > For one boolean set during probe (is_efuse_valid), each subsequent show results
>> > in a log message if it is false. Some errors result in multiple log messages.
>>
>> A user tries to set an invalid temperature threshold. The user should
>> be notified about this. The invalid temperature will not be set. The user
>> should not be allowed to set an invalid temperature. It is to inform
>> the user about precisely the problem with the parameter.
>>
> User is notified with -EINVAL. Unless on the console, which is unlikely,
> the user will likely not notice a message in the kernel log.
These messages on console are useful for debug purpose
so can i place it under dev_dbg? Only on explicit enabling
these messages can be seen.
>
>> In some of the samples the bandgap is not trimmed and hence
>> temperature reported will be wrong. So every time a user tries to read
>> he is alerted that the temperatures are not accurate.
>>
> In the kernel log ? Sorry, that doesn't make sense. You alert the system administrator,
> not the user.
Ok. Can this be a dev_dbg message? Only for debug purpose
this can be useful.
>
> Guenter
>
--
Regards and Thanks,
Keerthy
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 35+ messages in thread
* Re: [lm-sensors] [RFC PATCH 6/6 V2] hwmon: OMAP4: On die
@ 2011-08-19 13:13 ` J, KEERTHY
0 siblings, 0 replies; 35+ messages in thread
From: J, KEERTHY @ 2011-08-19 13:13 UTC (permalink / raw)
To: Guenter Roeck; +Cc: linux-omap, Jean Delvare, lm-sensors
On Fri, Aug 19, 2011 at 11:47 AM, Guenter Roeck
<guenter.roeck@ericsson.com> wrote:
> On Fri, Aug 19, 2011 at 02:04:58AM -0400, J, KEERTHY wrote:
>> On Fri, Aug 19, 2011 at 7:43 AM, Guenter Roeck
>> <guenter.roeck@ericsson.com> wrote:
>> > On Thu, Aug 18, 2011 at 06:52:15AM -0400, Keerthy wrote:
>> >> On chip temperature sensor driver. The driver monitors the temperature of
>> >> the MPU subsystem of the OMAP4. It sends notifications to the user space if
>> >> the temperature crosses user defined thresholds via kobject_uevent interface.
>> >> The user is allowed to configure the temperature thresholds vis sysfs nodes
>> >> exposed using hwmon interface.
>> >>
>> >> Signed-off-by: Keerthy <j-keerthy@ti.com>
>> >> Cc: Jean Delvare <khali@linux-fr.org>
>> >> Cc: Guenter Roeck <guenter.roeck@ericsson.com>
>> >> Cc: lm-sensors@lm-sensors.org
>> >
>> > High level review:
>> >
>> > - too much and too broad mutex locking. show functions should not need locks at all,
>> > set functions only while data is written into registers and into platform data.
>>
>> Ok. I will clean this.
>>
>> > - driver is quite noisy. There should definitely not be any log messages
>> > if a set parameter is wrong. Show functions already return an error value
>> > to the user; a log message indicating the error again just creates noise.
>> > For one boolean set during probe (is_efuse_valid), each subsequent show results
>> > in a log message if it is false. Some errors result in multiple log messages.
>>
>> A user tries to set an invalid temperature threshold. The user should
>> be notified about this. The invalid temperature will not be set. The user
>> should not be allowed to set an invalid temperature. It is to inform
>> the user about precisely the problem with the parameter.
>>
> User is notified with -EINVAL. Unless on the console, which is unlikely,
> the user will likely not notice a message in the kernel log.
These messages on console are useful for debug purpose
so can i place it under dev_dbg? Only on explicit enabling
these messages can be seen.
>
>> In some of the samples the bandgap is not trimmed and hence
>> temperature reported will be wrong. So every time a user tries to read
>> he is alerted that the temperatures are not accurate.
>>
> In the kernel log ? Sorry, that doesn't make sense. You alert the system administrator,
> not the user.
Ok. Can this be a dev_dbg message? Only for debug purpose
this can be useful.
>
> Guenter
>
--
Regards and Thanks,
Keerthy
_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors
^ permalink raw reply [flat|nested] 35+ messages in thread
* Re: [RFC PATCH 6/6 V2] hwmon: OMAP4: On die temperature sensor driver
2011-08-19 13:13 ` [lm-sensors] [RFC PATCH 6/6 V2] hwmon: OMAP4: On die J, KEERTHY
@ 2011-08-19 13:48 ` Guenter Roeck
-1 siblings, 0 replies; 35+ messages in thread
From: Guenter Roeck @ 2011-08-19 13:48 UTC (permalink / raw)
To: J, KEERTHY; +Cc: linux-omap, Jean Delvare, lm-sensors
On Fri, Aug 19, 2011 at 09:01:53AM -0400, J, KEERTHY wrote:
> On Fri, Aug 19, 2011 at 11:47 AM, Guenter Roeck
> <guenter.roeck@ericsson.com> wrote:
> > On Fri, Aug 19, 2011 at 02:04:58AM -0400, J, KEERTHY wrote:
> >> On Fri, Aug 19, 2011 at 7:43 AM, Guenter Roeck
> >> <guenter.roeck@ericsson.com> wrote:
> >> > On Thu, Aug 18, 2011 at 06:52:15AM -0400, Keerthy wrote:
> >> >> On chip temperature sensor driver. The driver monitors the temperature of
> >> >> the MPU subsystem of the OMAP4. It sends notifications to the user space if
> >> >> the temperature crosses user defined thresholds via kobject_uevent interface.
> >> >> The user is allowed to configure the temperature thresholds vis sysfs nodes
> >> >> exposed using hwmon interface.
> >> >>
> >> >> Signed-off-by: Keerthy <j-keerthy@ti.com>
> >> >> Cc: Jean Delvare <khali@linux-fr.org>
> >> >> Cc: Guenter Roeck <guenter.roeck@ericsson.com>
> >> >> Cc: lm-sensors@lm-sensors.org
> >> >
> >> > High level review:
> >> >
> >> > - too much and too broad mutex locking. show functions should not need locks at all,
> >> > set functions only while data is written into registers and into platform data.
> >>
> >> Ok. I will clean this.
> >>
> >> > - driver is quite noisy. There should definitely not be any log messages
> >> > if a set parameter is wrong. Show functions already return an error value
> >> > to the user; a log message indicating the error again just creates noise.
> >> > For one boolean set during probe (is_efuse_valid), each subsequent show results
> >> > in a log message if it is false. Some errors result in multiple log messages.
> >>
> >> A user tries to set an invalid temperature threshold. The user should
> >> be notified about this. The invalid temperature will not be set. The user
> >> should not be allowed to set an invalid temperature. It is to inform
> >> the user about precisely the problem with the parameter.
> >>
> > User is notified with -EINVAL. Unless on the console, which is unlikely,
> > the user will likely not notice a message in the kernel log.
>
> These messages on console are useful for debug purpose
> so can i place it under dev_dbg? Only on explicit enabling
> these messages can be seen.
>
Not really for user errors.
The user is already alerted with EINVAL that the parameter was invalid. _Why_
it was invalid should be well defined. Are you really sure you want to have
something like "user entered 'Hello' as temperature" even in a debug log ?
The debug log should be to debug driver problems and behavior, not to debug
wrong parameters entered by users.
> >
> >> In some of the samples the bandgap is not trimmed and hence
> >> temperature reported will be wrong. So every time a user tries to read
> >> he is alerted that the temperatures are not accurate.
> >>
> > In the kernel log ? Sorry, that doesn't make sense. You alert the system administrator,
> > not the user.
>
> Ok. Can this be a dev_dbg message? Only for debug purpose
> this can be useful.
>
Yes, but even then you need the message only once.
Thanks,
Guenter
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 35+ messages in thread
* Re: [lm-sensors] [RFC PATCH 6/6 V2] hwmon: OMAP4: On die
@ 2011-08-19 13:48 ` Guenter Roeck
0 siblings, 0 replies; 35+ messages in thread
From: Guenter Roeck @ 2011-08-19 13:48 UTC (permalink / raw)
To: J, KEERTHY; +Cc: linux-omap, Jean Delvare, lm-sensors
On Fri, Aug 19, 2011 at 09:01:53AM -0400, J, KEERTHY wrote:
> On Fri, Aug 19, 2011 at 11:47 AM, Guenter Roeck
> <guenter.roeck@ericsson.com> wrote:
> > On Fri, Aug 19, 2011 at 02:04:58AM -0400, J, KEERTHY wrote:
> >> On Fri, Aug 19, 2011 at 7:43 AM, Guenter Roeck
> >> <guenter.roeck@ericsson.com> wrote:
> >> > On Thu, Aug 18, 2011 at 06:52:15AM -0400, Keerthy wrote:
> >> >> On chip temperature sensor driver. The driver monitors the temperature of
> >> >> the MPU subsystem of the OMAP4. It sends notifications to the user space if
> >> >> the temperature crosses user defined thresholds via kobject_uevent interface.
> >> >> The user is allowed to configure the temperature thresholds vis sysfs nodes
> >> >> exposed using hwmon interface.
> >> >>
> >> >> Signed-off-by: Keerthy <j-keerthy@ti.com>
> >> >> Cc: Jean Delvare <khali@linux-fr.org>
> >> >> Cc: Guenter Roeck <guenter.roeck@ericsson.com>
> >> >> Cc: lm-sensors@lm-sensors.org
> >> >
> >> > High level review:
> >> >
> >> > - too much and too broad mutex locking. show functions should not need locks at all,
> >> > set functions only while data is written into registers and into platform data.
> >>
> >> Ok. I will clean this.
> >>
> >> > - driver is quite noisy. There should definitely not be any log messages
> >> > if a set parameter is wrong. Show functions already return an error value
> >> > to the user; a log message indicating the error again just creates noise.
> >> > For one boolean set during probe (is_efuse_valid), each subsequent show results
> >> > in a log message if it is false. Some errors result in multiple log messages.
> >>
> >> A user tries to set an invalid temperature threshold. The user should
> >> be notified about this. The invalid temperature will not be set. The user
> >> should not be allowed to set an invalid temperature. It is to inform
> >> the user about precisely the problem with the parameter.
> >>
> > User is notified with -EINVAL. Unless on the console, which is unlikely,
> > the user will likely not notice a message in the kernel log.
>
> These messages on console are useful for debug purpose
> so can i place it under dev_dbg? Only on explicit enabling
> these messages can be seen.
>
Not really for user errors.
The user is already alerted with EINVAL that the parameter was invalid. _Why_
it was invalid should be well defined. Are you really sure you want to have
something like "user entered 'Hello' as temperature" even in a debug log ?
The debug log should be to debug driver problems and behavior, not to debug
wrong parameters entered by users.
> >
> >> In some of the samples the bandgap is not trimmed and hence
> >> temperature reported will be wrong. So every time a user tries to read
> >> he is alerted that the temperatures are not accurate.
> >>
> > In the kernel log ? Sorry, that doesn't make sense. You alert the system administrator,
> > not the user.
>
> Ok. Can this be a dev_dbg message? Only for debug purpose
> this can be useful.
>
Yes, but even then you need the message only once.
Thanks,
Guenter
_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors
^ permalink raw reply [flat|nested] 35+ messages in thread
* Re: [RFC PATCH 6/6 V2] hwmon: OMAP4: On die temperature sensor driver
2011-08-18 11:37 ` [lm-sensors] [RFC PATCH 6/6 V2] hwmon: OMAP4: On die Felipe Balbi
@ 2011-08-22 4:41 ` J, KEERTHY
-1 siblings, 0 replies; 35+ messages in thread
From: J, KEERTHY @ 2011-08-22 4:29 UTC (permalink / raw)
To: balbi; +Cc: linux-omap, lm-sensors
On Thu, Aug 18, 2011 at 5:07 PM, Felipe Balbi <balbi@ti.com> wrote:
> Hi,
>
> On Thu, Aug 18, 2011 at 04:22:15PM +0530, Keerthy wrote:
>> On chip temperature sensor driver. The driver monitors the temperature of
>> the MPU subsystem of the OMAP4. It sends notifications to the user space if
>> the temperature crosses user defined thresholds via kobject_uevent interface.
>> The user is allowed to configure the temperature thresholds vis sysfs nodes
>> exposed using hwmon interface.
>>
>> Signed-off-by: Keerthy <j-keerthy@ti.com>
>> Cc: Jean Delvare <khali@linux-fr.org>
>> Cc: Guenter Roeck <guenter.roeck@ericsson.com>
>> Cc: lm-sensors@lm-sensors.org
>> ---
>> Documentation/hwmon/omap_temp_sensor | 27 +
>> drivers/hwmon/Kconfig | 11 +
>> drivers/hwmon/Makefile | 1 +
>> drivers/hwmon/omap_temp_sensor.c | 918 ++++++++++++++++++++++++++++++++++
>> 4 files changed, 957 insertions(+), 0 deletions(-)
>> create mode 100644 Documentation/hwmon/omap_temp_sensor
>> create mode 100644 drivers/hwmon/omap_temp_sensor.c
>>
>> diff --git a/Documentation/hwmon/omap_temp_sensor b/Documentation/hwmon/omap_temp_sensor
>> new file mode 100644
>> index 0000000..e01a6d6
>> --- /dev/null
>> +++ b/Documentation/hwmon/omap_temp_sensor
>> @@ -0,0 +1,27 @@
>> +Kernel driver omap_temp_sensor
>> +==============================
>> +
>> +Supported chips:
>> + * Texas Instruments OMAP4460
>> + Prefix: 'omap_temp_sensor'
>> + Datasheet: http://focus.ti.com/docs/prod/folders/print/tmp102.html
>> +
>> +Author:
>> + J Keerthy <j-keerthy@ti.com>
>> +
>> +Description
>> +-----------
>> +
>> +The Texas Instruments OMAP4 family of chips have a bandgap temperature sensor.
>> +The temperature sensor feature is used to convert the temperature of the device
>> +into a decimal value coded on 10 bits. An internal ADC is used for conversion.
>> +The recommended operating temperatures must be in the range -40 degree Celsius
>> +to 123 degree celsius for standard conversion.
>> +The thresholds are programmable and upon crossing the thresholds an interrupt
>> +is generated. The OMAP temperature sensor has a programmable update rate in
>> +milli seconds.
>> +(Currently the driver programs a default of 2000 milli seconds).
>> +
>> +The driver provides the common sysfs-interface for temperatures (see
>> +Documentation/hwmon/sysfs-interface under Temperatures).
>> +
>> diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
>> index 5f888f7..9c9cd8b 100644
>> --- a/drivers/hwmon/Kconfig
>> +++ b/drivers/hwmon/Kconfig
>> @@ -323,6 +323,17 @@ config SENSORS_F71805F
>> This driver can also be built as a module. If so, the module
>> will be called f71805f.
>>
>> +config SENSORS_OMAP_BANDGAP_TEMP_SENSOR
>> + bool "OMAP on-die temperature sensor hwmon driver"
>> + depends on HWMON && ARCH_OMAP && OMAP_TEMP_SENSOR
>> + help
>> + If you say yes here you get support for hardware
>> + monitoring features of the OMAP on die temperature
>> + sensor.
>> +
>> + Continuous conversion programmable delay
>> + mode is used for temperature conversion.
>> +
>> config SENSORS_F71882FG
>> tristate "Fintek F71882FG and compatibles"
>> help
>> diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
>> index 28061cf..d0f89f5 100644
>> --- a/drivers/hwmon/Makefile
>> +++ b/drivers/hwmon/Makefile
>> @@ -91,6 +91,7 @@ obj-$(CONFIG_SENSORS_MAX6639) += max6639.o
>> obj-$(CONFIG_SENSORS_MAX6642) += max6642.o
>> obj-$(CONFIG_SENSORS_MAX6650) += max6650.o
>> obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o
>> +obj-$(CONFIG_SENSORS_OMAP_BANDGAP_TEMP_SENSOR) += omap_temp_sensor.o
>> obj-$(CONFIG_SENSORS_PC87360) += pc87360.o
>> obj-$(CONFIG_SENSORS_PC87427) += pc87427.o
>> obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o
>> diff --git a/drivers/hwmon/omap_temp_sensor.c b/drivers/hwmon/omap_temp_sensor.c
>> new file mode 100644
>> index 0000000..586a361
>> --- /dev/null
>> +++ b/drivers/hwmon/omap_temp_sensor.c
>> @@ -0,0 +1,918 @@
>> +/*
>> + * OMAP4 Temperature sensor driver file
>> + *
>> + * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
>> + * Author: J Keerthy <j-keerthy@ti.com>
>> + * Author: Moiz Sonasath <m-sonasath@ti.com>
>> + *
>> + * This program is free software; you can redistribute it and/or
>> + * modify it under the terms of the GNU General Public License
>> + * 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.
>> + *
>> + * You should have received a copy of the GNU General Public License
>> + * along with this program; if not, write to the Free Software
>> + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
>> + * 02110-1301 USA
>> + *
>> + */
>> +
>> +#include <linux/interrupt.h>
>> +#include <linux/clk.h>
>> +#include <linux/io.h>
>> +#include <linux/slab.h>
>> +#include <linux/init.h>
>> +#include <plat/omap_device.h>
>> +#include <linux/kernel.h>
>> +#include <linux/device.h>
>> +#include <linux/jiffies.h>
>> +#include <linux/hwmon.h>
>> +#include <linux/hwmon-sysfs.h>
>> +#include <linux/stddef.h>
>> +#include <linux/sysfs.h>
>> +#include <linux/err.h>
>> +#include <linux/types.h>
>> +#include <linux/mutex.h>
>> +#include <linux/pm_runtime.h>
>> +#include <linux/delay.h>
>> +#include <plat/temperature_sensor.h>
>> +
>> +#define TSHUT_THRESHOLD_HOT 122000 /* 122 deg C */
>> +#define TSHUT_THRESHOLD_COLD 100000 /* 100 deg C */
>> +#define BGAP_THRESHOLD_T_HOT 73000 /* 73 deg C */
>> +#define BGAP_THRESHOLD_T_COLD 71000 /* 71 deg C */
>> +#define OMAP_ADC_START_VALUE 530
>> +#define OMAP_ADC_END_VALUE 923
>> +
>> +/*
>> + * omap_temp_sensor structure
>> + * @hwmon_dev - device pointer
>> + * @clock - Clock pointer
>> + * @registers - Pointer to structure with register offsets and bitfields
>> + * @sensor_mutex - Mutex for sysfs, irq and PM
>> + * @irq - MPU Irq number for thermal alert
>> + * @phy_base - Physical base of the temp I/O
>> + * @clk_rate - Holds current clock rate
>> + * @temp_sensor_ctrl - temp sensor control register value
>> + * @bg_ctrl - bandgap ctrl register value
>> + * @bg_counter - bandgap counter value
>> + * @bg_threshold - bandgap threshold register value
>> + * @temp_sensor_tshut_threshold - bandgap tshut register value
>> + * @is_efuse_valid - Flag to determine if efuse is valid or not
>> + * @clk_on - Manages the current clock state
>> + */
>> +struct omap_temp_sensor {
>> + struct device *hwmon_dev;
>> + struct clk *clock;
>> + struct omap_temp_sensor_registers *registers;
>> + struct mutex sensor_mutex; /* Mutex for sysfs, irq and PM */
>> + unsigned int irq;
>> + void __iomem *phy_base;
>> + u32 clk_rate;
>> + u32 temp_sensor_ctrl;
>> + u32 bg_ctrl;
>> + u32 bg_counter;
>> + u32 bg_threshold;
>> + u32 temp_sensor_tshut_threshold;
>> + bool is_efuse_valid;
>> + bool clk_on;
>> +};
>> +
>> +/*
>> + * Temperature values in milli degree celsius
>> + * ADC code values from 530 to 923
>> + */
>> +static int adc_to_temp[] = {
>> + -40000, -40000, -40000, -40000, -39800, -39400, -39000, -38600, -38200,
>> + -37800, -37300, -36800, -36400, -36000, -35600, -35200, -34800,
>> + -34300, -33800, -33400, -33000, -32600, -32200, -31800, -31300,
>> + -30800, -30400, -30000, -29600, -29200, -28700, -28200, -27800,
>> + -27400, -27000, -26600, -26200, -25700, -25200, -24800, -24400,
>> + -24000, -23600, -23200, -22700, -22200, -21800, -21400, -21000,
>> + -20600, -20200, -19700, -19200, -18800, -18400, -18000, -17600,
>> + -17200, -16700, -16200, -15800, -15400, -15000, -14600, -14200,
>> + -13700, -13200, -12800, -12400, -12000, -11600, -11200, -10700,
>> + -10200, -9800, -9400, -9000, -8600, -8200, -7700, -7200, -6800,
>> + -6400, -6000, -5600, -5200, -4800, -4300, -3800, -3400, -3000,
>> + -2600, -2200, -1800, -1300, -800, -400, 0, 400, 800, 1200, 1600,
>> + 2100, 2600, 3000, 3400, 3800, 4200, 4600, 5100, 5600, 6000, 6400,
>> + 6800, 7200, 7600, 8000, 8500, 9000, 9400, 9800, 10200, 10600, 11000,
>> + 11400, 11900, 12400, 12800, 13200, 13600, 14000, 14400, 14800,
>> + 15300, 15800, 16200, 16600, 17000, 17400, 17800, 18200, 18700,
>> + 19200, 19600, 20000, 20400, 20800, 21200, 21600, 22100, 22600,
>> + 23000, 23400, 23800, 24200, 24600, 25000, 25400, 25900, 26400,
>> + 26800, 27200, 27600, 28000, 28400, 28800, 29300, 29800, 30200,
>> + 30600, 31000, 31400, 31800, 32200, 32600, 33100, 33600, 34000,
>> + 34400, 34800, 35200, 35600, 36000, 36400, 36800, 37300, 37800,
>> + 38200, 38600, 39000, 39400, 39800, 40200, 40600, 41100, 41600,
>> + 42000, 42400, 42800, 43200, 43600, 44000, 44400, 44800, 45300,
>> + 45800, 46200, 46600, 47000, 47400, 47800, 48200, 48600, 49000,
>> + 49500, 50000, 50400, 50800, 51200, 51600, 52000, 52400, 52800,
>> + 53200, 53700, 54200, 54600, 55000, 55400, 55800, 56200, 56600,
>> + 57000, 57400, 57800, 58200, 58700, 59200, 59600, 60000, 60400,
>> + 60800, 61200, 61600, 62000, 62400, 62800, 63300, 63800, 64200,
>> + 64600, 65000, 65400, 65800, 66200, 66600, 67000, 67400, 67800,
>> + 68200, 68700, 69200, 69600, 70000, 70400, 70800, 71200, 71600,
>> + 72000, 72400, 72800, 73200, 73600, 74100, 74600, 75000, 75400,
>> + 75800, 76200, 76600, 77000, 77400, 77800, 78200, 78600, 79000,
>> + 79400, 79800, 80300, 80800, 81200, 81600, 82000, 82400, 82800,
>> + 83200, 83600, 84000, 84400, 84800, 85200, 85600, 86000, 86400,
>> + 86800, 87300, 87800, 88200, 88600, 89000, 89400, 89800, 90200,
>> + 90600, 91000, 91400, 91800, 92200, 92600, 93000, 93400, 93800,
>> + 94200, 94600, 95000, 95500, 96000, 96400, 96800, 97200, 97600,
>> + 98000, 98400, 98800, 99200, 99600, 100000, 100400, 100800, 101200,
>> + 101600, 102000, 102400, 102800, 103200, 103600, 104000, 104400,
>> + 104800, 105200, 105600, 106100, 106600, 107000, 107400, 107800,
>> + 108200, 108600, 109000, 109400, 109800, 110200, 110600, 111000,
>> + 111400, 111800, 112200, 112600, 113000, 113400, 113800, 114200,
>> + 114600, 115000, 115400, 115800, 116200, 116600, 117000, 117400,
>> + 117800, 118200, 118600, 119000, 119400, 119800, 120200, 120600,
>> + 121000, 121400, 121800, 122200, 122600, 123000
>> +};
>> +
>> +static unsigned long omap_temp_sensor_readl(struct omap_temp_sensor
>> + *temp_sensor, u32 reg)
>> +{
>> + return __raw_readl(temp_sensor->phy_base + reg);
>> +}
>> +
>> +static void omap_temp_sensor_writel(struct omap_temp_sensor *temp_sensor,
>> + u32 val, u32 reg)
>> +{
>> + __raw_writel(val, (temp_sensor->phy_base + reg));
>> +}
>> +
>> +static int adc_to_temp_conversion(int adc_val)
>> +{
>> + return adc_to_temp[adc_val - OMAP_ADC_START_VALUE];
>> +}
>> +
>> +static int temp_to_adc_conversion(long temp)
>> +{
>> + int i;
>> +
>> + for (i = 0; i <= OMAP_ADC_END_VALUE - OMAP_ADC_START_VALUE; i++)
>> + if (temp < adc_to_temp[i])
>> + return OMAP_ADC_START_VALUE + i - 1;
>> +
>> + return -EINVAL;
>> +}
>> +
>> +static void omap_configure_temp_sensor_thresholds(struct omap_temp_sensor
>> + *temp_sensor)
>> +{
>> + u32 temp, t_hot, t_cold, tshut_hot, tshut_cold;
>> +
>> + t_hot = temp_to_adc_conversion(BGAP_THRESHOLD_T_HOT);
>> + t_cold = temp_to_adc_conversion(BGAP_THRESHOLD_T_COLD);
>> +
>> + /* Configure the TALERT thresholds */
>> + temp = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_threshold);
>> + temp |= (t_hot << __ffs(temp_sensor->registers->threshold_thot_mask)) |
>> + (t_cold << __ffs(temp_sensor->registers->threshold_tcold_mask));
>> + omap_temp_sensor_writel(temp_sensor, temp,
>> + temp_sensor->registers->bgap_threshold);
>> +
>> + tshut_hot = temp_to_adc_conversion(TSHUT_THRESHOLD_HOT);
>> + tshut_cold = temp_to_adc_conversion(TSHUT_THRESHOLD_COLD);
>> +
>> + /* Configure the TSHUT thresholds */
>> + temp = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->thsut_threshold);
>> + temp |= (tshut_hot << __ffs(temp_sensor->registers->tshut_hot_mask))
>> + | (tshut_cold << __ffs(temp_sensor->registers->tshut_hot_mask));
>> + omap_temp_sensor_writel(temp_sensor, temp,
>> + temp_sensor->registers->thsut_threshold);
>> +}
>> +
>> +static void omap_configure_temp_sensor_counter(struct omap_temp_sensor
>> + *temp_sensor, u32 counter)
>> +{
>> + u32 val;
>> +
>> + val = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_counter);
>> + val &= ~(temp_sensor->registers->counter_mask);
>> + val |= (counter << __ffs(temp_sensor->registers->counter_mask));
>> + omap_temp_sensor_writel(temp_sensor, val,
>> + temp_sensor->registers->bgap_counter);
>> +}
>> +
>> +static void omap_enable_continuous_mode(struct omap_temp_sensor *temp_sensor,
>> + bool enable)
>> +{
>> + u32 val;
>> +
>> + val = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_mode_ctrl);
>> +
>> + if (enable)
>> + val |= (1 << __ffs(temp_sensor->registers->mode_ctrl_mask));
>> + else
>> + val &= ~(temp_sensor->registers->mode_ctrl_mask);
>> +
>> + omap_temp_sensor_writel(temp_sensor, val,
>> + temp_sensor->registers->bgap_mode_ctrl);
>> +}
>> +
>> +/* Sysfs hook functions */
>> +
>> +static ssize_t show_temp_max(struct device *dev,
>> + struct device_attribute *devattr, char *buf)
>> +{
>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>> + int temp;
>> +
>> + mutex_lock(&temp_sensor->sensor_mutex);
>> +
>> + temp = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_threshold);
>> + temp = (temp & temp_sensor->registers->threshold_thot_mask)
>> + >> __ffs(temp_sensor->registers->threshold_thot_mask);
>> +
>> + if (temp < OMAP_ADC_START_VALUE || temp > OMAP_ADC_END_VALUE) {
>> + dev_err(dev, "invalid value\n");
>> + mutex_unlock(&temp_sensor->sensor_mutex);
>> + return -EINVAL;
>> + }
>> +
>> + temp = adc_to_temp_conversion(temp);
>> + mutex_unlock(&temp_sensor->sensor_mutex);
>> +
>> + return snprintf(buf, 16, "%d\n", temp);
>> +}
>> +
>> +static ssize_t set_temp_max(struct device *dev,
>> + struct device_attribute *devattr,
>> + const char *buf, size_t count)
>> +{
>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>> + long val;
>> + u32 reg_val, t_cold, t_hot, temp;
>> +
>> + mutex_lock(&temp_sensor->sensor_mutex);
>> +
>> + if (strict_strtol(buf, 10, &val)) {
>> + count = -EINVAL;
>> + goto out;
>> + }
>> +
>> + t_hot = temp_to_adc_conversion(val);
>> + if ((t_hot < OMAP_ADC_START_VALUE || t_hot > OMAP_ADC_END_VALUE)) {
>> + dev_err(dev, "invalid range\n");
>> + count = -EINVAL;
>> + goto out;
>> + }
>> +
>> + /* obtain the T cold value */
>> + t_cold = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_threshold);
>> + t_cold = (t_cold & temp_sensor->registers->threshold_tcold_mask) >>
>> + __ffs(temp_sensor->registers->threshold_tcold_mask);
>> +
>> + if (t_hot < t_cold) {
>> + dev_err(dev, "Error! T_HOT value lesser than T_COLD\n");
>> + count = -EINVAL;
>> + goto out;
>> + }
>> +
>> + /* write the new t_hot value */
>> + reg_val = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_threshold);
>> + reg_val &= ~(temp_sensor->registers->threshold_thot_mask);
>> + reg_val |= (t_hot <<
>> + __ffs(temp_sensor->registers->threshold_thot_mask));
>> + omap_temp_sensor_writel(temp_sensor, reg_val,
>> + temp_sensor->registers->bgap_threshold);
>> +
>> + /* Read the current temperature */
>> + temp = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->temp_sensor_ctrl);
>> + temp &= (temp_sensor->registers->bgap_dtemp_mask);
>> +
>> + /*
>> + * If user sets the HIGH threshold(t_hot) greater than the current
>> + * temperature(temp) unmask the HOT interrupts
>> + */
>> + if (t_hot > temp) {
>> + reg_val = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_mask_ctrl);
>> + reg_val &= ~(temp_sensor->registers->mask_cold_mask);
>> + reg_val |= temp_sensor->registers->mask_hot_mask;
>> + omap_temp_sensor_writel(temp_sensor, reg_val,
>> + temp_sensor->registers->bgap_mask_ctrl);
>> + }
>> +
>> + /*
>> + * If current temperature is in-between the hot and cold thresholds,
>> + * Enable both masks.
>> + */
>> + if (temp > t_cold && temp < t_hot) {
>> + reg_val = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_mask_ctrl);
>> + reg_val |= temp_sensor->registers->mask_cold_mask;
>> + reg_val |= temp_sensor->registers->mask_hot_mask;
>> + omap_temp_sensor_writel(temp_sensor, reg_val,
>> + OMAP4460_BGAP_CTRL_OFFSET);
>> + }
>> + /*
>> + * else no need to do anything since HW will immediately compare
>> + * the new threshold and generate interrupt accordingly
>> + */
>> +out:
>> + mutex_unlock(&temp_sensor->sensor_mutex);
>> + return count;
>> +}
>> +
>> +static ssize_t show_temp_max_hyst(struct device *dev,
>> + struct device_attribute *devattr, char *buf)
>> +{
>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>> + u32 temp;
>> +
>> + mutex_lock(&temp_sensor->sensor_mutex);
>> +
>> + temp = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_threshold);
>> + temp = (temp & temp_sensor->registers->threshold_tcold_mask) >>
>> + __ffs(temp_sensor->registers->threshold_tcold_mask);
>> +
>> + if (temp < OMAP_ADC_START_VALUE || temp > OMAP_ADC_END_VALUE) {
>> + dev_err(dev, "invalid value\n");
>> + mutex_unlock(&temp_sensor->sensor_mutex);
>> + return -EINVAL;
>> + }
>> +
>> + temp = adc_to_temp_conversion(temp);
>> + mutex_unlock(&temp_sensor->sensor_mutex);
>> +
>> + return snprintf(buf, 16, "%d\n", temp);
>> +}
>> +
>> +static ssize_t set_temp_max_hyst(struct device *dev,
>> + struct device_attribute *devattr,
>> + const char *buf, size_t count)
>> +{
>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>> + u32 reg_val, t_hot, t_cold, temp;
>> + long val;
>> +
>> + mutex_lock(&temp_sensor->sensor_mutex);
>> +
>> + if (strict_strtol(buf, 10, &val)) {
>> + count = -EINVAL;
>> + goto out;
>> + }
>> +
>> + t_cold = temp_to_adc_conversion(val);
>> + if (t_cold < OMAP_ADC_START_VALUE || t_cold > OMAP_ADC_END_VALUE) {
>> + dev_err(dev, "invalid range");
>> + count = -EINVAL;
>> + goto out;
>> + }
>> +
>> + /* obtain the T HOT value */
>> + t_hot = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_threshold);
>> + t_hot = (t_hot & temp_sensor->registers->threshold_thot_mask) >>
>> + __ffs(temp_sensor->registers->threshold_thot_mask);
>> +
>> + if (t_cold > t_hot) {
>> + dev_err(dev, "Error! T_COLD value greater than T_HOT\n");
>> + count = -EINVAL;
>> + goto out;
>> + }
>> +
>> + /* write the new t_cold value */
>> + reg_val = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_threshold);
>> + reg_val &= ~(temp_sensor->registers->threshold_tcold_mask);
>> + reg_val |= (t_cold <<
>> + __ffs(temp_sensor->registers->threshold_tcold_mask));
>> + omap_temp_sensor_writel(temp_sensor, reg_val,
>> + temp_sensor->registers->bgap_threshold);
>> +
>> + /* Read the current temperature */
>> + temp = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->temp_sensor_ctrl);
>> + temp &= (temp_sensor->registers->bgap_dtemp_mask);
>> +
>> + /*
>> + * If user sets the LOW threshold(t_cold) lower than the current
>> + * temperature(temp) unmask the COLD interrupts
>> + */
>> + if (t_cold < temp) {
>> + reg_val = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_mask_ctrl);
>> + reg_val &= ~(temp_sensor->registers->mask_hot_mask);
>> + reg_val |= temp_sensor->registers->mask_cold_mask;
>> + omap_temp_sensor_writel(temp_sensor, reg_val,
>> + temp_sensor->registers->bgap_mask_ctrl);
>> + }
>> +
>> + /*
>> + * If current temperature is in-between the hot and cold thresholds,
>> + * Enable both masks.
>> + */
>> + if (temp < t_hot && temp > t_cold) {
>> + reg_val = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_mask_ctrl);
>> + reg_val |= temp_sensor->registers->mask_cold_mask;
>> + reg_val |= temp_sensor->registers->mask_hot_mask;
>> + omap_temp_sensor_writel(temp_sensor, reg_val,
>> + temp_sensor->registers->bgap_mask_ctrl);
>> + }
>> +
>> + /*
>> + * else no need to do anything since HW will immediately compare
>> + * the new threshold and generate interrupt accordingly
>> + */
>> +
>> +out:
>> + mutex_unlock(&temp_sensor->sensor_mutex);
>> + return count;
>> +}
>> +
>> +static ssize_t show_update_rate(struct device *dev,
>> + struct device_attribute *devattr, char *buf)
>> +{
>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>> + u32 temp = 0, ret = 0;
>> +
>> + mutex_lock(&temp_sensor->sensor_mutex);
>> +
>> + if (!temp_sensor->clk_rate) {
>> + dev_err(dev, "clk_rate is NULL\n");
>> + ret = -EINVAL;
>> + goto out;
>> + }
>> +
>> + temp = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_counter);
>> + temp = (temp & temp_sensor->registers->counter_mask) >>
>> + __ffs(temp_sensor->registers->counter_mask);
>> + temp = temp * 1000 / (temp_sensor->clk_rate);
>> +
>> +out:
>> + mutex_unlock(&temp_sensor->sensor_mutex);
>> + if (!ret)
>> + return sprintf(buf, "%d\n", temp);
>> +
>> + return ret;
>> +}
>> +
>> +static ssize_t set_update_rate(struct device *dev,
>> + struct device_attribute *devattr,
>> + const char *buf, size_t count)
>> +{
>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>> + u32 reg_val;
>> + long val;
>> +
>> + mutex_lock(&temp_sensor->sensor_mutex);
>> +
>> + if (strict_strtol(buf, 10, &val)) {
>> + count = -EINVAL;
>> + goto out;
>> + }
>> +
>> + val *= temp_sensor->clk_rate / 1000;
>> + reg_val = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_counter);
>> +
>> + reg_val &= ~(temp_sensor->registers->counter_mask);
>> + reg_val |= val;
>> + omap_temp_sensor_writel(temp_sensor, reg_val,
>> + temp_sensor->registers->bgap_counter);
>> +
>> +out:
>> + mutex_unlock(&temp_sensor->sensor_mutex);
>> + return count;
>> +}
>> +
>> +static int omap_temp_sensor_read_temp(struct device *dev,
>> + struct device_attribute *devattr,
>> + char *buf)
>> +{
>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>> + int temp, ret = 0;
>> +
>> + mutex_lock(&temp_sensor->sensor_mutex);
>> +
>> + temp = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->temp_sensor_ctrl);
>> + temp &= temp_sensor->registers->bgap_dtemp_mask;
>> +
>> + if (!temp_sensor->is_efuse_valid)
>> + dev_err(dev, "Invalid EFUSE, Non-trimmed BGAP, Temp not accurate\n");
>> +
>> + /* look up for temperature in the table and return the temperature */
>> + if (temp < OMAP_ADC_START_VALUE || temp > OMAP_ADC_END_VALUE) {
>> + dev_err(dev, "invalid adc code reported %d", temp);
>> + ret = -EINVAL;
>> + goto out;
>> + }
>> +
>> + temp = adc_to_temp[temp - OMAP_ADC_START_VALUE];
>> +
>> +out:
>> + mutex_unlock(&temp_sensor->sensor_mutex);
>> + if (!ret)
>> + return sprintf(buf, "%d\n", temp);
>> +
>> + return ret;
>> +}
>> +
>> +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, omap_temp_sensor_read_temp,
>> + NULL, 0);
>> +static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp_max,
>> + set_temp_max, 0);
>> +static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO, show_temp_max_hyst,
>> + set_temp_max_hyst, 0);
>> +static SENSOR_DEVICE_ATTR(update_rate, S_IWUSR | S_IRUGO, show_update_rate,
>> + set_update_rate, 0);
>> +
>> +static struct attribute *omap_temp_sensor_attributes[] = {
>> + &sensor_dev_attr_temp1_input.dev_attr.attr,
>> + &sensor_dev_attr_temp1_max.dev_attr.attr,
>> + &sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
>> + &sensor_dev_attr_update_rate.dev_attr.attr,
>> + NULL
>> +};
>> +
>> +static const struct attribute_group omap_temp_sensor_group = {
>> + .attrs = omap_temp_sensor_attributes,
>> +};
>> +
>> +static int omap_temp_sensor_clk_enable(struct omap_temp_sensor *temp_sensor)
>> +{
>> + u32 ret = 0;
>> +
>> + if (temp_sensor->clk_on) {
>> + dev_err(temp_sensor->hwmon_dev, "clock already on\n");
>> + goto out;
>> + }
>> +
>> + ret = pm_runtime_get_sync(temp_sensor->hwmon_dev);
>> + if (ret < 0) {
>> + dev_err(temp_sensor->hwmon_dev, "get sync failed\n");
>> + goto out;
>> + }
>> +
>> + temp_sensor->clk_on = 1;
>> +
>> +out:
>> + return ret;
>> +}
>> +
>> +static int omap_temp_sensor_clk_disable(struct omap_temp_sensor *temp_sensor)
>> +{
>> + u32 ret = 0;
>> +
>> + if (!temp_sensor->clk_on) {
>> + dev_err(temp_sensor->hwmon_dev, "clock already off\n");
>> + goto out;
>> + }
>> +
>> + /* Gate the clock */
>> + ret = pm_runtime_put_sync(temp_sensor->hwmon_dev);
>> + if (ret < 0) {
>> + dev_err(temp_sensor->hwmon_dev, "put sync failed\n");
>> + goto out;
>> + }
>> + temp_sensor->clk_on = 0;
>> +
>> +out:
>> + return ret;
>> +}
>> +
>> +static irqreturn_t omap_talert_irq_handler(int irq, void *data)
>> +{
>> + struct omap_temp_sensor *temp_sensor;
>> + int t_hot, t_cold, temp;
>> +
>> + temp_sensor = data;
>> + mutex_lock(&temp_sensor->sensor_mutex);
>> +
>> + /* Read the status of t_hot */
>> + t_hot = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_status)
>> + & temp_sensor->registers->status_hot_mask;
>> +
>> + /* Read the status of t_cold */
>> + t_cold = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_status)
>> + & temp_sensor->registers->status_cold_mask;
>> +
>> + temp = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_mask_ctrl);
>> + /*
>> + * One TALERT interrupt: Two sources
>> + * If the interrupt is due to t_hot then mask t_hot and
>> + * and unmask t_cold else mask t_cold and unmask t_hot
>> + */
>> + if (t_hot) {
>> + temp &= ~(temp_sensor->registers->mask_hot_mask);
>> + temp |= temp_sensor->registers->mask_cold_mask;
>> + } else if (t_cold) {
>> + temp &= ~(temp_sensor->registers->mask_cold_mask);
>> + temp |= temp_sensor->registers->mask_hot_mask;
>> + }
>> +
>> + omap_temp_sensor_writel(temp_sensor, temp,
>> + temp_sensor->registers->bgap_mask_ctrl);
>> +
>> + /* kobject_uvent to user space telling thermal threshold crossed */
>> + kobject_uevent(&temp_sensor->hwmon_dev->kobj, KOBJ_CHANGE);
>> +
>> + mutex_unlock(&temp_sensor->sensor_mutex);
>> +
>> + return IRQ_HANDLED;
>> +}
>> +
>> +static int __devinit omap_temp_sensor_probe(struct platform_device *pdev)
>> +{
>> + struct omap_temp_sensor_pdata *pdata = pdev->dev.platform_data;
>> + struct omap_temp_sensor *temp_sensor;
>> + struct resource *mem;
>> + int ret = 0;
>> + int val, clk_rate;
>> +
>> + if (!pdata) {
>> + dev_err(&pdev->dev, "platform data missing\n");
>> + return -EINVAL;
>> + }
>> +
>> + temp_sensor = kzalloc(sizeof(*temp_sensor), GFP_KERNEL);
>> + if (!temp_sensor)
>> + return -ENOMEM;
>> +
>> + mutex_init(&temp_sensor->sensor_mutex);
>> +
>> + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> + if (!mem) {
>> + dev_err(&pdev->dev, "no mem resource\n");
>> + ret = -ENOMEM;
>> + goto plat_res_err;
>> + }
>> +
>> + temp_sensor->irq = platform_get_irq_byname(pdev, "thermal_alert");
>> + if (temp_sensor->irq < 0) {
>> + dev_err(&pdev->dev, "get_irq_byname failed\n");
>> + ret = temp_sensor->irq;
>> + goto plat_res_err;
>> + }
>> +
>> + temp_sensor->phy_base = ioremap(mem->start, resource_size(mem));
>> + temp_sensor->clock = NULL;
>> + temp_sensor->registers = pdata->registers;
>> + temp_sensor->hwmon_dev = &pdev->dev;
>> +
>> + pm_runtime_enable(&pdev->dev);
>> + pm_runtime_irq_safe(&pdev->dev);
>> +
>> + /*
>> + * check if the efuse has a non-zero value if not
>> + * it is an untrimmed sample and the temperatures
>> + * may not be accurate
>> + */
>> +
>> + if (omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_efuse))
>> + temp_sensor->is_efuse_valid = 1;
>> +
>> + platform_set_drvdata(pdev, temp_sensor);
>> + dev_set_drvdata(&pdev->dev, temp_sensor);
>> + temp_sensor->clock = clk_get(temp_sensor->hwmon_dev, "fck");
>> + if (IS_ERR(temp_sensor->clock)) {
>> + ret = PTR_ERR(temp_sensor->clock);
>> + dev_err(temp_sensor->hwmon_dev,
>> + "unable to get fclk: %d\n", ret);
>> + ret = -EINVAL;
>> + goto plat_res_err;
>> + }
>> +
>> + ret = omap_temp_sensor_clk_enable(temp_sensor);
>> + if (ret) {
>> + dev_err(&pdev->dev, "Cannot enable temp sensor\n");
>> + goto clken_err;
>> + }
>> +
>> + clk_rate = clk_round_rate(temp_sensor->clock, 2000000);
>> + if (clk_rate < 1000000 || clk_rate == 0xffffffff) {
>> + dev_err(&pdev->dev, "Error round rate\n");
>> + ret = -EINVAL;
>> + goto clken_err;
>> + }
>> +
>> + ret = clk_set_rate(temp_sensor->clock, clk_rate);
>> + if (ret) {
>> + dev_err(&pdev->dev, "Cannot set clock rate\n");
>> + goto clken_err;
>> + }
>> +
>> + temp_sensor->clk_rate = clk_rate;
>> + omap_enable_continuous_mode(temp_sensor, 1);
>> + omap_configure_temp_sensor_thresholds(temp_sensor);
>> + /* 1 ms */
>> + omap_configure_temp_sensor_counter(temp_sensor, 1);
>> +
>> + /* Wait till the first conversion is done wait for at least 1ms */
>> + usleep_range(1000, 2000);
>> +
>> + /* Read the temperature once due to hw issue*/
>> + omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->temp_sensor_ctrl);
>> +
>> + /* Set 2 seconds time as default counter */
>> + omap_configure_temp_sensor_counter(temp_sensor,
>> + temp_sensor->clk_rate * 2);
>> +
>> + temp_sensor->hwmon_dev = hwmon_device_register(&pdev->dev);
>> + if (IS_ERR(temp_sensor->hwmon_dev)) {
>> + dev_err(&pdev->dev, "hwmon_device_register failed.\n");
>> + ret = PTR_ERR(temp_sensor->hwmon_dev);
>> + goto hwmon_reg_err;
>> + }
>> +
>> + ret = sysfs_create_group(&pdev->dev.kobj,
>> + &omap_temp_sensor_group);
>> + if (ret) {
>> + dev_err(&pdev->dev, "could not create sysfs files\n");
>> + goto sysfs_create_err;
>> + }
>> +
>> + kobject_uevent(&temp_sensor->hwmon_dev->kobj, KOBJ_ADD);
>> +
>> + ret = request_threaded_irq(temp_sensor->irq, NULL,
>> + omap_talert_irq_handler, IRQF_TRIGGER_RISING | IRQF_ONESHOT,
>> + "temp_sensor", temp_sensor);
>> + if (ret) {
>> + dev_err(&pdev->dev, "Request threaded irq failed.\n");
>> + goto req_irq_err;
>> + }
>> +
>> + /* unmask the T_COLD and unmask T_HOT at init */
>> + val = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_mask_ctrl);
>> + val |= temp_sensor->registers->mask_cold_mask
>> + | temp_sensor->registers->mask_hot_mask;
>> +
>> + omap_temp_sensor_writel(temp_sensor, val,
>> + temp_sensor->registers->bgap_mask_ctrl);
>> +
>> + return 0;
>> +
>> +req_irq_err:
>> + kobject_uevent(&temp_sensor->hwmon_dev->kobj, KOBJ_REMOVE);
>> + sysfs_remove_group(&temp_sensor->hwmon_dev->kobj,
>> + &omap_temp_sensor_group);
>> +sysfs_create_err:
>> + hwmon_device_unregister(&pdev->dev);
>> +hwmon_reg_err:
>> + omap_temp_sensor_clk_disable(temp_sensor);
>> +clken_err:
>> + clk_put(temp_sensor->clock);
>> +plat_res_err:
>> + mutex_destroy(&temp_sensor->sensor_mutex);
>> + kfree(temp_sensor);
>> + return ret;
>> +}
>> +
>> +static int __devexit omap_temp_sensor_remove(struct platform_device *pdev)
>> +{
>> + struct omap_temp_sensor *temp_sensor = platform_get_drvdata(pdev);
>> +
>> + hwmon_device_unregister(&pdev->dev);
>> + kobject_uevent(&temp_sensor->hwmon_dev->kobj, KOBJ_REMOVE);
>> + sysfs_remove_group(&temp_sensor->hwmon_dev->kobj,
>> + &omap_temp_sensor_group);
>> + omap_temp_sensor_clk_disable(temp_sensor);
>> + clk_put(temp_sensor->clock);
>> + platform_set_drvdata(pdev, NULL);
>> + free_irq(temp_sensor->irq, temp_sensor);
>> + mutex_destroy(&temp_sensor->sensor_mutex);
>> + kfree(temp_sensor);
>> +
>> + return 0;
>> +}
>> +
>> +#ifdef CONFIG_PM
>> +static void omap_temp_sensor_save_ctxt(struct omap_temp_sensor *temp_sensor)
>> +{
>> + temp_sensor->temp_sensor_ctrl = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->temp_sensor_ctrl);
>> + temp_sensor->bg_ctrl = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_mask_ctrl);
>> + temp_sensor->bg_counter = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_counter);
>> + temp_sensor->bg_threshold = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_threshold);
>> + temp_sensor->temp_sensor_tshut_threshold =
>> + omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->thsut_threshold);
>> +}
>> +
>> +static void omap_temp_sensor_restore_ctxt(struct omap_temp_sensor *temp_sensor)
>> +{
>> + omap_temp_sensor_writel(temp_sensor,
>> + temp_sensor->temp_sensor_ctrl,
>> + temp_sensor->registers->temp_sensor_ctrl);
>> + omap_temp_sensor_writel(temp_sensor,
>> + temp_sensor->bg_ctrl,
>> + temp_sensor->registers->bgap_mask_ctrl);
>> + omap_temp_sensor_writel(temp_sensor,
>> + temp_sensor->bg_counter,
>> + temp_sensor->registers->bgap_counter);
>> + omap_temp_sensor_writel(temp_sensor,
>> + temp_sensor->bg_threshold,
>> + temp_sensor->registers->bgap_threshold);
>> + omap_temp_sensor_writel(temp_sensor,
>> + temp_sensor->temp_sensor_tshut_threshold,
>> + temp_sensor->registers->thsut_threshold);
>> +}
>> +
>> +static int omap_temp_sensor_suspend(struct device *dev)
>> +{
>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>> +
>> + omap_temp_sensor_save_ctxt(temp_sensor);
>> +
>> + return 0;
>> +}
>> +
>> +static int omap_temp_sensor_resume(struct device *dev)
>> +{
>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>> +
>> + omap_temp_sensor_restore_ctxt(temp_sensor);
>> +
>> + return 0;
>> +}
>> +
>> +static int omap_temp_sensor_runtime_suspend(struct device *dev)
>> +{
>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>> +
>> + omap_temp_sensor_save_ctxt(temp_sensor);
>> +
>> + return 0;
>> +}
>> +
>> +static int omap_temp_sensor_runtime_resume(struct device *dev)
>> +{
>> + static int context_loss_count;
>> + int temp;
>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>> +
>> + temp = omap_device_get_context_loss_count(to_platform_device(dev));
>> +
>> + if (temp != context_loss_count && context_loss_count != 0)
>> + omap_temp_sensor_restore_ctxt(temp_sensor);
>> +
>> + context_loss_count = temp;
>> +
>> + return 0;
>> +}
>> +
>> +static const struct dev_pm_ops omap_temp_sensor_dev_pm_ops = {
>> + .suspend = omap_temp_sensor_suspend,
>> + .resume = omap_temp_sensor_resume,
>> + .runtime_suspend = omap_temp_sensor_runtime_suspend,
>> + .runtime_resume = omap_temp_sensor_runtime_resume,
>> +};
>
> this is not enough... you need to populate the other fields. You might
> want to use something like:
>
> static const struct dev_pm_ops omap_temp_sensor_dev_pm_ops = {
> SET_SYSTEM_SLEEP_PM_OPS(omap_temp_sensor_suspend,
> omap_temp_sensor_resume)
> SET_RUNTIME_PM_OPS(omap_temp_sensor_runtime_suspend,
> omap_temp_sensor_runtime_resume, omap_temp_sensor_idle)
> };
omap_temp_sensor_idle is undefined for temperature sensor as of now. So i will
populate these fields once the idle function is defined.
>
>> +
>> +#endif
>
> instead of endif, try something like:
>
> dev_pm_ops....
>
> #define DEV_PM_OPS (&omap_temp_sensor_dev_pm_ops)
> #else
> #define DEV_PM_OPS NULL
> #endif
>
> then you can remove the ifdef on the driver structure.
Ok
>
> --
> balbi
>
--
Regards and Thanks,
Keerthy
_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors
^ permalink raw reply [flat|nested] 35+ messages in thread
* Re: [RFC PATCH 6/6 V2] hwmon: OMAP4: On die temperature sensor driver
2011-08-19 5:34 ` [lm-sensors] [RFC PATCH 6/6 V2] hwmon: OMAP4: On die Todd Poynor
@ 2011-08-22 4:52 ` J, KEERTHY
-1 siblings, 0 replies; 35+ messages in thread
From: J, KEERTHY @ 2011-08-22 4:40 UTC (permalink / raw)
To: Todd Poynor; +Cc: linux-omap, Jean Delvare, Guenter Roeck, lm-sensors
On Fri, Aug 19, 2011 at 11:04 AM, Todd Poynor <toddpoynor@google.com> wrote:
> On Thu, Aug 18, 2011 at 04:22:15PM +0530, Keerthy wrote:
> ...
>> +static int omap_temp_sensor_clk_enable(struct omap_temp_sensor *temp_sensor)
>> +{
>> + u32 ret = 0;
>> +
>> + if (temp_sensor->clk_on) {
>> + dev_err(temp_sensor->hwmon_dev, "clock already on\n");
>> + goto out;
>> + }
>> +
>> + ret = pm_runtime_get_sync(temp_sensor->hwmon_dev);
>> + if (ret < 0) {
>> + dev_err(temp_sensor->hwmon_dev, "get sync failed\n");
>> + goto out;
>> + }
>> +
>> + temp_sensor->clk_on = 1;
>
>
> Probably should hold the mutex around this to keep clk_on consistent
> with runtime PM state (and in disable method).
Ok
>
> ...
>> +static int __devinit omap_temp_sensor_probe(struct platform_device *pdev)
>> +{
>> + struct omap_temp_sensor_pdata *pdata = pdev->dev.platform_data;
>> + struct omap_temp_sensor *temp_sensor;
>> + struct resource *mem;
>> + int ret = 0;
>> + int val, clk_rate;
>> +
>> + if (!pdata) {
>> + dev_err(&pdev->dev, "platform data missing\n");
>> + return -EINVAL;
>> + }
>> +
>> + temp_sensor = kzalloc(sizeof(*temp_sensor), GFP_KERNEL);
>> + if (!temp_sensor)
>> + return -ENOMEM;
>> +
>> + mutex_init(&temp_sensor->sensor_mutex);
>> +
>> + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> + if (!mem) {
>> + dev_err(&pdev->dev, "no mem resource\n");
>> + ret = -ENOMEM;
>> + goto plat_res_err;
>> + }
>> +
>> + temp_sensor->irq = platform_get_irq_byname(pdev, "thermal_alert");
>> + if (temp_sensor->irq < 0) {
>> + dev_err(&pdev->dev, "get_irq_byname failed\n");
>> + ret = temp_sensor->irq;
>> + goto plat_res_err;
>> + }
>> +
>> + temp_sensor->phy_base = ioremap(mem->start, resource_size(mem));
>> + temp_sensor->clock = NULL;
>> + temp_sensor->registers = pdata->registers;
>> + temp_sensor->hwmon_dev = &pdev->dev;
>> +
>> + pm_runtime_enable(&pdev->dev);
>> + pm_runtime_irq_safe(&pdev->dev);
>> +
>> + /*
>> + * check if the efuse has a non-zero value if not
>> + * it is an untrimmed sample and the temperatures
>> + * may not be accurate
>> + */
>> +
>> + if (omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_efuse))
>> + temp_sensor->is_efuse_valid = 1;
>> +
>> + platform_set_drvdata(pdev, temp_sensor);
>> + dev_set_drvdata(&pdev->dev, temp_sensor);
>> + temp_sensor->clock = clk_get(temp_sensor->hwmon_dev, "fck");
>> + if (IS_ERR(temp_sensor->clock)) {
>> + ret = PTR_ERR(temp_sensor->clock);
>> + dev_err(temp_sensor->hwmon_dev,
>> + "unable to get fclk: %d\n", ret);
>> + ret = -EINVAL;
>> + goto plat_res_err;
>> + }
>> +
>> + ret = omap_temp_sensor_clk_enable(temp_sensor);
>> + if (ret) {
>> + dev_err(&pdev->dev, "Cannot enable temp sensor\n");
>> + goto clken_err;
>> + }
>> +
>> + clk_rate = clk_round_rate(temp_sensor->clock, 2000000);
>> + if (clk_rate < 1000000 || clk_rate == 0xffffffff) {
>> + dev_err(&pdev->dev, "Error round rate\n");
>> + ret = -EINVAL;
>> + goto clken_err;
>> + }
>> +
>> + ret = clk_set_rate(temp_sensor->clock, clk_rate);
>> + if (ret) {
>> + dev_err(&pdev->dev, "Cannot set clock rate\n");
>> + goto clken_err;
>> + }
>> +
>> + temp_sensor->clk_rate = clk_rate;
>> + omap_enable_continuous_mode(temp_sensor, 1);
>> + omap_configure_temp_sensor_thresholds(temp_sensor);
>> + /* 1 ms */
>> + omap_configure_temp_sensor_counter(temp_sensor, 1);
>> +
>> + /* Wait till the first conversion is done wait for at least 1ms */
>> + usleep_range(1000, 2000);
>> +
>> + /* Read the temperature once due to hw issue*/
>> + omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->temp_sensor_ctrl);
>> +
>> + /* Set 2 seconds time as default counter */
>> + omap_configure_temp_sensor_counter(temp_sensor,
>> + temp_sensor->clk_rate * 2);
>> +
>> + temp_sensor->hwmon_dev = hwmon_device_register(&pdev->dev);
>> + if (IS_ERR(temp_sensor->hwmon_dev)) {
>> + dev_err(&pdev->dev, "hwmon_device_register failed.\n");
>> + ret = PTR_ERR(temp_sensor->hwmon_dev);
>> + goto hwmon_reg_err;
>> + }
>> +
>> + ret = sysfs_create_group(&pdev->dev.kobj,
>> + &omap_temp_sensor_group);
>> + if (ret) {
>> + dev_err(&pdev->dev, "could not create sysfs files\n");
>> + goto sysfs_create_err;
>> + }
>> +
>> + kobject_uevent(&temp_sensor->hwmon_dev->kobj, KOBJ_ADD);
>> +
>> + ret = request_threaded_irq(temp_sensor->irq, NULL,
>> + omap_talert_irq_handler, IRQF_TRIGGER_RISING | IRQF_ONESHOT,
>> + "temp_sensor", temp_sensor);
>> + if (ret) {
>> + dev_err(&pdev->dev, "Request threaded irq failed.\n");
>> + goto req_irq_err;
>> + }
>> +
>> + /* unmask the T_COLD and unmask T_HOT at init */
>> + val = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_mask_ctrl);
>> + val |= temp_sensor->registers->mask_cold_mask
>> + | temp_sensor->registers->mask_hot_mask;
>> +
>> + omap_temp_sensor_writel(temp_sensor, val,
>> + temp_sensor->registers->bgap_mask_ctrl);
>> +
>> + return 0;
>> +
>> +req_irq_err:
>> + kobject_uevent(&temp_sensor->hwmon_dev->kobj, KOBJ_REMOVE);
>> + sysfs_remove_group(&temp_sensor->hwmon_dev->kobj,
>> + &omap_temp_sensor_group);
>> +sysfs_create_err:
>> + hwmon_device_unregister(&pdev->dev);
>> +hwmon_reg_err:
>> + omap_temp_sensor_clk_disable(temp_sensor);
>> +clken_err:
>> + clk_put(temp_sensor->clock);
>> +plat_res_err:
>> + mutex_destroy(&temp_sensor->sensor_mutex);
>> + kfree(temp_sensor);
>
> Should also:
> platform_set_drvdata(pdev, NULL);
> dev_set_drvdata(&pdev->dev, NULL);
Ok
>
>
>> + return ret;
>> +}
>> +
>> +static int __devexit omap_temp_sensor_remove(struct platform_device *pdev)
>> +{
>> + struct omap_temp_sensor *temp_sensor = platform_get_drvdata(pdev);
>> +
>> + hwmon_device_unregister(&pdev->dev);
>> + kobject_uevent(&temp_sensor->hwmon_dev->kobj, KOBJ_REMOVE);
>> + sysfs_remove_group(&temp_sensor->hwmon_dev->kobj,
>> + &omap_temp_sensor_group);
>> + omap_temp_sensor_clk_disable(temp_sensor);
>> + clk_put(temp_sensor->clock);
>> + platform_set_drvdata(pdev, NULL);
>
> And:
> dev_set_drvdata(&pdev->dev, NULL);
Ok
>
>> + free_irq(temp_sensor->irq, temp_sensor);
>
> Need to free IRQ before clock is disabled (else ISR may access while
> clock stopped, possible L3 interconnect error and ARM imprecise
> external abort)?
Ok
>
>> + mutex_destroy(&temp_sensor->sensor_mutex);
>> + kfree(temp_sensor);
>> +
>> + return 0;
>> +}
>
>
> Todd
>
--
Regards and Thanks,
Keerthy
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 35+ messages in thread
* Re: [lm-sensors] [RFC PATCH 6/6 V2] hwmon: OMAP4: On die
@ 2011-08-22 4:41 ` J, KEERTHY
0 siblings, 0 replies; 35+ messages in thread
From: J, KEERTHY @ 2011-08-22 4:41 UTC (permalink / raw)
To: balbi; +Cc: linux-omap, lm-sensors
On Thu, Aug 18, 2011 at 5:07 PM, Felipe Balbi <balbi@ti.com> wrote:
> Hi,
>
> On Thu, Aug 18, 2011 at 04:22:15PM +0530, Keerthy wrote:
>> On chip temperature sensor driver. The driver monitors the temperature of
>> the MPU subsystem of the OMAP4. It sends notifications to the user space if
>> the temperature crosses user defined thresholds via kobject_uevent interface.
>> The user is allowed to configure the temperature thresholds vis sysfs nodes
>> exposed using hwmon interface.
>>
>> Signed-off-by: Keerthy <j-keerthy@ti.com>
>> Cc: Jean Delvare <khali@linux-fr.org>
>> Cc: Guenter Roeck <guenter.roeck@ericsson.com>
>> Cc: lm-sensors@lm-sensors.org
>> ---
>> Documentation/hwmon/omap_temp_sensor | 27 +
>> drivers/hwmon/Kconfig | 11 +
>> drivers/hwmon/Makefile | 1 +
>> drivers/hwmon/omap_temp_sensor.c | 918 ++++++++++++++++++++++++++++++++++
>> 4 files changed, 957 insertions(+), 0 deletions(-)
>> create mode 100644 Documentation/hwmon/omap_temp_sensor
>> create mode 100644 drivers/hwmon/omap_temp_sensor.c
>>
>> diff --git a/Documentation/hwmon/omap_temp_sensor b/Documentation/hwmon/omap_temp_sensor
>> new file mode 100644
>> index 0000000..e01a6d6
>> --- /dev/null
>> +++ b/Documentation/hwmon/omap_temp_sensor
>> @@ -0,0 +1,27 @@
>> +Kernel driver omap_temp_sensor
>> +===============
>> +
>> +Supported chips:
>> + * Texas Instruments OMAP4460
>> + Prefix: 'omap_temp_sensor'
>> + Datasheet: http://focus.ti.com/docs/prod/folders/print/tmp102.html
>> +
>> +Author:
>> + J Keerthy <j-keerthy@ti.com>
>> +
>> +Description
>> +-----------
>> +
>> +The Texas Instruments OMAP4 family of chips have a bandgap temperature sensor.
>> +The temperature sensor feature is used to convert the temperature of the device
>> +into a decimal value coded on 10 bits. An internal ADC is used for conversion.
>> +The recommended operating temperatures must be in the range -40 degree Celsius
>> +to 123 degree celsius for standard conversion.
>> +The thresholds are programmable and upon crossing the thresholds an interrupt
>> +is generated. The OMAP temperature sensor has a programmable update rate in
>> +milli seconds.
>> +(Currently the driver programs a default of 2000 milli seconds).
>> +
>> +The driver provides the common sysfs-interface for temperatures (see
>> +Documentation/hwmon/sysfs-interface under Temperatures).
>> +
>> diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
>> index 5f888f7..9c9cd8b 100644
>> --- a/drivers/hwmon/Kconfig
>> +++ b/drivers/hwmon/Kconfig
>> @@ -323,6 +323,17 @@ config SENSORS_F71805F
>> This driver can also be built as a module. If so, the module
>> will be called f71805f.
>>
>> +config SENSORS_OMAP_BANDGAP_TEMP_SENSOR
>> + bool "OMAP on-die temperature sensor hwmon driver"
>> + depends on HWMON && ARCH_OMAP && OMAP_TEMP_SENSOR
>> + help
>> + If you say yes here you get support for hardware
>> + monitoring features of the OMAP on die temperature
>> + sensor.
>> +
>> + Continuous conversion programmable delay
>> + mode is used for temperature conversion.
>> +
>> config SENSORS_F71882FG
>> tristate "Fintek F71882FG and compatibles"
>> help
>> diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
>> index 28061cf..d0f89f5 100644
>> --- a/drivers/hwmon/Makefile
>> +++ b/drivers/hwmon/Makefile
>> @@ -91,6 +91,7 @@ obj-$(CONFIG_SENSORS_MAX6639) += max6639.o
>> obj-$(CONFIG_SENSORS_MAX6642) += max6642.o
>> obj-$(CONFIG_SENSORS_MAX6650) += max6650.o
>> obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o
>> +obj-$(CONFIG_SENSORS_OMAP_BANDGAP_TEMP_SENSOR) += omap_temp_sensor.o
>> obj-$(CONFIG_SENSORS_PC87360) += pc87360.o
>> obj-$(CONFIG_SENSORS_PC87427) += pc87427.o
>> obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o
>> diff --git a/drivers/hwmon/omap_temp_sensor.c b/drivers/hwmon/omap_temp_sensor.c
>> new file mode 100644
>> index 0000000..586a361
>> --- /dev/null
>> +++ b/drivers/hwmon/omap_temp_sensor.c
>> @@ -0,0 +1,918 @@
>> +/*
>> + * OMAP4 Temperature sensor driver file
>> + *
>> + * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
>> + * Author: J Keerthy <j-keerthy@ti.com>
>> + * Author: Moiz Sonasath <m-sonasath@ti.com>
>> + *
>> + * This program is free software; you can redistribute it and/or
>> + * modify it under the terms of the GNU General Public License
>> + * 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.
>> + *
>> + * You should have received a copy of the GNU General Public License
>> + * along with this program; if not, write to the Free Software
>> + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
>> + * 02110-1301 USA
>> + *
>> + */
>> +
>> +#include <linux/interrupt.h>
>> +#include <linux/clk.h>
>> +#include <linux/io.h>
>> +#include <linux/slab.h>
>> +#include <linux/init.h>
>> +#include <plat/omap_device.h>
>> +#include <linux/kernel.h>
>> +#include <linux/device.h>
>> +#include <linux/jiffies.h>
>> +#include <linux/hwmon.h>
>> +#include <linux/hwmon-sysfs.h>
>> +#include <linux/stddef.h>
>> +#include <linux/sysfs.h>
>> +#include <linux/err.h>
>> +#include <linux/types.h>
>> +#include <linux/mutex.h>
>> +#include <linux/pm_runtime.h>
>> +#include <linux/delay.h>
>> +#include <plat/temperature_sensor.h>
>> +
>> +#define TSHUT_THRESHOLD_HOT 122000 /* 122 deg C */
>> +#define TSHUT_THRESHOLD_COLD 100000 /* 100 deg C */
>> +#define BGAP_THRESHOLD_T_HOT 73000 /* 73 deg C */
>> +#define BGAP_THRESHOLD_T_COLD 71000 /* 71 deg C */
>> +#define OMAP_ADC_START_VALUE 530
>> +#define OMAP_ADC_END_VALUE 923
>> +
>> +/*
>> + * omap_temp_sensor structure
>> + * @hwmon_dev - device pointer
>> + * @clock - Clock pointer
>> + * @registers - Pointer to structure with register offsets and bitfields
>> + * @sensor_mutex - Mutex for sysfs, irq and PM
>> + * @irq - MPU Irq number for thermal alert
>> + * @phy_base - Physical base of the temp I/O
>> + * @clk_rate - Holds current clock rate
>> + * @temp_sensor_ctrl - temp sensor control register value
>> + * @bg_ctrl - bandgap ctrl register value
>> + * @bg_counter - bandgap counter value
>> + * @bg_threshold - bandgap threshold register value
>> + * @temp_sensor_tshut_threshold - bandgap tshut register value
>> + * @is_efuse_valid - Flag to determine if efuse is valid or not
>> + * @clk_on - Manages the current clock state
>> + */
>> +struct omap_temp_sensor {
>> + struct device *hwmon_dev;
>> + struct clk *clock;
>> + struct omap_temp_sensor_registers *registers;
>> + struct mutex sensor_mutex; /* Mutex for sysfs, irq and PM */
>> + unsigned int irq;
>> + void __iomem *phy_base;
>> + u32 clk_rate;
>> + u32 temp_sensor_ctrl;
>> + u32 bg_ctrl;
>> + u32 bg_counter;
>> + u32 bg_threshold;
>> + u32 temp_sensor_tshut_threshold;
>> + bool is_efuse_valid;
>> + bool clk_on;
>> +};
>> +
>> +/*
>> + * Temperature values in milli degree celsius
>> + * ADC code values from 530 to 923
>> + */
>> +static int adc_to_temp[] = {
>> + -40000, -40000, -40000, -40000, -39800, -39400, -39000, -38600, -38200,
>> + -37800, -37300, -36800, -36400, -36000, -35600, -35200, -34800,
>> + -34300, -33800, -33400, -33000, -32600, -32200, -31800, -31300,
>> + -30800, -30400, -30000, -29600, -29200, -28700, -28200, -27800,
>> + -27400, -27000, -26600, -26200, -25700, -25200, -24800, -24400,
>> + -24000, -23600, -23200, -22700, -22200, -21800, -21400, -21000,
>> + -20600, -20200, -19700, -19200, -18800, -18400, -18000, -17600,
>> + -17200, -16700, -16200, -15800, -15400, -15000, -14600, -14200,
>> + -13700, -13200, -12800, -12400, -12000, -11600, -11200, -10700,
>> + -10200, -9800, -9400, -9000, -8600, -8200, -7700, -7200, -6800,
>> + -6400, -6000, -5600, -5200, -4800, -4300, -3800, -3400, -3000,
>> + -2600, -2200, -1800, -1300, -800, -400, 0, 400, 800, 1200, 1600,
>> + 2100, 2600, 3000, 3400, 3800, 4200, 4600, 5100, 5600, 6000, 6400,
>> + 6800, 7200, 7600, 8000, 8500, 9000, 9400, 9800, 10200, 10600, 11000,
>> + 11400, 11900, 12400, 12800, 13200, 13600, 14000, 14400, 14800,
>> + 15300, 15800, 16200, 16600, 17000, 17400, 17800, 18200, 18700,
>> + 19200, 19600, 20000, 20400, 20800, 21200, 21600, 22100, 22600,
>> + 23000, 23400, 23800, 24200, 24600, 25000, 25400, 25900, 26400,
>> + 26800, 27200, 27600, 28000, 28400, 28800, 29300, 29800, 30200,
>> + 30600, 31000, 31400, 31800, 32200, 32600, 33100, 33600, 34000,
>> + 34400, 34800, 35200, 35600, 36000, 36400, 36800, 37300, 37800,
>> + 38200, 38600, 39000, 39400, 39800, 40200, 40600, 41100, 41600,
>> + 42000, 42400, 42800, 43200, 43600, 44000, 44400, 44800, 45300,
>> + 45800, 46200, 46600, 47000, 47400, 47800, 48200, 48600, 49000,
>> + 49500, 50000, 50400, 50800, 51200, 51600, 52000, 52400, 52800,
>> + 53200, 53700, 54200, 54600, 55000, 55400, 55800, 56200, 56600,
>> + 57000, 57400, 57800, 58200, 58700, 59200, 59600, 60000, 60400,
>> + 60800, 61200, 61600, 62000, 62400, 62800, 63300, 63800, 64200,
>> + 64600, 65000, 65400, 65800, 66200, 66600, 67000, 67400, 67800,
>> + 68200, 68700, 69200, 69600, 70000, 70400, 70800, 71200, 71600,
>> + 72000, 72400, 72800, 73200, 73600, 74100, 74600, 75000, 75400,
>> + 75800, 76200, 76600, 77000, 77400, 77800, 78200, 78600, 79000,
>> + 79400, 79800, 80300, 80800, 81200, 81600, 82000, 82400, 82800,
>> + 83200, 83600, 84000, 84400, 84800, 85200, 85600, 86000, 86400,
>> + 86800, 87300, 87800, 88200, 88600, 89000, 89400, 89800, 90200,
>> + 90600, 91000, 91400, 91800, 92200, 92600, 93000, 93400, 93800,
>> + 94200, 94600, 95000, 95500, 96000, 96400, 96800, 97200, 97600,
>> + 98000, 98400, 98800, 99200, 99600, 100000, 100400, 100800, 101200,
>> + 101600, 102000, 102400, 102800, 103200, 103600, 104000, 104400,
>> + 104800, 105200, 105600, 106100, 106600, 107000, 107400, 107800,
>> + 108200, 108600, 109000, 109400, 109800, 110200, 110600, 111000,
>> + 111400, 111800, 112200, 112600, 113000, 113400, 113800, 114200,
>> + 114600, 115000, 115400, 115800, 116200, 116600, 117000, 117400,
>> + 117800, 118200, 118600, 119000, 119400, 119800, 120200, 120600,
>> + 121000, 121400, 121800, 122200, 122600, 123000
>> +};
>> +
>> +static unsigned long omap_temp_sensor_readl(struct omap_temp_sensor
>> + *temp_sensor, u32 reg)
>> +{
>> + return __raw_readl(temp_sensor->phy_base + reg);
>> +}
>> +
>> +static void omap_temp_sensor_writel(struct omap_temp_sensor *temp_sensor,
>> + u32 val, u32 reg)
>> +{
>> + __raw_writel(val, (temp_sensor->phy_base + reg));
>> +}
>> +
>> +static int adc_to_temp_conversion(int adc_val)
>> +{
>> + return adc_to_temp[adc_val - OMAP_ADC_START_VALUE];
>> +}
>> +
>> +static int temp_to_adc_conversion(long temp)
>> +{
>> + int i;
>> +
>> + for (i = 0; i <= OMAP_ADC_END_VALUE - OMAP_ADC_START_VALUE; i++)
>> + if (temp < adc_to_temp[i])
>> + return OMAP_ADC_START_VALUE + i - 1;
>> +
>> + return -EINVAL;
>> +}
>> +
>> +static void omap_configure_temp_sensor_thresholds(struct omap_temp_sensor
>> + *temp_sensor)
>> +{
>> + u32 temp, t_hot, t_cold, tshut_hot, tshut_cold;
>> +
>> + t_hot = temp_to_adc_conversion(BGAP_THRESHOLD_T_HOT);
>> + t_cold = temp_to_adc_conversion(BGAP_THRESHOLD_T_COLD);
>> +
>> + /* Configure the TALERT thresholds */
>> + temp = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_threshold);
>> + temp |= (t_hot << __ffs(temp_sensor->registers->threshold_thot_mask)) |
>> + (t_cold << __ffs(temp_sensor->registers->threshold_tcold_mask));
>> + omap_temp_sensor_writel(temp_sensor, temp,
>> + temp_sensor->registers->bgap_threshold);
>> +
>> + tshut_hot = temp_to_adc_conversion(TSHUT_THRESHOLD_HOT);
>> + tshut_cold = temp_to_adc_conversion(TSHUT_THRESHOLD_COLD);
>> +
>> + /* Configure the TSHUT thresholds */
>> + temp = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->thsut_threshold);
>> + temp |= (tshut_hot << __ffs(temp_sensor->registers->tshut_hot_mask))
>> + | (tshut_cold << __ffs(temp_sensor->registers->tshut_hot_mask));
>> + omap_temp_sensor_writel(temp_sensor, temp,
>> + temp_sensor->registers->thsut_threshold);
>> +}
>> +
>> +static void omap_configure_temp_sensor_counter(struct omap_temp_sensor
>> + *temp_sensor, u32 counter)
>> +{
>> + u32 val;
>> +
>> + val = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_counter);
>> + val &= ~(temp_sensor->registers->counter_mask);
>> + val |= (counter << __ffs(temp_sensor->registers->counter_mask));
>> + omap_temp_sensor_writel(temp_sensor, val,
>> + temp_sensor->registers->bgap_counter);
>> +}
>> +
>> +static void omap_enable_continuous_mode(struct omap_temp_sensor *temp_sensor,
>> + bool enable)
>> +{
>> + u32 val;
>> +
>> + val = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_mode_ctrl);
>> +
>> + if (enable)
>> + val |= (1 << __ffs(temp_sensor->registers->mode_ctrl_mask));
>> + else
>> + val &= ~(temp_sensor->registers->mode_ctrl_mask);
>> +
>> + omap_temp_sensor_writel(temp_sensor, val,
>> + temp_sensor->registers->bgap_mode_ctrl);
>> +}
>> +
>> +/* Sysfs hook functions */
>> +
>> +static ssize_t show_temp_max(struct device *dev,
>> + struct device_attribute *devattr, char *buf)
>> +{
>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>> + int temp;
>> +
>> + mutex_lock(&temp_sensor->sensor_mutex);
>> +
>> + temp = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_threshold);
>> + temp = (temp & temp_sensor->registers->threshold_thot_mask)
>> + >> __ffs(temp_sensor->registers->threshold_thot_mask);
>> +
>> + if (temp < OMAP_ADC_START_VALUE || temp > OMAP_ADC_END_VALUE) {
>> + dev_err(dev, "invalid value\n");
>> + mutex_unlock(&temp_sensor->sensor_mutex);
>> + return -EINVAL;
>> + }
>> +
>> + temp = adc_to_temp_conversion(temp);
>> + mutex_unlock(&temp_sensor->sensor_mutex);
>> +
>> + return snprintf(buf, 16, "%d\n", temp);
>> +}
>> +
>> +static ssize_t set_temp_max(struct device *dev,
>> + struct device_attribute *devattr,
>> + const char *buf, size_t count)
>> +{
>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>> + long val;
>> + u32 reg_val, t_cold, t_hot, temp;
>> +
>> + mutex_lock(&temp_sensor->sensor_mutex);
>> +
>> + if (strict_strtol(buf, 10, &val)) {
>> + count = -EINVAL;
>> + goto out;
>> + }
>> +
>> + t_hot = temp_to_adc_conversion(val);
>> + if ((t_hot < OMAP_ADC_START_VALUE || t_hot > OMAP_ADC_END_VALUE)) {
>> + dev_err(dev, "invalid range\n");
>> + count = -EINVAL;
>> + goto out;
>> + }
>> +
>> + /* obtain the T cold value */
>> + t_cold = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_threshold);
>> + t_cold = (t_cold & temp_sensor->registers->threshold_tcold_mask) >>
>> + __ffs(temp_sensor->registers->threshold_tcold_mask);
>> +
>> + if (t_hot < t_cold) {
>> + dev_err(dev, "Error! T_HOT value lesser than T_COLD\n");
>> + count = -EINVAL;
>> + goto out;
>> + }
>> +
>> + /* write the new t_hot value */
>> + reg_val = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_threshold);
>> + reg_val &= ~(temp_sensor->registers->threshold_thot_mask);
>> + reg_val |= (t_hot <<
>> + __ffs(temp_sensor->registers->threshold_thot_mask));
>> + omap_temp_sensor_writel(temp_sensor, reg_val,
>> + temp_sensor->registers->bgap_threshold);
>> +
>> + /* Read the current temperature */
>> + temp = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->temp_sensor_ctrl);
>> + temp &= (temp_sensor->registers->bgap_dtemp_mask);
>> +
>> + /*
>> + * If user sets the HIGH threshold(t_hot) greater than the current
>> + * temperature(temp) unmask the HOT interrupts
>> + */
>> + if (t_hot > temp) {
>> + reg_val = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_mask_ctrl);
>> + reg_val &= ~(temp_sensor->registers->mask_cold_mask);
>> + reg_val |= temp_sensor->registers->mask_hot_mask;
>> + omap_temp_sensor_writel(temp_sensor, reg_val,
>> + temp_sensor->registers->bgap_mask_ctrl);
>> + }
>> +
>> + /*
>> + * If current temperature is in-between the hot and cold thresholds,
>> + * Enable both masks.
>> + */
>> + if (temp > t_cold && temp < t_hot) {
>> + reg_val = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_mask_ctrl);
>> + reg_val |= temp_sensor->registers->mask_cold_mask;
>> + reg_val |= temp_sensor->registers->mask_hot_mask;
>> + omap_temp_sensor_writel(temp_sensor, reg_val,
>> + OMAP4460_BGAP_CTRL_OFFSET);
>> + }
>> + /*
>> + * else no need to do anything since HW will immediately compare
>> + * the new threshold and generate interrupt accordingly
>> + */
>> +out:
>> + mutex_unlock(&temp_sensor->sensor_mutex);
>> + return count;
>> +}
>> +
>> +static ssize_t show_temp_max_hyst(struct device *dev,
>> + struct device_attribute *devattr, char *buf)
>> +{
>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>> + u32 temp;
>> +
>> + mutex_lock(&temp_sensor->sensor_mutex);
>> +
>> + temp = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_threshold);
>> + temp = (temp & temp_sensor->registers->threshold_tcold_mask) >>
>> + __ffs(temp_sensor->registers->threshold_tcold_mask);
>> +
>> + if (temp < OMAP_ADC_START_VALUE || temp > OMAP_ADC_END_VALUE) {
>> + dev_err(dev, "invalid value\n");
>> + mutex_unlock(&temp_sensor->sensor_mutex);
>> + return -EINVAL;
>> + }
>> +
>> + temp = adc_to_temp_conversion(temp);
>> + mutex_unlock(&temp_sensor->sensor_mutex);
>> +
>> + return snprintf(buf, 16, "%d\n", temp);
>> +}
>> +
>> +static ssize_t set_temp_max_hyst(struct device *dev,
>> + struct device_attribute *devattr,
>> + const char *buf, size_t count)
>> +{
>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>> + u32 reg_val, t_hot, t_cold, temp;
>> + long val;
>> +
>> + mutex_lock(&temp_sensor->sensor_mutex);
>> +
>> + if (strict_strtol(buf, 10, &val)) {
>> + count = -EINVAL;
>> + goto out;
>> + }
>> +
>> + t_cold = temp_to_adc_conversion(val);
>> + if (t_cold < OMAP_ADC_START_VALUE || t_cold > OMAP_ADC_END_VALUE) {
>> + dev_err(dev, "invalid range");
>> + count = -EINVAL;
>> + goto out;
>> + }
>> +
>> + /* obtain the T HOT value */
>> + t_hot = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_threshold);
>> + t_hot = (t_hot & temp_sensor->registers->threshold_thot_mask) >>
>> + __ffs(temp_sensor->registers->threshold_thot_mask);
>> +
>> + if (t_cold > t_hot) {
>> + dev_err(dev, "Error! T_COLD value greater than T_HOT\n");
>> + count = -EINVAL;
>> + goto out;
>> + }
>> +
>> + /* write the new t_cold value */
>> + reg_val = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_threshold);
>> + reg_val &= ~(temp_sensor->registers->threshold_tcold_mask);
>> + reg_val |= (t_cold <<
>> + __ffs(temp_sensor->registers->threshold_tcold_mask));
>> + omap_temp_sensor_writel(temp_sensor, reg_val,
>> + temp_sensor->registers->bgap_threshold);
>> +
>> + /* Read the current temperature */
>> + temp = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->temp_sensor_ctrl);
>> + temp &= (temp_sensor->registers->bgap_dtemp_mask);
>> +
>> + /*
>> + * If user sets the LOW threshold(t_cold) lower than the current
>> + * temperature(temp) unmask the COLD interrupts
>> + */
>> + if (t_cold < temp) {
>> + reg_val = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_mask_ctrl);
>> + reg_val &= ~(temp_sensor->registers->mask_hot_mask);
>> + reg_val |= temp_sensor->registers->mask_cold_mask;
>> + omap_temp_sensor_writel(temp_sensor, reg_val,
>> + temp_sensor->registers->bgap_mask_ctrl);
>> + }
>> +
>> + /*
>> + * If current temperature is in-between the hot and cold thresholds,
>> + * Enable both masks.
>> + */
>> + if (temp < t_hot && temp > t_cold) {
>> + reg_val = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_mask_ctrl);
>> + reg_val |= temp_sensor->registers->mask_cold_mask;
>> + reg_val |= temp_sensor->registers->mask_hot_mask;
>> + omap_temp_sensor_writel(temp_sensor, reg_val,
>> + temp_sensor->registers->bgap_mask_ctrl);
>> + }
>> +
>> + /*
>> + * else no need to do anything since HW will immediately compare
>> + * the new threshold and generate interrupt accordingly
>> + */
>> +
>> +out:
>> + mutex_unlock(&temp_sensor->sensor_mutex);
>> + return count;
>> +}
>> +
>> +static ssize_t show_update_rate(struct device *dev,
>> + struct device_attribute *devattr, char *buf)
>> +{
>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>> + u32 temp = 0, ret = 0;
>> +
>> + mutex_lock(&temp_sensor->sensor_mutex);
>> +
>> + if (!temp_sensor->clk_rate) {
>> + dev_err(dev, "clk_rate is NULL\n");
>> + ret = -EINVAL;
>> + goto out;
>> + }
>> +
>> + temp = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_counter);
>> + temp = (temp & temp_sensor->registers->counter_mask) >>
>> + __ffs(temp_sensor->registers->counter_mask);
>> + temp = temp * 1000 / (temp_sensor->clk_rate);
>> +
>> +out:
>> + mutex_unlock(&temp_sensor->sensor_mutex);
>> + if (!ret)
>> + return sprintf(buf, "%d\n", temp);
>> +
>> + return ret;
>> +}
>> +
>> +static ssize_t set_update_rate(struct device *dev,
>> + struct device_attribute *devattr,
>> + const char *buf, size_t count)
>> +{
>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>> + u32 reg_val;
>> + long val;
>> +
>> + mutex_lock(&temp_sensor->sensor_mutex);
>> +
>> + if (strict_strtol(buf, 10, &val)) {
>> + count = -EINVAL;
>> + goto out;
>> + }
>> +
>> + val *= temp_sensor->clk_rate / 1000;
>> + reg_val = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_counter);
>> +
>> + reg_val &= ~(temp_sensor->registers->counter_mask);
>> + reg_val |= val;
>> + omap_temp_sensor_writel(temp_sensor, reg_val,
>> + temp_sensor->registers->bgap_counter);
>> +
>> +out:
>> + mutex_unlock(&temp_sensor->sensor_mutex);
>> + return count;
>> +}
>> +
>> +static int omap_temp_sensor_read_temp(struct device *dev,
>> + struct device_attribute *devattr,
>> + char *buf)
>> +{
>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>> + int temp, ret = 0;
>> +
>> + mutex_lock(&temp_sensor->sensor_mutex);
>> +
>> + temp = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->temp_sensor_ctrl);
>> + temp &= temp_sensor->registers->bgap_dtemp_mask;
>> +
>> + if (!temp_sensor->is_efuse_valid)
>> + dev_err(dev, "Invalid EFUSE, Non-trimmed BGAP, Temp not accurate\n");
>> +
>> + /* look up for temperature in the table and return the temperature */
>> + if (temp < OMAP_ADC_START_VALUE || temp > OMAP_ADC_END_VALUE) {
>> + dev_err(dev, "invalid adc code reported %d", temp);
>> + ret = -EINVAL;
>> + goto out;
>> + }
>> +
>> + temp = adc_to_temp[temp - OMAP_ADC_START_VALUE];
>> +
>> +out:
>> + mutex_unlock(&temp_sensor->sensor_mutex);
>> + if (!ret)
>> + return sprintf(buf, "%d\n", temp);
>> +
>> + return ret;
>> +}
>> +
>> +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, omap_temp_sensor_read_temp,
>> + NULL, 0);
>> +static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp_max,
>> + set_temp_max, 0);
>> +static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO, show_temp_max_hyst,
>> + set_temp_max_hyst, 0);
>> +static SENSOR_DEVICE_ATTR(update_rate, S_IWUSR | S_IRUGO, show_update_rate,
>> + set_update_rate, 0);
>> +
>> +static struct attribute *omap_temp_sensor_attributes[] = {
>> + &sensor_dev_attr_temp1_input.dev_attr.attr,
>> + &sensor_dev_attr_temp1_max.dev_attr.attr,
>> + &sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
>> + &sensor_dev_attr_update_rate.dev_attr.attr,
>> + NULL
>> +};
>> +
>> +static const struct attribute_group omap_temp_sensor_group = {
>> + .attrs = omap_temp_sensor_attributes,
>> +};
>> +
>> +static int omap_temp_sensor_clk_enable(struct omap_temp_sensor *temp_sensor)
>> +{
>> + u32 ret = 0;
>> +
>> + if (temp_sensor->clk_on) {
>> + dev_err(temp_sensor->hwmon_dev, "clock already on\n");
>> + goto out;
>> + }
>> +
>> + ret = pm_runtime_get_sync(temp_sensor->hwmon_dev);
>> + if (ret < 0) {
>> + dev_err(temp_sensor->hwmon_dev, "get sync failed\n");
>> + goto out;
>> + }
>> +
>> + temp_sensor->clk_on = 1;
>> +
>> +out:
>> + return ret;
>> +}
>> +
>> +static int omap_temp_sensor_clk_disable(struct omap_temp_sensor *temp_sensor)
>> +{
>> + u32 ret = 0;
>> +
>> + if (!temp_sensor->clk_on) {
>> + dev_err(temp_sensor->hwmon_dev, "clock already off\n");
>> + goto out;
>> + }
>> +
>> + /* Gate the clock */
>> + ret = pm_runtime_put_sync(temp_sensor->hwmon_dev);
>> + if (ret < 0) {
>> + dev_err(temp_sensor->hwmon_dev, "put sync failed\n");
>> + goto out;
>> + }
>> + temp_sensor->clk_on = 0;
>> +
>> +out:
>> + return ret;
>> +}
>> +
>> +static irqreturn_t omap_talert_irq_handler(int irq, void *data)
>> +{
>> + struct omap_temp_sensor *temp_sensor;
>> + int t_hot, t_cold, temp;
>> +
>> + temp_sensor = data;
>> + mutex_lock(&temp_sensor->sensor_mutex);
>> +
>> + /* Read the status of t_hot */
>> + t_hot = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_status)
>> + & temp_sensor->registers->status_hot_mask;
>> +
>> + /* Read the status of t_cold */
>> + t_cold = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_status)
>> + & temp_sensor->registers->status_cold_mask;
>> +
>> + temp = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_mask_ctrl);
>> + /*
>> + * One TALERT interrupt: Two sources
>> + * If the interrupt is due to t_hot then mask t_hot and
>> + * and unmask t_cold else mask t_cold and unmask t_hot
>> + */
>> + if (t_hot) {
>> + temp &= ~(temp_sensor->registers->mask_hot_mask);
>> + temp |= temp_sensor->registers->mask_cold_mask;
>> + } else if (t_cold) {
>> + temp &= ~(temp_sensor->registers->mask_cold_mask);
>> + temp |= temp_sensor->registers->mask_hot_mask;
>> + }
>> +
>> + omap_temp_sensor_writel(temp_sensor, temp,
>> + temp_sensor->registers->bgap_mask_ctrl);
>> +
>> + /* kobject_uvent to user space telling thermal threshold crossed */
>> + kobject_uevent(&temp_sensor->hwmon_dev->kobj, KOBJ_CHANGE);
>> +
>> + mutex_unlock(&temp_sensor->sensor_mutex);
>> +
>> + return IRQ_HANDLED;
>> +}
>> +
>> +static int __devinit omap_temp_sensor_probe(struct platform_device *pdev)
>> +{
>> + struct omap_temp_sensor_pdata *pdata = pdev->dev.platform_data;
>> + struct omap_temp_sensor *temp_sensor;
>> + struct resource *mem;
>> + int ret = 0;
>> + int val, clk_rate;
>> +
>> + if (!pdata) {
>> + dev_err(&pdev->dev, "platform data missing\n");
>> + return -EINVAL;
>> + }
>> +
>> + temp_sensor = kzalloc(sizeof(*temp_sensor), GFP_KERNEL);
>> + if (!temp_sensor)
>> + return -ENOMEM;
>> +
>> + mutex_init(&temp_sensor->sensor_mutex);
>> +
>> + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> + if (!mem) {
>> + dev_err(&pdev->dev, "no mem resource\n");
>> + ret = -ENOMEM;
>> + goto plat_res_err;
>> + }
>> +
>> + temp_sensor->irq = platform_get_irq_byname(pdev, "thermal_alert");
>> + if (temp_sensor->irq < 0) {
>> + dev_err(&pdev->dev, "get_irq_byname failed\n");
>> + ret = temp_sensor->irq;
>> + goto plat_res_err;
>> + }
>> +
>> + temp_sensor->phy_base = ioremap(mem->start, resource_size(mem));
>> + temp_sensor->clock = NULL;
>> + temp_sensor->registers = pdata->registers;
>> + temp_sensor->hwmon_dev = &pdev->dev;
>> +
>> + pm_runtime_enable(&pdev->dev);
>> + pm_runtime_irq_safe(&pdev->dev);
>> +
>> + /*
>> + * check if the efuse has a non-zero value if not
>> + * it is an untrimmed sample and the temperatures
>> + * may not be accurate
>> + */
>> +
>> + if (omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_efuse))
>> + temp_sensor->is_efuse_valid = 1;
>> +
>> + platform_set_drvdata(pdev, temp_sensor);
>> + dev_set_drvdata(&pdev->dev, temp_sensor);
>> + temp_sensor->clock = clk_get(temp_sensor->hwmon_dev, "fck");
>> + if (IS_ERR(temp_sensor->clock)) {
>> + ret = PTR_ERR(temp_sensor->clock);
>> + dev_err(temp_sensor->hwmon_dev,
>> + "unable to get fclk: %d\n", ret);
>> + ret = -EINVAL;
>> + goto plat_res_err;
>> + }
>> +
>> + ret = omap_temp_sensor_clk_enable(temp_sensor);
>> + if (ret) {
>> + dev_err(&pdev->dev, "Cannot enable temp sensor\n");
>> + goto clken_err;
>> + }
>> +
>> + clk_rate = clk_round_rate(temp_sensor->clock, 2000000);
>> + if (clk_rate < 1000000 || clk_rate = 0xffffffff) {
>> + dev_err(&pdev->dev, "Error round rate\n");
>> + ret = -EINVAL;
>> + goto clken_err;
>> + }
>> +
>> + ret = clk_set_rate(temp_sensor->clock, clk_rate);
>> + if (ret) {
>> + dev_err(&pdev->dev, "Cannot set clock rate\n");
>> + goto clken_err;
>> + }
>> +
>> + temp_sensor->clk_rate = clk_rate;
>> + omap_enable_continuous_mode(temp_sensor, 1);
>> + omap_configure_temp_sensor_thresholds(temp_sensor);
>> + /* 1 ms */
>> + omap_configure_temp_sensor_counter(temp_sensor, 1);
>> +
>> + /* Wait till the first conversion is done wait for at least 1ms */
>> + usleep_range(1000, 2000);
>> +
>> + /* Read the temperature once due to hw issue*/
>> + omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->temp_sensor_ctrl);
>> +
>> + /* Set 2 seconds time as default counter */
>> + omap_configure_temp_sensor_counter(temp_sensor,
>> + temp_sensor->clk_rate * 2);
>> +
>> + temp_sensor->hwmon_dev = hwmon_device_register(&pdev->dev);
>> + if (IS_ERR(temp_sensor->hwmon_dev)) {
>> + dev_err(&pdev->dev, "hwmon_device_register failed.\n");
>> + ret = PTR_ERR(temp_sensor->hwmon_dev);
>> + goto hwmon_reg_err;
>> + }
>> +
>> + ret = sysfs_create_group(&pdev->dev.kobj,
>> + &omap_temp_sensor_group);
>> + if (ret) {
>> + dev_err(&pdev->dev, "could not create sysfs files\n");
>> + goto sysfs_create_err;
>> + }
>> +
>> + kobject_uevent(&temp_sensor->hwmon_dev->kobj, KOBJ_ADD);
>> +
>> + ret = request_threaded_irq(temp_sensor->irq, NULL,
>> + omap_talert_irq_handler, IRQF_TRIGGER_RISING | IRQF_ONESHOT,
>> + "temp_sensor", temp_sensor);
>> + if (ret) {
>> + dev_err(&pdev->dev, "Request threaded irq failed.\n");
>> + goto req_irq_err;
>> + }
>> +
>> + /* unmask the T_COLD and unmask T_HOT at init */
>> + val = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_mask_ctrl);
>> + val |= temp_sensor->registers->mask_cold_mask
>> + | temp_sensor->registers->mask_hot_mask;
>> +
>> + omap_temp_sensor_writel(temp_sensor, val,
>> + temp_sensor->registers->bgap_mask_ctrl);
>> +
>> + return 0;
>> +
>> +req_irq_err:
>> + kobject_uevent(&temp_sensor->hwmon_dev->kobj, KOBJ_REMOVE);
>> + sysfs_remove_group(&temp_sensor->hwmon_dev->kobj,
>> + &omap_temp_sensor_group);
>> +sysfs_create_err:
>> + hwmon_device_unregister(&pdev->dev);
>> +hwmon_reg_err:
>> + omap_temp_sensor_clk_disable(temp_sensor);
>> +clken_err:
>> + clk_put(temp_sensor->clock);
>> +plat_res_err:
>> + mutex_destroy(&temp_sensor->sensor_mutex);
>> + kfree(temp_sensor);
>> + return ret;
>> +}
>> +
>> +static int __devexit omap_temp_sensor_remove(struct platform_device *pdev)
>> +{
>> + struct omap_temp_sensor *temp_sensor = platform_get_drvdata(pdev);
>> +
>> + hwmon_device_unregister(&pdev->dev);
>> + kobject_uevent(&temp_sensor->hwmon_dev->kobj, KOBJ_REMOVE);
>> + sysfs_remove_group(&temp_sensor->hwmon_dev->kobj,
>> + &omap_temp_sensor_group);
>> + omap_temp_sensor_clk_disable(temp_sensor);
>> + clk_put(temp_sensor->clock);
>> + platform_set_drvdata(pdev, NULL);
>> + free_irq(temp_sensor->irq, temp_sensor);
>> + mutex_destroy(&temp_sensor->sensor_mutex);
>> + kfree(temp_sensor);
>> +
>> + return 0;
>> +}
>> +
>> +#ifdef CONFIG_PM
>> +static void omap_temp_sensor_save_ctxt(struct omap_temp_sensor *temp_sensor)
>> +{
>> + temp_sensor->temp_sensor_ctrl = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->temp_sensor_ctrl);
>> + temp_sensor->bg_ctrl = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_mask_ctrl);
>> + temp_sensor->bg_counter = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_counter);
>> + temp_sensor->bg_threshold = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_threshold);
>> + temp_sensor->temp_sensor_tshut_threshold >> + omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->thsut_threshold);
>> +}
>> +
>> +static void omap_temp_sensor_restore_ctxt(struct omap_temp_sensor *temp_sensor)
>> +{
>> + omap_temp_sensor_writel(temp_sensor,
>> + temp_sensor->temp_sensor_ctrl,
>> + temp_sensor->registers->temp_sensor_ctrl);
>> + omap_temp_sensor_writel(temp_sensor,
>> + temp_sensor->bg_ctrl,
>> + temp_sensor->registers->bgap_mask_ctrl);
>> + omap_temp_sensor_writel(temp_sensor,
>> + temp_sensor->bg_counter,
>> + temp_sensor->registers->bgap_counter);
>> + omap_temp_sensor_writel(temp_sensor,
>> + temp_sensor->bg_threshold,
>> + temp_sensor->registers->bgap_threshold);
>> + omap_temp_sensor_writel(temp_sensor,
>> + temp_sensor->temp_sensor_tshut_threshold,
>> + temp_sensor->registers->thsut_threshold);
>> +}
>> +
>> +static int omap_temp_sensor_suspend(struct device *dev)
>> +{
>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>> +
>> + omap_temp_sensor_save_ctxt(temp_sensor);
>> +
>> + return 0;
>> +}
>> +
>> +static int omap_temp_sensor_resume(struct device *dev)
>> +{
>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>> +
>> + omap_temp_sensor_restore_ctxt(temp_sensor);
>> +
>> + return 0;
>> +}
>> +
>> +static int omap_temp_sensor_runtime_suspend(struct device *dev)
>> +{
>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>> +
>> + omap_temp_sensor_save_ctxt(temp_sensor);
>> +
>> + return 0;
>> +}
>> +
>> +static int omap_temp_sensor_runtime_resume(struct device *dev)
>> +{
>> + static int context_loss_count;
>> + int temp;
>> + struct omap_temp_sensor *temp_sensor = dev_get_drvdata(dev);
>> +
>> + temp = omap_device_get_context_loss_count(to_platform_device(dev));
>> +
>> + if (temp != context_loss_count && context_loss_count != 0)
>> + omap_temp_sensor_restore_ctxt(temp_sensor);
>> +
>> + context_loss_count = temp;
>> +
>> + return 0;
>> +}
>> +
>> +static const struct dev_pm_ops omap_temp_sensor_dev_pm_ops = {
>> + .suspend = omap_temp_sensor_suspend,
>> + .resume = omap_temp_sensor_resume,
>> + .runtime_suspend = omap_temp_sensor_runtime_suspend,
>> + .runtime_resume = omap_temp_sensor_runtime_resume,
>> +};
>
> this is not enough... you need to populate the other fields. You might
> want to use something like:
>
> static const struct dev_pm_ops omap_temp_sensor_dev_pm_ops = {
> SET_SYSTEM_SLEEP_PM_OPS(omap_temp_sensor_suspend,
> omap_temp_sensor_resume)
> SET_RUNTIME_PM_OPS(omap_temp_sensor_runtime_suspend,
> omap_temp_sensor_runtime_resume, omap_temp_sensor_idle)
> };
omap_temp_sensor_idle is undefined for temperature sensor as of now. So i will
populate these fields once the idle function is defined.
>
>> +
>> +#endif
>
> instead of endif, try something like:
>
> dev_pm_ops....
>
> #define DEV_PM_OPS (&omap_temp_sensor_dev_pm_ops)
> #else
> #define DEV_PM_OPS NULL
> #endif
>
> then you can remove the ifdef on the driver structure.
Ok
>
> --
> balbi
>
--
Regards and Thanks,
Keerthy
_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors
^ permalink raw reply [flat|nested] 35+ messages in thread
* Re: [lm-sensors] [RFC PATCH 6/6 V2] hwmon: OMAP4: On die
@ 2011-08-22 4:52 ` J, KEERTHY
0 siblings, 0 replies; 35+ messages in thread
From: J, KEERTHY @ 2011-08-22 4:52 UTC (permalink / raw)
To: Todd Poynor; +Cc: linux-omap, Jean Delvare, Guenter Roeck, lm-sensors
On Fri, Aug 19, 2011 at 11:04 AM, Todd Poynor <toddpoynor@google.com> wrote:
> On Thu, Aug 18, 2011 at 04:22:15PM +0530, Keerthy wrote:
> ...
>> +static int omap_temp_sensor_clk_enable(struct omap_temp_sensor *temp_sensor)
>> +{
>> + u32 ret = 0;
>> +
>> + if (temp_sensor->clk_on) {
>> + dev_err(temp_sensor->hwmon_dev, "clock already on\n");
>> + goto out;
>> + }
>> +
>> + ret = pm_runtime_get_sync(temp_sensor->hwmon_dev);
>> + if (ret < 0) {
>> + dev_err(temp_sensor->hwmon_dev, "get sync failed\n");
>> + goto out;
>> + }
>> +
>> + temp_sensor->clk_on = 1;
>
>
> Probably should hold the mutex around this to keep clk_on consistent
> with runtime PM state (and in disable method).
Ok
>
> ...
>> +static int __devinit omap_temp_sensor_probe(struct platform_device *pdev)
>> +{
>> + struct omap_temp_sensor_pdata *pdata = pdev->dev.platform_data;
>> + struct omap_temp_sensor *temp_sensor;
>> + struct resource *mem;
>> + int ret = 0;
>> + int val, clk_rate;
>> +
>> + if (!pdata) {
>> + dev_err(&pdev->dev, "platform data missing\n");
>> + return -EINVAL;
>> + }
>> +
>> + temp_sensor = kzalloc(sizeof(*temp_sensor), GFP_KERNEL);
>> + if (!temp_sensor)
>> + return -ENOMEM;
>> +
>> + mutex_init(&temp_sensor->sensor_mutex);
>> +
>> + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> + if (!mem) {
>> + dev_err(&pdev->dev, "no mem resource\n");
>> + ret = -ENOMEM;
>> + goto plat_res_err;
>> + }
>> +
>> + temp_sensor->irq = platform_get_irq_byname(pdev, "thermal_alert");
>> + if (temp_sensor->irq < 0) {
>> + dev_err(&pdev->dev, "get_irq_byname failed\n");
>> + ret = temp_sensor->irq;
>> + goto plat_res_err;
>> + }
>> +
>> + temp_sensor->phy_base = ioremap(mem->start, resource_size(mem));
>> + temp_sensor->clock = NULL;
>> + temp_sensor->registers = pdata->registers;
>> + temp_sensor->hwmon_dev = &pdev->dev;
>> +
>> + pm_runtime_enable(&pdev->dev);
>> + pm_runtime_irq_safe(&pdev->dev);
>> +
>> + /*
>> + * check if the efuse has a non-zero value if not
>> + * it is an untrimmed sample and the temperatures
>> + * may not be accurate
>> + */
>> +
>> + if (omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_efuse))
>> + temp_sensor->is_efuse_valid = 1;
>> +
>> + platform_set_drvdata(pdev, temp_sensor);
>> + dev_set_drvdata(&pdev->dev, temp_sensor);
>> + temp_sensor->clock = clk_get(temp_sensor->hwmon_dev, "fck");
>> + if (IS_ERR(temp_sensor->clock)) {
>> + ret = PTR_ERR(temp_sensor->clock);
>> + dev_err(temp_sensor->hwmon_dev,
>> + "unable to get fclk: %d\n", ret);
>> + ret = -EINVAL;
>> + goto plat_res_err;
>> + }
>> +
>> + ret = omap_temp_sensor_clk_enable(temp_sensor);
>> + if (ret) {
>> + dev_err(&pdev->dev, "Cannot enable temp sensor\n");
>> + goto clken_err;
>> + }
>> +
>> + clk_rate = clk_round_rate(temp_sensor->clock, 2000000);
>> + if (clk_rate < 1000000 || clk_rate = 0xffffffff) {
>> + dev_err(&pdev->dev, "Error round rate\n");
>> + ret = -EINVAL;
>> + goto clken_err;
>> + }
>> +
>> + ret = clk_set_rate(temp_sensor->clock, clk_rate);
>> + if (ret) {
>> + dev_err(&pdev->dev, "Cannot set clock rate\n");
>> + goto clken_err;
>> + }
>> +
>> + temp_sensor->clk_rate = clk_rate;
>> + omap_enable_continuous_mode(temp_sensor, 1);
>> + omap_configure_temp_sensor_thresholds(temp_sensor);
>> + /* 1 ms */
>> + omap_configure_temp_sensor_counter(temp_sensor, 1);
>> +
>> + /* Wait till the first conversion is done wait for at least 1ms */
>> + usleep_range(1000, 2000);
>> +
>> + /* Read the temperature once due to hw issue*/
>> + omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->temp_sensor_ctrl);
>> +
>> + /* Set 2 seconds time as default counter */
>> + omap_configure_temp_sensor_counter(temp_sensor,
>> + temp_sensor->clk_rate * 2);
>> +
>> + temp_sensor->hwmon_dev = hwmon_device_register(&pdev->dev);
>> + if (IS_ERR(temp_sensor->hwmon_dev)) {
>> + dev_err(&pdev->dev, "hwmon_device_register failed.\n");
>> + ret = PTR_ERR(temp_sensor->hwmon_dev);
>> + goto hwmon_reg_err;
>> + }
>> +
>> + ret = sysfs_create_group(&pdev->dev.kobj,
>> + &omap_temp_sensor_group);
>> + if (ret) {
>> + dev_err(&pdev->dev, "could not create sysfs files\n");
>> + goto sysfs_create_err;
>> + }
>> +
>> + kobject_uevent(&temp_sensor->hwmon_dev->kobj, KOBJ_ADD);
>> +
>> + ret = request_threaded_irq(temp_sensor->irq, NULL,
>> + omap_talert_irq_handler, IRQF_TRIGGER_RISING | IRQF_ONESHOT,
>> + "temp_sensor", temp_sensor);
>> + if (ret) {
>> + dev_err(&pdev->dev, "Request threaded irq failed.\n");
>> + goto req_irq_err;
>> + }
>> +
>> + /* unmask the T_COLD and unmask T_HOT at init */
>> + val = omap_temp_sensor_readl(temp_sensor,
>> + temp_sensor->registers->bgap_mask_ctrl);
>> + val |= temp_sensor->registers->mask_cold_mask
>> + | temp_sensor->registers->mask_hot_mask;
>> +
>> + omap_temp_sensor_writel(temp_sensor, val,
>> + temp_sensor->registers->bgap_mask_ctrl);
>> +
>> + return 0;
>> +
>> +req_irq_err:
>> + kobject_uevent(&temp_sensor->hwmon_dev->kobj, KOBJ_REMOVE);
>> + sysfs_remove_group(&temp_sensor->hwmon_dev->kobj,
>> + &omap_temp_sensor_group);
>> +sysfs_create_err:
>> + hwmon_device_unregister(&pdev->dev);
>> +hwmon_reg_err:
>> + omap_temp_sensor_clk_disable(temp_sensor);
>> +clken_err:
>> + clk_put(temp_sensor->clock);
>> +plat_res_err:
>> + mutex_destroy(&temp_sensor->sensor_mutex);
>> + kfree(temp_sensor);
>
> Should also:
> platform_set_drvdata(pdev, NULL);
> dev_set_drvdata(&pdev->dev, NULL);
Ok
>
>
>> + return ret;
>> +}
>> +
>> +static int __devexit omap_temp_sensor_remove(struct platform_device *pdev)
>> +{
>> + struct omap_temp_sensor *temp_sensor = platform_get_drvdata(pdev);
>> +
>> + hwmon_device_unregister(&pdev->dev);
>> + kobject_uevent(&temp_sensor->hwmon_dev->kobj, KOBJ_REMOVE);
>> + sysfs_remove_group(&temp_sensor->hwmon_dev->kobj,
>> + &omap_temp_sensor_group);
>> + omap_temp_sensor_clk_disable(temp_sensor);
>> + clk_put(temp_sensor->clock);
>> + platform_set_drvdata(pdev, NULL);
>
> And:
> dev_set_drvdata(&pdev->dev, NULL);
Ok
>
>> + free_irq(temp_sensor->irq, temp_sensor);
>
> Need to free IRQ before clock is disabled (else ISR may access while
> clock stopped, possible L3 interconnect error and ARM imprecise
> external abort)?
Ok
>
>> + mutex_destroy(&temp_sensor->sensor_mutex);
>> + kfree(temp_sensor);
>> +
>> + return 0;
>> +}
>
>
> Todd
>
--
Regards and Thanks,
Keerthy
_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors
^ permalink raw reply [flat|nested] 35+ messages in thread
* Re: [RFC PATCH 6/6 V2] hwmon: OMAP4: On die temperature sensor driver
2011-08-22 4:41 ` [lm-sensors] [RFC PATCH 6/6 V2] hwmon: OMAP4: On die J, KEERTHY
@ 2011-08-22 9:24 ` Felipe Balbi
-1 siblings, 0 replies; 35+ messages in thread
From: Felipe Balbi @ 2011-08-22 9:24 UTC (permalink / raw)
To: J, KEERTHY; +Cc: balbi, linux-omap, Jean Delvare, Guenter Roeck, lm-sensors
[-- Attachment #1: Type: text/plain, Size: 1166 bytes --]
Hi,
On Mon, Aug 22, 2011 at 09:59:46AM +0530, J, KEERTHY wrote:
> >> +static const struct dev_pm_ops omap_temp_sensor_dev_pm_ops = {
> >> + .suspend = omap_temp_sensor_suspend,
> >> + .resume = omap_temp_sensor_resume,
> >> + .runtime_suspend = omap_temp_sensor_runtime_suspend,
> >> + .runtime_resume = omap_temp_sensor_runtime_resume,
> >> +};
> >
> > this is not enough... you need to populate the other fields. You might
> > want to use something like:
> >
> > static const struct dev_pm_ops omap_temp_sensor_dev_pm_ops = {
> > SET_SYSTEM_SLEEP_PM_OPS(omap_temp_sensor_suspend,
> > omap_temp_sensor_resume)
> > SET_RUNTIME_PM_OPS(omap_temp_sensor_runtime_suspend,
> > omap_temp_sensor_runtime_resume, omap_temp_sensor_idle)
> > };
>
> omap_temp_sensor_idle is undefined for temperature sensor as of now. So i will
> populate these fields once the idle function is defined.
make a nop idle function. Those other fields _must_ be populated. Use
something like:
static int omap_temp_sensor_idle(struct device *dev)
{
/* nothing to do here */
return 0;
}
--
balbi
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 490 bytes --]
^ permalink raw reply [flat|nested] 35+ messages in thread
* Re: [lm-sensors] [RFC PATCH 6/6 V2] hwmon: OMAP4: On die
@ 2011-08-22 9:24 ` Felipe Balbi
0 siblings, 0 replies; 35+ messages in thread
From: Felipe Balbi @ 2011-08-22 9:24 UTC (permalink / raw)
To: J, KEERTHY; +Cc: balbi, linux-omap, Jean Delvare, Guenter Roeck, lm-sensors
[-- Attachment #1.1: Type: text/plain, Size: 1166 bytes --]
Hi,
On Mon, Aug 22, 2011 at 09:59:46AM +0530, J, KEERTHY wrote:
> >> +static const struct dev_pm_ops omap_temp_sensor_dev_pm_ops = {
> >> + .suspend = omap_temp_sensor_suspend,
> >> + .resume = omap_temp_sensor_resume,
> >> + .runtime_suspend = omap_temp_sensor_runtime_suspend,
> >> + .runtime_resume = omap_temp_sensor_runtime_resume,
> >> +};
> >
> > this is not enough... you need to populate the other fields. You might
> > want to use something like:
> >
> > static const struct dev_pm_ops omap_temp_sensor_dev_pm_ops = {
> > SET_SYSTEM_SLEEP_PM_OPS(omap_temp_sensor_suspend,
> > omap_temp_sensor_resume)
> > SET_RUNTIME_PM_OPS(omap_temp_sensor_runtime_suspend,
> > omap_temp_sensor_runtime_resume, omap_temp_sensor_idle)
> > };
>
> omap_temp_sensor_idle is undefined for temperature sensor as of now. So i will
> populate these fields once the idle function is defined.
make a nop idle function. Those other fields _must_ be populated. Use
something like:
static int omap_temp_sensor_idle(struct device *dev)
{
/* nothing to do here */
return 0;
}
--
balbi
[-- Attachment #1.2: Digital signature --]
[-- Type: application/pgp-signature, Size: 490 bytes --]
[-- Attachment #2: Type: text/plain, Size: 153 bytes --]
_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors
^ permalink raw reply [flat|nested] 35+ messages in thread
* Re: [RFC PATCH 1/6 V2] OMAP4: Clock: Associate clocks for OMAP temperature sensor
2011-08-18 10:52 ` [RFC PATCH 1/6 V2] OMAP4: Clock: Associate clocks for OMAP temperature sensor Keerthy
@ 2011-08-29 21:47 ` Kevin Hilman
0 siblings, 0 replies; 35+ messages in thread
From: Kevin Hilman @ 2011-08-29 21:47 UTC (permalink / raw)
To: Keerthy; +Cc: linux-omap, tony, rnayak
Keerthy <j-keerthy@ti.com> writes:
> div_ts_ck feeds only the temperature sensor functional clock
> and also has a clksel associated (for divider selection). Mapping this
> as the functional clock for the temperature sensor in clkdev table,
> so a clk_set_rate() in the driver would have the effect of changing the
> temperature sensor clock rate indirectly.
>
> Reviewd-by: Rajendra Nayak <rnayak@ti.com>
s/Reviewd/Reviewed/
^ permalink raw reply [flat|nested] 35+ messages in thread
* Re: [RFC PATCH 2/6 V2] OMAP4: Adding the temperature sensor register set bit fields
2011-08-18 10:52 ` [RFC PATCH 2/6 V2] OMAP4: Adding the temperature sensor register set bit fields Keerthy
@ 2011-08-29 21:52 ` Kevin Hilman
0 siblings, 0 replies; 35+ messages in thread
From: Kevin Hilman @ 2011-08-29 21:52 UTC (permalink / raw)
To: Keerthy, Benoit Cousson; +Cc: linux-omap, tony
+Benoit
Keerthy <j-keerthy@ti.com> writes:
> OMAP4460 specific temperature sensor register bit fields are added.
...and existing OMAP4 entries are renamed to OMAP4430.
Please be sure to describe thoroughly *all* the changes that are being
done in the changelog.
> Signed-off-by: Keerthy <j-keerthy@ti.com>
> Cc: tony@atomide.com
The script that auto-generates this data needs to be updated.
Please coordinate with Benoit to ensure the scripts are updated as well.
Kevin
^ permalink raw reply [flat|nested] 35+ messages in thread
end of thread, other threads:[~2011-08-29 21:52 UTC | newest]
Thread overview: 35+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-08-18 10:52 [RFC PATCH 0/6 V2] OMAP4: Temperature sensor driver Keerthy
2011-08-18 10:52 ` [RFC PATCH 1/6 V2] OMAP4: Clock: Associate clocks for OMAP temperature sensor Keerthy
2011-08-29 21:47 ` Kevin Hilman
2011-08-18 10:52 ` [RFC PATCH 2/6 V2] OMAP4: Adding the temperature sensor register set bit fields Keerthy
2011-08-29 21:52 ` Kevin Hilman
2011-08-18 10:52 ` [RFC PATCH 3/6 V2] OMAP4460: Temperature sensor data Keerthy
2011-08-18 11:32 ` Felipe Balbi
2011-08-18 10:52 ` [RFC PATCH 4/6 V2] OMAP4: Hwmod: OMAP temperature sensor Keerthy
2011-08-18 10:52 ` [RFC PATCH 5/6 V2] OMAP4: Temperature sensor device support Keerthy
2011-08-19 5:47 ` Todd Poynor
2011-08-18 10:52 ` [RFC PATCH 6/6 V2] hwmon: OMAP4: On die temperature sensor driver Keerthy
2011-08-18 11:37 ` Felipe Balbi
2011-08-18 11:37 ` [lm-sensors] [RFC PATCH 6/6 V2] hwmon: OMAP4: On die Felipe Balbi
2011-08-22 4:29 ` [RFC PATCH 6/6 V2] hwmon: OMAP4: On die temperature sensor driver J, KEERTHY
2011-08-22 4:41 ` [lm-sensors] [RFC PATCH 6/6 V2] hwmon: OMAP4: On die J, KEERTHY
2011-08-22 9:24 ` [RFC PATCH 6/6 V2] hwmon: OMAP4: On die temperature sensor driver Felipe Balbi
2011-08-22 9:24 ` [lm-sensors] [RFC PATCH 6/6 V2] hwmon: OMAP4: On die Felipe Balbi
2011-08-19 2:13 ` [RFC PATCH 6/6 V2] hwmon: OMAP4: On die temperature sensor driver Guenter Roeck
2011-08-19 2:13 ` [lm-sensors] [RFC PATCH 6/6 V2] hwmon: OMAP4: On die Guenter Roeck
2011-08-19 6:04 ` [RFC PATCH 6/6 V2] hwmon: OMAP4: On die temperature sensor driver J, KEERTHY
2011-08-19 6:16 ` [lm-sensors] [RFC PATCH 6/6 V2] hwmon: OMAP4: On die J, KEERTHY
2011-08-19 6:17 ` [RFC PATCH 6/6 V2] hwmon: OMAP4: On die temperature sensor driver Guenter Roeck
2011-08-19 6:17 ` [lm-sensors] [RFC PATCH 6/6 V2] hwmon: OMAP4: On die Guenter Roeck
2011-08-19 13:01 ` [RFC PATCH 6/6 V2] hwmon: OMAP4: On die temperature sensor driver J, KEERTHY
2011-08-19 13:13 ` [lm-sensors] [RFC PATCH 6/6 V2] hwmon: OMAP4: On die J, KEERTHY
2011-08-19 13:48 ` [RFC PATCH 6/6 V2] hwmon: OMAP4: On die temperature sensor driver Guenter Roeck
2011-08-19 13:48 ` [lm-sensors] [RFC PATCH 6/6 V2] hwmon: OMAP4: On die Guenter Roeck
2011-08-19 9:04 ` [RFC PATCH 6/6 V2] hwmon: OMAP4: On die temperature sensor driver J, KEERTHY
2011-08-19 9:16 ` [lm-sensors] [RFC PATCH 6/6 V2] hwmon: OMAP4: On die J, KEERTHY
2011-08-19 12:53 ` [RFC PATCH 6/6 V2] hwmon: OMAP4: On die temperature sensor driver J, KEERTHY
2011-08-19 12:55 ` [lm-sensors] [RFC PATCH 6/6 V2] hwmon: OMAP4: On die J, KEERTHY
2011-08-19 5:34 ` [RFC PATCH 6/6 V2] hwmon: OMAP4: On die temperature sensor driver Todd Poynor
2011-08-19 5:34 ` [lm-sensors] [RFC PATCH 6/6 V2] hwmon: OMAP4: On die Todd Poynor
2011-08-22 4:40 ` [RFC PATCH 6/6 V2] hwmon: OMAP4: On die temperature sensor driver J, KEERTHY
2011-08-22 4:52 ` [lm-sensors] [RFC PATCH 6/6 V2] hwmon: OMAP4: On die J, KEERTHY
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.