cassemblyarmimx6

Enable ARM errata in inline assembly


I'm trying to learn asm by enabling workarounds for errata in a driver. That should be possible because kernel code is executed in the privileged world. The (minimalistic) code looks as follows.

unsigned int cp15c15 = 0, result = 0;
__asm__ volatile("mrc p15, 0, %0, c15, c0, 1" : "=r" (cp15c15));
cp15c15 |= (1<<22); /* Errata 845369 */
__asm__ volatile("mcr p15, 0, %0, c15, c0, 1" : "+r" (cp15c15));

This seems to be working, but when I read the register multiple times, I sometimes get a value without bit 22 enabled. (For example 0x000001 instead of 0x400001).

char buf[10];
__asm__ volatile("mrc p15, 0, %0, c15, c0, 1" : "=r" (cp15c15));
sprintf(buf, "0x%.8x", cp15c15);
copy_to_user(buffer, buf, 10);

I think I'm doing something wrong in the asm call. If someone can give me insight in why this only works 10% of the time, I really would appreciate it. (Asm is kind of cool).

EDIT:
The assembly code from the original NXP errata description:

MRC p15,0,rt,c15,c0,1
ORR rt,rt,#0x00400000
MCR p15,0,rt,c15,c0,1

EDIT 2:
If I enable this in ./linux/arch/arm/mm/proc-v7.S, the bit remains set when I read it from my driver. However if I disable it, it seems that bit switches off and on irregularly. It seems to corroborate to when the bit is set.


Solution

  • If the computer has multiple processor cores (like some i.MX 6 chips) then unless you make arrangements to run this all processor cores, the processor it runs on will be at the whim of the Linux scheduler.

    Therefore might set the register on one CPU, then if the check is run on the that CPU the modification will show, but if the check is run on a different CPU the original value will be read.