diff options
author | kongxinwei <kong.kongxinwei@hisilicon.com> | 2015-02-06 20:18:32 +0800 |
---|---|---|
committer | kongxinwei <kong.kongxinwei@hisilicon.com> | 2015-02-06 20:18:32 +0800 |
commit | 5faa467ab23218e9b3ae10fc6ee58f9c9835125c (patch) | |
tree | 5dcd1a16f07162a342abfab990d7caf4c870166e | |
parent | ab1c6fb74a9cd111df246aeaeefd03731c0f9b44 (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.dtsi | 28 | ||||
-rw-r--r-- | arch/arm64/configs/defconfig | 4 | ||||
-rw-r--r-- | drivers/hwmon/Kconfig | 9 | ||||
-rw-r--r-- | drivers/hwmon/Makefile | 1 | ||||
-rw-r--r-- | drivers/hwmon/hi6220_tsensor.c | 431 | ||||
-rw-r--r-- | drivers/hwmon/hi6220_tsensor.h | 70 |
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 |