Merge branch 'msm-next' of git://people.freedesktop.org/~robclark/linux into drm...
[cascardo/linux.git] / drivers / gpu / drm / amd / powerplay / hwmgr / tonga_powertune.c
1 /*
2  * Copyright 2015 Advanced Micro Devices, Inc.
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20  * OTHER DEALINGS IN THE SOFTWARE.
21  *
22  */
23
24 #include "hwmgr.h"
25 #include "smumgr.h"
26 #include "tonga_hwmgr.h"
27 #include "tonga_powertune.h"
28 #include "tonga_smumgr.h"
29 #include "smu72_discrete.h"
30 #include "pp_debug.h"
31 #include "tonga_ppsmc.h"
32
33 #define VOLTAGE_SCALE  4
34 #define POWERTUNE_DEFAULT_SET_MAX    1
35
36 struct tonga_pt_defaults tonga_power_tune_data_set_array[POWERTUNE_DEFAULT_SET_MAX] = {
37 /*    sviLoadLIneEn, SviLoadLineVddC, TDC_VDDC_ThrottleReleaseLimitPerc, TDC_MAWt, TdcWaterfallCtl, DTEAmbientTempBase, DisplayCac, BAPM_TEMP_GRADIENT */
38         {1,               0xF,             0xFD,                               0x19,     5,               45,                  0,          0xB0000,
39         {0x79,  0x253, 0x25D, 0xAE,  0x72,  0x80,    0x83,  0x86,  0x6F,  0xC8,    0xC9,  0xC9,  0x2F,  0x4D, 0x61},
40         {0x17C, 0x172, 0x180, 0x1BC, 0x1B3, 0x1BD, 0x206, 0x200, 0x203, 0x25D, 0x25A, 0x255, 0x2C3, 0x2C5, 0x2B4 } },
41 };
42
43 void tonga_initialize_power_tune_defaults(struct pp_hwmgr *hwmgr)
44 {
45         struct tonga_hwmgr *tonga_hwmgr = (struct tonga_hwmgr *)(hwmgr->backend);
46         struct  phm_ppt_v1_information *table_info =
47                         (struct  phm_ppt_v1_information *)(hwmgr->pptable);
48         uint32_t tmp = 0;
49
50         if (table_info &&
51                         table_info->cac_dtp_table->usPowerTuneDataSetID <= POWERTUNE_DEFAULT_SET_MAX &&
52                         table_info->cac_dtp_table->usPowerTuneDataSetID)
53                 tonga_hwmgr->power_tune_defaults =
54                                 &tonga_power_tune_data_set_array
55                                 [table_info->cac_dtp_table->usPowerTuneDataSetID - 1];
56         else
57                 tonga_hwmgr->power_tune_defaults = &tonga_power_tune_data_set_array[0];
58
59         phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
60                         PHM_PlatformCaps_CAC);
61         phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
62                         PHM_PlatformCaps_SQRamping);
63         phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
64                         PHM_PlatformCaps_DBRamping);
65         phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
66                         PHM_PlatformCaps_TDRamping);
67         phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
68                         PHM_PlatformCaps_TCPRamping);
69
70         tonga_hwmgr->dte_tj_offset = tmp;
71
72         if (!tmp) {
73                 phm_cap_set(hwmgr->platform_descriptor.platformCaps,
74                                 PHM_PlatformCaps_CAC);
75
76                 tonga_hwmgr->fast_watermark_threshold = 100;
77
78                 if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
79                                         PHM_PlatformCaps_PowerContainment)) {
80                         tmp = 1;
81                         tonga_hwmgr->enable_dte_feature = tmp ? false : true;
82                         tonga_hwmgr->enable_tdc_limit_feature = tmp ? true : false;
83                         tonga_hwmgr->enable_pkg_pwr_tracking_feature = tmp ? true : false;
84                 }
85         }
86 }
87
88
89 int tonga_populate_bapm_parameters_in_dpm_table(struct pp_hwmgr *hwmgr)
90 {
91         struct tonga_hwmgr *data = (struct tonga_hwmgr *)(hwmgr->backend);
92         struct tonga_pt_defaults *defaults = data->power_tune_defaults;
93         SMU72_Discrete_DpmTable  *dpm_table = &(data->smc_state_table);
94         struct phm_ppt_v1_information *table_info =
95                         (struct phm_ppt_v1_information *)(hwmgr->pptable);
96         struct phm_cac_tdp_table *cac_dtp_table = table_info->cac_dtp_table;
97         int  i, j, k;
98         uint16_t *pdef1;
99         uint16_t *pdef2;
100
101
102         /* TDP number of fraction bits are changed from 8 to 7 for Fiji
103          * as requested by SMC team
104          */
105         dpm_table->DefaultTdp = PP_HOST_TO_SMC_US(
106                         (uint16_t)(cac_dtp_table->usTDP * 256));
107         dpm_table->TargetTdp = PP_HOST_TO_SMC_US(
108                         (uint16_t)(cac_dtp_table->usConfigurableTDP * 256));
109
110         PP_ASSERT_WITH_CODE(cac_dtp_table->usTargetOperatingTemp <= 255,
111                         "Target Operating Temp is out of Range!",
112                         );
113
114         dpm_table->GpuTjMax = (uint8_t)(cac_dtp_table->usTargetOperatingTemp);
115         dpm_table->GpuTjHyst = 8;
116
117         dpm_table->DTEAmbientTempBase = defaults->dte_ambient_temp_base;
118
119         dpm_table->BAPM_TEMP_GRADIENT = PP_HOST_TO_SMC_UL(defaults->bamp_temp_gradient);
120         pdef1 = defaults->bapmti_r;
121         pdef2 = defaults->bapmti_rc;
122
123         for (i = 0; i < SMU72_DTE_ITERATIONS; i++) {
124                 for (j = 0; j < SMU72_DTE_SOURCES; j++) {
125                         for (k = 0; k < SMU72_DTE_SINKS; k++) {
126                                 dpm_table->BAPMTI_R[i][j][k] = PP_HOST_TO_SMC_US(*pdef1);
127                                 dpm_table->BAPMTI_RC[i][j][k] = PP_HOST_TO_SMC_US(*pdef2);
128                                 pdef1++;
129                                 pdef2++;
130                         }
131                 }
132         }
133
134         return 0;
135 }
136
137 static int tonga_populate_svi_load_line(struct pp_hwmgr *hwmgr)
138 {
139         struct tonga_hwmgr *data = (struct tonga_hwmgr *)(hwmgr->backend);
140         const struct tonga_pt_defaults *defaults = data->power_tune_defaults;
141
142         data->power_tune_table.SviLoadLineEn = defaults->svi_load_line_en;
143         data->power_tune_table.SviLoadLineVddC = defaults->svi_load_line_vddC;
144         data->power_tune_table.SviLoadLineTrimVddC = 3;
145         data->power_tune_table.SviLoadLineOffsetVddC = 0;
146
147         return 0;
148 }
149
150 static int tonga_populate_tdc_limit(struct pp_hwmgr *hwmgr)
151 {
152         uint16_t tdc_limit;
153         struct tonga_hwmgr *data = (struct tonga_hwmgr *)(hwmgr->backend);
154         struct phm_ppt_v1_information *table_info =
155                         (struct phm_ppt_v1_information *)(hwmgr->pptable);
156         const struct tonga_pt_defaults *defaults = data->power_tune_defaults;
157
158         /* TDC number of fraction bits are changed from 8 to 7
159          * for Fiji as requested by SMC team
160          */
161         tdc_limit = (uint16_t)(table_info->cac_dtp_table->usTDC * 256);
162         data->power_tune_table.TDC_VDDC_PkgLimit =
163                         CONVERT_FROM_HOST_TO_SMC_US(tdc_limit);
164         data->power_tune_table.TDC_VDDC_ThrottleReleaseLimitPerc =
165                         defaults->tdc_vddc_throttle_release_limit_perc;
166         data->power_tune_table.TDC_MAWt = defaults->tdc_mawt;
167
168         return 0;
169 }
170
171 static int tonga_populate_dw8(struct pp_hwmgr *hwmgr, uint32_t fuse_table_offset)
172 {
173         struct tonga_hwmgr *data = (struct tonga_hwmgr *)(hwmgr->backend);
174         const struct tonga_pt_defaults *defaults = data->power_tune_defaults;
175         uint32_t temp;
176
177         if (tonga_read_smc_sram_dword(hwmgr->smumgr,
178                         fuse_table_offset +
179                         offsetof(SMU72_Discrete_PmFuses, TdcWaterfallCtl),
180                         (uint32_t *)&temp, data->sram_end))
181                 PP_ASSERT_WITH_CODE(false,
182                                 "Attempt to read PmFuses.DW6 (SviLoadLineEn) from SMC Failed!",
183                                 return -EINVAL);
184         else
185                 data->power_tune_table.TdcWaterfallCtl = defaults->tdc_waterfall_ctl;
186
187         return 0;
188 }
189
190 static int tonga_populate_temperature_scaler(struct pp_hwmgr *hwmgr)
191 {
192         int i;
193         struct tonga_hwmgr *data = (struct tonga_hwmgr *)(hwmgr->backend);
194
195         /* Currently not used. Set all to zero. */
196         for (i = 0; i < 16; i++)
197                 data->power_tune_table.LPMLTemperatureScaler[i] = 0;
198
199         return 0;
200 }
201
202 static int tonga_populate_fuzzy_fan(struct pp_hwmgr *hwmgr)
203 {
204         struct tonga_hwmgr *data = (struct tonga_hwmgr *)(hwmgr->backend);
205
206         if ((hwmgr->thermal_controller.advanceFanControlParameters.
207                         usFanOutputSensitivity & (1 << 15)) ||
208                 (hwmgr->thermal_controller.advanceFanControlParameters.usFanOutputSensitivity == 0))
209                 hwmgr->thermal_controller.advanceFanControlParameters.
210                 usFanOutputSensitivity = hwmgr->thermal_controller.
211                         advanceFanControlParameters.usDefaultFanOutputSensitivity;
212
213         data->power_tune_table.FuzzyFan_PwmSetDelta =
214                         PP_HOST_TO_SMC_US(hwmgr->thermal_controller.
215                                         advanceFanControlParameters.usFanOutputSensitivity);
216         return 0;
217 }
218
219 static int tonga_populate_gnb_lpml(struct pp_hwmgr *hwmgr)
220 {
221         int i;
222         struct tonga_hwmgr *data = (struct tonga_hwmgr *)(hwmgr->backend);
223
224         /* Currently not used. Set all to zero. */
225         for (i = 0; i < 16; i++)
226                 data->power_tune_table.GnbLPML[i] = 0;
227
228         return 0;
229 }
230
231 static int tonga_min_max_vgnb_lpml_id_from_bapm_vddc(struct pp_hwmgr *hwmgr)
232 {
233         return 0;
234 }
235
236 static int tonga_populate_bapm_vddc_base_leakage_sidd(struct pp_hwmgr *hwmgr)
237 {
238         struct tonga_hwmgr *data = (struct tonga_hwmgr *)(hwmgr->backend);
239         struct phm_ppt_v1_information *table_info =
240                         (struct phm_ppt_v1_information *)(hwmgr->pptable);
241         uint16_t hi_sidd = data->power_tune_table.BapmVddCBaseLeakageHiSidd;
242         uint16_t lo_sidd = data->power_tune_table.BapmVddCBaseLeakageLoSidd;
243         struct phm_cac_tdp_table *cac_table = table_info->cac_dtp_table;
244
245         hi_sidd = (uint16_t)(cac_table->usHighCACLeakage / 100 * 256);
246         lo_sidd = (uint16_t)(cac_table->usLowCACLeakage / 100 * 256);
247
248         data->power_tune_table.BapmVddCBaseLeakageHiSidd =
249                         CONVERT_FROM_HOST_TO_SMC_US(hi_sidd);
250         data->power_tune_table.BapmVddCBaseLeakageLoSidd =
251                         CONVERT_FROM_HOST_TO_SMC_US(lo_sidd);
252
253         return 0;
254 }
255
256 int tonga_populate_pm_fuses(struct pp_hwmgr *hwmgr)
257 {
258         struct tonga_hwmgr *data = (struct tonga_hwmgr *)(hwmgr->backend);
259         uint32_t pm_fuse_table_offset;
260
261         if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
262                         PHM_PlatformCaps_PowerContainment)) {
263                 if (tonga_read_smc_sram_dword(hwmgr->smumgr,
264                                 SMU72_FIRMWARE_HEADER_LOCATION +
265                                 offsetof(SMU72_Firmware_Header, PmFuseTable),
266                                 &pm_fuse_table_offset, data->sram_end))
267                         PP_ASSERT_WITH_CODE(false,
268                                         "Attempt to get pm_fuse_table_offset Failed!",
269                                         return -EINVAL);
270
271                 /* DW6 */
272                 if (tonga_populate_svi_load_line(hwmgr))
273                         PP_ASSERT_WITH_CODE(false,
274                                         "Attempt to populate SviLoadLine Failed!",
275                                         return -EINVAL);
276                 /* DW7 */
277                 if (tonga_populate_tdc_limit(hwmgr))
278                         PP_ASSERT_WITH_CODE(false,
279                                         "Attempt to populate TDCLimit Failed!", return -EINVAL);
280                 /* DW8 */
281                 if (tonga_populate_dw8(hwmgr, pm_fuse_table_offset))
282                         PP_ASSERT_WITH_CODE(false,
283                                         "Attempt to populate TdcWaterfallCtl Failed !",
284                                         return -EINVAL);
285
286                 /* DW9-DW12 */
287                 if (tonga_populate_temperature_scaler(hwmgr) != 0)
288                         PP_ASSERT_WITH_CODE(false,
289                                         "Attempt to populate LPMLTemperatureScaler Failed!",
290                                         return -EINVAL);
291
292                 /* DW13-DW14 */
293                 if (tonga_populate_fuzzy_fan(hwmgr))
294                         PP_ASSERT_WITH_CODE(false,
295                                         "Attempt to populate Fuzzy Fan Control parameters Failed!",
296                                         return -EINVAL);
297
298                 /* DW15-DW18 */
299                 if (tonga_populate_gnb_lpml(hwmgr))
300                         PP_ASSERT_WITH_CODE(false,
301                                         "Attempt to populate GnbLPML Failed!",
302                                         return -EINVAL);
303
304                 /* DW19 */
305                 if (tonga_min_max_vgnb_lpml_id_from_bapm_vddc(hwmgr))
306                         PP_ASSERT_WITH_CODE(false,
307                                         "Attempt to populate GnbLPML Min and Max Vid Failed!",
308                                         return -EINVAL);
309
310                 /* DW20 */
311                 if (tonga_populate_bapm_vddc_base_leakage_sidd(hwmgr))
312                         PP_ASSERT_WITH_CODE(false,
313                                         "Attempt to populate BapmVddCBaseLeakage Hi and Lo Sidd Failed!",
314                                         return -EINVAL);
315
316                 if (tonga_copy_bytes_to_smc(hwmgr->smumgr, pm_fuse_table_offset,
317                                 (uint8_t *)&data->power_tune_table,
318                                 sizeof(struct SMU72_Discrete_PmFuses), data->sram_end))
319                         PP_ASSERT_WITH_CODE(false,
320                                         "Attempt to download PmFuseTable Failed!",
321                                         return -EINVAL);
322         }
323         return 0;
324 }
325
326 int tonga_enable_smc_cac(struct pp_hwmgr *hwmgr)
327 {
328         struct tonga_hwmgr *data = (struct tonga_hwmgr *)(hwmgr->backend);
329         int result = 0;
330
331         if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
332                         PHM_PlatformCaps_CAC)) {
333                 int smc_result;
334
335                 smc_result = smum_send_msg_to_smc(hwmgr->smumgr,
336                                 (uint16_t)(PPSMC_MSG_EnableCac));
337                 PP_ASSERT_WITH_CODE((smc_result == 0),
338                                 "Failed to enable CAC in SMC.", result = -1);
339
340                 data->cac_enabled = (smc_result == 0) ? true : false;
341         }
342         return result;
343 }
344
345 int tonga_disable_smc_cac(struct pp_hwmgr *hwmgr)
346 {
347         struct tonga_hwmgr *data = (struct tonga_hwmgr *)(hwmgr->backend);
348         int result = 0;
349
350         if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
351                         PHM_PlatformCaps_CAC) && data->cac_enabled) {
352                 int smc_result = smum_send_msg_to_smc(hwmgr->smumgr,
353                                 (uint16_t)(PPSMC_MSG_DisableCac));
354                 PP_ASSERT_WITH_CODE((smc_result == 0),
355                                 "Failed to disable CAC in SMC.", result = -1);
356
357                 data->cac_enabled = false;
358         }
359         return result;
360 }
361
362 int tonga_set_power_limit(struct pp_hwmgr *hwmgr, uint32_t n)
363 {
364         struct tonga_hwmgr *data = (struct tonga_hwmgr *)(hwmgr->backend);
365
366         if (data->power_containment_features &
367                         POWERCONTAINMENT_FEATURE_PkgPwrLimit)
368                 return smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
369                                 PPSMC_MSG_PkgPwrSetLimit, n);
370         return 0;
371 }
372
373 static int tonga_set_overdriver_target_tdp(struct pp_hwmgr *pHwMgr, uint32_t target_tdp)
374 {
375         return smum_send_msg_to_smc_with_parameter(pHwMgr->smumgr,
376                         PPSMC_MSG_OverDriveSetTargetTdp, target_tdp);
377 }
378
379 int tonga_enable_power_containment(struct pp_hwmgr *hwmgr)
380 {
381         struct tonga_hwmgr *data = (struct tonga_hwmgr *)(hwmgr->backend);
382         struct phm_ppt_v1_information *table_info =
383                         (struct phm_ppt_v1_information *)(hwmgr->pptable);
384         int smc_result;
385         int result = 0;
386
387         data->power_containment_features = 0;
388         if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
389                         PHM_PlatformCaps_PowerContainment)) {
390                 if (data->enable_dte_feature) {
391                         smc_result = smum_send_msg_to_smc(hwmgr->smumgr,
392                                         (uint16_t)(PPSMC_MSG_EnableDTE));
393                         PP_ASSERT_WITH_CODE((smc_result == 0),
394                                         "Failed to enable DTE in SMC.", result = -1;);
395                         if (smc_result == 0)
396                                 data->power_containment_features |= POWERCONTAINMENT_FEATURE_DTE;
397                 }
398
399                 if (data->enable_tdc_limit_feature) {
400                         smc_result = smum_send_msg_to_smc(hwmgr->smumgr,
401                                         (uint16_t)(PPSMC_MSG_TDCLimitEnable));
402                         PP_ASSERT_WITH_CODE((smc_result == 0),
403                                         "Failed to enable TDCLimit in SMC.", result = -1;);
404                         if (smc_result == 0)
405                                 data->power_containment_features |=
406                                                 POWERCONTAINMENT_FEATURE_TDCLimit;
407                 }
408
409                 if (data->enable_pkg_pwr_tracking_feature) {
410                         smc_result = smum_send_msg_to_smc(hwmgr->smumgr,
411                                         (uint16_t)(PPSMC_MSG_PkgPwrLimitEnable));
412                         PP_ASSERT_WITH_CODE((smc_result == 0),
413                                         "Failed to enable PkgPwrTracking in SMC.", result = -1;);
414                         if (smc_result == 0) {
415                                 struct phm_cac_tdp_table *cac_table =
416                                                 table_info->cac_dtp_table;
417                                 uint32_t default_limit =
418                                         (uint32_t)(cac_table->usMaximumPowerDeliveryLimit * 256);
419
420                                 data->power_containment_features |=
421                                                 POWERCONTAINMENT_FEATURE_PkgPwrLimit;
422
423                                 if (tonga_set_power_limit(hwmgr, default_limit))
424                                         printk(KERN_ERR "Failed to set Default Power Limit in SMC!");
425                         }
426                 }
427         }
428         return result;
429 }
430
431 int tonga_disable_power_containment(struct pp_hwmgr *hwmgr)
432 {
433         struct tonga_hwmgr *data = (struct tonga_hwmgr *)(hwmgr->backend);
434         int result = 0;
435
436         if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
437                         PHM_PlatformCaps_PowerContainment) &&
438                         data->power_containment_features) {
439                 int smc_result;
440
441                 if (data->power_containment_features &
442                                 POWERCONTAINMENT_FEATURE_TDCLimit) {
443                         smc_result = smum_send_msg_to_smc(hwmgr->smumgr,
444                                         (uint16_t)(PPSMC_MSG_TDCLimitDisable));
445                         PP_ASSERT_WITH_CODE((smc_result == 0),
446                                         "Failed to disable TDCLimit in SMC.",
447                                         result = smc_result);
448                 }
449
450                 if (data->power_containment_features &
451                                 POWERCONTAINMENT_FEATURE_DTE) {
452                         smc_result = smum_send_msg_to_smc(hwmgr->smumgr,
453                                         (uint16_t)(PPSMC_MSG_DisableDTE));
454                         PP_ASSERT_WITH_CODE((smc_result == 0),
455                                         "Failed to disable DTE in SMC.",
456                                         result = smc_result);
457                 }
458
459                 if (data->power_containment_features &
460                                 POWERCONTAINMENT_FEATURE_PkgPwrLimit) {
461                         smc_result = smum_send_msg_to_smc(hwmgr->smumgr,
462                                         (uint16_t)(PPSMC_MSG_PkgPwrLimitDisable));
463                         PP_ASSERT_WITH_CODE((smc_result == 0),
464                                         "Failed to disable PkgPwrTracking in SMC.",
465                                         result = smc_result);
466                 }
467                 data->power_containment_features = 0;
468         }
469
470         return result;
471 }
472
473 int tonga_power_control_set_level(struct pp_hwmgr *hwmgr)
474 {
475         struct phm_ppt_v1_information *table_info =
476                         (struct phm_ppt_v1_information *)(hwmgr->pptable);
477         struct phm_cac_tdp_table *cac_table = table_info->cac_dtp_table;
478         int adjust_percent, target_tdp;
479         int result = 0;
480
481         if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
482                         PHM_PlatformCaps_PowerContainment)) {
483                 /* adjustment percentage has already been validated */
484                 adjust_percent = hwmgr->platform_descriptor.TDPAdjustmentPolarity ?
485                                 hwmgr->platform_descriptor.TDPAdjustment :
486                                 (-1 * hwmgr->platform_descriptor.TDPAdjustment);
487                 /* SMC requested that target_tdp to be 7 bit fraction in DPM table
488                  * but message to be 8 bit fraction for messages
489                  */
490                 target_tdp = ((100 + adjust_percent) * (int)(cac_table->usTDP * 256)) / 100;
491                 result = tonga_set_overdriver_target_tdp(hwmgr, (uint32_t)target_tdp);
492         }
493
494         return result;
495 }