1e425aa517238fba7301c81d51d27955515a88ce
[fw/openocd] / src / target / armv7a.c
1 /***************************************************************************
2  *    Copyright (C) 2009 by David Brownell                                 *
3  *                                                                         *
4  *    Copyright (C) ST-Ericsson SA 2011 michel.jaouen@stericsson.com       *
5  *                                                                         *
6  *   This program is free software; you can redistribute it and/or modify  *
7  *   it under the terms of the GNU General Public License as published by  *
8  *   the Free Software Foundation; either version 2 of the License, or     *
9  *   (at your option) any later version.                                   *
10  *                                                                         *
11  *   This program is distributed in the hope that it will be useful,       *
12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
14  *   GNU General Public License for more details.                          *
15  *                                                                         *
16  *   You should have received a copy of the GNU General Public License     *
17  *   along with this program.  If not, see <http://www.gnu.org/licenses/>. *
18  ***************************************************************************/
19
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23
24 #include <helper/replacements.h>
25
26 #include "armv7a.h"
27 #include "armv7a_mmu.h"
28 #include "arm_disassembler.h"
29
30 #include "register.h"
31 #include <helper/binarybuffer.h>
32 #include <helper/command.h>
33
34 #include <stdlib.h>
35 #include <string.h>
36 #include <unistd.h>
37
38 #include "arm_opcodes.h"
39 #include "target.h"
40 #include "target_type.h"
41 #include "smp.h"
42
43 static void armv7a_show_fault_registers(struct target *target)
44 {
45         uint32_t dfsr, ifsr, dfar, ifar;
46         struct armv7a_common *armv7a = target_to_armv7a(target);
47         struct arm_dpm *dpm = armv7a->arm.dpm;
48         int retval;
49
50         retval = dpm->prepare(dpm);
51         if (retval != ERROR_OK)
52                 return;
53
54         /* ARMV4_5_MRC(cpnum, op1, r0, crn, crm, op2) */
55
56         /* c5/c0 - {data, instruction} fault status registers */
57         retval = dpm->instr_read_data_r0(dpm,
58                         ARMV4_5_MRC(15, 0, 0, 5, 0, 0),
59                         &dfsr);
60         if (retval != ERROR_OK)
61                 goto done;
62
63         retval = dpm->instr_read_data_r0(dpm,
64                         ARMV4_5_MRC(15, 0, 0, 5, 0, 1),
65                         &ifsr);
66         if (retval != ERROR_OK)
67                 goto done;
68
69         /* c6/c0 - {data, instruction} fault address registers */
70         retval = dpm->instr_read_data_r0(dpm,
71                         ARMV4_5_MRC(15, 0, 0, 6, 0, 0),
72                         &dfar);
73         if (retval != ERROR_OK)
74                 goto done;
75
76         retval = dpm->instr_read_data_r0(dpm,
77                         ARMV4_5_MRC(15, 0, 0, 6, 0, 2),
78                         &ifar);
79         if (retval != ERROR_OK)
80                 goto done;
81
82         LOG_USER("Data fault registers        DFSR: %8.8" PRIx32
83                 ", DFAR: %8.8" PRIx32, dfsr, dfar);
84         LOG_USER("Instruction fault registers IFSR: %8.8" PRIx32
85                 ", IFAR: %8.8" PRIx32, ifsr, ifar);
86
87 done:
88         /* (void) */ dpm->finish(dpm);
89 }
90
91
92 /*  retrieve main id register  */
93 static int armv7a_read_midr(struct target *target)
94 {
95         int retval = ERROR_FAIL;
96         struct armv7a_common *armv7a = target_to_armv7a(target);
97         struct arm_dpm *dpm = armv7a->arm.dpm;
98         uint32_t midr;
99         retval = dpm->prepare(dpm);
100         if (retval != ERROR_OK)
101                 goto done;
102         /* MRC p15,0,<Rd>,c0,c0,0; read main id register*/
103
104         retval = dpm->instr_read_data_r0(dpm,
105                         ARMV4_5_MRC(15, 0, 0, 0, 0, 0),
106                         &midr);
107         if (retval != ERROR_OK)
108                 goto done;
109
110         armv7a->rev = (midr & 0xf);
111         armv7a->partnum = (midr >> 4) & 0xfff;
112         armv7a->arch = (midr >> 16) & 0xf;
113         armv7a->variant = (midr >> 20) & 0xf;
114         armv7a->implementor = (midr >> 24) & 0xff;
115         LOG_DEBUG("%s rev %" PRIx32 ", partnum %" PRIx32 ", arch %" PRIx32
116                          ", variant %" PRIx32 ", implementor %" PRIx32,
117                  target->cmd_name,
118                  armv7a->rev,
119                  armv7a->partnum,
120                  armv7a->arch,
121                  armv7a->variant,
122                  armv7a->implementor);
123
124 done:
125         dpm->finish(dpm);
126         return retval;
127 }
128
129 int armv7a_read_ttbcr(struct target *target)
130 {
131         struct armv7a_common *armv7a = target_to_armv7a(target);
132         struct arm_dpm *dpm = armv7a->arm.dpm;
133         uint32_t ttbcr, ttbcr_n;
134         int ttbidx;
135         int retval;
136
137         retval = dpm->prepare(dpm);
138         if (retval != ERROR_OK)
139                 goto done;
140
141         /*  MRC p15,0,<Rt>,c2,c0,2 ; Read CP15 Translation Table Base Control Register*/
142         retval = dpm->instr_read_data_r0(dpm,
143                         ARMV4_5_MRC(15, 0, 0, 2, 0, 2),
144                         &ttbcr);
145         if (retval != ERROR_OK)
146                 goto done;
147
148         LOG_DEBUG("ttbcr %" PRIx32, ttbcr);
149
150         ttbcr_n = ttbcr & 0x7;
151         armv7a->armv7a_mmu.ttbcr = ttbcr;
152         armv7a->armv7a_mmu.cached = 1;
153
154         for (ttbidx = 0; ttbidx < 2; ttbidx++) {
155                 /*  MRC p15,0,<Rt>,c2,c0,ttbidx */
156                 retval = dpm->instr_read_data_r0(dpm,
157                                 ARMV4_5_MRC(15, 0, 0, 2, 0, ttbidx),
158                                 &armv7a->armv7a_mmu.ttbr[ttbidx]);
159                 if (retval != ERROR_OK)
160                         goto done;
161         }
162
163         /*
164          * ARM Architecture Reference Manual (ARMv7-A and ARMv7-R edition),
165          * document # ARM DDI 0406C
166          */
167         armv7a->armv7a_mmu.ttbr_range[0]  = 0xffffffff >> ttbcr_n;
168         armv7a->armv7a_mmu.ttbr_range[1] = 0xffffffff;
169         armv7a->armv7a_mmu.ttbr_mask[0] = 0xffffffff << (14 - ttbcr_n);
170         armv7a->armv7a_mmu.ttbr_mask[1] = 0xffffffff << 14;
171         armv7a->armv7a_mmu.cached = 1;
172
173         retval = armv7a_read_midr(target);
174         if (retval != ERROR_OK)
175                 goto done;
176
177         /* FIXME: why this special case based on part number? */
178         if ((armv7a->partnum & 0xf) == 0) {
179                 /*  ARM DDI 0344H , ARM DDI 0407F */
180                 armv7a->armv7a_mmu.ttbr_mask[0]  = 7 << (32 - ttbcr_n);
181         }
182
183         LOG_DEBUG("ttbr1 %s, ttbr0_mask %" PRIx32 " ttbr1_mask %" PRIx32,
184                   (ttbcr_n != 0) ? "used" : "not used",
185                   armv7a->armv7a_mmu.ttbr_mask[0],
186                   armv7a->armv7a_mmu.ttbr_mask[1]);
187
188 done:
189         dpm->finish(dpm);
190         return retval;
191 }
192
193 /* FIXME: remove it */
194 static int armv7a_l2x_cache_init(struct target *target, uint32_t base, uint32_t way)
195 {
196         struct armv7a_l2x_cache *l2x_cache;
197         struct target_list *head;
198
199         struct armv7a_common *armv7a = target_to_armv7a(target);
200         l2x_cache = calloc(1, sizeof(struct armv7a_l2x_cache));
201         l2x_cache->base = base;
202         l2x_cache->way = way;
203         /*LOG_INFO("cache l2 initialized base %x  way %d",
204         l2x_cache->base,l2x_cache->way);*/
205         if (armv7a->armv7a_mmu.armv7a_cache.outer_cache)
206                 LOG_INFO("outer cache already initialized\n");
207         armv7a->armv7a_mmu.armv7a_cache.outer_cache = l2x_cache;
208         /*  initialize all target in this cluster (smp target)
209          *  l2 cache must be configured after smp declaration */
210         foreach_smp_target(head, target->smp_targets) {
211                 struct target *curr = head->target;
212                 if (curr != target) {
213                         armv7a = target_to_armv7a(curr);
214                         if (armv7a->armv7a_mmu.armv7a_cache.outer_cache)
215                                 LOG_ERROR("smp target : outer cache already initialized\n");
216                         armv7a->armv7a_mmu.armv7a_cache.outer_cache = l2x_cache;
217                 }
218         }
219         return JIM_OK;
220 }
221
222 /* FIXME: remove it */
223 COMMAND_HANDLER(handle_cache_l2x)
224 {
225         struct target *target = get_current_target(CMD_CTX);
226         uint32_t base, way;
227
228         if (CMD_ARGC != 2)
229                 return ERROR_COMMAND_SYNTAX_ERROR;
230
231         /* command_print(CMD, "%s %s", CMD_ARGV[0], CMD_ARGV[1]); */
232         COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], base);
233         COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], way);
234
235         /* AP address is in bits 31:24 of DP_SELECT */
236         armv7a_l2x_cache_init(target, base, way);
237
238         return ERROR_OK;
239 }
240
241 int armv7a_handle_cache_info_command(struct command_invocation *cmd,
242         struct armv7a_cache_common *armv7a_cache)
243 {
244         struct armv7a_l2x_cache *l2x_cache = (struct armv7a_l2x_cache *)
245                 (armv7a_cache->outer_cache);
246
247         int cl;
248
249         if (armv7a_cache->info == -1) {
250                 command_print(cmd, "cache not yet identified");
251                 return ERROR_OK;
252         }
253
254         for (cl = 0; cl < armv7a_cache->loc; cl++) {
255                 struct armv7a_arch_cache *arch = &(armv7a_cache->arch[cl]);
256
257                 if (arch->ctype & 1) {
258                         command_print(cmd,
259                                 "L%d I-Cache: linelen %" PRIu32
260                                 ", associativity %" PRIu32
261                                 ", nsets %" PRIu32
262                                 ", cachesize %" PRIu32 " KBytes",
263                                 cl+1,
264                                 arch->i_size.linelen,
265                                 arch->i_size.associativity,
266                                 arch->i_size.nsets,
267                                 arch->i_size.cachesize);
268                 }
269
270                 if (arch->ctype >= 2) {
271                         command_print(cmd,
272                                 "L%d D-Cache: linelen %" PRIu32
273                                 ", associativity %" PRIu32
274                                 ", nsets %" PRIu32
275                                 ", cachesize %" PRIu32 " KBytes",
276                                 cl+1,
277                                 arch->d_u_size.linelen,
278                                 arch->d_u_size.associativity,
279                                 arch->d_u_size.nsets,
280                                 arch->d_u_size.cachesize);
281                 }
282         }
283
284         if (l2x_cache)
285                 command_print(cmd, "Outer unified cache Base Address 0x%" PRIx32 ", %" PRIu32 " ways",
286                         l2x_cache->base, l2x_cache->way);
287
288         return ERROR_OK;
289 }
290
291 /*  retrieve core id cluster id  */
292 static int armv7a_read_mpidr(struct target *target)
293 {
294         int retval = ERROR_FAIL;
295         struct armv7a_common *armv7a = target_to_armv7a(target);
296         struct arm_dpm *dpm = armv7a->arm.dpm;
297         uint32_t mpidr;
298         retval = dpm->prepare(dpm);
299         if (retval != ERROR_OK)
300                 goto done;
301         /* MRC p15,0,<Rd>,c0,c0,5; read Multiprocessor ID register*/
302
303         retval = dpm->instr_read_data_r0(dpm,
304                         ARMV4_5_MRC(15, 0, 0, 0, 0, 5),
305                         &mpidr);
306         if (retval != ERROR_OK)
307                 goto done;
308
309         /* Is register in Multiprocessing Extensions register format? */
310         if (mpidr & MPIDR_MP_EXT) {
311                 LOG_DEBUG("%s: MPIDR 0x%" PRIx32, target_name(target), mpidr);
312                 armv7a->multi_processor_system = (mpidr >> 30) & 1;
313                 armv7a->multi_threading_processor = (mpidr >> 24) & 1;
314                 armv7a->level2_id = (mpidr >> 16) & 0xf;
315                 armv7a->cluster_id = (mpidr >> 8) & 0xf;
316                 armv7a->cpu_id = mpidr & 0xf;
317                 LOG_INFO("%s: MPIDR level2 %x, cluster %x, core %x, %s, %s",
318                         target_name(target),
319                         armv7a->level2_id,
320                         armv7a->cluster_id,
321                         armv7a->cpu_id,
322                         armv7a->multi_processor_system == 0 ? "multi core" : "mono core",
323                         armv7a->multi_threading_processor == 1 ? "SMT" : "no SMT");
324
325         } else
326                 LOG_ERROR("MPIDR not in multiprocessor format");
327
328 done:
329         dpm->finish(dpm);
330         return retval;
331
332
333 }
334
335 static int get_cache_info(struct arm_dpm *dpm, int cl, int ct, uint32_t *cache_reg)
336 {
337         int retval = ERROR_OK;
338
339         /*  select cache level */
340         retval = dpm->instr_write_data_r0(dpm,
341                         ARMV4_5_MCR(15, 2, 0, 0, 0, 0),
342                         (cl << 1) | (ct == 1 ? 1 : 0));
343         if (retval != ERROR_OK)
344                 goto done;
345
346         retval = dpm->instr_read_data_r0(dpm,
347                         ARMV4_5_MRC(15, 1, 0, 0, 0, 0),
348                         cache_reg);
349  done:
350         return retval;
351 }
352
353 static struct armv7a_cachesize decode_cache_reg(uint32_t cache_reg)
354 {
355         struct armv7a_cachesize size;
356         int i = 0;
357
358         size.linelen = 16 << (cache_reg & 0x7);
359         size.associativity = ((cache_reg >> 3) & 0x3ff) + 1;
360         size.nsets = ((cache_reg >> 13) & 0x7fff) + 1;
361         size.cachesize = size.linelen * size.associativity * size.nsets / 1024;
362
363         /*  compute info for set way operation on cache */
364         size.index_shift = (cache_reg & 0x7) + 4;
365         size.index = (cache_reg >> 13) & 0x7fff;
366         size.way = ((cache_reg >> 3) & 0x3ff);
367
368         while (((size.way << i) & 0x80000000) == 0)
369                 i++;
370         size.way_shift = i;
371
372         return size;
373 }
374
375 int armv7a_identify_cache(struct target *target)
376 {
377         /*  read cache descriptor */
378         int retval = ERROR_FAIL;
379         struct armv7a_common *armv7a = target_to_armv7a(target);
380         struct arm_dpm *dpm = armv7a->arm.dpm;
381         uint32_t csselr, clidr, ctr;
382         uint32_t cache_reg;
383         int cl, ctype;
384         struct armv7a_cache_common *cache =
385                 &(armv7a->armv7a_mmu.armv7a_cache);
386
387         retval = dpm->prepare(dpm);
388         if (retval != ERROR_OK)
389                 goto done;
390
391         /* retrieve CTR
392          * mrc p15, 0, r0, c0, c0, 1            @ read ctr */
393         retval = dpm->instr_read_data_r0(dpm,
394                         ARMV4_5_MRC(15, 0, 0, 0, 0, 1),
395                         &ctr);
396         if (retval != ERROR_OK)
397                 goto done;
398
399         cache->iminline = 4UL << (ctr & 0xf);
400         cache->dminline = 4UL << ((ctr & 0xf0000) >> 16);
401         LOG_DEBUG("ctr %" PRIx32 " ctr.iminline %" PRIu32 " ctr.dminline %" PRIu32,
402                  ctr, cache->iminline, cache->dminline);
403
404         /*  retrieve CLIDR
405          *  mrc p15, 1, r0, c0, c0, 1           @ read clidr */
406         retval = dpm->instr_read_data_r0(dpm,
407                         ARMV4_5_MRC(15, 1, 0, 0, 0, 1),
408                         &clidr);
409         if (retval != ERROR_OK)
410                 goto done;
411
412         cache->loc = (clidr & 0x7000000) >> 24;
413         LOG_DEBUG("Number of cache levels to PoC %" PRId32, cache->loc);
414
415         /*  retrieve selected cache for later restore
416          *  MRC p15, 2,<Rd>, c0, c0, 0; Read CSSELR */
417         retval = dpm->instr_read_data_r0(dpm,
418                         ARMV4_5_MRC(15, 2, 0, 0, 0, 0),
419                         &csselr);
420         if (retval != ERROR_OK)
421                 goto done;
422
423         /* retrieve all available inner caches */
424         for (cl = 0; cl < cache->loc; clidr >>= 3, cl++) {
425
426                 /* isolate cache type at current level */
427                 ctype = clidr & 7;
428
429                 /* skip reserved values */
430                 if (ctype > CACHE_LEVEL_HAS_UNIFIED_CACHE)
431                         continue;
432
433                 /* separate d or unified d/i cache at this level ? */
434                 if (ctype & (CACHE_LEVEL_HAS_UNIFIED_CACHE | CACHE_LEVEL_HAS_D_CACHE)) {
435                         /* retrieve d-cache info */
436                         retval = get_cache_info(dpm, cl, 0, &cache_reg);
437                         if (retval != ERROR_OK)
438                                 goto done;
439                         cache->arch[cl].d_u_size = decode_cache_reg(cache_reg);
440
441                         LOG_DEBUG("data/unified cache index %" PRIu32 " << %" PRIu32 ", way %" PRIu32 " << %" PRIu32,
442                                         cache->arch[cl].d_u_size.index,
443                                         cache->arch[cl].d_u_size.index_shift,
444                                         cache->arch[cl].d_u_size.way,
445                                         cache->arch[cl].d_u_size.way_shift);
446
447                         LOG_DEBUG("cacheline %" PRIu32 " bytes %" PRIu32 " KBytes asso %" PRIu32 " ways",
448                                         cache->arch[cl].d_u_size.linelen,
449                                         cache->arch[cl].d_u_size.cachesize,
450                                         cache->arch[cl].d_u_size.associativity);
451                 }
452
453                 /* separate i-cache at this level ? */
454                 if (ctype & CACHE_LEVEL_HAS_I_CACHE) {
455                         /* retrieve i-cache info */
456                         retval = get_cache_info(dpm, cl, 1, &cache_reg);
457                         if (retval != ERROR_OK)
458                                 goto done;
459                         cache->arch[cl].i_size = decode_cache_reg(cache_reg);
460
461                         LOG_DEBUG("instruction cache index %" PRIu32 " << %" PRIu32 ", way %" PRIu32 " << %" PRIu32,
462                                         cache->arch[cl].i_size.index,
463                                         cache->arch[cl].i_size.index_shift,
464                                         cache->arch[cl].i_size.way,
465                                         cache->arch[cl].i_size.way_shift);
466
467                         LOG_DEBUG("cacheline %" PRIu32 " bytes %" PRIu32 " KBytes asso %" PRIu32 " ways",
468                                         cache->arch[cl].i_size.linelen,
469                                         cache->arch[cl].i_size.cachesize,
470                                         cache->arch[cl].i_size.associativity);
471                 }
472
473                 cache->arch[cl].ctype = ctype;
474         }
475
476         /*  restore selected cache  */
477         dpm->instr_write_data_r0(dpm,
478                 ARMV4_5_MRC(15, 2, 0, 0, 0, 0),
479                 csselr);
480
481         if (retval != ERROR_OK)
482                 goto done;
483
484         /*  if no l2 cache initialize l1 data cache flush function function */
485         if (!armv7a->armv7a_mmu.armv7a_cache.flush_all_data_cache) {
486                 armv7a->armv7a_mmu.armv7a_cache.flush_all_data_cache =
487                         armv7a_cache_auto_flush_all_data;
488         }
489
490         armv7a->armv7a_mmu.armv7a_cache.info = 1;
491 done:
492         dpm->finish(dpm);
493         armv7a_read_mpidr(target);
494         return retval;
495
496 }
497
498 static int armv7a_setup_semihosting(struct target *target, int enable)
499 {
500         struct armv7a_common *armv7a = target_to_armv7a(target);
501         uint32_t vcr;
502         int ret;
503
504         ret = mem_ap_read_atomic_u32(armv7a->debug_ap,
505                                          armv7a->debug_base + CPUDBG_VCR,
506                                          &vcr);
507         if (ret < 0) {
508                 LOG_ERROR("Failed to read VCR register\n");
509                 return ret;
510         }
511
512         if (enable)
513                 vcr |= DBG_VCR_SVC_MASK;
514         else
515                 vcr &= ~DBG_VCR_SVC_MASK;
516
517         ret = mem_ap_write_atomic_u32(armv7a->debug_ap,
518                                           armv7a->debug_base + CPUDBG_VCR,
519                                           vcr);
520         if (ret < 0)
521                 LOG_ERROR("Failed to write VCR register\n");
522
523         return ret;
524 }
525
526 int armv7a_init_arch_info(struct target *target, struct armv7a_common *armv7a)
527 {
528         struct arm *arm = &armv7a->arm;
529         arm->arch_info = armv7a;
530         target->arch_info = &armv7a->arm;
531         arm->setup_semihosting = armv7a_setup_semihosting;
532         /*  target is useful in all function arm v4 5 compatible */
533         armv7a->arm.target = target;
534         armv7a->arm.common_magic = ARM_COMMON_MAGIC;
535         armv7a->common_magic = ARMV7_COMMON_MAGIC;
536         armv7a->armv7a_mmu.armv7a_cache.info = -1;
537         armv7a->armv7a_mmu.armv7a_cache.outer_cache = NULL;
538         armv7a->armv7a_mmu.armv7a_cache.flush_all_data_cache = NULL;
539         armv7a->armv7a_mmu.armv7a_cache.auto_cache_enabled = 1;
540         return ERROR_OK;
541 }
542
543 int armv7a_arch_state(struct target *target)
544 {
545         static const char *state[] = {
546                 "disabled", "enabled"
547         };
548
549         struct armv7a_common *armv7a = target_to_armv7a(target);
550         struct arm *arm = &armv7a->arm;
551
552         if (armv7a->common_magic != ARMV7_COMMON_MAGIC) {
553                 LOG_ERROR("BUG: called for a non-ARMv7A target");
554                 return ERROR_COMMAND_SYNTAX_ERROR;
555         }
556
557         arm_arch_state(target);
558
559         if (armv7a->is_armv7r) {
560                 LOG_USER("D-Cache: %s, I-Cache: %s",
561                         state[armv7a->armv7a_mmu.armv7a_cache.d_u_cache_enabled],
562                         state[armv7a->armv7a_mmu.armv7a_cache.i_cache_enabled]);
563         } else {
564                 LOG_USER("MMU: %s, D-Cache: %s, I-Cache: %s",
565                         state[armv7a->armv7a_mmu.mmu_enabled],
566                         state[armv7a->armv7a_mmu.armv7a_cache.d_u_cache_enabled],
567                         state[armv7a->armv7a_mmu.armv7a_cache.i_cache_enabled]);
568         }
569
570         if (arm->core_mode == ARM_MODE_ABT)
571                 armv7a_show_fault_registers(target);
572
573         return ERROR_OK;
574 }
575
576 static const struct command_registration l2_cache_commands[] = {
577         {
578                 .name = "l2x",
579                 .handler = handle_cache_l2x,
580                 .mode = COMMAND_EXEC,
581                 .help = "configure l2x cache",
582                 .usage = "[base_addr] [number_of_way]",
583         },
584         COMMAND_REGISTRATION_DONE
585
586 };
587
588 static const struct command_registration l2x_cache_command_handlers[] = {
589         {
590                 .name = "cache_config",
591                 .mode = COMMAND_EXEC,
592                 .help = "cache configuration for a target",
593                 .usage = "",
594                 .chain = l2_cache_commands,
595         },
596         COMMAND_REGISTRATION_DONE
597 };
598
599 const struct command_registration armv7a_command_handlers[] = {
600         {
601                 .chain = l2x_cache_command_handlers,
602         },
603         {
604                 .chain = arm7a_cache_command_handlers,
605         },
606         COMMAND_REGISTRATION_DONE
607 };