blob: b41eee47cc2d414ccf0aa3ab94de89b5b320b641 [file] [log] [blame]
David Hendricksce6b2fa2011-07-11 22:12:43 -07001/*
2 * This file is part of the flashrom project.
3 *
4 * Copyright (c) 2010 Matthias Wenzel <bios at mazzoo dot de>
5 * Copyright (c) 2011 Stefan Tauner
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22#include "ich_descriptors.h"
23
24#if defined(__i386__) || defined(__x86_64__)
25
26#ifdef ICH_DESCRIPTORS_FROM_MMAP_DUMP
27
28#define DESCRIPTOR_MODE_MAGIC 0x0ff0a55a
29#define msg_pdbg printf
30#define msg_perr printf
31#include <stdio.h>
32
33struct flash_strap fisba;
34struct flash_upper_map flumap;
35
36#else
37
38#include "flash.h" /* for msg_* */
39
40#endif // ICH_DESCRIPTORS_FROM_MMAP_DUMP
41
42struct flash_descriptor fdbar = { 0 };
43struct flash_component fcba = { {0} };
44struct flash_region frba = { {0} };
45struct flash_master fmba = { {0} };
46
47uint32_t getFCBA(void)
48{
49 return (fdbar.FLMAP0 << 4) & 0x00000ff0;
50}
51
52uint32_t getFRBA(void)
53{
54 return (fdbar.FLMAP0 >> 12) & 0x00000ff0;
55}
56
57uint32_t getFMBA(void)
58{
59 return (fdbar.FLMAP1 << 4) & 0x00000ff0;
60}
61
62uint32_t getFMSBA(void)
63{
64 return (fdbar.FLMAP2 << 4) & 0x00000ff0;
65}
66
67uint32_t getFLREG_limit(uint32_t flreg)
68{
69 return (flreg >> 4) & 0x01fff000;
70}
71
72uint32_t getFLREG_base(uint32_t flreg)
73{
74 return (flreg << 12) & 0x01fff000;
75}
76
77uint32_t getFISBA(void)
78{
79 return (fdbar.FLMAP1 >> 12) & 0x00000ff0;
80}
81
82/** Returns the integer representation of the component density with index
83comp in bytes or 0 if a correct size can not be determined. */
84int getFCBA_component_density(uint8_t comp)
85{
86 uint8_t size_enc;
87 const int dec_mem[6] = {
88 512 * 1024,
89 1 * 1024 * 1024,
90 2 * 1024 * 1024,
91 4 * 1024 * 1024,
92 8 * 1024 * 1024,
93 16 * 1024 * 1024,
94 };
95
96 switch(comp) {
97 case 0:
98 size_enc = fcba.comp1_density;
99 break;
100 case 1:
101 if (fdbar.NC == 0)
102 return 0;
103 size_enc = fcba.comp2_density;
104 break;
105 default:
106 msg_perr("Only component index 0 or 1 are supported yet.\n");
107 return 0;
108 }
109 if (size_enc > 5) {
110 msg_perr("Density of component with index %d illegal or "
111 "unsupported. Encoded density is 0x%x.\n", comp,
112 size_enc);
113 return 0;
114 }
115 return dec_mem[size_enc];
116}
117
118#ifdef ICH_DESCRIPTORS_FROM_MMAP_DUMP
119uint32_t getVTBA(void)
120{ /* The bits in FLUMAP1 describe bits 4-11 out of 24 in total;
121 * others are 0. */
122 return (flumap.FLUMAP1 << 4) & 0x0ff0;
123}
124#endif
125
126const struct flash_descriptor_addresses desc_addr = {
127 .FCBA = getFCBA,
128 .FRBA = getFRBA,
129 .FMBA = getFMBA,
130 .FMSBA = getFMSBA,
131 .FISBA = getFISBA,
132 .FLREG_limit = getFLREG_limit,
133 .FLREG_base = getFLREG_base,
134#ifdef ICH_DESCRIPTORS_FROM_MMAP_DUMP
135 .VTBA = getVTBA,
136#endif
137};
138
139void prettyprint_ich_descriptors(enum chipset cs)
140{
141 prettyprint_ich_descriptor_map();
142 prettyprint_ich_descriptor_component();
143 prettyprint_ich_descriptor_region();
144 prettyprint_ich_descriptor_master();
145#ifdef ICH_DESCRIPTORS_FROM_MMAP_DUMP
146 if (cs >= CHIPSET_ICH8) {
147 prettyprint_ich_descriptor_upper_map();
148 prettyprint_ich_descriptor_straps(cs);
149 }
150#endif // ICH_DESCRIPTORS_FROM_MMAP_DUMP
151 msg_pdbg("\n");
152}
153
154void prettyprint_ich_descriptor_map(void)
155{
156 msg_pdbg("=== FDBAR ===\n");
157 msg_pdbg("FLVALSIG 0x%8.8x\n", fdbar.FLVALSIG);
158 msg_pdbg("FLMAP0 0x%8.8x\n", fdbar.FLMAP0 );
159 msg_pdbg("FLMAP1 0x%8.8x\n", fdbar.FLMAP1 );
160 msg_pdbg("FLMAP2 0x%8.8x\n", fdbar.FLMAP2 );
161 msg_pdbg("\n");
162 msg_pdbg("--- FDBAR details ---\n");
163 msg_pdbg("0x%2.2x NR Number of Regions\n", fdbar.NR );
164 msg_pdbg("0x%8.8x FRBA Flash Region Base Address\n", desc_addr.FRBA() );
165 msg_pdbg("0x%2.2x NC Number of Components\n", fdbar.NC );
166 msg_pdbg("0x%8.8x FCBA Flash Component Base Address\n", desc_addr.FCBA() );
167 msg_pdbg("\n");
168 msg_pdbg("0x%2.2x ISL ICH Strap Length\n", fdbar.ISL );
169 msg_pdbg("0x%8.8x FISBA Flash ICH Strap Base Address\n", desc_addr.FISBA());
170 msg_pdbg("0x%2.2x NM Number of Masters\n", fdbar.NM );
171 msg_pdbg("0x%8.8x FMBA Flash Master Base Address\n", desc_addr.FMBA() );
172 msg_pdbg("\n");
173 msg_pdbg("0x%2.2x MSL MCH Strap Length\n", fdbar.MSL );
174 msg_pdbg("0x%8.8x FMSBA Flash MCH Strap Base Address\n", desc_addr.FMSBA());
175}
176
177void prettyprint_ich_descriptor_component(void)
178{
179 const char * const str_freq[8] = {
180 "20 MHz", /* 000 */
181 "33 MHz", /* 001 */
182 "reserved/illegal", /* 010 */
183 "reserved/illegal", /* 011 */
184 "50 MHz", /* 100 */
185 "reserved/illegal", /* 101 */
186 "reserved/illegal", /* 110 */
187 "reserved/illegal" /* 111 */
188 };
189 const char * const str_mem[8] = {
190 "512kB",
191 "1 MB",
192 "2 MB",
193 "4 MB",
194 "8 MB",
195 "16 MB",
196 "undocumented/illegal",
197 "reserved/illegal"
198 };
199
200 msg_pdbg("\n");
201 msg_pdbg("=== FCBA ===\n");
202 msg_pdbg("FLCOMP 0x%8.8x\n", fcba.FLCOMP);
203 msg_pdbg("FLILL 0x%8.8x\n", fcba.FLILL );
204 msg_pdbg("\n");
205 msg_pdbg("--- FCBA details ---\n");
206 msg_pdbg("0x%2.2x freq_read_id %s\n",
207 fcba.freq_read_id , str_freq[fcba.freq_read_id ]);
208 msg_pdbg("0x%2.2x freq_write %s\n",
209 fcba.freq_write , str_freq[fcba.freq_write ]);
210 msg_pdbg("0x%2.2x freq_fastread %s\n",
211 fcba.freq_fastread, str_freq[fcba.freq_fastread]);
212 msg_pdbg("0x%2.2x fastread %ssupported\n",
213 fcba.fastread, fcba.fastread ? "" : "not ");
214 msg_pdbg("0x%2.2x freq_read %s\n",
215 fcba.freq_read, str_freq[fcba.freq_read ]);
216 msg_pdbg("0x%2.2x comp 1 density %s\n",
217 fcba.comp1_density, str_mem[fcba.comp1_density]);
218 if (fdbar.NC)
219 msg_pdbg("0x%2.2x comp 2 density %s\n",
220 fcba.comp2_density, str_mem[fcba.comp2_density]);
221 else
222 msg_pdbg("0x%2.2x comp 2 is not used (FLMAP0.NC=0)\n",
223 fcba.comp2_density);
224 msg_pdbg("\n");
225 msg_pdbg("0x%2.2x invalid instr 0\n", fcba.invalid_instr0);
226 msg_pdbg("0x%2.2x invalid instr 1\n", fcba.invalid_instr1);
227 msg_pdbg("0x%2.2x invalid instr 2\n", fcba.invalid_instr2);
228 msg_pdbg("0x%2.2x invalid instr 3\n", fcba.invalid_instr3);
229}
230
231void prettyprint_ich_descriptor_region(void)
232{
233 msg_pdbg("\n");
234 msg_pdbg("=== FRBA ===\n");
235 msg_pdbg("FLREG0 0x%8.8x\n", frba.FLREG0);
236 msg_pdbg("FLREG1 0x%8.8x\n", frba.FLREG1);
237 msg_pdbg("FLREG2 0x%8.8x\n", frba.FLREG2);
238 msg_pdbg("FLREG3 0x%8.8x\n", frba.FLREG3);
239 msg_pdbg("\n");
240 msg_pdbg("--- FRBA details ---\n");
241 msg_pdbg("0x%8.8x region 0 limit (descr)\n", desc_addr.FLREG_limit(frba.FLREG0));
242 msg_pdbg("0x%8.8x region 0 base (descr)\n", desc_addr.FLREG_base(frba.FLREG0));
243 msg_pdbg("0x%8.8x region 1 limit ( BIOS)\n", desc_addr.FLREG_limit(frba.FLREG1));
244 msg_pdbg("0x%8.8x region 1 base ( BIOS)\n", desc_addr.FLREG_base(frba.FLREG1));
245 msg_pdbg("0x%8.8x region 2 limit ( ME )\n", desc_addr.FLREG_limit(frba.FLREG2));
246 msg_pdbg("0x%8.8x region 2 base ( ME )\n", desc_addr.FLREG_base(frba.FLREG2));
247 msg_pdbg("0x%8.8x region 3 limit ( GbE )\n", desc_addr.FLREG_limit(frba.FLREG3));
248 msg_pdbg("0x%8.8x region 3 base ( GbE )\n", desc_addr.FLREG_base(frba.FLREG3));
249}
250
251void prettyprint_ich_descriptor_master(void)
252{
253 msg_pdbg("\n");
254 msg_pdbg("=== FMBA ===\n");
255 msg_pdbg("FLMSTR1 0x%8.8x\n", fmba.FLMSTR1);
256 msg_pdbg("FLMSTR2 0x%8.8x\n", fmba.FLMSTR2);
257 msg_pdbg("FLMSTR3 0x%8.8x\n", fmba.FLMSTR3);
258
259 msg_pdbg("\n");
260 msg_pdbg("--- FMBA details ---\n");
261 msg_pdbg("BIOS can %s write GbE\n", fmba.BIOS_GbE_write ? " " : "NOT");
262 msg_pdbg("BIOS can %s write ME\n", fmba.BIOS_ME_write ? " " : "NOT");
263 msg_pdbg("BIOS can %s write BIOS\n", fmba.BIOS_BIOS_write ? " " : "NOT");
264 msg_pdbg("BIOS can %s write descr\n", fmba.BIOS_descr_write ? " " : "NOT");
265 msg_pdbg("BIOS can %s read GbE\n", fmba.BIOS_GbE_read ? " " : "NOT");
266 msg_pdbg("BIOS can %s read ME\n", fmba.BIOS_ME_read ? " " : "NOT");
267 msg_pdbg("BIOS can %s read BIOS\n", fmba.BIOS_BIOS_read ? " " : "NOT");
268 msg_pdbg("BIOS can %s read descr\n", fmba.BIOS_descr_read ? " " : "NOT");
269 msg_pdbg("ME can %s write GbE\n", fmba.ME_GbE_write ? " " : "NOT");
270 msg_pdbg("ME can %s write ME\n", fmba.ME_ME_write ? " " : "NOT");
271 msg_pdbg("ME can %s write BIOS\n", fmba.ME_BIOS_write ? " " : "NOT");
272 msg_pdbg("ME can %s write descr\n", fmba.ME_descr_write ? " " : "NOT");
273 msg_pdbg("ME can %s read GbE\n", fmba.ME_GbE_read ? " " : "NOT");
274 msg_pdbg("ME can %s read ME\n", fmba.ME_ME_read ? " " : "NOT");
275 msg_pdbg("ME can %s read BIOS\n", fmba.ME_BIOS_read ? " " : "NOT");
276 msg_pdbg("ME can %s read descr\n", fmba.ME_descr_read ? " " : "NOT");
277 msg_pdbg("GbE can %s write GbE\n", fmba.GbE_GbE_write ? " " : "NOT");
278 msg_pdbg("GbE can %s write ME\n", fmba.GbE_ME_write ? " " : "NOT");
279 msg_pdbg("GbE can %s write BIOS\n", fmba.GbE_BIOS_write ? " " : "NOT");
280 msg_pdbg("GbE can %s write descr\n", fmba.GbE_descr_write ? " " : "NOT");
281 msg_pdbg("GbE can %s read GbE\n", fmba.GbE_GbE_read ? " " : "NOT");
282 msg_pdbg("GbE can %s read ME\n", fmba.GbE_ME_read ? " " : "NOT");
283 msg_pdbg("GbE can %s read BIOS\n", fmba.GbE_BIOS_read ? " " : "NOT");
284 msg_pdbg("GbE can %s read descr\n", fmba.GbE_descr_read ? " " : "NOT");
285}
286
287void prettyprint_ich9_reg_vscc(uint32_t reg_val)
288{
289 pprint_reg_hex(VSCC, BES, reg_val, ", ");
290 pprint_reg(VSCC, WG, reg_val, ", ");
291 pprint_reg(VSCC, WSR, reg_val, ", ");
292 pprint_reg(VSCC, WEWS, reg_val, ", ");
293 pprint_reg_hex(VSCC, EO, reg_val, ", ");
294 pprint_reg(VSCC, VCL, reg_val, "\n");
295}
296
297
298#ifdef ICH_DESCRIPTORS_FROM_MMAP_DUMP
299void prettyprint_ich_descriptor_straps_ich8(void)
300{
301 const char * const str_GPIO12[4] = {
302 "GPIO12",
303 "LAN PHY Power Control Function (Native Output)",
304 "GLAN_DOCK# (Native Input)",
305 "invalid configuration",
306 };
307
308 msg_pdbg("\n");
309 msg_pdbg("=== FISBA ===\n");
310 msg_pdbg("STRP0 0x%8.8x\n", fisba.ich8.STRP0);
311
312 msg_pdbg("\n");
313 msg_pdbg("--- FISBA details ---\n");
314 msg_pdbg("ME SMBus addr2 0x%2.2x\n", fisba.ich8.ASD2);
315 msg_pdbg("ME SMBus addr1 0x%2.2x\n", fisba.ich8.ASD);
316 msg_pdbg("ME SMBus Controller is connected to %s\n", fisba.ich8.MESM2SEL ? "SMLink pins" : "SMBus pins");
317 msg_pdbg("SPI CS1 is used for %s\n", fisba.ich8.SPICS1_LANPHYPC_SEL ? "LAN PHY Power Control Function" : "SPI Chip Select");
318 msg_pdbg("GPIO12_SEL is used as %s\n", str_GPIO12[fisba.ich8.GPIO12_SEL]);
319 msg_pdbg("PCIe Port 6 is used for %s\n", fisba.ich8.GLAN_PCIE_SEL ? "integrated GLAN" : "PCI Express");
320 msg_pdbg("Intel AMT SMBus Controller 1 is connected to %s\n", fisba.ich8.BMCMODE ? "SMLink" : "SMBus");
321 msg_pdbg("TCO slave is on %s. Intel AMT SMBus Controller 1 is %sabled\n",
322 fisba.ich8.TCOMODE ? "SMBus" : "SMLink", fisba.ich8.TCOMODE ? "en" : "dis");
323 msg_pdbg("ME A is %sabled\n", fisba.ich8.ME_DISABLE ? "dis" : "en");
324
325 msg_pdbg("\n");
326 msg_pdbg("=== FMSBA ===\n");
327 msg_pdbg("STRP1 0x%8.8x\n", fisba.ich8.STRP1);
328
329 msg_pdbg("\n");
330 msg_pdbg("--- FMSBA details ---\n");
331 msg_pdbg("ME B is %sabled\n", fisba.ich8.ME_disable_B ? "dis" : "en");
332}
333
334void prettyprint_ich_descriptor_straps_ibex(void)
335{
336 int i;
337 msg_pdbg("\n");
338 msg_pdbg("=== FPSBA ===\n");
339 for(i = 0; i <= 15; i++)
340 msg_pdbg("STRP%-2d = 0x%8.8x\n", i, fisba.ibex.STRPs[i]);
341}
342
343void prettyprint_ich_descriptor_straps(enum chipset cs)
344{
345 switch (cs) {
346 case CHIPSET_ICH8:
347 prettyprint_ich_descriptor_straps_ich8();
348 break;
349 case CHIPSET_SERIES_5_IBEX_PEAK:
350 prettyprint_ich_descriptor_straps_ibex();
351 break;
352 case CHIPSET_UNKNOWN:
353 break;
354 default:
355 msg_pdbg("\n");
356 msg_pdbg("The meaning of the descriptor straps are unknown yet.\n");
357 break;
358 }
359}
360
361void prettyprint_rdid(uint32_t reg_val)
362{
363 uint8_t mid = reg_val & 0xFF;
364 uint16_t did = ((reg_val >> 16) & 0xFF) | (reg_val & 0xFF00);
365 msg_pdbg("Manufacturer ID 0x%02x, Device ID 0x%04x\n", mid, did);
366}
367
368void prettyprint_ich_descriptor_upper_map(void)
369{
370 int i;
371 msg_pdbg("\n");
372 msg_pdbg("=== FLUMAP ===\n");
373 msg_pdbg("FLUMAP1 0x%8.8x\n", flumap.FLUMAP1);
374
375 msg_pdbg("\n");
376 msg_pdbg("--- FLUMAP details ---\n");
377 msg_pdbg("VTL (length) = %d\n", flumap.VTL);
378 msg_pdbg("VTBA (base address) = 0x%6.6x\n", desc_addr.VTBA());
379 msg_pdbg("\n");
380
381 for (i=0; i < flumap.VTL/2; i++)
382 {
383 uint32_t jid = flumap.vscc_table[i].JID;
384 uint32_t vscc = flumap.vscc_table[i].VSCC;
385 msg_pdbg(" JID%d = 0x%8.8x\n", i, jid);
386 msg_pdbg(" VSCC%d = 0x%8.8x\n", i, vscc);
387 msg_pdbg(" "); /* indention */
388 prettyprint_rdid(jid);
389 msg_pdbg(" "); /* indention */
390 prettyprint_ich9_reg_vscc(vscc);
391 }
392}
393
394int read_ich_descriptors_from_dump(uint32_t *dump, enum chipset cs)
395{
396 int i;
397 uint8_t pch_bug_offset = 0;
398 if (dump[0] != DESCRIPTOR_MODE_MAGIC) {
399 if (dump[4] == DESCRIPTOR_MODE_MAGIC)
400 pch_bug_offset = 4;
401 else
402 return -1;
403 }
404
405 /* map */
406 fdbar.FLVALSIG = dump[0 + pch_bug_offset];
407 fdbar.FLMAP0 = dump[1 + pch_bug_offset];
408 fdbar.FLMAP1 = dump[2 + pch_bug_offset];
409 fdbar.FLMAP2 = dump[3 + pch_bug_offset];
410
411 /* component */
412 fcba.FLCOMP = dump[(desc_addr.FCBA() >> 2) + 0];
413 fcba.FLILL = dump[(desc_addr.FCBA() >> 2) + 1];
414 fcba.FLPB = dump[(desc_addr.FCBA() >> 2) + 2];
415
416 /* region */
417 frba.FLREG0 = dump[(desc_addr.FRBA() >> 2) + 0];
418 frba.FLREG1 = dump[(desc_addr.FRBA() >> 2) + 1];
419 frba.FLREG2 = dump[(desc_addr.FRBA() >> 2) + 2];
420 frba.FLREG3 = dump[(desc_addr.FRBA() >> 2) + 3];
421
422 /* master */
423 fmba.FLMSTR1 = dump[(desc_addr.FMBA() >> 2) + 0];
424 fmba.FLMSTR2 = dump[(desc_addr.FMBA() >> 2) + 1];
425 fmba.FLMSTR3 = dump[(desc_addr.FMBA() >> 2) + 2];
426
427 /* upper map */
428 flumap.FLUMAP1 = dump[(0x0efc >> 2) + 0];
429
430 for (i=0; i < flumap.VTL; i++)
431 {
432 flumap.vscc_table[i].JID = dump[(desc_addr.VTBA() >> 2) + i * 2 + 0];
433 flumap.vscc_table[i].VSCC = dump[(desc_addr.VTBA() >> 2) + i * 2 + 1];
434 }
435 /* straps */
436 /* FIXME: detect chipset correctly */
437 switch (cs) {
438 case CHIPSET_ICH8:
439 fisba.ich8.STRP0 = dump[(desc_addr.FISBA() >> 2) + 0];
440 fisba.ich8.STRP1 = dump[(desc_addr.FMSBA() >> 2) + 0];
441 break;
442 case CHIPSET_SERIES_5_IBEX_PEAK:
443 for(i = 0; i <= 15; i++)
444 fisba.ibex.STRPs[i] = dump[(desc_addr.FISBA() >> 2) + i];
445 break;
446 default:
447 break;
448 }
449
450 return 0;
451}
452#else // ICH_DESCRIPTORS_FROM_MMAP_DUMP
453
454static uint32_t read_descriptor_reg(uint8_t section, uint16_t offset, void *spibar)
455{
456 uint32_t control = 0;
457 control |= (section << FDOC_FDSS_OFF) & FDOC_FDSS;
458 control |= (offset << FDOC_FDSI_OFF) & FDOC_FDSI;
459 *(volatile uint32_t *) (spibar + ICH9_REG_FDOC) = control;
460 return *(volatile uint32_t *)(spibar + ICH9_REG_FDOD);
461}
462
463void read_ich_descriptors_from_fdo(void *spibar)
464{
465 msg_pdbg("Reading flash descriptors "
466 "mapped by the chipset via FDOC/FDOD...");
467 /* descriptor map section */
468 fdbar.FLVALSIG = read_descriptor_reg(0, 0, spibar);
469 fdbar.FLMAP0 = read_descriptor_reg(0, 1, spibar);
470 fdbar.FLMAP1 = read_descriptor_reg(0, 2, spibar);
471 fdbar.FLMAP2 = read_descriptor_reg(0, 3, spibar);
472
473 /* component section */
474 fcba.FLCOMP = read_descriptor_reg(1, 0, spibar);
475 fcba.FLILL = read_descriptor_reg(1, 1, spibar);
476 fcba.FLPB = read_descriptor_reg(1, 2, spibar);
477
478 /* region section */
479 frba.FLREG0 = read_descriptor_reg(2, 0, spibar);
480 frba.FLREG1 = read_descriptor_reg(2, 1, spibar);
481 frba.FLREG2 = read_descriptor_reg(2, 2, spibar);
482 frba.FLREG3 = read_descriptor_reg(2, 3, spibar);
483
484 /* master section */
485 fmba.FLMSTR1 = read_descriptor_reg(3, 0, spibar);
486 fmba.FLMSTR2 = read_descriptor_reg(3, 1, spibar);
487 fmba.FLMSTR3 = read_descriptor_reg(3, 2, spibar);
488
489 /* accessing strap section and upper map is impossible via FDOC/D(?) */
490 msg_pdbg(" done\n");
491}
492
493#endif // ICH_DESCRIPTORS_FROM_MMAP_DUMP
494#endif // defined(__i386__) || defined(__x86_64__)