blob: 0b150b20f3a1e312aab79055e6bea27738e05245 [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0
Stefan Roeseae6223d2015-01-19 11:33:40 +01002/*
3 * Copyright (C) Marvell International Ltd. and its affiliates
Stefan Roeseae6223d2015-01-19 11:33:40 +01004 */
5
6#include <common.h>
7#include <i2c.h>
8#include <spl.h>
9#include <asm/io.h>
10#include <asm/arch/cpu.h>
11#include <asm/arch/soc.h>
12
13#include "ddr3_hw_training.h"
14#include "xor.h"
15#include "xor_regs.h"
16
17static void ddr3_flush_l1_line(u32 line);
18
19extern u32 pbs_pattern[2][LEN_16BIT_PBS_PATTERN];
20extern u32 pbs_pattern_32b[2][LEN_PBS_PATTERN];
21#if defined(MV88F78X60)
22extern u32 pbs_pattern_64b[2][LEN_PBS_PATTERN];
23#endif
Marek BehĂșnc61e5e42021-03-07 00:00:34 +010024extern u32 pbs_dq_mapping[PUP_NUM_64BIT + 1][DQ_NUM];
Stefan Roeseae6223d2015-01-19 11:33:40 +010025
26#if defined(MV88F78X60) || defined(MV88F672X)
27/* PBS locked dq (per pup) */
28u32 pbs_locked_dq[MAX_PUP_NUM][DQ_NUM] = { { 0 } };
29u32 pbs_locked_dm[MAX_PUP_NUM] = { 0 };
30u32 pbs_locked_value[MAX_PUP_NUM][DQ_NUM] = { { 0 } };
31
32int per_bit_data[MAX_PUP_NUM][DQ_NUM];
33#endif
34
35static u32 sdram_data[LEN_KILLER_PATTERN] __aligned(32) = { 0 };
36
37static struct crc_dma_desc dma_desc __aligned(32) = { 0 };
38
39#define XOR_TIMEOUT 0x8000000
40
41struct xor_channel_t {
42 struct crc_dma_desc *desc;
43 unsigned long desc_phys_addr;
44};
45
46#define XOR_CAUSE_DONE_MASK(chan) ((0x1 | 0x2) << (chan * 16))
47
48void xor_waiton_eng(int chan)
49{
50 int timeout;
51
52 timeout = 0;
53 while (!(reg_read(XOR_CAUSE_REG(XOR_UNIT(chan))) &
54 XOR_CAUSE_DONE_MASK(XOR_CHAN(chan)))) {
55 if (timeout > XOR_TIMEOUT)
56 goto timeout;
57
58 timeout++;
59 }
60
61 timeout = 0;
62 while (mv_xor_state_get(chan) != MV_IDLE) {
63 if (timeout > XOR_TIMEOUT)
64 goto timeout;
65
66 timeout++;
67 }
68
69 /* Clear int */
70 reg_write(XOR_CAUSE_REG(XOR_UNIT(chan)),
71 ~(XOR_CAUSE_DONE_MASK(XOR_CHAN(chan))));
72
73timeout:
74 return;
75}
76
77static int special_compare_pattern(u32 uj)
78{
79 if ((uj == 30) || (uj == 31) || (uj == 61) || (uj == 62) ||
80 (uj == 93) || (uj == 94) || (uj == 126) || (uj == 127))
81 return 1;
82
83 return 0;
84}
85
86/*
87 * Compare code extracted as its used by multiple functions. This
88 * reduces code-size and makes it easier to maintain it. Additionally
89 * the code is not indented that much and therefore easier to read.
90 */
91static void compare_pattern_v1(u32 uj, u32 *pup, u32 *pattern,
92 u32 pup_groups, int debug_dqs)
93{
94 u32 val;
95 u32 uk;
96 u32 var1;
97 u32 var2;
98 __maybe_unused u32 dq;
99
100 if (((sdram_data[uj]) != (pattern[uj])) && (*pup != 0xFF)) {
101 for (uk = 0; uk < PUP_NUM_32BIT; uk++) {
102 val = CMP_BYTE_SHIFT * uk;
103 var1 = ((sdram_data[uj] >> val) & CMP_BYTE_MASK);
104 var2 = ((pattern[uj] >> val) & CMP_BYTE_MASK);
105
106 if (var1 != var2) {
107 *pup |= (1 << (uk + (PUP_NUM_32BIT *
108 (uj % pup_groups))));
109
110#ifdef MV_DEBUG_DQS
111 if (!debug_dqs)
112 continue;
113
114 for (dq = 0; dq < DQ_NUM; dq++) {
115 val = uk + (PUP_NUM_32BIT *
116 (uj % pup_groups));
117 if (((var1 >> dq) & 0x1) !=
118 ((var2 >> dq) & 0x1))
119 per_bit_data[val][dq] = 1;
120 else
121 per_bit_data[val][dq] = 0;
122 }
123#endif
124 }
125 }
126 }
127}
128
129static void compare_pattern_v2(u32 uj, u32 *pup, u32 *pattern)
130{
131 u32 val;
132 u32 uk;
133 u32 var1;
134 u32 var2;
135
136 if (((sdram_data[uj]) != (pattern[uj])) && (*pup != 0x3)) {
137 /* Found error */
138 for (uk = 0; uk < PUP_NUM_32BIT; uk++) {
139 val = CMP_BYTE_SHIFT * uk;
140 var1 = (sdram_data[uj] >> val) & CMP_BYTE_MASK;
141 var2 = (pattern[uj] >> val) & CMP_BYTE_MASK;
142 if (var1 != var2)
143 *pup |= (1 << (uk % PUP_NUM_16BIT));
144 }
145 }
146}
147
148/*
149 * Name: ddr3_sdram_compare
150 * Desc: Execute compare per PUP
151 * Args: unlock_pup Bit array of the unlock pups
152 * new_locked_pup Output bit array of the pups with failed compare
153 * pattern Pattern to compare
154 * pattern_len Length of pattern (in bytes)
155 * sdram_offset offset address to the SDRAM
156 * write write to the SDRAM before read
157 * mask compare pattern with mask;
158 * mask_pattern Mask to compare pattern
159 *
160 * Notes:
161 * Returns: MV_OK if success, other error code if fail.
162 */
163int ddr3_sdram_compare(MV_DRAM_INFO *dram_info, u32 unlock_pup,
164 u32 *new_locked_pup, u32 *pattern,
165 u32 pattern_len, u32 sdram_offset, int write,
166 int mask, u32 *mask_pattern,
167 int special_compare)
168{
169 u32 uj;
170 __maybe_unused u32 pup_groups;
171 __maybe_unused u32 dq;
172
173#if !defined(MV88F67XX)
174 if (dram_info->num_of_std_pups == PUP_NUM_64BIT)
175 pup_groups = 2;
176 else
177 pup_groups = 1;
178#endif
179
180 ddr3_reset_phy_read_fifo();
181
182 /* Check if need to write to sdram before read */
183 if (write == 1)
184 ddr3_dram_sram_burst((u32)pattern, sdram_offset, pattern_len);
185
186 ddr3_dram_sram_burst(sdram_offset, (u32)sdram_data, pattern_len);
187
188 /* Compare read result to write */
189 for (uj = 0; uj < pattern_len; uj++) {
190 if (special_compare && special_compare_pattern(uj))
191 continue;
192
193#if defined(MV88F78X60) || defined(MV88F672X)
194 compare_pattern_v1(uj, new_locked_pup, pattern, pup_groups, 1);
195#elif defined(MV88F67XX)
196 compare_pattern_v2(uj, new_locked_pup, pattern);
197#endif
198 }
199
200 return MV_OK;
201}
202
203#if defined(MV88F78X60) || defined(MV88F672X)
204/*
205 * Name: ddr3_sdram_dm_compare
206 * Desc: Execute compare per PUP
207 * Args: unlock_pup Bit array of the unlock pups
208 * new_locked_pup Output bit array of the pups with failed compare
209 * pattern Pattern to compare
210 * pattern_len Length of pattern (in bytes)
211 * sdram_offset offset address to the SDRAM
212 * write write to the SDRAM before read
213 * mask compare pattern with mask;
214 * mask_pattern Mask to compare pattern
215 *
216 * Notes:
217 * Returns: MV_OK if success, other error code if fail.
218 */
219int ddr3_sdram_dm_compare(MV_DRAM_INFO *dram_info, u32 unlock_pup,
220 u32 *new_locked_pup, u32 *pattern,
221 u32 sdram_offset)
222{
223 u32 uj, uk, var1, var2, pup_groups;
224 u32 val;
225 u32 pup = 0;
226
227 if (dram_info->num_of_std_pups == PUP_NUM_64BIT)
228 pup_groups = 2;
229 else
230 pup_groups = 1;
231
232 ddr3_dram_sram_burst((u32)pattern, SDRAM_PBS_TX_OFFS,
233 LEN_PBS_PATTERN);
234 ddr3_dram_sram_burst(SDRAM_PBS_TX_OFFS, (u32)sdram_data,
235 LEN_PBS_PATTERN);
236
237 /* Validate the correctness of the results */
238 for (uj = 0; uj < LEN_PBS_PATTERN; uj++)
239 compare_pattern_v1(uj, &pup, pattern, pup_groups, 0);
240
241 /* Test the DM Signals */
242 *(u32 *)(SDRAM_PBS_TX_OFFS + 0x10) = 0x12345678;
243 *(u32 *)(SDRAM_PBS_TX_OFFS + 0x14) = 0x12345678;
244
245 sdram_data[0] = *(u32 *)(SDRAM_PBS_TX_OFFS + 0x10);
246 sdram_data[1] = *(u32 *)(SDRAM_PBS_TX_OFFS + 0x14);
247
248 for (uj = 0; uj < 2; uj++) {
249 if (((sdram_data[uj]) != (pattern[uj])) &&
250 (*new_locked_pup != 0xFF)) {
251 for (uk = 0; uk < PUP_NUM_32BIT; uk++) {
252 val = CMP_BYTE_SHIFT * uk;
253 var1 = ((sdram_data[uj] >> val) & CMP_BYTE_MASK);
254 var2 = ((pattern[uj] >> val) & CMP_BYTE_MASK);
255 if (var1 != var2) {
256 *new_locked_pup |= (1 << (uk +
257 (PUP_NUM_32BIT * (uj % pup_groups))));
258 *new_locked_pup |= pup;
259 }
260 }
261 }
262 }
263
264 return MV_OK;
265}
266
267/*
268 * Name: ddr3_sdram_pbs_compare
269 * Desc: Execute SRAM compare per PUP and DQ.
270 * Args: pup_locked bit array of locked pups
271 * is_tx Indicate whether Rx or Tx
272 * pbs_pattern_idx Index of PBS pattern
273 * pbs_curr_val The PBS value
274 * pbs_lock_val The value to set to locked PBS
275 * skew_array Global array to update with the compare results
276 * ai_unlock_pup_dq_array bit array of the locked / unlocked pups per dq.
277 * Notes:
278 * Returns: MV_OK if success, other error code if fail.
279 */
280int ddr3_sdram_pbs_compare(MV_DRAM_INFO *dram_info, u32 pup_locked,
281 int is_tx, u32 pbs_pattern_idx,
282 u32 pbs_curr_val, u32 pbs_lock_val,
283 u32 *skew_array, u8 *unlock_pup_dq_array,
284 u32 ecc)
285{
286 /* bit array failed dq per pup for current compare */
287 u32 pbs_write_pup[DQ_NUM] = { 0 };
288 u32 update_pup; /* pup as HW convention */
289 u32 max_pup; /* maximal pup index */
290 u32 pup_addr;
291 u32 ui, dq, pup;
292 int var1, var2;
293 u32 sdram_offset, pup_groups, tmp_pup;
294 u32 *pattern_ptr;
295 u32 val;
296
297 /* Choose pattern */
298 switch (dram_info->ddr_width) {
299#if defined(MV88F672X)
300 case 16:
301 pattern_ptr = (u32 *)&pbs_pattern[pbs_pattern_idx];
302 break;
303#endif
304 case 32:
305 pattern_ptr = (u32 *)&pbs_pattern_32b[pbs_pattern_idx];
306 break;
307#if defined(MV88F78X60)
308 case 64:
309 pattern_ptr = (u32 *)&pbs_pattern_64b[pbs_pattern_idx];
310 break;
311#endif
312 default:
313 return MV_FAIL;
314 }
315
316 max_pup = dram_info->num_of_std_pups;
317
318 sdram_offset = SDRAM_PBS_I_OFFS + pbs_pattern_idx * SDRAM_PBS_NEXT_OFFS;
319
320 if (dram_info->num_of_std_pups == PUP_NUM_64BIT)
321 pup_groups = 2;
322 else
323 pup_groups = 1;
324
325 ddr3_reset_phy_read_fifo();
326
327 /* Check if need to write to sdram before read */
328 if (is_tx == 1) {
329 ddr3_dram_sram_burst((u32)pattern_ptr, sdram_offset,
330 LEN_PBS_PATTERN);
331 }
332
333 ddr3_dram_sram_read(sdram_offset, (u32)sdram_data, LEN_PBS_PATTERN);
334
335 /* Compare read result to write */
336 for (ui = 0; ui < LEN_PBS_PATTERN; ui++) {
337 if ((sdram_data[ui]) != (pattern_ptr[ui])) {
338 /* found error */
339 /* error in low pup group */
340 for (pup = 0; pup < PUP_NUM_32BIT; pup++) {
341 val = CMP_BYTE_SHIFT * pup;
342 var1 = ((sdram_data[ui] >> val) &
343 CMP_BYTE_MASK);
344 var2 = ((pattern_ptr[ui] >> val) &
345 CMP_BYTE_MASK);
346
347 if (var1 != var2) {
348 if (dram_info->ddr_width > 16) {
349 tmp_pup = (pup + PUP_NUM_32BIT *
350 (ui % pup_groups));
351 } else {
352 tmp_pup = (pup % PUP_NUM_16BIT);
353 }
354
355 update_pup = (1 << tmp_pup);
356 if (ecc && (update_pup != 0x1))
357 continue;
358
359 /*
360 * Pup is failed - Go over all DQs and
361 * look for failures
362 */
363 for (dq = 0; dq < DQ_NUM; dq++) {
364 val = tmp_pup * (1 - ecc) +
365 ecc * ECC_PUP;
366 if (((var1 >> dq) & 0x1) !=
367 ((var2 >> dq) & 0x1)) {
368 if (pbs_locked_dq[val][dq] == 1 &&
369 pbs_locked_value[val][dq] != pbs_curr_val)
370 continue;
371
372 /*
373 * Activate write to
374 * update PBS to
375 * pbs_lock_val
376 */
377 pbs_write_pup[dq] |=
378 update_pup;
379
380 /*
381 * Update the
382 * unlock_pup_dq_array
383 */
384 unlock_pup_dq_array[dq] &=
385 ~update_pup;
386
387 /*
388 * Lock PBS value for
389 * failed bits in
390 * compare operation
391 */
392 skew_array[tmp_pup * DQ_NUM + dq] =
393 pbs_curr_val;
394 }
395 }
396 }
397 }
398 }
399 }
400
401 pup_addr = (is_tx == 1) ? PUP_PBS_TX : PUP_PBS_RX;
402
403 /* Set last failed bits PBS to min / max pbs value */
404 for (dq = 0; dq < DQ_NUM; dq++) {
405 for (pup = 0; pup < max_pup; pup++) {
406 if (pbs_write_pup[dq] & (1 << pup)) {
407 val = pup * (1 - ecc) + ecc * ECC_PUP;
408 if (pbs_locked_dq[val][dq] == 1 &&
409 pbs_locked_value[val][dq] != pbs_curr_val)
410 continue;
411
412 /* Mark the dq as locked */
413 pbs_locked_dq[val][dq] = 1;
414 pbs_locked_value[val][dq] = pbs_curr_val;
415 ddr3_write_pup_reg(pup_addr +
416 pbs_dq_mapping[val][dq],
417 CS0, val, 0, pbs_lock_val);
418 }
419 }
420 }
421
422 return MV_OK;
423}
424#endif
425
426/*
427 * Name: ddr3_sdram_direct_compare
428 * Desc: Execute compare per PUP without DMA (no burst mode)
429 * Args: unlock_pup Bit array of the unlock pups
430 * new_locked_pup Output bit array of the pups with failed compare
431 * pattern Pattern to compare
432 * pattern_len Length of pattern (in bytes)
433 * sdram_offset offset address to the SDRAM
434 * write write to the SDRAM before read
435 * mask compare pattern with mask;
436 * auiMaskPatter Mask to compare pattern
437 *
438 * Notes:
439 * Returns: MV_OK if success, other error code if fail.
440 */
441int ddr3_sdram_direct_compare(MV_DRAM_INFO *dram_info, u32 unlock_pup,
442 u32 *new_locked_pup, u32 *pattern,
443 u32 pattern_len, u32 sdram_offset,
444 int write, int mask, u32 *mask_pattern)
445{
446 u32 uj, uk, pup_groups;
447 u32 *sdram_addr; /* used to read from SDRAM */
448
449 sdram_addr = (u32 *)sdram_offset;
450
451 if (dram_info->num_of_std_pups == PUP_NUM_64BIT)
452 pup_groups = 2;
453 else
454 pup_groups = 1;
455
456 /* Check if need to write before read */
457 if (write == 1) {
458 for (uk = 0; uk < pattern_len; uk++) {
459 *sdram_addr = pattern[uk];
460 sdram_addr++;
461 }
462 }
463
464 sdram_addr = (u32 *)sdram_offset;
465
466 for (uk = 0; uk < pattern_len; uk++) {
467 sdram_data[uk] = *sdram_addr;
468 sdram_addr++;
469 }
470
471 /* Compare read result to write */
472 for (uj = 0; uj < pattern_len; uj++) {
473 if (dram_info->ddr_width > 16) {
474 compare_pattern_v1(uj, new_locked_pup, pattern,
475 pup_groups, 0);
476 } else {
477 compare_pattern_v2(uj, new_locked_pup, pattern);
478 }
479 }
480
481 return MV_OK;
482}
483
484/*
485 * Name: ddr3_dram_sram_burst
486 * Desc: Read from the SDRAM in burst of 64 bytes
487 * Args: src
488 * dst
489 * Notes: Using the XOR mechanism
490 * Returns: MV_OK if success, other error code if fail.
491 */
492int ddr3_dram_sram_burst(u32 src, u32 dst, u32 len)
493{
494 u32 chan, byte_count, cs_num, byte;
495 struct xor_channel_t channel;
496
497 chan = 0;
498 byte_count = len * 4;
499
500 /* Wait for previous transfer completion */
501 while (mv_xor_state_get(chan) != MV_IDLE)
502 ;
503
504 /* Build the channel descriptor */
505 channel.desc = &dma_desc;
506
507 /* Enable Address Override and set correct src and dst */
508 if (src < SRAM_BASE) {
509 /* src is DRAM CS, dst is SRAM */
510 cs_num = (src / (1 + SDRAM_CS_SIZE));
511 reg_write(XOR_ADDR_OVRD_REG(0, 0),
512 ((cs_num << 1) | (1 << 0)));
513 channel.desc->src_addr0 = (src % (1 + SDRAM_CS_SIZE));
514 channel.desc->dst_addr = dst;
515 } else {
516 /* src is SRAM, dst is DRAM CS */
517 cs_num = (dst / (1 + SDRAM_CS_SIZE));
518 reg_write(XOR_ADDR_OVRD_REG(0, 0),
519 ((cs_num << 25) | (1 << 24)));
520 channel.desc->src_addr0 = (src);
521 channel.desc->dst_addr = (dst % (1 + SDRAM_CS_SIZE));
522 channel.desc->src_addr0 = src;
523 channel.desc->dst_addr = (dst % (1 + SDRAM_CS_SIZE));
524 }
525
526 channel.desc->src_addr1 = 0;
527 channel.desc->byte_cnt = byte_count;
528 channel.desc->next_desc_ptr = 0;
529 channel.desc->status = 1 << 31;
530 channel.desc->desc_cmd = 0x0;
531 channel.desc_phys_addr = (unsigned long)&dma_desc;
532
533 ddr3_flush_l1_line((u32)&dma_desc);
534
535 /* Issue the transfer */
536 if (mv_xor_transfer(chan, MV_DMA, channel.desc_phys_addr) != MV_OK)
537 return MV_FAIL;
538
539 /* Wait for completion */
540 xor_waiton_eng(chan);
541
542 if (dst > SRAM_BASE) {
543 for (byte = 0; byte < byte_count; byte += 0x20)
544 cache_inv(dst + byte);
545 }
546
547 return MV_OK;
548}
549
550/*
551 * Name: ddr3_flush_l1_line
552 * Desc:
553 * Args:
554 * Notes:
555 * Returns: MV_OK if success, other error code if fail.
556 */
557static void ddr3_flush_l1_line(u32 line)
558{
559 u32 reg;
560
561#if defined(MV88F672X)
562 reg = 1;
563#else
564 reg = reg_read(REG_SAMPLE_RESET_LOW_ADDR) &
565 (1 << REG_SAMPLE_RESET_CPU_ARCH_OFFS);
566#ifdef MV88F67XX
567 reg = ~reg & (1 << REG_SAMPLE_RESET_CPU_ARCH_OFFS);
568#endif
569#endif
570
571 if (reg) {
572 /* V7 Arch mode */
573 flush_l1_v7(line);
574 flush_l1_v7(line + CACHE_LINE_SIZE);
575 } else {
576 /* V6 Arch mode */
577 flush_l1_v6(line);
578 flush_l1_v6(line + CACHE_LINE_SIZE);
579 }
580}
581
582int ddr3_dram_sram_read(u32 src, u32 dst, u32 len)
583{
584 u32 ui;
585 u32 *dst_ptr, *src_ptr;
586
587 dst_ptr = (u32 *)dst;
588 src_ptr = (u32 *)src;
589
590 for (ui = 0; ui < len; ui++) {
591 *dst_ptr = *src_ptr;
592 dst_ptr++;
593 src_ptr++;
594 }
595
596 return MV_OK;
597}
598
599int ddr3_sdram_dqs_compare(MV_DRAM_INFO *dram_info, u32 unlock_pup,
600 u32 *new_locked_pup, u32 *pattern,
601 u32 pattern_len, u32 sdram_offset, int write,
602 int mask, u32 *mask_pattern,
603 int special_compare)
604{
605 u32 uj, pup_groups;
606
607 if (dram_info->num_of_std_pups == PUP_NUM_64BIT)
608 pup_groups = 2;
609 else
610 pup_groups = 1;
611
612 ddr3_reset_phy_read_fifo();
613
614 /* Check if need to write to sdram before read */
615 if (write == 1)
616 ddr3_dram_sram_burst((u32)pattern, sdram_offset, pattern_len);
617
618 ddr3_dram_sram_burst(sdram_offset, (u32)sdram_data, pattern_len);
619
620 /* Compare read result to write */
621 for (uj = 0; uj < pattern_len; uj++) {
622 if (special_compare && special_compare_pattern(uj))
623 continue;
624
625 if (dram_info->ddr_width > 16) {
626 compare_pattern_v1(uj, new_locked_pup, pattern,
627 pup_groups, 1);
628 } else {
629 compare_pattern_v2(uj, new_locked_pup, pattern);
630 }
631 }
632
633 return MV_OK;
634}
635
636void ddr3_reset_phy_read_fifo(void)
637{
638 u32 reg;
639
640 /* reset read FIFO */
641 reg = reg_read(REG_DRAM_TRAINING_ADDR);
642 /* Start Auto Read Leveling procedure */
643 reg |= (1 << REG_DRAM_TRAINING_RL_OFFS);
644
645 /* 0x15B0 - Training Register */
646 reg_write(REG_DRAM_TRAINING_ADDR, reg);
647
648 reg = reg_read(REG_DRAM_TRAINING_2_ADDR);
649 reg |= ((1 << REG_DRAM_TRAINING_2_FIFO_RST_OFFS) +
650 (1 << REG_DRAM_TRAINING_2_SW_OVRD_OFFS));
651
652 /* [0] = 1 - Enable SW override, [4] = 1 - FIFO reset */
653 /* 0x15B8 - Training SW 2 Register */
654 reg_write(REG_DRAM_TRAINING_2_ADDR, reg);
655
656 do {
657 reg = reg_read(REG_DRAM_TRAINING_2_ADDR) &
658 (1 << REG_DRAM_TRAINING_2_FIFO_RST_OFFS);
659 } while (reg); /* Wait for '0' */
660
661 reg = reg_read(REG_DRAM_TRAINING_ADDR);
662
663 /* Clear Auto Read Leveling procedure */
664 reg &= ~(1 << REG_DRAM_TRAINING_RL_OFFS);
665
666 /* 0x15B0 - Training Register */
667 reg_write(REG_DRAM_TRAINING_ADDR, reg);
668}