summaryrefslogtreecommitdiff
path: root/nuttx/arch/arm/src/armv7-a/arm_l2cc_pl310.c
blob: 066759a129c74701bc998738e243ed4143d880b7 (plain) (blame)
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
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
/************************************************************************************
 * arch/arm/src/armv7-a/chip/arm-l2cc_pl310.c
 *
 *   Copyright (C) 2014 Gregory Nutt. All rights reserved.
 *   Author: Gregory Nutt <gnutt@nuttx.org>
 *
 * Reference: "CoreLink� Level 2 Cache Controller L2C-310", Revision r3p2,
 *   Technical Reference Manual, ARM DDI 0246F (ID011711), ARM
 *
 * NOTE: This logic is incompatible with older versions of the PL310!
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 3. Neither the name NuttX nor the names of its contributors may be
 *    used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 ************************************************************************************/

/***************************************************************************
 * Included Files
 ***************************************************************************/

#include <nuttx/config.h>

#include <stdint.h>
#include <assert.h>
#include <debug.h>

#include <arch/irq.h>

#include "up_arch.h"
#include "l2cc.h"
#include "l2cc_pl310.h"

#ifdef CONFIG_ARMV7A_L2CC_PL310

/***************************************************************************
 * Pre-Processor Definitions
 ***************************************************************************/
/* Configuration ***********************************************************/
/* Number of ways depends on ARM configuration */

#if defined(CONFIG_ARMV7A_ASSOCIATIVITY_8WAY)
#  define PL310_NWAYS      8
#  define PL310_WAY_MASK   0x000000ff
#elif defined(CONFIG_ARMV7A_ASSOCIATIVITY_8WAY)
#  define PL310_NWAYS 16
#  define PL310_WAY_MASK   0x0000ffff
#else
#  error "Number of ways not selected"
#endif

/* The size of one depends on ARM configuration */

#if defined(CONFIG_ARMV7A_WAYSIZE_16KB)
#  define PL310_WAYSIZE (16*1024)
#elif defined(CONFIG_ARMV7A_WAYSIZE_32KB)
#  define PL310_WAYSIZE (32*1024)
#elif defined(CONFIG_ARMV7A_WAYSIZE_64KB)
#  define PL310_WAYSIZE (64*1024)
#elif defined(CONFIG_ARMV7A_WAYSIZE_128KB)
#  define PL310_WAYSIZE (128*1024)
#elif defined(CONFIG_ARMV7A_WAYSIZE_256KB)
#  define PL310_WAYSIZE (256*1024)
#elif defined(CONFIG_ARMV7A_WAYSIZE_512KB)
#  define PL310_WAYSIZE (512*1024)
#else
#  error "Way size not selected"
#endif

/* The size of the cache is then the product of the number of ways times
 * the size of each way.
 */

#define PL310_CACHE_SIZE           (PL310_NWAYS * PL310_WAYSIZE)

/* Use for aligning addresses to a cache line boundary */

#define PL310_CACHE_LINE_MASK      (PL310_CACHE_LINE_SIZE - 1)

/* Configurable options
 *
 * REVISIT: Currently there are not configuration options.  All values
 * are just set to the default.
 */

/* Bit 0:  Full line zero enable
 *
 * Default:  0=Full line of write zero behavior disabled
 */

#define L2CC_ACR_FLZE_CONFIG       (0)  /* 0=Full line of write zero behavior disabled */

/* Bit 10: High Priority for SO and Dev Reads Enable
 *
 * Default: 0=Strongly Ordered and Device reads have lower priority than
 *          cacheable accesses
 */

#define L2CC_ACR_HPSO_CONFIG       (0) /* 0=Have lower priority than cache */

/* Bit 11: Store Buffer Device Limitation Enable
 *
 * Default: 0=Store buffer device limitation disabled
 */

#define L2CC_ACR_SBDLE_CONFIG     (0) /* 0=Store buffer device limitation disabled */

/* Bit 12: Exclusive Cache Configuration
 *
 * Default: 0=Disabled
 */

#define L2CC_ACR_EXCC_CONFIG       (0) /* 0=Disabled */

/* Bit 13: Shared Attribute Invalidate Enable
 *
 * Default: 0=Shared invalidate behavior disabled
 */

#define L2CC_ACR_SAIE_CONFIG       (0) /* 0=Shared invalidate behavior disabled */

/* Bit 20: Event Monitor Bus Enable
 *
 * Default: 0=Disabled
 */

#define L2CC_ACR_EMBEN_CONFIG      (0) /* 0=Disabled */

/* Bit 21: Parity Enable
 *
 * Default: 0=Disabled
 */

#define L2CC_ACR_PEN_CONFIG        (0) /* 0=Disabled */

/* Bit 22: Shared Attribute Override Enable
 *
 * Default: 0=Treats shared accesses as specified in the TRM
 */

#define L2CC_ACR_SAOEN_CONFIG      (0) /* 0=As specified in the TRM */

/* Bits 23-24:  Force Write Allocate
 *
 * Default: 0=Use AWCACHE attributes for WA
 */

#define L2CC_ACR_FWA_CONFIG        L2CC_ACR_FWA_AWCACHE /* Use AWCACHE attributes for WA */

/* Bit 25: Cache Replacement Policy
 *
 * Default: 1=Round robin replacement policy
 */

#define L2CC_ACR_CRPOL_CONFIG      L2CC_ACR_CRPOL /* 1=Round robin replacement policy */

/* Bit 26: Non-Secure Lockdown Enable
 *
 * Default: 0=Lockdown registers cannot be modified using non-secure acceses
 */

#define L2CC_ACR_NSLEN_CONFIG      (0) /* 0=Secure access only */

/* Bit 27: Non-Secure Interrupt Access Control
 *
 * Default: 0=Interrupt Clear and Mask can only be modified or read with
 *          secure accesses
 */

#define L2CC_ACR_NSIAC_CONFIG      (0) /* 0=Secure access only */

/* Bit 28: Data Prefetch Enable
 *
 * Default: 0=Data prefetching disabled
 */

#define L2CC_ACR_DPEN_CONFIG       (0) /* 0=Data prefetching disabled */

/* Bit 29: Instruction Prefetch Enable
 *
 * Default: 0=Instruction prefetching disabled
 */

#define L2CC_ACR_IPEN_CONFIG       (0) /* 0=Instruction prefetching disabled */

/* Bit 30: Early BRESP enable
 *
 * Default: 0=Early BRESP disabled
 */

#define L2CC_ACR_EBRESP_CONFIG     (0) /* 0=Early BRESP disabled */

#define L2CC_ACR_CONFIG \
  (L2CC_ACR_FLZE_CONFIG  | L2CC_ACR_HPSO_CONFIG  | L2CC_ACR_SBDLE_CONFIG | \
   L2CC_ACR_EXCC_CONFIG  | L2CC_ACR_SAIE_CONFIG  | L2CC_ACR_EMBEN_CONFIG | \
   L2CC_ACR_PEN_CONFIG   | L2CC_ACR_SAOEN_CONFIG | L2CC_ACR_FWA_CONFIG   | \
   L2CC_ACR_CRPOL_CONFIG | L2CC_ACR_NSLEN_CONFIG | L2CC_ACR_NSIAC_CONFIG | \
   L2CC_ACR_DPEN_CONFIG  | L2CC_ACR_IPEN_CONFIG  | L2CC_ACR_EBRESP_CONFIG)

#define L2CC_ACR_ALLCONFIGS        (0x7f303c01)
#define L2CC_ACR_CONFIGMASK        (L2CC_ACR_SBZ | L2CC_ACR_ALLCONFIGS)

/* Filter end address */

#define CONFIG_PL310_FLEND         (CONFIG_PL310_FLSTRT + CONFIG_PL310_FLSIZE)

/* Block size.  Used to break up long operations so that interrupts are not
 * disabled for a long time.
 */

#define PL310_GULP_SIZE            4096

/* Misc commoly defined and re-defined things */

#ifndef MIN
#  define MIN(a,b)                 (((a) < (b)) ? (a) : (b))
#endif

#ifndef MAX
#  define MAX(a,b)                 (((a) > (b)) ? (a) : (b))
#endif

#ifndef OK
#  define OK                       0
#endif

/* Data synchronization barrier */

#define dsb(a) __asm__ __volatile__ ("dsb " #a : : : "memory")

/***************************************************************************
 * Private Functions
 ***************************************************************************/

/***************************************************************************
 * Name: pl310_flush_all
 *
 * Description:
 *   Flush all ways using the Clean Invalidate Way Register (CIWR).
 *
 * Input Parameters:
 *   None
 *
 * Returned Value:
 *   None
 *
 ***************************************************************************/

static void pl310_flush_all(void)
{
  /* Flush all ways by writing the set of ways to be cleaned to the Clean
   * Invalidate Way Register (CIWR).
   */

  putreg32(PL310_WAY_MASK, L2CC_CIWR);

  /* Wait for cache operation by way to complete */

  while ((getreg32(L2CC_CIWR) & PL310_WAY_MASK) != 0);

  /* Drain the STB. Operation complete when all buffers, LRB, LFB, STB, and
   * EB, are empty.
   */

  putreg32(0, L2CC_CSR);
}

/****************************************************************************
 * Public Functions
 ****************************************************************************/

/***************************************************************************
 * Name: up_l2ccinitialize
 *
 * Description:
 *   One time configuration of the L2 cache.  The L2 cache will be enabled
 *   upon return.
 *
 * Input Parameters:
 *   None.  The L2 cache configuration is controlled by configuration
 *   settings.
 *
 * Returned Value:
 *   None
 *
 ***************************************************************************/

void up_l2ccinitialize(void)
{
  uint32_t regval;
  int i;

  /* Make sure that this is a PL310 cache, version r3p2.
   *
   * REVISIT: The SAMA5D4 is supposed to report its ID as 0x410000C8 which is
   * r3p2, but the chip that I have actually* reports 0x410000C9 which is some
   * later revision.
   */

  //DEBUGASSERT((getreg32(L2CC_IDR) & L2CC_IDR_REV_MASK) == L2CC_IDR_REV_R3P2);

  /* Make sure that actual cache configuration agrees with the configured
   * cache configuration.
   */


#if defined(CONFIG_ARMV7A_ASSOCIATIVITY_8WAY)
  DEBUGASSERT((getreg32(L2CC_ACR) & L2CC_ACR_ASS) == 0);
#elif defined(CONFIG_ARMV7A_ASSOCIATIVITY_16WAY)
  DEBUGASSERT((getreg32(L2CC_ACR) & L2CC_ACR_ASS) == 1);
#else
#  error No associativity selected
#endif

#if defined(CONFIG_ARMV7A_WAYSIZE_16KB)
  DEBUGASSERT((getreg32(L2CC_ACR) & L2CC_ACR_WAYSIZE_MASK) == L2CC_ACR_WAYSIZE_16KB);
#elif defined(CONFIG_ARMV7A_WAYSIZE_32KB)
  DEBUGASSERT((getreg32(L2CC_ACR) & L2CC_ACR_WAYSIZE_MASK) == L2CC_ACR_WAYSIZE_32KB);
#elif defined(CONFIG_ARMV7A_WAYSIZE_64KB)
  DEBUGASSERT((getreg32(L2CC_ACR) & L2CC_ACR_WAYSIZE_MASK) == L2CC_ACR_WAYSIZE_64KB);
#elif defined(CONFIG_ARMV7A_WAYSIZE_128KB)
  DEBUGASSERT((getreg32(L2CC_ACR) & L2CC_ACR_WAYSIZE_MASK) == L2CC_ACR_WAYSIZE_128KB);
#elif defined(CONFIG_ARMV7A_WAYSIZE_256KB)
  DEBUGASSERT((getreg32(L2CC_ACR) & L2CC_ACR_WAYSIZE_MASK) == L2CC_ACR_WAYSIZE_256KB);
#elif defined(CONFIG_ARMV7A_WAYSIZE_512KB)
  DEBUGASSERT((getreg32(L2CC_ACR) & L2CC_ACR_WAYSIZE_MASK) == L2CC_ACR_WAYSIZE_512KB);
#else
#  error No way size selected
#endif

  /* L2 configuration can only be changed if the cache is disabled,
   *
   * NOTE: This register access will fail if we are not in secure more.
   */

  if ((getreg32(L2CC_CR) & L2CC_CR_L2CEN) == 0)
    {
#if defined(CONFIG_PL310_TRCR_TSETLAT) && defined(CONFIG_PL310_TRCR_TRDLAT) && \
    defined(CONFIG_PL310_TRCR_TWRLAT)
      /* Configure Tag RAM control */

      regval = ((CONFIG_PL310_TRCR_TSETLAT - 1) << L2CC_TRCR_TSETLAT_SHIFT)
               ((CONFIG_PL310_TRCR_TRDLAT - 1) << L2CC_TRCR_TRDLAT_SHIFT) |
               ((CONFIG_PL310_TRCR_TWRLAT - 1) << L2CC_TRCR_TWRLAT_SHIFT);
      putreg32(regval, L2CC_TRCR);
#endif

#if defined(CONFIG_PL310_DRCR_DSETLAT) && defined(CONFIG_PL310_DRCR_DRDLAT) && \
    defined(CONFIG_PL310_DRCR_DWRLAT)
      /* Configure Data RAM control */

      regval = ((CONFIG_PL310_DRCR_DSETLAT - 1) << L2CC_DRCR_DSETLAT_SHIFT) |
               ((CONFIG_PL310_DRCR_DRDLAT - 1) << L2CC_DRCR_DRDLAT_SHIFT) |
               ((CONFIG_PL310_DRCR_DWRLAT - 1) << L2CC_DRCR_DWRLAT_SHIFT);
      putreg32(regval, L2CC_DRCR);
#endif

#ifdef PL310_ADDRESS_FILTERING
#if defined(CONFIG_PL310_FLSTRT) && defined(CONFIG_PL310_FLSIZE)
      /* Configure the address filter */

      regval = (CONFIG_PL310_FLEND + ~L2CC_FLEND_MASK) & L2CC_FLEND_MASK;
      putreg32(regval, L2CC_FLEND);

      regval = (CONFIG_PL310_FLSTRT & L2CC_FLSTRT_MASK) | L2CC_FLSTRT_ENABLE;
      putreg32(regval | L2X0_ADDR_FILTER_EN, L2CC_FLSTRT);
#endif
#endif

      /* Make sure that the memory is not locked down */

      for (i = 0; i < PL310_NLOCKREGS; i++)
        {
          putreg32(0, L2CC_DLKR(i));
          putreg32(0, L2CC_ILKR(i));
        }

      /* Configure the cache properties */

      regval  = getreg32(L2CC_ACR);
      regval &= ~L2CC_ACR_CONFIGMASK;
      regval |= L2CC_ACR_CONFIG;
      putreg32(regval, L2CC_ACR);

      /* Invalidate and enable the cache */

      l2cc_invalidate_all();
      putreg32(L2CC_CR_L2CEN, L2CC_CR);
    }

  lldbg("(%d ways) * (%d bytes/way) = %d bytes\n",
        PL310_NWAYS, PL310_WAYSIZE, PL310_CACHE_SIZE);
}

/***************************************************************************
 * Name: l2cc_enable
 *
 * Description:
 *    Re-enable the L2CC-P310 L2 cache by setting the enable bit in the
 *    Control Register (CR)
 *
 * Input Parameters:
 *    None
 *
 * Returned Value:
 *    None
 *
 ***************************************************************************/

void l2cc_enable(void)
{
  irqstate_t flags;

  /* Invalidate and enable the cache (must be disabled to do this!) */

  flags = irqsave();
  l2cc_invalidate_all();
  putreg32(L2CC_CR_L2CEN, L2CC_CR);
  irqrestore(flags);
}

/***************************************************************************
 * Name: l2cc_disable
 *
 * Description:
 *    Disable the L2CC-P310 L2 cache by clearing the Control Register (CR)
 *
 * Input Parameters:
 *    None
 *
 * Returned Value:
 *    None
 *
 ***************************************************************************/

void l2cc_disable(void)
{
  irqstate_t flags;

  /* Flush all ways using the Clean Invalidate Way Register (CIWR). */

  flags = irqsave();
  pl310_flush_all();

  /* Disable the L2CC-P310 L2 cache by clearing the Control Register (CR) */

  putreg32(0, L2CC_CR);
  dsb();
  irqrestore(flags);
}

/***************************************************************************
 * Name: l2cc_sync
 *
 * Description:
 *   Drain the STB. Operation complete when all buffers, LRB, LFB, STB, and
 *   EB, are empty.
 *
 * Input Parameters:
 *   None
 *
 * Returned Value:
 *   None
 *
 ***************************************************************************/

void l2cc_sync(void)
{
  irqstate_t flags;

  /* Drain the STB. Operation complete when all buffers, LRB, LFB, STB, and
   * EB, are empty.
   */

  flags = irqsave();
  putreg32(0, L2CC_CSR);
  irqrestore(flags);
}

/***************************************************************************
 * Name: l2cc_invalidate_all
 *
 * Description:
 *   Invalidate all ways using the Invalidate Way Register (IWR).
 *
 * Input Parameters:
 *   None
 *
 * Returned Value:
 *   None
 *
 ***************************************************************************/

void l2cc_invalidate_all(void)
{
  irqstate_t flags;
  uint32_t regval;

  /* Invalidate all ways */

  flags = irqsave();

  /* Disable the L2 cache while we invalidate it */

  regval = getreg32(L2CC_CR);
  l2cc_disable();

  /* Invalidate all ways by writing the bit mask of ways to be invalidated
   * the Invalidate Way Register (IWR).
   */

  putreg32(PL310_WAY_MASK, L2CC_IWR);

  /* Wait for cache operation by way to complete */

  while ((getreg32(L2CC_IWR) & PL310_WAY_MASK) != 0);

  /* Drain the STB. Operation complete when all buffers, LRB, LFB, STB, and
   * EB, are empty.
   */

  putreg32(0, L2CC_CSR);

  /* Then re-enable the L2 cache if it was enabled before */

  putreg32(regval, L2CC_CR);
  irqrestore(flags);
}

/***************************************************************************
 * Name: l2cc_invalidate
 *
 * Description:
 *   Invalidate a range of addresses by writing to the Invalidate Physical
 *   Address Line Register (IPALR) repeatedly.
 *
 * Input Parameters:
 *   startaddr - The first address to be invalidated
 *   endaddr   - The last address to be invalidated
 *
 * Returned Value:
 *   None
 *
 ***************************************************************************/

void l2cc_invalidate(uintptr_t startaddr, uintptr_t endaddr)
{
  uintptr_t invalsize;
  uintptr_t gulpend;
  irqstate_t flags;

  /* Check if the start address is aligned with a cacheline */

  flags = irqsave();
  if ((startaddr & PL310_CACHE_LINE_MASK) != 0)
    {
      /* No.. align down and flush the cache line by writing the address to
       * the Clean Invalidate Physical Address Line Register (CIPALR).
       */

      startaddr &= ~PL310_CACHE_LINE_MASK;
      putreg32(startaddr, L2CC_CIPALR);

      /* Then start invalidating at the next cache line */

      startaddr += PL310_CACHE_LINE_SIZE;
    }

  /* Check if the end address is aligned with a cache line */

  if ((endaddr & PL310_CACHE_LINE_MASK) != 0)
    {
      /* No.. align down and flush cache line by writing the address to
       * the Clean Invalidate Physical Address Line Register (CIPALR).
       */

      endaddr &= ~PL310_CACHE_LINE_MASK;
      putreg32(endaddr, L2CC_CIPALR);
    }

  irqrestore(flags);

  /* Loop, invalidated the address range by cache line.  Interrupts are re-
   * enabled momentarily every PL310_GULP_SIZE bytes.
   */

  while (startaddr < endaddr)
    {
      /* Get the size of the next gulp of cache lines to invalidate.  We do
       * this in small chunks so that we do not have to keep interrupts
       * disabled throughout the whole flush.
       */

      invalsize = endaddr - startaddr;
      gulpend   = startaddr + MIN(invalsize, PL310_GULP_SIZE);

      /* Disable interrupts and invalidate the gulp */

      flags = irqsave();
      while (startaddr < gulpend)
        {
          /* Invalidate the cache line by writing the address to the
           * Invalidate Physical Address Line Register (IPALR).
           */

          putreg32(startaddr, L2CC_IPALR);

          /* Start of the next cache line */

          startaddr += PL310_CACHE_LINE_SIZE;
        }

      /* Enable interrupts momentarily */

      irqrestore(flags);
    }

  /* Drain the STB. Operation complete when all buffers, LRB, LFB, STB, and
   * EB, are empty.
   */

  flags = irqsave();
  putreg32(0, L2CC_CSR);
  irqrestore(flags);
}

/***************************************************************************
 * Name: l2cc_clean_all
 *
 * Description:
 *   Clean all ways by using the Clean Ways Register (CWR).
 *
 * Input Parameters:
 *   None
 *
 * Returned Value:
 *   None
 *
 ***************************************************************************/

void l2cc_clean_all(void)
{
  irqstate_t flags;

  /* Clean all ways by writing the set of ways to be cleaned to the Clean
   * Ways Register (CWR).
   */

  flags = irqsave();
  putreg32(PL310_WAY_MASK, L2CC_CWR);

  /* Wait for cache operation by way to complete */

  while ((getreg32(L2CC_CWR) & PL310_WAY_MASK) != 0);

  /* Drain the STB. Operation complete when all buffers, LRB, LFB, STB, and
   * EB, are empty.
   */

  putreg32(0, L2CC_CSR);
  irqrestore(flags);
}

/***************************************************************************
 * Name: l2cc_clean
 *
 * Description:
 *   Clean the cache line over a range of addresses uing the Clean Physical
 *   Address Line Register (CPALR) repeatedly.
 *
 * Input Parameters:
 *   startaddr - The first address to be cleaned
 *   endaddr   - The last address to be cleaned
 *
 * Returned Value:
 *   None
 *
 ***************************************************************************/

void l2cc_clean(uintptr_t startaddr, uintptr_t endaddr)
{
  uintptr_t cleansize;
  uintptr_t gulpend;
  irqstate_t flags;

  /* If the range of addresses to clean is as large or larger the L2 cache,
   * then just clean the whole thing.
   */

  cleansize = endaddr - startaddr;
  if (cleansize >= PL310_CACHE_SIZE)
    {
      l2cc_clean_all();
      return;
    }

  /* Align the starting address to a cache line boundary */

  startaddr &= ~PL310_CACHE_LINE_MASK;

  /* Clean the L2 cache by cache line, enabling interrupts momentarily
   * every PL310_GULP_SIZE bytes.
   */

  while (startaddr < endaddr)
    {
      /* Get the size of the next gulp of cache lines to flush.  We do
       * this in small chunks so that we do not have to keep interrupts
       * disabled throughout the whole flush.
       */

      cleansize = endaddr - startaddr;
      gulpend   = startaddr + MIN(cleansize, PL310_GULP_SIZE);

      /* Disable interrupts and clean the gulp */

      flags = irqsave();
      while (startaddr < gulpend)
        {
          /* Clean the cache line by writing the address to the Clean
           * Physical Address Line Register (CPALR).
           */

          putreg32(startaddr, L2CC_CPALR);

          /* Start of the next cache line */

          startaddr += PL310_CACHE_LINE_SIZE;
        }

      /* Enable interrupts momentarily */

      irqrestore(flags);
    }

  /* Drain the STB. Operation complete when all buffers, LRB, LFB, STB, and
   * EB, are empty.
   */

  flags = irqsave();
  putreg32(0, L2CC_CSR);
  irqrestore(flags);
}

/***************************************************************************
 * Name: l2cc_flush_all
 *
 * Description:
 *   Flush all ways using the Clean Invalidate Way Register (CIWR).
 *
 * Input Parameters:
 *   None
 *
 * Returned Value:
 *   None
 *
 ***************************************************************************/

void l2cc_flush_all(void)
{
  irqstate_t flags;

  /* Flush all ways using the Clean Invalidate Way Register (CIWR). */

  flags = irqsave();
  pl310_flush_all();
  irqrestore(flags);
}

/***************************************************************************
 * Name: l2cc_flush
 *
 * Description:
 *   Flush a range of address by using the Clean Invalidate Physical Address
 *   Line Register (CIPALR) repeatedly.
 *
 * Input Parameters:
 *   startaddr - The first address to be flushed
 *   endaddr   - The last address to be flushed
 *
 * Returned Value:
 *   None
 *
 ***************************************************************************/

void l2cc_flush(uint32_t startaddr, uint32_t endaddr)
{
  uintptr_t  flushsize;
  uintptr_t  gulpend;
  irqstate_t flags;

  /* If the range of addresses to flush is as large or larger the L2 cache,
   * then just flush the whole thing.
   */

  flushsize = endaddr - startaddr;
  if (flushsize >= PL310_CACHE_SIZE)
    {
      l2cc_flush_all();
      return;
    }

  /* Align the starting address to a cache line boundary */

  startaddr &= ~PL310_CACHE_LINE_MASK;

  /* Flush the L2 cache by cache line, enabling interrupts momentarily
   * every PL310_GULP_SIZE bytes.
   */

  while (startaddr < endaddr)
    {
      /* Get the size of the next gulp of cache lines to flush.  We do
       * this in small chunks so that we do not have to keep interrupts
       * disabled throughout the whole flush.
       */

      flushsize = endaddr - startaddr;
      gulpend   = startaddr + MIN(flushsize, PL310_GULP_SIZE);

      /* Disable interrupts and flush the gulp */

      flags = irqsave();
      while (startaddr < gulpend)
        {
          /* Flush the cache line by writing the address to the Clean
           * Invalidate Physical Address Line Register (CIPALR).
           */

          putreg32(startaddr, L2CC_CIPALR);

          /* Start of the next cache line */

          startaddr += PL310_CACHE_LINE_SIZE;
        }

      /* Enable interrupts momentarily */

      irqrestore(flags);
    }

  /* Drain the STB. Operation complete when all buffers, LRB, LFB, STB, and
   * EB, are empty.
   */

  flags = irqsave();
  putreg32(0, L2CC_CSR);
  irqrestore(flags);
}

#endif /* CONFIG_ARMV7A_L2CC_PL310 */