You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

609 lines
17 KiB

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