diff options
-rw-r--r-- | Documentation/thermal/exynos_thermal_emulation | 8 | ||||
-rw-r--r-- | drivers/thermal/Kconfig | 9 | ||||
-rw-r--r-- | drivers/thermal/exynos_thermal.c | 158 |
3 files changed, 67 insertions, 108 deletions
diff --git a/Documentation/thermal/exynos_thermal_emulation b/Documentation/thermal/exynos_thermal_emulation index b73bbfb697bb..36a3e79c1203 100644 --- a/Documentation/thermal/exynos_thermal_emulation +++ b/Documentation/thermal/exynos_thermal_emulation | |||
@@ -13,11 +13,11 @@ Thermal emulation mode supports software debug for TMU's operation. User can set | |||
13 | manually with software code and TMU will read current temperature from user value not from | 13 | manually with software code and TMU will read current temperature from user value not from |
14 | sensor's value. | 14 | sensor's value. |
15 | 15 | ||
16 | Enabling CONFIG_EXYNOS_THERMAL_EMUL option will make this support in available. | 16 | Enabling CONFIG_THERMAL_EMULATION option will make this support available. |
17 | When it's enabled, sysfs node will be created under | 17 | When it's enabled, sysfs node will be created as |
18 | /sys/bus/platform/devices/'exynos device name'/ with name of 'emulation'. | 18 | /sys/devices/virtual/thermal/thermal_zone'zone id'/emul_temp. |
19 | 19 | ||
20 | The sysfs node, 'emulation', will contain value 0 for the initial state. When you input any | 20 | The sysfs node, 'emul_node', will contain value 0 for the initial state. When you input any |
21 | temperature you want to update to sysfs node, it automatically enable emulation mode and | 21 | temperature you want to update to sysfs node, it automatically enable emulation mode and |
22 | current temperature will be changed into it. | 22 | current temperature will be changed into it. |
23 | (Exynos also supports user changable delay time which would be used to delay of | 23 | (Exynos also supports user changable delay time which would be used to delay of |
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index 9eddf744c94f..2a19120c32bd 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig | |||
@@ -117,15 +117,6 @@ config EXYNOS_THERMAL | |||
117 | If you say yes here you get support for TMU (Thermal Management | 117 | If you say yes here you get support for TMU (Thermal Management |
118 | Unit) on SAMSUNG EXYNOS series of SoC. | 118 | Unit) on SAMSUNG EXYNOS series of SoC. |
119 | 119 | ||
120 | config EXYNOS_THERMAL_EMUL | ||
121 | bool "EXYNOS TMU emulation mode support" | ||
122 | depends on EXYNOS_THERMAL | ||
123 | help | ||
124 | Exynos 4412 and 4414 and 5 series has emulation mode on TMU. | ||
125 | Enable this option will be make sysfs node in exynos thermal platform | ||
126 | device directory to support emulation mode. With emulation mode sysfs | ||
127 | node, you can manually input temperature to TMU for simulation purpose. | ||
128 | |||
129 | config DOVE_THERMAL | 120 | config DOVE_THERMAL |
130 | tristate "Temperature sensor on Marvell Dove SoCs" | 121 | tristate "Temperature sensor on Marvell Dove SoCs" |
131 | depends on ARCH_DOVE | 122 | depends on ARCH_DOVE |
diff --git a/drivers/thermal/exynos_thermal.c b/drivers/thermal/exynos_thermal.c index 541257888c3e..75bca0d6daf0 100644 --- a/drivers/thermal/exynos_thermal.c +++ b/drivers/thermal/exynos_thermal.c | |||
@@ -100,13 +100,13 @@ | |||
100 | #define IDLE_INTERVAL 10000 | 100 | #define IDLE_INTERVAL 10000 |
101 | #define MCELSIUS 1000 | 101 | #define MCELSIUS 1000 |
102 | 102 | ||
103 | #ifdef CONFIG_EXYNOS_THERMAL_EMUL | 103 | #ifdef CONFIG_THERMAL_EMULATION |
104 | #define EXYNOS_EMUL_TIME 0x57F0 | 104 | #define EXYNOS_EMUL_TIME 0x57F0 |
105 | #define EXYNOS_EMUL_TIME_SHIFT 16 | 105 | #define EXYNOS_EMUL_TIME_SHIFT 16 |
106 | #define EXYNOS_EMUL_DATA_SHIFT 8 | 106 | #define EXYNOS_EMUL_DATA_SHIFT 8 |
107 | #define EXYNOS_EMUL_DATA_MASK 0xFF | 107 | #define EXYNOS_EMUL_DATA_MASK 0xFF |
108 | #define EXYNOS_EMUL_ENABLE 0x1 | 108 | #define EXYNOS_EMUL_ENABLE 0x1 |
109 | #endif /* CONFIG_EXYNOS_THERMAL_EMUL */ | 109 | #endif /* CONFIG_THERMAL_EMULATION */ |
110 | 110 | ||
111 | /* CPU Zone information */ | 111 | /* CPU Zone information */ |
112 | #define PANIC_ZONE 4 | 112 | #define PANIC_ZONE 4 |
@@ -145,6 +145,7 @@ struct thermal_cooling_conf { | |||
145 | struct thermal_sensor_conf { | 145 | struct thermal_sensor_conf { |
146 | char name[SENSOR_NAME_LEN]; | 146 | char name[SENSOR_NAME_LEN]; |
147 | int (*read_temperature)(void *data); | 147 | int (*read_temperature)(void *data); |
148 | int (*write_emul_temp)(void *drv_data, unsigned long temp); | ||
148 | struct thermal_trip_point_conf trip_data; | 149 | struct thermal_trip_point_conf trip_data; |
149 | struct thermal_cooling_conf cooling_data; | 150 | struct thermal_cooling_conf cooling_data; |
150 | void *private_data; | 151 | void *private_data; |
@@ -349,6 +350,23 @@ static int exynos_get_temp(struct thermal_zone_device *thermal, | |||
349 | return 0; | 350 | return 0; |
350 | } | 351 | } |
351 | 352 | ||
353 | /* Get temperature callback functions for thermal zone */ | ||
354 | static int exynos_set_emul_temp(struct thermal_zone_device *thermal, | ||
355 | unsigned long temp) | ||
356 | { | ||
357 | void *data; | ||
358 | int ret = -EINVAL; | ||
359 | |||
360 | if (!th_zone->sensor_conf) { | ||
361 | pr_info("Temperature sensor not initialised\n"); | ||
362 | return -EINVAL; | ||
363 | } | ||
364 | data = th_zone->sensor_conf->private_data; | ||
365 | if (th_zone->sensor_conf->write_emul_temp) | ||
366 | ret = th_zone->sensor_conf->write_emul_temp(data, temp); | ||
367 | return ret; | ||
368 | } | ||
369 | |||
352 | /* Get the temperature trend */ | 370 | /* Get the temperature trend */ |
353 | static int exynos_get_trend(struct thermal_zone_device *thermal, | 371 | static int exynos_get_trend(struct thermal_zone_device *thermal, |
354 | int trip, enum thermal_trend *trend) | 372 | int trip, enum thermal_trend *trend) |
@@ -372,6 +390,7 @@ static struct thermal_zone_device_ops const exynos_dev_ops = { | |||
372 | .bind = exynos_bind, | 390 | .bind = exynos_bind, |
373 | .unbind = exynos_unbind, | 391 | .unbind = exynos_unbind, |
374 | .get_temp = exynos_get_temp, | 392 | .get_temp = exynos_get_temp, |
393 | .set_emul_temp = exynos_set_emul_temp, | ||
375 | .get_trend = exynos_get_trend, | 394 | .get_trend = exynos_get_trend, |
376 | .get_mode = exynos_get_mode, | 395 | .get_mode = exynos_get_mode, |
377 | .set_mode = exynos_set_mode, | 396 | .set_mode = exynos_set_mode, |
@@ -694,6 +713,47 @@ static int exynos_tmu_read(struct exynos_tmu_data *data) | |||
694 | return temp; | 713 | return temp; |
695 | } | 714 | } |
696 | 715 | ||
716 | #ifdef CONFIG_THERMAL_EMULATION | ||
717 | static int exynos_tmu_set_emulation(void *drv_data, unsigned long temp) | ||
718 | { | ||
719 | struct exynos_tmu_data *data = drv_data; | ||
720 | unsigned int reg; | ||
721 | int ret = -EINVAL; | ||
722 | |||
723 | if (data->soc == SOC_ARCH_EXYNOS4210) | ||
724 | goto out; | ||
725 | |||
726 | if (temp && temp < MCELSIUS) | ||
727 | goto out; | ||
728 | |||
729 | mutex_lock(&data->lock); | ||
730 | clk_enable(data->clk); | ||
731 | |||
732 | reg = readl(data->base + EXYNOS_EMUL_CON); | ||
733 | |||
734 | if (temp) { | ||
735 | temp /= MCELSIUS; | ||
736 | |||
737 | reg = (EXYNOS_EMUL_TIME << EXYNOS_EMUL_TIME_SHIFT) | | ||
738 | (temp_to_code(data, temp) | ||
739 | << EXYNOS_EMUL_DATA_SHIFT) | EXYNOS_EMUL_ENABLE; | ||
740 | } else { | ||
741 | reg &= ~EXYNOS_EMUL_ENABLE; | ||
742 | } | ||
743 | |||
744 | writel(reg, data->base + EXYNOS_EMUL_CON); | ||
745 | |||
746 | clk_disable(data->clk); | ||
747 | mutex_unlock(&data->lock); | ||
748 | return 0; | ||
749 | out: | ||
750 | return ret; | ||
751 | } | ||
752 | #else | ||
753 | static int exynos_tmu_set_emulation(void *drv_data, unsigned long temp) | ||
754 | { return -EINVAL; } | ||
755 | #endif/*CONFIG_THERMAL_EMULATION*/ | ||
756 | |||
697 | static void exynos_tmu_work(struct work_struct *work) | 757 | static void exynos_tmu_work(struct work_struct *work) |
698 | { | 758 | { |
699 | struct exynos_tmu_data *data = container_of(work, | 759 | struct exynos_tmu_data *data = container_of(work, |
@@ -727,6 +787,7 @@ static irqreturn_t exynos_tmu_irq(int irq, void *id) | |||
727 | static struct thermal_sensor_conf exynos_sensor_conf = { | 787 | static struct thermal_sensor_conf exynos_sensor_conf = { |
728 | .name = "exynos-therm", | 788 | .name = "exynos-therm", |
729 | .read_temperature = (int (*)(void *))exynos_tmu_read, | 789 | .read_temperature = (int (*)(void *))exynos_tmu_read, |
790 | .write_emul_temp = exynos_tmu_set_emulation, | ||
730 | }; | 791 | }; |
731 | 792 | ||
732 | #if defined(CONFIG_CPU_EXYNOS4210) | 793 | #if defined(CONFIG_CPU_EXYNOS4210) |
@@ -833,93 +894,6 @@ static inline struct exynos_tmu_platform_data *exynos_get_driver_data( | |||
833 | platform_get_device_id(pdev)->driver_data; | 894 | platform_get_device_id(pdev)->driver_data; |
834 | } | 895 | } |
835 | 896 | ||
836 | #ifdef CONFIG_EXYNOS_THERMAL_EMUL | ||
837 | static ssize_t exynos_tmu_emulation_show(struct device *dev, | ||
838 | struct device_attribute *attr, | ||
839 | char *buf) | ||
840 | { | ||
841 | struct platform_device *pdev = container_of(dev, | ||
842 | struct platform_device, dev); | ||
843 | struct exynos_tmu_data *data = platform_get_drvdata(pdev); | ||
844 | unsigned int reg; | ||
845 | u8 temp_code; | ||
846 | int temp = 0; | ||
847 | |||
848 | if (data->soc == SOC_ARCH_EXYNOS4210) | ||
849 | goto out; | ||
850 | |||
851 | mutex_lock(&data->lock); | ||
852 | clk_enable(data->clk); | ||
853 | reg = readl(data->base + EXYNOS_EMUL_CON); | ||
854 | clk_disable(data->clk); | ||
855 | mutex_unlock(&data->lock); | ||
856 | |||
857 | if (reg & EXYNOS_EMUL_ENABLE) { | ||
858 | reg >>= EXYNOS_EMUL_DATA_SHIFT; | ||
859 | temp_code = reg & EXYNOS_EMUL_DATA_MASK; | ||
860 | temp = code_to_temp(data, temp_code); | ||
861 | } | ||
862 | out: | ||
863 | return sprintf(buf, "%d\n", temp * MCELSIUS); | ||
864 | } | ||
865 | |||
866 | static ssize_t exynos_tmu_emulation_store(struct device *dev, | ||
867 | struct device_attribute *attr, | ||
868 | const char *buf, size_t count) | ||
869 | { | ||
870 | struct platform_device *pdev = container_of(dev, | ||
871 | struct platform_device, dev); | ||
872 | struct exynos_tmu_data *data = platform_get_drvdata(pdev); | ||
873 | unsigned int reg; | ||
874 | int temp; | ||
875 | |||
876 | if (data->soc == SOC_ARCH_EXYNOS4210) | ||
877 | goto out; | ||
878 | |||
879 | if (!sscanf(buf, "%d\n", &temp) || temp < 0) | ||
880 | return -EINVAL; | ||
881 | |||
882 | mutex_lock(&data->lock); | ||
883 | clk_enable(data->clk); | ||
884 | |||
885 | reg = readl(data->base + EXYNOS_EMUL_CON); | ||
886 | |||
887 | if (temp) { | ||
888 | /* Both CELSIUS and MCELSIUS type are available for input */ | ||
889 | if (temp > MCELSIUS) | ||
890 | temp /= MCELSIUS; | ||
891 | |||
892 | reg = (EXYNOS_EMUL_TIME << EXYNOS_EMUL_TIME_SHIFT) | | ||
893 | (temp_to_code(data, (temp / MCELSIUS)) | ||
894 | << EXYNOS_EMUL_DATA_SHIFT) | EXYNOS_EMUL_ENABLE; | ||
895 | } else { | ||
896 | reg &= ~EXYNOS_EMUL_ENABLE; | ||
897 | } | ||
898 | |||
899 | writel(reg, data->base + EXYNOS_EMUL_CON); | ||
900 | |||
901 | clk_disable(data->clk); | ||
902 | mutex_unlock(&data->lock); | ||
903 | |||
904 | out: | ||
905 | return count; | ||
906 | } | ||
907 | |||
908 | static DEVICE_ATTR(emulation, 0644, exynos_tmu_emulation_show, | ||
909 | exynos_tmu_emulation_store); | ||
910 | static int create_emulation_sysfs(struct device *dev) | ||
911 | { | ||
912 | return device_create_file(dev, &dev_attr_emulation); | ||
913 | } | ||
914 | static void remove_emulation_sysfs(struct device *dev) | ||
915 | { | ||
916 | device_remove_file(dev, &dev_attr_emulation); | ||
917 | } | ||
918 | #else | ||
919 | static inline int create_emulation_sysfs(struct device *dev) { return 0; } | ||
920 | static inline void remove_emulation_sysfs(struct device *dev) {} | ||
921 | #endif | ||
922 | |||
923 | static int exynos_tmu_probe(struct platform_device *pdev) | 897 | static int exynos_tmu_probe(struct platform_device *pdev) |
924 | { | 898 | { |
925 | struct exynos_tmu_data *data; | 899 | struct exynos_tmu_data *data; |
@@ -1019,10 +993,6 @@ static int exynos_tmu_probe(struct platform_device *pdev) | |||
1019 | goto err_clk; | 993 | goto err_clk; |
1020 | } | 994 | } |
1021 | 995 | ||
1022 | ret = create_emulation_sysfs(&pdev->dev); | ||
1023 | if (ret) | ||
1024 | dev_err(&pdev->dev, "Failed to create emulation mode sysfs node\n"); | ||
1025 | |||
1026 | return 0; | 996 | return 0; |
1027 | err_clk: | 997 | err_clk: |
1028 | platform_set_drvdata(pdev, NULL); | 998 | platform_set_drvdata(pdev, NULL); |
@@ -1034,8 +1004,6 @@ static int exynos_tmu_remove(struct platform_device *pdev) | |||
1034 | { | 1004 | { |
1035 | struct exynos_tmu_data *data = platform_get_drvdata(pdev); | 1005 | struct exynos_tmu_data *data = platform_get_drvdata(pdev); |
1036 | 1006 | ||
1037 | remove_emulation_sysfs(&pdev->dev); | ||
1038 | |||
1039 | exynos_tmu_control(pdev, false); | 1007 | exynos_tmu_control(pdev, false); |
1040 | 1008 | ||
1041 | exynos_unregister_thermal(); | 1009 | exynos_unregister_thermal(); |