aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorkongxinwei <kong.kongxinwei@hisilicon.com>2015-02-06 20:18:32 +0800
committerkongxinwei <kong.kongxinwei@hisilicon.com>2015-02-06 20:18:32 +0800
commit5faa467ab23218e9b3ae10fc6ee58f9c9835125c (patch)
tree5dcd1a16f07162a342abfab990d7caf4c870166e
parentab1c6fb74a9cd111df246aeaeefd03731c0f9b44 (diff)
Tsensor: temperature sensor is enable and use the mainline framework
for hikey board,when the temperature is too high,the systerm will reboot or crash it Signed-off-by: kongxinwei <kong.kongxinwei@hisilicon.com>
-rw-r--r--arch/arm64/boot/dts/hi6220.dtsi28
-rw-r--r--arch/arm64/configs/defconfig4
-rw-r--r--drivers/hwmon/Kconfig9
-rw-r--r--drivers/hwmon/Makefile1
-rw-r--r--drivers/hwmon/hi6220_tsensor.c431
-rw-r--r--drivers/hwmon/hi6220_tsensor.h70
6 files changed, 543 insertions, 0 deletions
diff --git a/arch/arm64/boot/dts/hi6220.dtsi b/arch/arm64/boot/dts/hi6220.dtsi
index 6631ffdf421e..ba37864ed4af 100644
--- a/arch/arm64/boot/dts/hi6220.dtsi
+++ b/arch/arm64/boot/dts/hi6220.dtsi
@@ -1101,4 +1101,32 @@
g-tx-fifo-size = <128>;
interrupts = <0 77 0x4>;
};
+
+ thermal {
+ compatible = "hisilicon,hisi-tsensor-driver";
+ reg = <0x0 0xf7030000 0x0 0x1000>;
+ tsensor-enable = <1>;
+ tsensor-num = <3>;
+
+ acpu_c1: temp@a1 {
+ compatible = "hisilicon,hisi-tsensor0";
+ tsensor-type = <0x1>; /*acpu_c0:0; acpu_c1:1; gpu:2*/
+ tsensor-sel = <1>; /*sensor select value*/
+ tsensor-reset-value = <80>; /*temperatur to reset soc*/
+ };
+
+ acpu_c0: temp@a2 {
+ compatible = "hisilicon,hisi-tsensor1";
+ tsensor-type = <0x0>; /*acpu_c0:0; acpu_c1:1; gpu:2*/
+ tsensor-sel = <2>; /*sensor select value*/
+ tsensor-reset-value = <80>; /*temperatur to reset soc*/
+ };
+
+ gpu: temp@a3 {
+ compatible = "hisilicon,hisi-tsensor2";
+ tsensor-type = <0x2>; /*acpu_c0:0; acpu_c1:1; gpu:2*/
+ tsensor-sel = <3>; /*sensor select value*/
+ tsensor-reset-value = <80>; /*temperatur to reset soc*/
+ };
+ };
};
diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig
index 6f1e9bf7172e..df4706b661ce 100644
--- a/arch/arm64/configs/defconfig
+++ b/arch/arm64/configs/defconfig
@@ -225,6 +225,7 @@ CONFIG_SERIAL_AMBA_PL011_CONSOLE=y
CONFIG_SERIAL_OF_PLATFORM=y
CONFIG_VIRTIO_CONSOLE=y
# CONFIG_HW_RANDOM is not set
+
CONFIG_I2C_CHARDEV=y
CONFIG_I2C_DESIGNWARE_PLATFORM=y
CONFIG_SPI=y
@@ -364,3 +365,6 @@ CONFIG_CRYPTO_AES_ARM64_CE_BLK=y
CONFIG_CRYPTO_AES_ARM64_NEON_BLK=y
CONFIG_CRC_CCITT=y
CONFIG_CRC_T10DIF=y
+
+CONFIG_HWMON=y
+CONFIG_SENSORS_HI6220=y
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 5286d7ce1f9e..5035035e65ad 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -24,6 +24,15 @@ menuconfig HWMON
if HWMON
+config SENSORS_HI6220
+ tristate "Hisilicon Devices HI6220 SENSOR"
+ help
+ If you say yes here you get support for the Hisilicon Devices
+ HI6220 temperature sensors.
+
+ This driver can also be built as a module. If so, the module
+ will be called HI6220_SENSOR.
+
config HWMON_VID
tristate
default n
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index c90a7611efaa..86efd4ee384e 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -153,6 +153,7 @@ obj-$(CONFIG_SENSORS_W83L785TS) += w83l785ts.o
obj-$(CONFIG_SENSORS_W83L786NG) += w83l786ng.o
obj-$(CONFIG_SENSORS_WM831X) += wm831x-hwmon.o
obj-$(CONFIG_SENSORS_WM8350) += wm8350-hwmon.o
+obj-$(CONFIG_SENSORS_HI6220) += hi6220_tsensor.o
obj-$(CONFIG_PMBUS) += pmbus/
diff --git a/drivers/hwmon/hi6220_tsensor.c b/drivers/hwmon/hi6220_tsensor.c
new file mode 100644
index 000000000000..0fb449224b05
--- /dev/null
+++ b/drivers/hwmon/hi6220_tsensor.c
@@ -0,0 +1,431 @@
+/*
+ * Hisilicon Terminal SoCs drm driver
+ *
+ * Copyright (c) 2014-2015 Hisilicon Limited.
+ * Author:
+ *
+ * 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.
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/of.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#ifdef CONFIG_DEBUG_FS
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/uaccess.h>
+#endif
+
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include "hi6220_tsensor.h"
+
+#define DRIVER_NAME "hisi-tsensor"
+
+static struct efuse_trim efuse_trim_data = {0, 0, 0, 0, 0};
+
+static void tsensor_init_config(struct tsensor_devinfo *sensor_info)
+{
+ writel(SOC_PERI_SCTRL_CLKEN3_DATA, sensor_info->virt_base_addr +
+ SOC_PERI_SCTRL_CLKEN3_ADDR);
+ writel(0x00, sensor_info->virt_base_addr
+ + SOC_PERI_SCTRL_TEMP0_CFG_ADDR);
+}
+
+static
+void tsensor_config_set(unsigned int index, struct tsensor_devinfo *sensor_info)
+{
+ unsigned int temp_cfg;
+
+ temp_cfg = sensor_info->sensor_config[index].sel
+ << SOC_TSENSOR_SCTRL_TEMP0_CFG;
+
+ writel(0x0, sensor_info->virt_base_addr
+ + SOC_PERI_SCTRL_TEMP0_RST_MSK_ADDR);
+ writel(0x0, sensor_info->virt_base_addr
+ + SOC_PERI_SCTRL_TEMP0_EN_ADDR);
+
+ writel(temp_cfg, sensor_info->virt_base_addr
+ + SOC_PERI_SCTRL_TEMP0_CFG_ADDR);
+
+ writel(sensor_info->sensor_config[index].reset_cfg_value,
+ sensor_info->virt_base_addr + SOC_PERI_SCTRL_TEMP0_RST_TH_ADDR);
+
+ writel(SOC_TSENSOR_TEMP0_RST_MSK, sensor_info->virt_base_addr
+ + SOC_PERI_SCTRL_TEMP0_RST_MSK_ADDR);
+
+ writel(SOC_TSENSOR_TEMP0_EN, sensor_info->virt_base_addr
+ + SOC_PERI_SCTRL_TEMP0_EN_ADDR);
+}
+
+static int tsensor_temp_read_by_index(unsigned int index,
+ struct tsensor_devinfo *sensor_info)
+{
+ int regvalue = 0;
+ int i = 0;
+
+ mutex_lock(&sensor_info->get_tmp_lock);
+
+ tsensor_config_set(index, sensor_info);
+ mdelay(3);
+
+ for (i = 0; i < TSENSOR_READ_TEMP_COUNT; i++) {
+ regvalue += readl(sensor_info->virt_base_addr
+ + SOC_PERI_SCTRL_SC_TEMP0_VALUE_ADDR);
+ udelay(50);
+ }
+
+ regvalue = regvalue/TSENSOR_READ_TEMP_COUNT;
+
+ switch (sensor_info->sensor_config[index].sensor_type) {
+
+ case TSENSOR_TYPE_ACPU_CLUSTER0:
+ sensor_info->temperature = ((regvalue * 200) / 255) - 60
+ - efuse_trim_data.remote_acpu_c0;
+ break;
+
+ case TSENSOR_TYPE_ACPU_CLUSTER1:
+ sensor_info->temperature = ((regvalue * 200) / 255) - 60
+ - efuse_trim_data.remote_acpu_c1;
+ break;
+
+ case TSENSOR_TYPE_GPU:
+ sensor_info->temperature = ((regvalue * 200) / 255) - 60
+ - efuse_trim_data.remote_gpu;
+ break;
+
+ default:
+ break;
+ }
+
+ mutex_unlock(&sensor_info->get_tmp_lock);
+
+ return TSENSOR_OK;
+}
+
+static unsigned int tsensor_get_index_by_type(int tsensor_type,
+ struct tsensor_devinfo *sensor_info)
+{
+ unsigned int i = 0;
+
+ for (i = 0; i < sensor_info->num; i++) {
+ if (sensor_info->sensor_config[i].sensor_type == tsensor_type)
+ return i;
+ }
+
+ return TSENSOR_INVALID_INDEX;
+}
+
+int tsensor_temp_get(int tsensor_type,
+ struct tsensor_devinfo *sensor_info)
+{
+ unsigned int index = 0;
+ int ret = TSENSOR_ERR;
+
+ index = tsensor_get_index_by_type(tsensor_type, sensor_info);
+ if (TSENSOR_INVALID_INDEX == index) {
+ pr_err("get tsensor_type is error\n");
+ return TSENSOR_ERR;
+ }
+
+ ret = tsensor_temp_read_by_index(index, sensor_info);
+ if (TSENSOR_OK != ret) {
+ pr_err("tsensor_temp_get read temp error\n");
+ return TSENSOR_ERR;
+ }
+
+ return ret;
+}
+
+static ssize_t show_acpu_c0_temp(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct tsensor_devinfo *sensor_info = dev->driver_data;
+ int ret = 0;
+
+ ret = tsensor_temp_get(TSENSOR_TYPE_ACPU_CLUSTER0, sensor_info);
+ if (TSENSOR_OK != ret)
+ return sprintf(buf, "Error\n");
+
+ return snprintf(buf, sizeof(int), "%d\n", sensor_info->temperature);
+}
+
+static ssize_t show_acpu_c1_temp(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct tsensor_devinfo *sensor_info = dev->driver_data;
+ int ret = 0;
+
+ ret = tsensor_temp_get(TSENSOR_TYPE_ACPU_CLUSTER1, sensor_info);
+ if (TSENSOR_OK != ret)
+ return sprintf(buf, "Error\n");
+
+ return snprintf(buf, sizeof(int), "%d\n", sensor_info->temperature);
+}
+
+static ssize_t show_gpu_temp(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct tsensor_devinfo *sensor_info = dev->driver_data;
+ int ret = 0;
+
+ ret = tsensor_temp_get(TSENSOR_TYPE_GPU, sensor_info);
+ if (TSENSOR_OK != ret)
+ return sprintf(buf, "Error\n");
+
+ return snprintf(buf, sizeof(int), "%d\n", sensor_info->temperature);
+}
+
+static SENSOR_DEVICE_ATTR(acpu_c0_temp, S_IRUGO
+ | S_IWUSR, show_acpu_c0_temp, 0, 0);
+static SENSOR_DEVICE_ATTR(acpu_c1_temp, S_IRUGO
+ | S_IWUSR, show_acpu_c1_temp, 0, 0);
+static SENSOR_DEVICE_ATTR(gpu_temp, S_IRUGO
+ | S_IWUSR, show_gpu_temp, 0, 0);
+
+static struct attribute *tsensor_attrs[] = {
+ &sensor_dev_attr_acpu_c0_temp.dev_attr.attr,
+ &sensor_dev_attr_acpu_c1_temp.dev_attr.attr,
+ &sensor_dev_attr_gpu_temp.dev_attr.attr,
+ NULL
+};
+ATTRIBUTE_GROUPS(tsensor);
+
+static void efuse_trim_init(void)
+{
+ efuse_trim_data.local = -5;
+ efuse_trim_data.remote_acpu_c0 = -5;
+ efuse_trim_data.remote_acpu_c1 = -5;
+ efuse_trim_data.remote_gpu = -5;
+}
+
+static void tsensor_init(struct tsensor_devinfo *sensor_info)
+{
+ int i;
+
+ efuse_trim_init();
+ tsensor_init_config(sensor_info);
+
+ for (i = 0; i < sensor_info->num; i++) {
+ switch (sensor_info->sensor_config[i].sensor_type) {
+ case TSENSOR_TYPE_ACPU_CLUSTER0:
+ sensor_info->sensor_config[i].reset_cfg_value =
+ ((sensor_info->sensor_config[i].reset_value + 60
+ + efuse_trim_data.remote_acpu_c0)*255/200);
+ sensor_info->sensor_config[i].reset_cfg_value =
+ (sensor_info->sensor_config[i].reset_cfg_value > 254)
+ ? 254:sensor_info->sensor_config[i].reset_cfg_value;
+ break;
+
+ case TSENSOR_TYPE_ACPU_CLUSTER1:
+ sensor_info->sensor_config[i].reset_cfg_value =
+ ((sensor_info->sensor_config[i].reset_value + 60
+ + efuse_trim_data.remote_acpu_c1)*255/200);
+ sensor_info->sensor_config[i].reset_cfg_value =
+ (sensor_info->sensor_config[i].reset_cfg_value > 254)
+ ? 254:sensor_info->sensor_config[i].reset_cfg_value;
+ break;
+
+ case TSENSOR_TYPE_GPU:
+ sensor_info->sensor_config[i].reset_cfg_value =
+ ((sensor_info->sensor_config[i].reset_value + 60
+ + efuse_trim_data.remote_gpu)*255/200);
+ sensor_info->sensor_config[i].reset_cfg_value =
+ (sensor_info->sensor_config[i].reset_cfg_value > 254)
+ ? 254:sensor_info->sensor_config[i].reset_cfg_value;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+}
+
+static int tsensor_dts_parse(struct platform_device *pdev,
+ struct tsensor_devinfo *sensor_info)
+{
+ const struct device_node *of_node = pdev->dev.of_node;
+ struct device_node *root;
+ u32 dts_value = 0;
+ char *buf = NULL;
+ int index = 0;
+ int ret = -1;
+
+ ret = of_property_read_u32(of_node, "tsensor-enable", &dts_value);
+ if (ret) {
+ pr_err("no tsensor-enable in DTS!\n");
+ return ret;
+ }
+ sensor_info->enable = dts_value;
+
+ ret = of_property_read_u32(of_node, "tsensor-num", &dts_value);
+ if (ret) {
+ pr_err("no tsensor-num in DTS!\n");
+ return ret;
+ }
+ sensor_info->num = dts_value;
+
+ sensor_info->sensor_config = devm_kzalloc(&pdev->dev,
+ (sizeof(struct sensor_config)*sensor_info->num), GFP_KERNEL);
+ if (!sensor_info->sensor_config)
+ ret = -ENOMEM;
+
+ memset((void *)sensor_info->sensor_config, 0,
+ (sizeof(struct sensor_config)*sensor_info->num));
+
+ buf = devm_kzalloc(&pdev->dev, 32, GFP_KERNEL);
+ if (!buf) {
+ ret = -ENOMEM;
+ return ret;
+ }
+
+ for (index = 0; index < sensor_info->num; index++) {
+ sprintf(buf, "hisilicon,hisi-tsensor%d", index);
+ root = of_find_compatible_node(NULL, NULL, buf);
+ if (!root) {
+ ret = -ENOMEM;
+ return ret;
+ }
+
+ ret = of_property_read_u32_array(root,
+ "tsensor-type", &dts_value, 1);
+ if (ret) {
+ pr_err("no tsensor-type in tsensor\n");
+ return ret;
+ }
+
+ sensor_info->sensor_config[index].sensor_type = dts_value;
+
+ ret = of_property_read_u32_array(root,
+ "tsensor-sel", &dts_value, 1);
+ if (ret) {
+ pr_err("no tsensor-sel in tsensor\n");
+ return ret;
+ }
+ sensor_info->sensor_config[index].sel = dts_value;
+
+ ret = of_property_read_u32_array(root,
+ "tsensor-reset-value", &dts_value, 1);
+ if (ret) {
+ pr_err("no tsensor-reset-value in tsensor\n");
+ return ret;
+ }
+ sensor_info->sensor_config[index].reset_value = dts_value;
+ }
+ return ret;
+
+}
+
+static void tsensor_monitor_work_fn(struct work_struct *work)
+{
+ int index;
+ int ret;
+ struct tsensor_devinfo *sensor_info = container_of(work,
+ struct tsensor_devinfo, tsensor_monitor_work.work);
+
+ for (index = 0; index < sensor_info->num; index++) {
+ ret = tsensor_temp_get(index, sensor_info);
+ if (TSENSOR_OK != ret)
+ pr_err("monitor temperature and get it is Error\n");
+ }
+
+ schedule_delayed_work(&sensor_info->tsensor_monitor_work,
+ msecs_to_jiffies(sensor_info->average_period));
+
+}
+
+static int hisi_tsensor_probe(struct platform_device *pdev)
+{
+ struct tsensor_devinfo *sensor_info = NULL;
+ struct device *hwmon_dev;
+ struct resource *res;
+ int ret;
+
+ pr_info("hisi_tsensor_probe enter\n");
+
+ sensor_info = devm_kzalloc(&pdev->dev,
+ sizeof(struct tsensor_devinfo), GFP_KERNEL);
+ if (!sensor_info)
+ return TSENSOR_ERR;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ sensor_info->virt_base_addr = devm_ioremap_resource(&pdev->dev, res);
+ if (!sensor_info->virt_base_addr) {
+ pr_err("tsensor baseaddr ioremap error.\n");
+ return TSENSOR_ERR;
+ }
+
+ ret = tsensor_dts_parse(pdev, sensor_info);
+ if (ret) {
+ pr_err("tsensor dts parse error.\n");
+ return ret;
+ }
+
+ tsensor_init(sensor_info);
+
+ mutex_init(&sensor_info->get_tmp_lock);
+ sensor_info->pdev = pdev;
+ sensor_info->dev = &pdev->dev;
+
+ INIT_DELAYED_WORK(&sensor_info->tsensor_monitor_work,
+ tsensor_monitor_work_fn);
+
+ platform_set_drvdata(pdev, sensor_info);
+
+ hwmon_dev = devm_hwmon_device_register_with_groups(&pdev->dev,
+ "hw_tsensor", sensor_info, tsensor_groups);
+ if (IS_ERR(hwmon_dev))
+ return PTR_ERR(hwmon_dev);
+
+ schedule_delayed_work(&sensor_info->tsensor_monitor_work,
+ msecs_to_jiffies(sensor_info->average_period));
+
+ pr_info("hisi_tsensor_probe success\n");
+
+ return ret;
+}
+
+static struct of_device_id tsensors_match[] = {
+ { .compatible = "hisilicon,hisi-tsensor-driver" },
+ { }
+};
+
+static struct platform_driver hisi_tsensor_driver = {
+ .probe = hisi_tsensor_probe,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = DRIVER_NAME,
+ .of_match_table = tsensors_match,
+ },
+};
+
+static int __init hisi_tsensor_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&hisi_tsensor_driver);
+ if (ret < 0) {
+ pr_err("hisi platform_driver_register is fail.\n");
+ return ret;
+ }
+
+ return ret;
+}
+
+static void __exit hisi_tsensor_exit(void)
+{
+ platform_driver_unregister(&hisi_tsensor_driver);
+}
+
+late_initcall(hisi_tsensor_init);
+module_exit(hisi_tsensor_exit);
+
+MODULE_DESCRIPTION("Hisi Tsensor Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("<kong.kongxinwei@hisilicon.com>");
diff --git a/drivers/hwmon/hi6220_tsensor.h b/drivers/hwmon/hi6220_tsensor.h
new file mode 100644
index 000000000000..a3ef35f9d4af
--- /dev/null
+++ b/drivers/hwmon/hi6220_tsensor.h
@@ -0,0 +1,70 @@
+/*
+ * Hisilicon Terminal SoCs Tsensor driver
+ *
+ * Copyright (c) 2014-2015 Hisilicon Limited.
+ * Author:
+ *
+ * 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.
+ *
+ */
+
+#ifndef __HISI_HI6220_SENSOR_H__
+#define __HISI_HI6220_SENSOR_H__
+
+#define SOC_PERI_SCTRL_CLKEN3_ADDR (0x230)
+#define SOC_PERI_SCTRL_TEMP0_CFG_ADDR (0x70C)
+#define SOC_PERI_SCTRL_TEMP0_RST_MSK_ADDR (0x71C)
+#define SOC_PERI_SCTRL_TEMP0_EN_ADDR (0x710)
+#define SOC_PERI_SCTRL_TEMP0_RST_TH_ADDR (0x708)
+#define SOC_PERI_SCTRL_SC_TEMP0_EN_ADDR (0x710)
+#define SOC_PERI_SCTRL_SC_TEMP0_VALUE_ADDR (0x728)
+
+#define SOC_PERI_SCTRL_CLKEN3_DATA (1<<12)
+#define SOC_TSENSOR_SCTRL_TEMP0_CFG (12)
+#define SOC_TSENSOR_TEMP0_EN (1)
+#define SOC_TSENSOR_TEMP0_RST_MSK (1)
+
+#define TSENSOR_OK (0)
+#define TSENSOR_ERR (-1)
+#define TSENSOR_INVALID_INDEX (255)
+#define TSENSOR_READ_TEMP_COUNT (3)
+
+#define TSENSOR_TYPE_ACPU_CLUSTER0 0
+#define TSENSOR_TYPE_ACPU_CLUSTER1 1
+#define TSENSOR_TYPE_GPU 2
+
+struct sensor_config {
+ unsigned int sensor_type;
+ unsigned int sel;
+ int reset_value;
+ int reset_cfg_value;
+};
+
+struct tsensor_devinfo {
+ u8 __iomem *virt_base_addr;
+
+ int temperature;
+ unsigned int enable;
+ unsigned int num;
+
+ int average_period;
+ struct delayed_work tsensor_monitor_work;
+ struct mutex get_tmp_lock;
+
+ struct sensor_config *sensor_config;
+ struct platform_device *pdev;
+ struct device *dev;
+};
+
+struct efuse_trim {
+ unsigned int trimdata;
+
+ int local;
+ int remote_acpu_c0;
+ int remote_acpu_c1;
+ int remote_gpu;
+};
+
+#endif