Merge commit 'c1e140bf79d817d4a7aa9932eb98b0359c87af33' from mac80211-next
[cascardo/linux.git] / drivers / net / wireless / ath / ath9k / eeprom.c
1 /*
2  * Copyright (c) 2008-2011 Atheros Communications Inc.
3  *
4  * Permission to use, copy, modify, and/or distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15  */
16
17 #include "hw.h"
18
19 void ath9k_hw_analog_shift_regwrite(struct ath_hw *ah, u32 reg, u32 val)
20 {
21         REG_WRITE(ah, reg, val);
22
23         if (ah->config.analog_shiftreg)
24                 udelay(100);
25 }
26
27 void ath9k_hw_analog_shift_rmw(struct ath_hw *ah, u32 reg, u32 mask,
28                                u32 shift, u32 val)
29 {
30         u32 regVal;
31
32         regVal = REG_READ(ah, reg) & ~mask;
33         regVal |= (val << shift) & mask;
34
35         REG_WRITE(ah, reg, regVal);
36
37         if (ah->config.analog_shiftreg)
38                 udelay(100);
39 }
40
41 int16_t ath9k_hw_interpolate(u16 target, u16 srcLeft, u16 srcRight,
42                              int16_t targetLeft, int16_t targetRight)
43 {
44         int16_t rv;
45
46         if (srcRight == srcLeft) {
47                 rv = targetLeft;
48         } else {
49                 rv = (int16_t) (((target - srcLeft) * targetRight +
50                                  (srcRight - target) * targetLeft) /
51                                 (srcRight - srcLeft));
52         }
53         return rv;
54 }
55
56 bool ath9k_hw_get_lower_upper_index(u8 target, u8 *pList, u16 listSize,
57                                     u16 *indexL, u16 *indexR)
58 {
59         u16 i;
60
61         if (target <= pList[0]) {
62                 *indexL = *indexR = 0;
63                 return true;
64         }
65         if (target >= pList[listSize - 1]) {
66                 *indexL = *indexR = (u16) (listSize - 1);
67                 return true;
68         }
69
70         for (i = 0; i < listSize - 1; i++) {
71                 if (pList[i] == target) {
72                         *indexL = *indexR = i;
73                         return true;
74                 }
75                 if (target < pList[i + 1]) {
76                         *indexL = i;
77                         *indexR = (u16) (i + 1);
78                         return false;
79                 }
80         }
81         return false;
82 }
83
84 void ath9k_hw_usb_gen_fill_eeprom(struct ath_hw *ah, u16 *eep_data,
85                                   int eep_start_loc, int size)
86 {
87         int i = 0, j, addr;
88         u32 addrdata[8];
89         u32 data[8];
90
91         for (addr = 0; addr < size; addr++) {
92                 addrdata[i] = AR5416_EEPROM_OFFSET +
93                         ((addr + eep_start_loc) << AR5416_EEPROM_S);
94                 i++;
95                 if (i == 8) {
96                         REG_READ_MULTI(ah, addrdata, data, i);
97
98                         for (j = 0; j < i; j++) {
99                                 *eep_data = data[j];
100                                 eep_data++;
101                         }
102                         i = 0;
103                 }
104         }
105
106         if (i != 0) {
107                 REG_READ_MULTI(ah, addrdata, data, i);
108
109                 for (j = 0; j < i; j++) {
110                         *eep_data = data[j];
111                         eep_data++;
112                 }
113         }
114 }
115
116 static bool ath9k_hw_nvram_read_blob(struct ath_hw *ah, u32 off,
117                                      u16 *data)
118 {
119         u16 *blob_data;
120
121         if (off * sizeof(u16) > ah->eeprom_blob->size)
122                 return false;
123
124         blob_data = (u16 *)ah->eeprom_blob->data;
125         *data =  blob_data[off];
126         return true;
127 }
128
129 bool ath9k_hw_nvram_read(struct ath_hw *ah, u32 off, u16 *data)
130 {
131         struct ath_common *common = ath9k_hw_common(ah);
132         bool ret;
133
134         if (ah->eeprom_blob)
135                 ret = ath9k_hw_nvram_read_blob(ah, off, data);
136         else
137                 ret = common->bus_ops->eeprom_read(common, off, data);
138
139         if (!ret)
140                 ath_dbg(common, EEPROM,
141                         "unable to read eeprom region at offset %u\n", off);
142
143         return ret;
144 }
145
146 void ath9k_hw_fill_vpd_table(u8 pwrMin, u8 pwrMax, u8 *pPwrList,
147                              u8 *pVpdList, u16 numIntercepts,
148                              u8 *pRetVpdList)
149 {
150         u16 i, k;
151         u8 currPwr = pwrMin;
152         u16 idxL = 0, idxR = 0;
153
154         for (i = 0; i <= (pwrMax - pwrMin) / 2; i++) {
155                 ath9k_hw_get_lower_upper_index(currPwr, pPwrList,
156                                                numIntercepts, &(idxL),
157                                                &(idxR));
158                 if (idxR < 1)
159                         idxR = 1;
160                 if (idxL == numIntercepts - 1)
161                         idxL = (u16) (numIntercepts - 2);
162                 if (pPwrList[idxL] == pPwrList[idxR])
163                         k = pVpdList[idxL];
164                 else
165                         k = (u16)(((currPwr - pPwrList[idxL]) * pVpdList[idxR] +
166                                    (pPwrList[idxR] - currPwr) * pVpdList[idxL]) /
167                                   (pPwrList[idxR] - pPwrList[idxL]));
168                 pRetVpdList[i] = (u8) k;
169                 currPwr += 2;
170         }
171 }
172
173 void ath9k_hw_get_legacy_target_powers(struct ath_hw *ah,
174                                        struct ath9k_channel *chan,
175                                        struct cal_target_power_leg *powInfo,
176                                        u16 numChannels,
177                                        struct cal_target_power_leg *pNewPower,
178                                        u16 numRates, bool isExtTarget)
179 {
180         struct chan_centers centers;
181         u16 clo, chi;
182         int i;
183         int matchIndex = -1, lowIndex = -1;
184         u16 freq;
185
186         ath9k_hw_get_channel_centers(ah, chan, &centers);
187         freq = (isExtTarget) ? centers.ext_center : centers.ctl_center;
188
189         if (freq <= ath9k_hw_fbin2freq(powInfo[0].bChannel,
190                                        IS_CHAN_2GHZ(chan))) {
191                 matchIndex = 0;
192         } else {
193                 for (i = 0; (i < numChannels) &&
194                              (powInfo[i].bChannel != AR5416_BCHAN_UNUSED); i++) {
195                         if (freq == ath9k_hw_fbin2freq(powInfo[i].bChannel,
196                                                        IS_CHAN_2GHZ(chan))) {
197                                 matchIndex = i;
198                                 break;
199                         } else if (freq < ath9k_hw_fbin2freq(powInfo[i].bChannel,
200                                                 IS_CHAN_2GHZ(chan)) && i > 0 &&
201                                    freq > ath9k_hw_fbin2freq(powInfo[i - 1].bChannel,
202                                                 IS_CHAN_2GHZ(chan))) {
203                                 lowIndex = i - 1;
204                                 break;
205                         }
206                 }
207                 if ((matchIndex == -1) && (lowIndex == -1))
208                         matchIndex = i - 1;
209         }
210
211         if (matchIndex != -1) {
212                 *pNewPower = powInfo[matchIndex];
213         } else {
214                 clo = ath9k_hw_fbin2freq(powInfo[lowIndex].bChannel,
215                                          IS_CHAN_2GHZ(chan));
216                 chi = ath9k_hw_fbin2freq(powInfo[lowIndex + 1].bChannel,
217                                          IS_CHAN_2GHZ(chan));
218
219                 for (i = 0; i < numRates; i++) {
220                         pNewPower->tPow2x[i] =
221                                 (u8)ath9k_hw_interpolate(freq, clo, chi,
222                                                 powInfo[lowIndex].tPow2x[i],
223                                                 powInfo[lowIndex + 1].tPow2x[i]);
224                 }
225         }
226 }
227
228 void ath9k_hw_get_target_powers(struct ath_hw *ah,
229                                 struct ath9k_channel *chan,
230                                 struct cal_target_power_ht *powInfo,
231                                 u16 numChannels,
232                                 struct cal_target_power_ht *pNewPower,
233                                 u16 numRates, bool isHt40Target)
234 {
235         struct chan_centers centers;
236         u16 clo, chi;
237         int i;
238         int matchIndex = -1, lowIndex = -1;
239         u16 freq;
240
241         ath9k_hw_get_channel_centers(ah, chan, &centers);
242         freq = isHt40Target ? centers.synth_center : centers.ctl_center;
243
244         if (freq <= ath9k_hw_fbin2freq(powInfo[0].bChannel, IS_CHAN_2GHZ(chan))) {
245                 matchIndex = 0;
246         } else {
247                 for (i = 0; (i < numChannels) &&
248                              (powInfo[i].bChannel != AR5416_BCHAN_UNUSED); i++) {
249                         if (freq == ath9k_hw_fbin2freq(powInfo[i].bChannel,
250                                                        IS_CHAN_2GHZ(chan))) {
251                                 matchIndex = i;
252                                 break;
253                         } else
254                                 if (freq < ath9k_hw_fbin2freq(powInfo[i].bChannel,
255                                                 IS_CHAN_2GHZ(chan)) && i > 0 &&
256                                     freq > ath9k_hw_fbin2freq(powInfo[i - 1].bChannel,
257                                                 IS_CHAN_2GHZ(chan))) {
258                                         lowIndex = i - 1;
259                                         break;
260                                 }
261                 }
262                 if ((matchIndex == -1) && (lowIndex == -1))
263                         matchIndex = i - 1;
264         }
265
266         if (matchIndex != -1) {
267                 *pNewPower = powInfo[matchIndex];
268         } else {
269                 clo = ath9k_hw_fbin2freq(powInfo[lowIndex].bChannel,
270                                          IS_CHAN_2GHZ(chan));
271                 chi = ath9k_hw_fbin2freq(powInfo[lowIndex + 1].bChannel,
272                                          IS_CHAN_2GHZ(chan));
273
274                 for (i = 0; i < numRates; i++) {
275                         pNewPower->tPow2x[i] = (u8)ath9k_hw_interpolate(freq,
276                                                 clo, chi,
277                                                 powInfo[lowIndex].tPow2x[i],
278                                                 powInfo[lowIndex + 1].tPow2x[i]);
279                 }
280         }
281 }
282
283 u16 ath9k_hw_get_max_edge_power(u16 freq, struct cal_ctl_edges *pRdEdgesPower,
284                                 bool is2GHz, int num_band_edges)
285 {
286         u16 twiceMaxEdgePower = MAX_RATE_POWER;
287         int i;
288
289         for (i = 0; (i < num_band_edges) &&
290                      (pRdEdgesPower[i].bChannel != AR5416_BCHAN_UNUSED); i++) {
291                 if (freq == ath9k_hw_fbin2freq(pRdEdgesPower[i].bChannel, is2GHz)) {
292                         twiceMaxEdgePower = CTL_EDGE_TPOWER(pRdEdgesPower[i].ctl);
293                         break;
294                 } else if ((i > 0) &&
295                            (freq < ath9k_hw_fbin2freq(pRdEdgesPower[i].bChannel,
296                                                       is2GHz))) {
297                         if (ath9k_hw_fbin2freq(pRdEdgesPower[i - 1].bChannel,
298                                                is2GHz) < freq &&
299                             CTL_EDGE_FLAGS(pRdEdgesPower[i - 1].ctl)) {
300                                 twiceMaxEdgePower =
301                                         CTL_EDGE_TPOWER(pRdEdgesPower[i - 1].ctl);
302                         }
303                         break;
304                 }
305         }
306
307         return twiceMaxEdgePower;
308 }
309
310 u16 ath9k_hw_get_scaled_power(struct ath_hw *ah, u16 power_limit,
311                               u8 antenna_reduction)
312 {
313         u16 reduction = antenna_reduction;
314
315         /*
316          * Reduce scaled Power by number of chains active
317          * to get the per chain tx power level.
318          */
319         switch (ar5416_get_ntxchains(ah->txchainmask)) {
320         case 1:
321                 break;
322         case 2:
323                 reduction += POWER_CORRECTION_FOR_TWO_CHAIN;
324                 break;
325         case 3:
326                 reduction += POWER_CORRECTION_FOR_THREE_CHAIN;
327                 break;
328         }
329
330         if (power_limit > reduction)
331                 power_limit -= reduction;
332         else
333                 power_limit = 0;
334
335         return power_limit;
336 }
337
338 void ath9k_hw_update_regulatory_maxpower(struct ath_hw *ah)
339 {
340         struct ath_common *common = ath9k_hw_common(ah);
341         struct ath_regulatory *regulatory = ath9k_hw_regulatory(ah);
342
343         switch (ar5416_get_ntxchains(ah->txchainmask)) {
344         case 1:
345                 break;
346         case 2:
347                 regulatory->max_power_level += POWER_CORRECTION_FOR_TWO_CHAIN;
348                 break;
349         case 3:
350                 regulatory->max_power_level += POWER_CORRECTION_FOR_THREE_CHAIN;
351                 break;
352         default:
353                 ath_dbg(common, EEPROM, "Invalid chainmask configuration\n");
354                 break;
355         }
356 }
357
358 void ath9k_hw_get_gain_boundaries_pdadcs(struct ath_hw *ah,
359                                 struct ath9k_channel *chan,
360                                 void *pRawDataSet,
361                                 u8 *bChans, u16 availPiers,
362                                 u16 tPdGainOverlap,
363                                 u16 *pPdGainBoundaries, u8 *pPDADCValues,
364                                 u16 numXpdGains)
365 {
366         int i, j, k;
367         int16_t ss;
368         u16 idxL = 0, idxR = 0, numPiers;
369         static u8 vpdTableL[AR5416_NUM_PD_GAINS]
370                 [AR5416_MAX_PWR_RANGE_IN_HALF_DB];
371         static u8 vpdTableR[AR5416_NUM_PD_GAINS]
372                 [AR5416_MAX_PWR_RANGE_IN_HALF_DB];
373         static u8 vpdTableI[AR5416_NUM_PD_GAINS]
374                 [AR5416_MAX_PWR_RANGE_IN_HALF_DB];
375
376         u8 *pVpdL, *pVpdR, *pPwrL, *pPwrR;
377         u8 minPwrT4[AR5416_NUM_PD_GAINS];
378         u8 maxPwrT4[AR5416_NUM_PD_GAINS];
379         int16_t vpdStep;
380         int16_t tmpVal;
381         u16 sizeCurrVpdTable, maxIndex, tgtIndex;
382         bool match;
383         int16_t minDelta = 0;
384         struct chan_centers centers;
385         int pdgain_boundary_default;
386         struct cal_data_per_freq *data_def = pRawDataSet;
387         struct cal_data_per_freq_4k *data_4k = pRawDataSet;
388         struct cal_data_per_freq_ar9287 *data_9287 = pRawDataSet;
389         bool eeprom_4k = AR_SREV_9285(ah) || AR_SREV_9271(ah);
390         int intercepts;
391
392         if (AR_SREV_9287(ah))
393                 intercepts = AR9287_PD_GAIN_ICEPTS;
394         else
395                 intercepts = AR5416_PD_GAIN_ICEPTS;
396
397         memset(&minPwrT4, 0, AR5416_NUM_PD_GAINS);
398         ath9k_hw_get_channel_centers(ah, chan, &centers);
399
400         for (numPiers = 0; numPiers < availPiers; numPiers++) {
401                 if (bChans[numPiers] == AR5416_BCHAN_UNUSED)
402                         break;
403         }
404
405         match = ath9k_hw_get_lower_upper_index((u8)FREQ2FBIN(centers.synth_center,
406                                                              IS_CHAN_2GHZ(chan)),
407                                                bChans, numPiers, &idxL, &idxR);
408
409         if (match) {
410                 if (AR_SREV_9287(ah)) {
411                         /* FIXME: array overrun? */
412                         for (i = 0; i < numXpdGains; i++) {
413                                 minPwrT4[i] = data_9287[idxL].pwrPdg[i][0];
414                                 maxPwrT4[i] = data_9287[idxL].pwrPdg[i][4];
415                                 ath9k_hw_fill_vpd_table(minPwrT4[i], maxPwrT4[i],
416                                                 data_9287[idxL].pwrPdg[i],
417                                                 data_9287[idxL].vpdPdg[i],
418                                                 intercepts,
419                                                 vpdTableI[i]);
420                         }
421                 } else if (eeprom_4k) {
422                         for (i = 0; i < numXpdGains; i++) {
423                                 minPwrT4[i] = data_4k[idxL].pwrPdg[i][0];
424                                 maxPwrT4[i] = data_4k[idxL].pwrPdg[i][4];
425                                 ath9k_hw_fill_vpd_table(minPwrT4[i], maxPwrT4[i],
426                                                 data_4k[idxL].pwrPdg[i],
427                                                 data_4k[idxL].vpdPdg[i],
428                                                 intercepts,
429                                                 vpdTableI[i]);
430                         }
431                 } else {
432                         for (i = 0; i < numXpdGains; i++) {
433                                 minPwrT4[i] = data_def[idxL].pwrPdg[i][0];
434                                 maxPwrT4[i] = data_def[idxL].pwrPdg[i][4];
435                                 ath9k_hw_fill_vpd_table(minPwrT4[i], maxPwrT4[i],
436                                                 data_def[idxL].pwrPdg[i],
437                                                 data_def[idxL].vpdPdg[i],
438                                                 intercepts,
439                                                 vpdTableI[i]);
440                         }
441                 }
442         } else {
443                 for (i = 0; i < numXpdGains; i++) {
444                         if (AR_SREV_9287(ah)) {
445                                 pVpdL = data_9287[idxL].vpdPdg[i];
446                                 pPwrL = data_9287[idxL].pwrPdg[i];
447                                 pVpdR = data_9287[idxR].vpdPdg[i];
448                                 pPwrR = data_9287[idxR].pwrPdg[i];
449                         } else if (eeprom_4k) {
450                                 pVpdL = data_4k[idxL].vpdPdg[i];
451                                 pPwrL = data_4k[idxL].pwrPdg[i];
452                                 pVpdR = data_4k[idxR].vpdPdg[i];
453                                 pPwrR = data_4k[idxR].pwrPdg[i];
454                         } else {
455                                 pVpdL = data_def[idxL].vpdPdg[i];
456                                 pPwrL = data_def[idxL].pwrPdg[i];
457                                 pVpdR = data_def[idxR].vpdPdg[i];
458                                 pPwrR = data_def[idxR].pwrPdg[i];
459                         }
460
461                         minPwrT4[i] = max(pPwrL[0], pPwrR[0]);
462
463                         maxPwrT4[i] =
464                                 min(pPwrL[intercepts - 1],
465                                     pPwrR[intercepts - 1]);
466
467
468                         ath9k_hw_fill_vpd_table(minPwrT4[i], maxPwrT4[i],
469                                                 pPwrL, pVpdL,
470                                                 intercepts,
471                                                 vpdTableL[i]);
472                         ath9k_hw_fill_vpd_table(minPwrT4[i], maxPwrT4[i],
473                                                 pPwrR, pVpdR,
474                                                 intercepts,
475                                                 vpdTableR[i]);
476
477                         for (j = 0; j <= (maxPwrT4[i] - minPwrT4[i]) / 2; j++) {
478                                 vpdTableI[i][j] =
479                                         (u8)(ath9k_hw_interpolate((u16)
480                                              FREQ2FBIN(centers.
481                                                        synth_center,
482                                                        IS_CHAN_2GHZ
483                                                        (chan)),
484                                              bChans[idxL], bChans[idxR],
485                                              vpdTableL[i][j], vpdTableR[i][j]));
486                         }
487                 }
488         }
489
490         k = 0;
491
492         for (i = 0; i < numXpdGains; i++) {
493                 if (i == (numXpdGains - 1))
494                         pPdGainBoundaries[i] =
495                                 (u16)(maxPwrT4[i] / 2);
496                 else
497                         pPdGainBoundaries[i] =
498                                 (u16)((maxPwrT4[i] + minPwrT4[i + 1]) / 4);
499
500                 pPdGainBoundaries[i] =
501                         min((u16)MAX_RATE_POWER, pPdGainBoundaries[i]);
502
503                 minDelta = 0;
504
505                 if (i == 0) {
506                         if (AR_SREV_9280_20_OR_LATER(ah))
507                                 ss = (int16_t)(0 - (minPwrT4[i] / 2));
508                         else
509                                 ss = 0;
510                 } else {
511                         ss = (int16_t)((pPdGainBoundaries[i - 1] -
512                                         (minPwrT4[i] / 2)) -
513                                        tPdGainOverlap + 1 + minDelta);
514                 }
515                 vpdStep = (int16_t)(vpdTableI[i][1] - vpdTableI[i][0]);
516                 vpdStep = (int16_t)((vpdStep < 1) ? 1 : vpdStep);
517
518                 while ((ss < 0) && (k < (AR5416_NUM_PDADC_VALUES - 1))) {
519                         tmpVal = (int16_t)(vpdTableI[i][0] + ss * vpdStep);
520                         pPDADCValues[k++] = (u8)((tmpVal < 0) ? 0 : tmpVal);
521                         ss++;
522                 }
523
524                 sizeCurrVpdTable = (u8) ((maxPwrT4[i] - minPwrT4[i]) / 2 + 1);
525                 tgtIndex = (u8)(pPdGainBoundaries[i] + tPdGainOverlap -
526                                 (minPwrT4[i] / 2));
527                 maxIndex = (tgtIndex < sizeCurrVpdTable) ?
528                         tgtIndex : sizeCurrVpdTable;
529
530                 while ((ss < maxIndex) && (k < (AR5416_NUM_PDADC_VALUES - 1))) {
531                         pPDADCValues[k++] = vpdTableI[i][ss++];
532                 }
533
534                 vpdStep = (int16_t)(vpdTableI[i][sizeCurrVpdTable - 1] -
535                                     vpdTableI[i][sizeCurrVpdTable - 2]);
536                 vpdStep = (int16_t)((vpdStep < 1) ? 1 : vpdStep);
537
538                 if (tgtIndex >= maxIndex) {
539                         while ((ss <= tgtIndex) &&
540                                (k < (AR5416_NUM_PDADC_VALUES - 1))) {
541                                 tmpVal = (int16_t)((vpdTableI[i][sizeCurrVpdTable - 1] +
542                                                     (ss - maxIndex + 1) * vpdStep));
543                                 pPDADCValues[k++] = (u8)((tmpVal > 255) ?
544                                                          255 : tmpVal);
545                                 ss++;
546                         }
547                 }
548         }
549
550         if (eeprom_4k)
551                 pdgain_boundary_default = 58;
552         else
553                 pdgain_boundary_default = pPdGainBoundaries[i - 1];
554
555         while (i < AR5416_PD_GAINS_IN_MASK) {
556                 pPdGainBoundaries[i] = pdgain_boundary_default;
557                 i++;
558         }
559
560         while (k < AR5416_NUM_PDADC_VALUES) {
561                 pPDADCValues[k] = pPDADCValues[k - 1];
562                 k++;
563         }
564 }
565
566 int ath9k_hw_eeprom_init(struct ath_hw *ah)
567 {
568         int status;
569
570         if (AR_SREV_9300_20_OR_LATER(ah))
571                 ah->eep_ops = &eep_ar9300_ops;
572         else if (AR_SREV_9287(ah)) {
573                 ah->eep_ops = &eep_ar9287_ops;
574         } else if (AR_SREV_9285(ah) || AR_SREV_9271(ah)) {
575                 ah->eep_ops = &eep_4k_ops;
576         } else {
577                 ah->eep_ops = &eep_def_ops;
578         }
579
580         if (!ah->eep_ops->fill_eeprom(ah))
581                 return -EIO;
582
583         status = ah->eep_ops->check_eeprom(ah);
584
585         return status;
586 }