summaryrefslogtreecommitdiff
path: root/drivers/mxc/security/sahara2/sah_hardware_interface.c
blob: ff3cb987dc7fa7d403134be1e50af0d8af582e4f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
/*
 * Copyright (C) 2004-2010 Freescale Semiconductor, Inc. All Rights Reserved.
 */

/*
 * The code contained herein is licensed under the GNU General Public
 * License. You may obtain a copy of the GNU General Public License
 * Version 2 or later at the following locations:
 *
 * http://www.opensource.org/licenses/gpl-license.html
 * http://www.gnu.org/copyleft/gpl.html
 */

/*!
 * @file sah_hardware_interface.c
 *
 * @brief Provides an interface to the SAHARA hardware registers.
 *
 */

/* SAHARA Includes */
#include <sah_driver_common.h>
#include <sah_hardware_interface.h>
#include <sah_memory_mapper.h>
#include <sah_kernel.h>

#if defined DIAG_DRV_IF || defined(DO_DBG)
#include <diagnostic.h>
#ifndef LOG_KDIAG
#define LOG_KDIAG(x) os_printk("%s\n", x)
#endif

static void sah_Dump_Link(const char *prefix, const sah_Link * link,
			  dma_addr_t addr);

/* This is for sprintf() to use when constructing output. */
#define DIAG_MSG_SIZE   1024
/* was 200 */
#define MAX_DUMP        200
static char Diag_msg[DIAG_MSG_SIZE];

#endif				/* DIAG_DRV_IF */

/*!
 * Number of descriptors sent to Sahara.  This value should only be updated
 * with the main queue lock held.
 */
uint32_t dar_count;

/* sahara virtual base address */
void *sah_virt_base;

/*! The "link-list optimize" bit in the Header of a Descriptor */
#define SAH_HDR_LLO 0x01000000

#define SAHARA_VERSION_REGISTER_OFFSET  0x000
#define SAHARA_DAR_REGISTER_OFFSET      0x004
#define SAHARA_CONTROL_REGISTER_OFFSET  0x008
#define SAHARA_COMMAND_REGISTER_OFFSET  0x00C
#define SAHARA_STATUS_REGISTER_OFFSET   0x010
#define SAHARA_ESTATUS_REGISTER_OFFSET  0x014
#define SAHARA_FLT_ADD_REGISTER_OFFSET  0x018
#define SAHARA_CDAR_REGISTER_OFFSET     0x01C
#define SAHARA_IDAR_REGISTER_OFFSET     0x020
#define SAHARA_OSTATUS_REGISTER_OFFSET  0x028
#define SAHARA_CONFIG_REGISTER_OFFSET   0x02C
#define SAHARA_MM_STAT_REGISTER_OFFSET  0x030


/* Local Functions */
#if defined DIAG_DRV_IF || defined DO_DBG
void sah_Dump_Region(const char *prefix, const unsigned char *data,
		     dma_addr_t addr, unsigned length);

#endif				/* DIAG_DRV_IF */

/* time out value when polling SAHARA status register for completion */
static uint32_t sah_poll_timeout = 0xFFFFFFFF;

/*!
 * Polls Sahara to determine when its current operation is complete
 *
 * @return   last value found in Sahara's status register
 */
sah_Execute_Status sah_Wait_On_Sahara()
{
	uint32_t count = 0;	/* ensure we don't get stuck in the loop forever */
	sah_Execute_Status status;	/* Sahara's status register */
	uint32_t stat_reg;

	pr_debug("Entered sah_Wait_On_Sahara\n");

	do {
		/* get current status register from Sahara */
		stat_reg = sah_HW_Read_Status();
		status = stat_reg & SAH_EXEC_STATE_MASK;

		/* timeout if SAHARA takes too long to complete */
		if (++count == sah_poll_timeout) {
			status = SAH_EXEC_FAULT;
			printk("sah_Wait_On_Sahara timed out\n");
		}

		/* stay in loop as long as Sahara is still busy */
	} while ((status == SAH_EXEC_BUSY) || (status == SAH_EXEC_DONE1_BUSY2));

	if (status == SAH_EXEC_ERROR1) {
		if (stat_reg & OP_STATUS) {
			status = SAH_EXEC_OPSTAT1;
		}
	}

	return status;
}				/* sah_Wait_on_Sahara() */

/*!
 * This function resets the SAHARA hardware. The following operations are
 * performed:
 *   1. Resets SAHARA.
 *   2. Requests BATCH mode.
 *   3. Enables interrupts.
 *   4. Requests Little Endian mode.
 *
 * @brief     SAHARA hardware reset function.
 *
 * @return   void
 */
int sah_HW_Reset(void)
{
	sah_Execute_Status sah_state;
	int status;		/* this is the value to return to the calling routine */
	uint32_t saha_control = 0;

#ifndef USE_3WORD_BURST
#ifdef FSL_HAVE_SAHARA2
	saha_control |= (8 << 16);	/* Allow 8-word burst */
#endif
#else
/***************** HARDWARE BUG WORK AROUND ******************/
/* A burst size of > 4 can cause Sahara DMA to issue invalid AHB transactions
 * when crossing 1KB boundaries.  By limiting the 'burst size' to 3, these
 * invalid transactions will not be generated, but Sahara will still transfer
 * data more efficiently than if the burst size were set to 1.
 */
	saha_control |= (3 << 16);	/* Limit DMA burst size. For versions 2/3 */
#endif				/* USE_3WORD_BURST */

#ifdef DIAG_DRV_IF
	snprintf(Diag_msg, DIAG_MSG_SIZE,
		 "Address of sah_virt_base = 0x%08x\n",
		 sah_virt_base);
	LOG_KDIAG(Diag_msg);
	snprintf(Diag_msg, DIAG_MSG_SIZE,
		 "Sahara Status register before reset: %08x",
		 sah_HW_Read_Status());
	LOG_KDIAG(Diag_msg);
#endif

	/* Write the Reset & BATCH mode command to the SAHARA Command register. */
	sah_HW_Write_Command(CMD_BATCH | CMD_RESET);
#ifdef SAHARA4_NO_USE_SQUIB
	{
		uint32_t cfg = sah_HW_Read_Config();
		cfg &= ~0x10000;
		sah_HW_Write_Config(cfg);
	}
#endif

	sah_poll_timeout = 0x0FFFFFFF;
	sah_state = sah_Wait_On_Sahara();
#ifdef DIAG_DRV_IF
	snprintf(Diag_msg, DIAG_MSG_SIZE,
		 "Sahara Status register after reset: %08x",
		 sah_HW_Read_Status());
	LOG_KDIAG(Diag_msg);
#endif
	/* on reset completion, check that Sahara is in the idle state */
	status = (sah_state == SAH_EXEC_IDLE) ? 0 : OS_ERROR_FAIL_S;

	/* Set initial value out of reset */
	sah_HW_Write_Control(saha_control);

#ifndef NO_RESEED_WORKAROUND
/***************** HARDWARE BUG WORK AROUND ******************/
/* In order to set the 'auto reseed' bit, must first acquire a random value. */
	/*
	 * to solve a hardware bug, a random number must be generated before
	 * the 'RNG Auto Reseed' bit can be set. So this generates a random
	 * number that is thrown away.
	 *
	 * Note that the interrupt bit has not been set at this point so
	 * the result can be polled.
	 */
#ifdef DIAG_DRV_IF
	LOG_KDIAG("Create and submit Random Number Descriptor");
#endif

	if (status == OS_ERROR_OK_S) {
		/* place to put random number */
		volatile uint32_t *random_data_ptr;
		sah_Head_Desc *random_desc;
		dma_addr_t desc_dma;
		dma_addr_t rand_dma;
		const int rnd_cnt = 3;	/* how many random 32-bit values to get */

		/* Get space for data -- assume at least 32-bit aligned! */
		random_data_ptr = os_alloc_memory(rnd_cnt * sizeof(uint32_t),
						  GFP_ATOMIC);

		random_desc = sah_Alloc_Head_Descriptor();

		if ((random_data_ptr == NULL) || (random_desc == NULL)) {
			status = OS_ERROR_FAIL_S;
		} else {
			int i;

			/* Clear out values */
			for (i = 0; i < rnd_cnt; i++) {
				random_data_ptr[i] = 0;
			}

			rand_dma = os_pa(random_data_ptr);

			random_desc->desc.header = 0xB18C0000;	/* LLO get random number */
			random_desc->desc.len1 =
			    rnd_cnt * sizeof(*random_data_ptr);
			random_desc->desc.ptr1 = (void *)rand_dma;
			random_desc->desc.original_ptr1 =
			    (void *)random_data_ptr;

			random_desc->desc.len2 = 0;	/* not used */
			random_desc->desc.ptr2 = 0;	/* not used */

			random_desc->desc.next = 0;	/* chain terminates here */
			random_desc->desc.original_next = 0;	/* chain terminates here */

			desc_dma = random_desc->desc.dma_addr;

			/* Force in-cache data out to RAM */
			os_cache_clean_range(random_data_ptr,
					     rnd_cnt *
					     sizeof(*random_data_ptr));

			/* pass descriptor to Sahara */
			sah_HW_Write_DAR(desc_dma);

			/*
			 * Wait for RNG to complete (interrupts are disabled at this point
			 * due to sahara being reset previously) then check for error
			 */
			sah_state = sah_Wait_On_Sahara();
			/* Force CPU to ignore in-cache and reload from RAM */
			os_cache_inv_range(random_data_ptr,
					   rnd_cnt * sizeof(*random_data_ptr));

			/* if it didn't move to done state, an error occured */
			if (
#ifndef SUBMIT_MULTIPLE_DARS
				   (sah_state != SAH_EXEC_IDLE) &&
#endif
				   (sah_state != SAH_EXEC_DONE1)
			    ) {
				status = OS_ERROR_FAIL_S;
				os_printk
				    ("(sahara) Failure: state is %08x; random_data is"
				     " %08x\n", sah_state, *random_data_ptr);
				os_printk
				    ("(sahara) CDAR: %08x, IDAR: %08x, FADR: %08x,"
				     " ESTAT: %08x\n", sah_HW_Read_CDAR(),
				     sah_HW_Read_IDAR(),
				     sah_HW_Read_Fault_Address(),
				     sah_HW_Read_Error_Status());
			} else {
				int i;
				int seen_rand = 0;

				for (i = 0; i < rnd_cnt; i++) {
					if (*random_data_ptr != 0) {
						seen_rand = 1;
						break;
					}
				}
				if (!seen_rand) {
					status = OS_ERROR_FAIL_S;
					os_printk
					    ("(sahara) Error:  Random number is zero!\n");
				}
			}
		}

		if (random_data_ptr) {
			os_free_memory((void *)random_data_ptr);
		}
		if (random_desc) {
			sah_Free_Head_Descriptor(random_desc);
		}
	}
/***************** END HARDWARE BUG WORK AROUND ******************/
#endif

	if (status == 0) {
#ifdef FSL_HAVE_SAHARA2
		saha_control |= CTRL_RNG_RESEED;
#endif

#ifndef SAHARA_POLL_MODE
		saha_control |= CTRL_INT_EN;	/* enable interrupts */
#else
		sah_poll_timeout = SAHARA_POLL_MODE_TIMEOUT;
#endif

#ifdef DIAG_DRV_IF
		snprintf(Diag_msg, DIAG_MSG_SIZE,
			 "Setting up Sahara's Control Register: %08x\n",
			 saha_control);
		LOG_KDIAG(Diag_msg);
#endif

		/* Rewrite the setup to the SAHARA Control register */
		sah_HW_Write_Control(saha_control);
#ifdef DIAG_DRV_IF
		snprintf(Diag_msg, DIAG_MSG_SIZE,
			 "Sahara Status register after control write: %08x",
			 sah_HW_Read_Status());
		LOG_KDIAG(Diag_msg);
#endif

#ifdef FSL_HAVE_SAHARA4
		{
			uint32_t cfg = sah_HW_Read_Config();
			sah_HW_Write_Config(cfg | 0x100);	/* Add RNG auto-reseed */
		}
#endif
	} else {
#ifdef DIAG_DRV_IF
		LOG_KDIAG("Reset failed\n");
#endif
	}

	return status;
}				/* sah_HW_Reset() */

/*!
 * This function enables High Assurance mode.
 *
 * @brief     SAHARA hardware enable High Assurance mode.
 *
 * @return   FSL_RETURN_OK_S             - if HA was set successfully
 * @return   FSL_RETURN_INTERNAL_ERROR_S - if HA was not set due to SAHARA
 *                                         being busy.
 */
fsl_shw_return_t sah_HW_Set_HA(void)
{
	/* This is the value to write to the register */
	uint32_t value;

	/* Read from the control register. */
	value = sah_HW_Read_Control();

	/* Set the HA bit */
	value |= CTRL_HA;

	/* Write to the control register. */
	sah_HW_Write_Control(value);

	/* Read from the control register. */
	value = sah_HW_Read_Control();

	return (value & CTRL_HA) ? FSL_RETURN_OK_S :
	    FSL_RETURN_INTERNAL_ERROR_S;
}

/*!
 * This function reads the SAHARA hardware Version Register.
 *
 * @brief     Read SAHARA hardware Version Register.
 *
 * @return   uint32_t Register value.
 */
uint32_t sah_HW_Read_Version(void)
{
	return os_read32(sah_virt_base + SAHARA_VERSION_REGISTER_OFFSET);
}

/*!
 * This function reads the SAHARA hardware Control Register.
 *
 * @brief     Read SAHARA hardware Control Register.
 *
 * @return   uint32_t Register value.
 */
uint32_t sah_HW_Read_Control(void)
{
	return os_read32(sah_virt_base + SAHARA_CONTROL_REGISTER_OFFSET);
}

/*!
 * This function reads the SAHARA hardware Status Register.
 *
 * @brief     Read SAHARA hardware Status Register.
 *
 * @return   uint32_t Register value.
 */
uint32_t sah_HW_Read_Status(void)
{
	return os_read32(sah_virt_base + SAHARA_STATUS_REGISTER_OFFSET);
}

/*!
 * This function reads the SAHARA hardware Error Status Register.
 *
 * @brief     Read SAHARA hardware Error Status Register.
 *
 * @return   uint32_t Error Status value.
 */
uint32_t sah_HW_Read_Error_Status(void)
{
	return os_read32(sah_virt_base + SAHARA_ESTATUS_REGISTER_OFFSET);
}

/*!
 * This function reads the SAHARA hardware Op Status Register.
 *
 * @brief     Read SAHARA hardware Op Status Register.
 *
 * @return   uint32_t Op Status value.
 */
uint32_t sah_HW_Read_Op_Status(void)
{
	return os_read32(sah_virt_base + SAHARA_OSTATUS_REGISTER_OFFSET);
}

/*!
 * This function reads the SAHARA hardware Descriptor Address Register.
 *
 * @brief     Read SAHARA hardware DAR Register.
 *
 * @return   uint32_t DAR value.
 */
uint32_t sah_HW_Read_DAR(void)
{
	return os_read32(sah_virt_base + SAHARA_DAR_REGISTER_OFFSET);
}

/*!
 * This function reads the SAHARA hardware Current Descriptor Address Register.
 *
 * @brief     Read SAHARA hardware CDAR Register.
 *
 * @return   uint32_t CDAR value.
 */
uint32_t sah_HW_Read_CDAR(void)
{
	return os_read32(sah_virt_base + SAHARA_CDAR_REGISTER_OFFSET);
}

/*!
 * This function reads the SAHARA hardware Initial Descriptor Address Register.
 *
 * @brief     Read SAHARA hardware IDAR Register.
 *
 * @return   uint32_t IDAR value.
 */
uint32_t sah_HW_Read_IDAR(void)
{
	return os_read32(sah_virt_base + SAHARA_IDAR_REGISTER_OFFSET);
}

/*!
 * This function reads the SAHARA hardware Fault Address Register.
 *
 * @brief     Read SAHARA Fault Address Register.
 *
 * @return   uint32_t Fault Address value.
 */
uint32_t sah_HW_Read_Fault_Address(void)
{
	return os_read32(sah_virt_base + SAHARA_FLT_ADD_REGISTER_OFFSET);
}

/*!
 * This function reads the SAHARA hardware Multiple Master Status Register.
 *
 * @brief     Read SAHARA hardware MM Stat Register.
 *
 * @return   uint32_t MM Stat value.
 */
uint32_t sah_HW_Read_MM_Status(void)
{
	return os_read32(sah_virt_base + SAHARA_MM_STAT_REGISTER_OFFSET);
}

/*!
 * This function reads the SAHARA hardware Configuration Register.
 *
 * @brief     Read SAHARA Configuration Register.
 *
 * @return   uint32_t Configuration value.
 */
uint32_t sah_HW_Read_Config(void)
{
	return os_read32(sah_virt_base + SAHARA_CONFIG_REGISTER_OFFSET);
}

/*!
 * This function writes a command to the SAHARA hardware Command Register.
 *
 * @brief     Write to SAHARA hardware Command Register.
 *
 * @param    command     An unsigned 32bit command value.
 *
 * @return   void
 */
void sah_HW_Write_Command(uint32_t command)
{
	os_write32(sah_virt_base + SAHARA_COMMAND_REGISTER_OFFSET, command);
}

/*!
 * This function writes a control value to the SAHARA hardware Control
 * Register.
 *
 * @brief     Write to SAHARA hardware Control Register.
 *
 * @param    control     An unsigned 32bit control value.
 *
 * @return   void
 */
void sah_HW_Write_Control(uint32_t control)
{
	os_write32(sah_virt_base + SAHARA_CONTROL_REGISTER_OFFSET, control);
}

/*!
 * This function writes a configuration value to the SAHARA hardware Configuration
 * Register.
 *
 * @brief     Write to SAHARA hardware Configuration Register.
 *
 * @param    configuration     An unsigned 32bit configuration value.
 *
 * @return   void
 */
void sah_HW_Write_Config(uint32_t configuration)
{
	os_write32(sah_virt_base + SAHARA_CONFIG_REGISTER_OFFSET,
		configuration);
}

/*!
 * This function writes a descriptor address to the SAHARA Descriptor Address
 * Register.
 *
 * @brief     Write to SAHARA Descriptor Address Register.
 *
 * @param    pointer     An unsigned 32bit descriptor address value.
 *
 * @return   void
 */
void sah_HW_Write_DAR(uint32_t pointer)
{
	os_write32(sah_virt_base + SAHARA_DAR_REGISTER_OFFSET, pointer);
	dar_count++;
}

#if defined DIAG_DRV_IF || defined DO_DBG

static char *interpret_header(uint32_t header)
{
	unsigned desc_type = ((header >> 24) & 0x70) | ((header >> 16) & 0xF);

	switch (desc_type) {
	case 0x12:
		return "5/SKHA_ST_CTX";
	case 0x13:
		return "35/SKHA_LD_MODE_KEY";
	case 0x14:
		return "38/SKHA_LD_MODE_IN_CPHR_ST_CTX";
	case 0x15:
		return "4/SKHA_IN_CPHR_OUT";
	case 0x16:
		return "34/SKHA_ST_SBOX";
	case 0x18:
		return "1/SKHA_LD_MODE_IV_KEY";
	case 0x19:
		return "33/SKHA_ST_SBOX";
	case 0x1D:
		return "2/SKHA_LD_MODE_IN_CPHR_OUT";
	case 0x22:
		return "11/MDHA_ST_MD";
	case 0x25:
		return "10/MDHA_HASH_ST_MD";
	case 0x28:
		return "6/MDHA_LD_MODE_MD_KEY";
	case 0x2A:
		return "39/MDHA_ICV";
	case 0x2D:
		return "8/MDHA_LD_MODE_HASH_ST_MD";
	case 0x3C:
		return "18/RNG_GEN";
	case 0x40:
		return "19/PKHA_LD_N_E";
	case 0x41:
		return "36/PKHA_LD_A3_B0";
	case 0x42:
		return "27/PKHA_ST_A_B";
	case 0x43:
		return "22/PKHA_LD_A_B";
	case 0x44:
		return "23/PKHA_LD_A0_A1";
	case 0x45:
		return "24/PKHA_LD_A2_A3";
	case 0x46:
		return "25/PKHA_LD_B0_B1";
	case 0x47:
		return "26/PKHA_LD_B2_B3";
	case 0x48:
		return "28/PKHA_ST_A0_A1";
	case 0x49:
		return "29/PKHA_ST_A2_A3";
	case 0x4A:
		return "30/PKHA_ST_B0_B1";
	case 0x4B:
		return "31/PKHA_ST_B2_B3";
	case 0x4C:
		return "32/PKHA_EX_ST_B1";
	case 0x4D:
		return "20/PKHA_LD_A_EX_ST_B";
	case 0x4E:
		return "21/PKHA_LD_N_EX_ST_B";
	case 0x4F:
		return "37/PKHA_ST_B1_B2";
	default:
		return "??/UNKNOWN";
	}
}				/* cvt_desc_name() */

/*!
 * Dump chain of descriptors to the log.
 *
 * @brief Dump descriptor chain
 *
 * @param    chain     Kernel virtual address of start of chain of descriptors
 *
 * @return   void
 */
void sah_Dump_Chain(const sah_Desc * chain, dma_addr_t addr)
{
	int desc_no = 1;

	pr_debug("Chain for Sahara\n");

	while (chain != NULL) {
		char desc_name[50];

		sprintf(desc_name, "Desc %02d (%s)\n" KERN_DEBUG "Desc  ",
			desc_no++, interpret_header(chain->header));

		sah_Dump_Words(desc_name, (unsigned *)chain, addr,
			       6 /* #words in h/w link */ );
		if (chain->original_ptr1) {
			if (chain->header & SAH_HDR_LLO) {
				sah_Dump_Region(" Data1",
						(unsigned char *)chain->
						original_ptr1,
						(dma_addr_t) chain->ptr1,
						chain->len1);
			} else {
				sah_Dump_Link(" Link1", chain->original_ptr1,
					      (dma_addr_t) chain->ptr1);
			}
		}
		if (chain->ptr2) {
			if (chain->header & SAH_HDR_LLO) {
				sah_Dump_Region(" Data2",
						(unsigned char *)chain->
						original_ptr2,
						(dma_addr_t) chain->ptr2,
						chain->len2);
			} else {
				sah_Dump_Link(" Link2", chain->original_ptr2,
					      (dma_addr_t) chain->ptr2);
			}
		}

		addr = (dma_addr_t) chain->next;
		chain = (chain->next) ? (chain->original_next) : NULL;
	}
}

/*!
 * Dump chain of links to the log.
 *
 * @brief Dump chain of links
 *
 * @param    prefix    Text to put in front of dumped data
 * @param    link      Kernel virtual address of start of chain of links
 *
 * @return   void
 */
static void sah_Dump_Link(const char *prefix, const sah_Link * link,
			  dma_addr_t addr)
{
#ifdef DUMP_SCC_DATA
	extern uint8_t *sahara_partition_base;
	extern dma_addr_t sahara_partition_phys;
#endif

	while (link != NULL) {
		sah_Dump_Words(prefix, (unsigned *)link, addr,
			       3 /* # words in h/w link */ );
		if (link->flags & SAH_STORED_KEY_INFO) {
#ifdef SAH_DUMP_DATA
#ifdef DUMP_SCC_DATA
			sah_Dump_Region("  Data",
					(uint8_t *) link->data -
					(uint8_t *) sahara_partition_phys +
					sahara_partition_base,
					(dma_addr_t) link->data, link->len);
#else
			pr_debug("  Key Slot %d\n", link->slot);
#endif
#endif
		} else {
#ifdef SAH_DUMP_DATA
			sah_Dump_Region("  Data", link->original_data,
					(dma_addr_t) link->data, link->len);
#endif
		}
		addr = (dma_addr_t) link->next;
		link = link->original_next;
	}
}

/*!
 * Dump given region of data to the log.
 *
 * @brief Dump data
 *
 * @param    prefix    Text to put in front of dumped data
 * @param    data      Kernel virtual address of start of region to dump
 * @param    length    Amount of data to dump
 *
 * @return   void
 */
void sah_Dump_Region(const char *prefix, const unsigned char *data,
		     dma_addr_t addr, unsigned length)
{
	unsigned count;
	char *output;
	unsigned data_len;

	sprintf(Diag_msg, "%s (%08X,%u):", prefix, addr, length);

	/* Restrict amount of data to dump */
	if (length > MAX_DUMP) {
		data_len = MAX_DUMP;
	} else {
		data_len = length;
	}

	/* We've already printed some text in output buffer, skip over it */
	output = Diag_msg + strlen(Diag_msg);

	for (count = 0; count < data_len; count++) {
		if ((count % 4) == 0) {
			*output++ = ' ';
		}
		sprintf(output, "%02X", *data++);
		output += 2;
	}

	pr_debug("%s\n", Diag_msg);
}

/*!
 * Dump given word of data to the log.
 *
 * @brief Dump data
 *
 * @param    prefix       Text to put in front of dumped data
 * @param    data         Kernel virtual address of start of region to dump
 * @param    word_count   Amount of data to dump
 *
 * @return   void
 */
void sah_Dump_Words(const char *prefix, const unsigned *data, dma_addr_t addr,
		    unsigned word_count)
{
	char *output;

	sprintf(Diag_msg, "%s (%08X,%uw): ", prefix, addr, word_count);

	/* We've already printed some text in output buffer, skip over it */
	output = Diag_msg + strlen(Diag_msg);

	while (word_count--) {
		sprintf(output, "%08X ", *data++);
		output += 9;
	}

	pr_debug("%s\n", Diag_msg);

}

#endif				/* DIAG_DRV_IF */

/* End of sah_hardware_interface.c */