summaryrefslogtreecommitdiff
path: root/drivers/hwmon
diff options
context:
space:
mode:
authorsreenivasulu velpula <svelpula@nvidia.com>2013-02-18 11:30:25 +0530
committerDan Willemsen <dwillemsen@nvidia.com>2013-09-14 12:57:55 -0700
commit1bd331d4baf43f047ed41cfde9ac94aec8677fe6 (patch)
treee1160ff78e0622868e208d74586a97cbc259ba90 /drivers/hwmon
parentc37ce1de01dec8cc64cdfb458cbb2d98066708a4 (diff)
hwmon: Tegra Temperature Monitor driver
This driver monitors both local and remote temperatures at regular intervals. If there is certian degrees change in temperatures then it executes the callback registered for the platform. This driver assumes presence of on-board TMP411 sensor by TI. This driver also adds a sysfs interface to read current temperature of the remote hotspot and local temperature. Bug 1182410 Reviewed-on: http://git-master/r/190217 (cherry picked from commit fc1c166747f938f05e7491e7fb63e5c843549a95) Change-Id: I9789abe267cbabe05560c70e79a3cbbe4f73ccdb Signed-off-by: sreenivasulu velpula <svelpula@nvidia.com> Reviewed-on: http://git-master/r/201608 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Sumeet Gupta <sumeetg@nvidia.com> GVS: Gerrit_Virtual_Submit Reviewed-by: Laxman Dewangan <ldewangan@nvidia.com>
Diffstat (limited to 'drivers/hwmon')
-rw-r--r--drivers/hwmon/Kconfig11
-rw-r--r--drivers/hwmon/Makefile1
-rw-r--r--drivers/hwmon/tmon-tmp411.c353
3 files changed, 365 insertions, 0 deletions
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index e9cf548d8c15..35d8e9acf722 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -1322,6 +1322,17 @@ config SENSORS_VEXPRESS
the ARM Ltd's Versatile Express platform. It can provide wide
range of information like temperature, power, energy.
+config SENSORS_TMON_TMP411
+ tristate "Temperature Monitor driver"
+ depends on !SENSORS_TMP401
+ default n
+ help
+ This driver supports TMP411 sensor by TI. It is used to update
+ registers dynamically based on the curret temperature.
+
+ If you say yes here temperature monitor driver will monitor
+ temperature.
+
config SENSORS_VIA_CPUTEMP
tristate "VIA CPU temperature sensor"
depends on X86
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 5ab839e150c7..f73ea80f1553 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -148,6 +148,7 @@ obj-$(CONFIG_SENSORS_INA230) += ina230.o
obj-$(CONFIG_SENSORS_INA3221) += ina3221.o
CFLAGS_tegra-tsensor.o = -Werror
obj-$(CONFIG_SENSORS_TEGRA_TSENSOR) += tegra-tsensor.o
+obj-$(CONFIG_SENSORS_TMON_TMP411) += tmon-tmp411.o
obj-$(CONFIG_PMBUS) += pmbus/
diff --git a/drivers/hwmon/tmon-tmp411.c b/drivers/hwmon/tmon-tmp411.c
new file mode 100644
index 000000000000..8913a69d8a71
--- /dev/null
+++ b/drivers/hwmon/tmon-tmp411.c
@@ -0,0 +1,353 @@
+/*
+ * Temperature Monitor Driver
+ *
+ * Copyright (c) 2012-2013, NVIDIA CORPORATION. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * drivers/hwmon/tmon-tmp411.c
+ *
+ */
+
+/* Note: Copied temperature conversion code from tmp401 driver */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/workqueue.h>
+#include <linux/sysfs.h>
+#include <linux/platform_data/tmon_tmp411.h>
+
+#define STATUS_REG 0x02
+#define CONFIG_REG_READ 0x03
+#define CONFIG_REG_WRITE 0x09
+#define CONVERSION_REG_READ 0x04
+#define CONVERSION_REG_WRITE 0x0a
+#define TMON_OFFSET 0x40
+#define TMON_STANDBY_MASK 0x40
+
+#define MSB_LTEMP_REG 0x00
+#define LSB_LTEMP_REG 0x15
+#define MSB_RTEMP_REG 0x01
+#define LSB_RTEMP_REG 0x10
+#define CONFIG_RANGE 0x04
+
+#define DEFAULT_TMON_POLLING_TIME 2000 /* Time in ms */
+#define DEFAULT_TMON_DELTA_TEMP 4000 /* Temp. change to execute
+ platform callback */
+#define TMON_ERR INT_MAX
+#define TMON_NOCHANGE (INT_MAX - 1)
+
+struct tmon_info {
+ int mode;
+ struct i2c_client *client;
+ struct delayed_work tmon_work;
+ struct tmon_plat_data *pdata;
+};
+
+#define device_attr(type) \
+static struct device_attribute dev_attr_##type = { \
+ .attr = {.name = __stringify(type), \
+ .mode = S_IWUSR | S_IRUGO }, \
+ .show = show_##type, \
+}
+
+static int tmon_read(struct i2c_client *client, u8 reg, u8 *value)
+{
+ int tmp;
+
+ tmp = i2c_smbus_read_byte_data(client, reg);
+ if (tmp < 0)
+ return -EINVAL;
+
+ *value = tmp;
+
+ return 0;
+}
+
+static int tmon_to_temp(u16 reg, u8 config)
+{
+ int temp = reg;
+
+ if (config & CONFIG_RANGE)
+ temp -= 64 * 256;
+
+ return (temp * 625 + 80) / 160;
+}
+
+static int tmon_read_remote_temp(struct i2c_client *client,
+ int *ptemp)
+{
+ u8 config;
+ u8 temp;
+ int err;
+ int temperature = 0;
+ struct tmon_info *data = i2c_get_clientdata(client);
+
+ err = tmon_read(client, CONFIG_REG_READ, &config);
+ if (err)
+ return err;
+ err = tmon_read(client, MSB_RTEMP_REG, &temp);
+ if (err)
+ return err;
+ temperature = temp << 8;
+ err = tmon_read(client, LSB_RTEMP_REG, &temp);
+ if (err)
+ return err;
+ temperature |= temp;
+
+ if (data->pdata) {
+ *ptemp = tmon_to_temp(temperature, config) +
+ data->pdata->remote_offset;
+ } else {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int tmon_read_local_temp(struct i2c_client *client,
+ int *ptemp)
+{
+ u8 config;
+ u8 temp;
+ int err;
+ int temperature = 0;
+ err = tmon_read(client, CONFIG_REG_READ, &config);
+ if (err)
+ return err;
+ err = tmon_read(client, MSB_LTEMP_REG, &temp);
+ if (err)
+ return err;
+ temperature = temp << 8;
+ err = tmon_read(client, LSB_LTEMP_REG, &temp);
+ if (err)
+ return err;
+ temperature |= temp;
+
+ *ptemp = tmon_to_temp(temperature, config);
+
+ return 0;
+}
+
+static int tmon_check_local_temp(struct i2c_client *client,
+ u32 delta_temp)
+{
+ static int last_temp;
+ int err;
+ int curr_temp = 0;
+
+ err = tmon_read_local_temp(client, &curr_temp);
+ if (err)
+ return TMON_ERR;
+
+ if (abs(curr_temp - last_temp) >= delta_temp) {
+ last_temp = curr_temp;
+ return curr_temp;
+ }
+
+ return TMON_NOCHANGE;
+}
+
+static int tmon_rtemp_boundary_check(struct i2c_client *client,
+ int rtemp_low_boundary,
+ int rtemp_high_boundary, int *low_to_high)
+{
+ static int last_temp;
+ int err;
+ int curr_temp = 0;
+
+ *low_to_high = -1;
+
+ err = tmon_read_remote_temp(client, &curr_temp);
+ if (err)
+ return TMON_ERR;
+
+ /* If remote temp is changing from < rtemp_high_boundary
+ to >= rtemp_high_boundary. */
+ if (last_temp < rtemp_high_boundary &&
+ curr_temp >= rtemp_high_boundary)
+ *low_to_high = 1;
+
+ /* If remote temp is changing from >= rtemp_low_boundary
+ to < rtemp_low_boundary */
+ else if (last_temp >= rtemp_low_boundary &&
+ curr_temp < rtemp_low_boundary)
+ *low_to_high = 0;
+
+ if (*low_to_high == 0 || *low_to_high == 1) {
+ last_temp = curr_temp;
+ return curr_temp;
+ }
+
+ return TMON_NOCHANGE;
+}
+
+static ssize_t show_remote_temp(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ int temperature = 0;
+
+ tmon_read_remote_temp(to_i2c_client(dev), &temperature);
+
+ return sprintf(buf, "%d\n", temperature);
+}
+
+static ssize_t show_local_temp(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ int temperature = 0;
+
+ tmon_read_local_temp(to_i2c_client(dev), &temperature);
+
+ return sprintf(buf, "%d\n", temperature);
+}
+
+device_attr(remote_temp);
+device_attr(local_temp);
+
+static void tmon_update(struct work_struct *work)
+{
+ int ret;
+ int low_to_high;
+ struct tmon_info *tmon_data =
+ container_of(to_delayed_work(work),
+ struct tmon_info,
+ tmon_work);
+ struct tmon_plat_data *pdata = tmon_data->pdata;
+
+ if (pdata->delta_time <= 0)
+ pdata->delta_time = DEFAULT_TMON_POLLING_TIME;
+
+ if (pdata->delta_temp <= 0)
+ pdata->delta_temp = DEFAULT_TMON_DELTA_TEMP;
+
+ ret =
+ tmon_check_local_temp(tmon_data->client,
+ pdata->delta_temp);
+
+ if (ret != TMON_ERR && ret != TMON_NOCHANGE &&
+ pdata->ltemp_dependent_reg_update)
+ pdata->ltemp_dependent_reg_update(ret);
+
+ ret =
+ tmon_rtemp_boundary_check(tmon_data->client,
+ pdata->rtemp_low_boundary,
+ pdata->rtemp_high_boundary,
+ &low_to_high);
+
+ if (ret != TMON_ERR && ret != TMON_NOCHANGE &&
+ pdata->rtemp_dependent_reg_update)
+ pdata->rtemp_dependent_reg_update(ret, low_to_high);
+
+ schedule_delayed_work(&tmon_data->tmon_work,
+ msecs_to_jiffies(pdata->delta_time));
+}
+
+static int __devinit tmon_tmp411_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int err;
+ struct tmon_plat_data *tmon_pdata =
+ client->dev.platform_data;
+ struct tmon_info *data;
+
+ if (tmon_pdata == NULL) {
+ dev_err(&client->dev, "no platform data\n");
+ return -EINVAL;
+ }
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_BYTE_DATA)) {
+ dev_err(&client->dev, "insufficient functionality!\n");
+ return -ENODEV;
+ }
+
+ data = kzalloc(sizeof(struct tmon_info), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->client = client;
+ i2c_set_clientdata(client, data);
+ data->pdata = tmon_pdata;
+
+ err = device_create_file(&client->dev, &dev_attr_local_temp);
+ if (err) {
+ kfree(data);
+ return err;
+ }
+
+ err = device_create_file(&client->dev, &dev_attr_remote_temp);
+ if (err) {
+ kfree(data);
+ device_remove_file(&client->dev, &dev_attr_local_temp);
+ return err;
+ }
+
+ INIT_DELAYED_WORK(&data->tmon_work, tmon_update);
+
+ schedule_delayed_work(&data->tmon_work,
+ msecs_to_jiffies(data->pdata->delta_time));
+
+ dev_info(&client->dev, "golden_reg enabled\n");
+ return 0;
+}
+
+static int __devexit tmon_tmp411_remove(struct i2c_client *client)
+{
+ struct tmon_info *data = i2c_get_clientdata(client);
+
+ cancel_delayed_work(&data->tmon_work);
+ device_remove_file(&client->dev, &dev_attr_remote_temp);
+ device_remove_file(&client->dev, &dev_attr_local_temp);
+
+ kfree(data);
+
+ return 0;
+}
+
+/* tmon-tmp411 driver struct */
+static const struct i2c_device_id tmon_tmp411_id[] = {
+ {"tmon-tmp411-sensor", 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, tmon_tmp411_id);
+
+static struct i2c_driver tmon_tmp411_driver = {
+ .driver = {
+ .name = "tmon-tmp411-sensor",
+ },
+ .probe = tmon_tmp411_probe,
+ .remove = __devexit_p(tmon_tmp411_remove),
+ .id_table = tmon_tmp411_id,
+};
+
+static int __init tmon_tmp411_module_init(void)
+{
+ return i2c_add_driver(&tmon_tmp411_driver);
+}
+
+static void __exit tmon_tmp411_module_exit(void)
+{
+ i2c_del_driver(&tmon_tmp411_driver);
+}
+
+module_init(tmon_tmp411_module_init);
+module_exit(tmon_tmp411_module_exit);
+
+MODULE_AUTHOR("Manoj Chourasia <mchourasia@nvidia.com>");
+MODULE_DESCRIPTION("Temperature Monitor module");
+MODULE_LICENSE("GPL");