diff options
| -rw-r--r-- | Documentation/devicetree/bindings/misc/therm_est.txt | 90 | ||||
| -rw-r--r-- | drivers/misc/therm_fan_est.c | 152 | ||||
| -rw-r--r-- | include/linux/therm_est.h | 2 |
3 files changed, 225 insertions, 19 deletions
diff --git a/Documentation/devicetree/bindings/misc/therm_est.txt b/Documentation/devicetree/bindings/misc/therm_est.txt index 0dbcb147a..0538981a7 100644 --- a/Documentation/devicetree/bindings/misc/therm_est.txt +++ b/Documentation/devicetree/bindings/misc/therm_est.txt | |||
| @@ -6,6 +6,8 @@ Properties : | |||
| 6 | - tc1 : Coefficient 1 for thermal trend calculation. | 6 | - tc1 : Coefficient 1 for thermal trend calculation. |
| 7 | - tc2 : Coefficient 2 for thermal trend calculation. | 7 | - tc2 : Coefficient 2 for thermal trend calculation. |
| 8 | - node for subdev : Node for subdevice information. Required. | 8 | - node for subdev : Node for subdevice information. Required. |
| 9 | - use_tmargin : if tmargin algorithm should be used for calculating the | ||
| 10 | effective temp. Refer Tmargin section for more info. | ||
| 9 | 11 | ||
| 10 | Properties in subdev node : Required. | 12 | Properties in subdev node : Required. |
| 11 | - subdev_names : list of strings. It contains list of the name of the therm | 13 | - subdev_names : list of strings. It contains list of the name of the therm |
| @@ -45,3 +47,91 @@ Example: | |||
| 45 | }; | 47 | }; |
| 46 | }; | 48 | }; |
| 47 | }; | 49 | }; |
| 50 | |||
| 51 | Tmargin Algorithm: | ||
| 52 | The native max temp algorithm lacks support for accomodating multiple thermal | ||
| 53 | fan curves. Tmargin algorithm solves this by calculating the effective | ||
| 54 | temperature as a difference from critical temperature of the therm fan est | ||
| 55 | group. This allows us to take a union of two fan curves and satisfy the needs | ||
| 56 | of both the therm-fan-est groups. | ||
| 57 | The algorithm use the effective temperature(crit_temp - sensor_reading) to | ||
| 58 | drive the cooling device. | ||
| 59 | |||
| 60 | Properties rules for therm-fan-est dt entry: | ||
| 61 | - use_tmargin : To use the tmargin feature. | ||
| 62 | - profiles : New profile tmargin should be defined and chosen as default. | ||
| 63 | * The trip temperatures need to be given only in ascending order for the | ||
| 64 | thermal framework to register therm-fan-est zone. Hence, the dt values | ||
| 65 | for active trip temps should be given in ascending values of tmargin | ||
| 66 | values. | ||
| 67 | * Hysterysis is used only in cooling scenario. Since tmargin is | ||
| 68 | considered as a diff wrt crit_Temp, hysterysis values should be taken | ||
| 69 | as -ve values. | ||
| 70 | |||
| 71 | Properties rules for fan dt entry: | ||
| 72 | - profiles : New profile for tmargin should be defined and chosen as default. | ||
| 73 | * The trip temps are considered in a ascending order of tmargin temps. | ||
| 74 | Hence, the pwm values should be considered in a descending order of | ||
| 75 | pwms. | ||
| 76 | * active_steps - The tmargin trip values are considered in the ascending | ||
| 77 | order(hence actual trip temps will be in descending order), so the | ||
| 78 | pwm mapping should be in the reverse order starting from 255 for | ||
| 79 | index 0. | ||
| 80 | |||
| 81 | Example : | ||
| 82 | |||
| 83 | thermal-fan-est { | ||
| 84 | compatible = "thermal-fan-est"; | ||
| 85 | name = "thermal-fan-est"; | ||
| 86 | status = "okay"; | ||
| 87 | num_resources = <0>; | ||
| 88 | shared_data = <&thermal_fan_est_shared_data>; | ||
| 89 | trip_length = <10>; | ||
| 90 | use_tmargin; | ||
| 91 | |||
| 92 | profiles { | ||
| 93 | default = "tmargin"; | ||
| 94 | quiet { | ||
| 95 | active_trip_temps = <0 50000 63000 72000 81000 | ||
| 96 | 140000 150000 160000 170000 180000>; | ||
| 97 | active_hysteresis = <0 18000 8000 8000 8000 | ||
| 98 | 0 0 0 0 0>; | ||
| 99 | }; | ||
| 100 | cool { | ||
| 101 | active_trip_temps = <0 35000 53000 62000 73000 | ||
| 102 | 140000 150000 160000 170000 180000>; | ||
| 103 | active_hysteresis = <0 9000 8000 8000 9000 | ||
| 104 | 0 0 0 0 0>; | ||
| 105 | }; | ||
| 106 | tmargin { | ||
| 107 | active_trip_temps = <0 10000 15000 25000 35000 | ||
| 108 | 45000 55000 65000 75000 105000>; | ||
| 109 | active_hysteresis = <0 0 0 0 (-3000) | ||
| 110 | (-4000) (-4000) 0 0 0>; | ||
| 111 | }; | ||
| 112 | }; | ||
| 113 | }; | ||
| 114 | |||
| 115 | pwm-fan { | ||
| 116 | compatible = "pwm-fan"; | ||
| 117 | status = "okay"; | ||
| 118 | #pwm-cells = <1>; | ||
| 119 | pwms = <&tegra_pwm4 0 45334>; | ||
| 120 | shared_data = <&pwm_fan_shared_data>; | ||
| 121 | profiles { | ||
| 122 | default = "tmargin"; | ||
| 123 | quiet { | ||
| 124 | state_cap = <8>; | ||
| 125 | active_pwm = <0 60 90 120 150 180 210 240 255 255>; | ||
| 126 | }; | ||
| 127 | cool { | ||
| 128 | state_cap = <4>; | ||
| 129 | active_pwm = <0 77 120 160 255 255 255 255 255 255>; | ||
| 130 | }; | ||
| 131 | tmargin { | ||
| 132 | state_cap = <0>; | ||
| 133 | active_pwm = <255 255 240 210 180 150 120 90 60 0>; | ||
| 134 | }; | ||
| 135 | }; | ||
| 136 | }; | ||
| 137 | |||
diff --git a/drivers/misc/therm_fan_est.c b/drivers/misc/therm_fan_est.c index f07a31009..e37c1130d 100644 --- a/drivers/misc/therm_fan_est.c +++ b/drivers/misc/therm_fan_est.c | |||
| @@ -52,7 +52,8 @@ static void therm_fan_est_work_func(struct work_struct *work) | |||
| 52 | { | 52 | { |
| 53 | int i, j, group, index, trip_index = 0; | 53 | int i, j, group, index, trip_index = 0; |
| 54 | int sum[MAX_SUBDEVICE_GROUP] = {0, }; | 54 | int sum[MAX_SUBDEVICE_GROUP] = {0, }; |
| 55 | int sum_max = 0; | 55 | int sum_max = INT_MIN; |
| 56 | int sum_min = INT_MAX; | ||
| 56 | int temp = 0; | 57 | int temp = 0; |
| 57 | struct delayed_work *dwork = container_of(work, | 58 | struct delayed_work *dwork = container_of(work, |
| 58 | struct delayed_work, work); | 59 | struct delayed_work, work); |
| @@ -77,11 +78,21 @@ static void therm_fan_est_work_func(struct work_struct *work) | |||
| 77 | } | 78 | } |
| 78 | } | 79 | } |
| 79 | 80 | ||
| 81 | if (est->use_tmargin) { | ||
| 82 | for (i = 0; i < MAX_SUBDEVICE_GROUP; i++) | ||
| 83 | sum[i] = (est->crit_temp[i] * 100) - sum[i]; | ||
| 84 | } | ||
| 85 | |||
| 80 | #if !DEBUG | 86 | #if !DEBUG |
| 81 | for (i = 0; i < MAX_SUBDEVICE_GROUP; i++) | 87 | for (i = 0; i < MAX_SUBDEVICE_GROUP; i++) { |
| 88 | sum_min = min(sum_min, sum[i]); | ||
| 82 | sum_max = max(sum_max, sum[i]); | 89 | sum_max = max(sum_max, sum[i]); |
| 90 | } | ||
| 83 | 91 | ||
| 84 | est->cur_temp = sum_max / 100 + est->toffset; | 92 | if (est->use_tmargin) |
| 93 | est->cur_temp = sum_min / 100 + est->toffset; | ||
| 94 | else | ||
| 95 | est->cur_temp = sum_max / 100 + est->toffset; | ||
| 85 | #else | 96 | #else |
| 86 | est->cur_temp = est->cur_temp_debug; | 97 | est->cur_temp = est->cur_temp_debug; |
| 87 | #endif | 98 | #endif |
| @@ -95,7 +106,9 @@ static void therm_fan_est_work_func(struct work_struct *work) | |||
| 95 | } | 106 | } |
| 96 | 107 | ||
| 97 | if (est->cur_temp != est->pre_temp) { | 108 | if (est->cur_temp != est->pre_temp) { |
| 98 | if (est->cur_temp > est->pre_temp) { | 109 | if (est->use_tmargin ? |
| 110 | (est->cur_temp < est->pre_temp) : | ||
| 111 | (est->cur_temp > est->pre_temp)) { | ||
| 99 | /* temperature is rising */ | 112 | /* temperature is rising */ |
| 100 | read_lock(&est->state_lock); | 113 | read_lock(&est->state_lock); |
| 101 | for (trip_index = 0; | 114 | for (trip_index = 0; |
| @@ -105,10 +118,16 @@ static void therm_fan_est_work_func(struct work_struct *work) | |||
| 105 | } | 118 | } |
| 106 | read_unlock(&est->state_lock); | 119 | read_unlock(&est->state_lock); |
| 107 | 120 | ||
| 108 | if (est->current_trip_level < trip_index | 121 | if (est->use_tmargin) { |
| 109 | && est->current_trip_level != (trip_index - 1)) | 122 | if (est->current_trip_level >= trip_index |
| 110 | update_flag = true; | 123 | && est->current_trip_level != (trip_index - 1)) |
| 111 | } else if (est->cur_temp < est->pre_temp) { | 124 | update_flag = true; |
| 125 | } else { | ||
| 126 | if (est->current_trip_level < trip_index | ||
| 127 | && est->current_trip_level != (trip_index - 1)) | ||
| 128 | update_flag = true; | ||
| 129 | } | ||
| 130 | } else { | ||
| 112 | /* temperature is cooling */ | 131 | /* temperature is cooling */ |
| 113 | read_lock(&est->state_lock); | 132 | read_lock(&est->state_lock); |
| 114 | for (trip_index = 1; | 133 | for (trip_index = 1; |
| @@ -119,10 +138,16 @@ static void therm_fan_est_work_func(struct work_struct *work) | |||
| 119 | } | 138 | } |
| 120 | read_unlock(&est->state_lock); | 139 | read_unlock(&est->state_lock); |
| 121 | 140 | ||
| 122 | if (est->current_trip_level >= trip_index | 141 | if (est->use_tmargin) { |
| 123 | && est->current_trip_level != (trip_index - 1) | 142 | if (est->current_trip_level < trip_index |
| 124 | && trip_index != (MAX_ACTIVE_STATES + 1)) | ||
