* Copyright (C) 2002 Broadcom, Inc.
  *   memcpy/copy_user author: Mark Vandevoorde
  * Copyright (C) 2007  Maciej W. Rozycki
+ * Copyright (C) 2014 Imagination Technologies Ltd.
  *
  * Mnemonic names for arguments to memcpy/__copy_user
  */
  * they're not protected.
  */
 
-#define EXC(inst_reg,addr,handler)             \
-9:     inst_reg, addr;                         \
+/* Instruction type */
+#define LD_INSN 1
+#define ST_INSN 2
+
+/*
+ * Wrapper to add an entry in the exception table
+ * in case the insn causes a memory exception.
+ * Arguments:
+ * insn    : Load/store instruction
+ * type    : Instruction type
+ * reg     : Register
+ * addr    : Address
+ * handler : Exception handler
+ */
+#define EXC(insn, type, reg, addr, handler)    \
+9:     insn reg, addr;                         \
        .section __ex_table,"a";                \
        PTR     9b, handler;                    \
        .previous
 
 #ifdef USE_DOUBLE
 
-#define LOAD   ld
-#define LOADL  ldl
-#define LOADR  ldr
-#define STOREL sdl
-#define STORER sdr
-#define STORE  sd
+#define LOADK ld /* No exception */
+#define LOAD(reg, addr, handler)       EXC(ld, LD_INSN, reg, addr, handler)
+#define LOADL(reg, addr, handler)      EXC(ldl, LD_INSN, reg, addr, handler)
+#define LOADR(reg, addr, handler)      EXC(ldr, LD_INSN, reg, addr, handler)
+#define STOREL(reg, addr, handler)     EXC(sdl, ST_INSN, reg, addr, handler)
+#define STORER(reg, addr, handler)     EXC(sdr, ST_INSN, reg, addr, handler)
+#define STORE(reg, addr, handler)      EXC(sd, ST_INSN, reg, addr, handler)
 #define ADD    daddu
 #define SUB    dsubu
 #define SRL    dsrl
 
 #else
 
-#define LOAD   lw
-#define LOADL  lwl
-#define LOADR  lwr
-#define STOREL swl
-#define STORER swr
-#define STORE  sw
+#define LOADK lw /* No exception */
+#define LOAD(reg, addr, handler)       EXC(lw, LD_INSN, reg, addr, handler)
+#define LOADL(reg, addr, handler)      EXC(lwl, LD_INSN, reg, addr, handler)
+#define LOADR(reg, addr, handler)      EXC(lwr, LD_INSN, reg, addr, handler)
+#define STOREL(reg, addr, handler)     EXC(swl, ST_INSN, reg, addr, handler)
+#define STORER(reg, addr, handler)     EXC(swr, ST_INSN, reg, addr, handler)
+#define STORE(reg, addr, handler)      EXC(sw, ST_INSN, reg, addr, handler)
 #define ADD    addu
 #define SUB    subu
 #define SRL    srl
 
 #endif /* USE_DOUBLE */
 
+#define LOADB(reg, addr, handler)      EXC(lb, LD_INSN, reg, addr, handler)
+#define STOREB(reg, addr, handler)     EXC(sb, ST_INSN, reg, addr, handler)
+
 #ifdef CONFIG_CPU_LITTLE_ENDIAN
 #define LDFIRST LOADR
 #define LDREST LOADL
        .align  4
 1:
        R10KCBARRIER(0(ra))
-EXC(   LOAD    t0, UNIT(0)(src),       .Ll_exc)
-EXC(   LOAD    t1, UNIT(1)(src),       .Ll_exc_copy)
-EXC(   LOAD    t2, UNIT(2)(src),       .Ll_exc_copy)
-EXC(   LOAD    t3, UNIT(3)(src),       .Ll_exc_copy)
+       LOAD(t0, UNIT(0)(src), .Ll_exc)
+       LOAD(t1, UNIT(1)(src), .Ll_exc_copy)
+       LOAD(t2, UNIT(2)(src), .Ll_exc_copy)
+       LOAD(t3, UNIT(3)(src), .Ll_exc_copy)
        SUB     len, len, 8*NBYTES
-EXC(   LOAD    t4, UNIT(4)(src),       .Ll_exc_copy)
-EXC(   LOAD    t7, UNIT(5)(src),       .Ll_exc_copy)
-EXC(   STORE   t0, UNIT(0)(dst),       .Ls_exc_p8u)
-EXC(   STORE   t1, UNIT(1)(dst),       .Ls_exc_p7u)
-EXC(   LOAD    t0, UNIT(6)(src),       .Ll_exc_copy)
-EXC(   LOAD    t1, UNIT(7)(src),       .Ll_exc_copy)
+       LOAD(t4, UNIT(4)(src), .Ll_exc_copy)
+       LOAD(t7, UNIT(5)(src), .Ll_exc_copy)
+       STORE(t0, UNIT(0)(dst), .Ls_exc_p8u)
+       STORE(t1, UNIT(1)(dst), .Ls_exc_p7u)
+       LOAD(t0, UNIT(6)(src), .Ll_exc_copy)
+       LOAD(t1, UNIT(7)(src), .Ll_exc_copy)
        ADD     src, src, 8*NBYTES
        ADD     dst, dst, 8*NBYTES
-EXC(   STORE   t2, UNIT(-6)(dst),      .Ls_exc_p6u)
-EXC(   STORE   t3, UNIT(-5)(dst),      .Ls_exc_p5u)
-EXC(   STORE   t4, UNIT(-4)(dst),      .Ls_exc_p4u)
-EXC(   STORE   t7, UNIT(-3)(dst),      .Ls_exc_p3u)
-EXC(   STORE   t0, UNIT(-2)(dst),      .Ls_exc_p2u)
-EXC(   STORE   t1, UNIT(-1)(dst),      .Ls_exc_p1u)
+       STORE(t2, UNIT(-6)(dst), .Ls_exc_p6u)
+       STORE(t3, UNIT(-5)(dst), .Ls_exc_p5u)
+       STORE(t4, UNIT(-4)(dst), .Ls_exc_p4u)
+       STORE(t7, UNIT(-3)(dst), .Ls_exc_p3u)
+       STORE(t0, UNIT(-2)(dst), .Ls_exc_p2u)
+       STORE(t1, UNIT(-1)(dst), .Ls_exc_p1u)
        PREF(   0, 8*32(src) )
        PREF(   1, 8*32(dst) )
        bne     len, rem, 1b
        /*
         * len >= 4*NBYTES
         */
-EXC(   LOAD    t0, UNIT(0)(src),       .Ll_exc)
-EXC(   LOAD    t1, UNIT(1)(src),       .Ll_exc_copy)
-EXC(   LOAD    t2, UNIT(2)(src),       .Ll_exc_copy)
-EXC(   LOAD    t3, UNIT(3)(src),       .Ll_exc_copy)
+       LOAD( t0, UNIT(0)(src), .Ll_exc)
+       LOAD( t1, UNIT(1)(src), .Ll_exc_copy)
+       LOAD( t2, UNIT(2)(src), .Ll_exc_copy)
+       LOAD( t3, UNIT(3)(src), .Ll_exc_copy)
        SUB     len, len, 4*NBYTES
        ADD     src, src, 4*NBYTES
        R10KCBARRIER(0(ra))
-EXC(   STORE   t0, UNIT(0)(dst),       .Ls_exc_p4u)
-EXC(   STORE   t1, UNIT(1)(dst),       .Ls_exc_p3u)
-EXC(   STORE   t2, UNIT(2)(dst),       .Ls_exc_p2u)
-EXC(   STORE   t3, UNIT(3)(dst),       .Ls_exc_p1u)
+       STORE(t0, UNIT(0)(dst), .Ls_exc_p4u)
+       STORE(t1, UNIT(1)(dst), .Ls_exc_p3u)
+       STORE(t2, UNIT(2)(dst), .Ls_exc_p2u)
+       STORE(t3, UNIT(3)(dst), .Ls_exc_p1u)
        .set    reorder                         /* DADDI_WAR */
        ADD     dst, dst, 4*NBYTES
        beqz    len, .Ldone
         nop
 1:
        R10KCBARRIER(0(ra))
-EXC(   LOAD    t0, 0(src),             .Ll_exc)
+       LOAD(t0, 0(src), .Ll_exc)
        ADD     src, src, NBYTES
        SUB     len, len, NBYTES
-EXC(   STORE   t0, 0(dst),             .Ls_exc_p1u)
+       STORE(t0, 0(dst), .Ls_exc_p1u)
        .set    reorder                         /* DADDI_WAR */
        ADD     dst, dst, NBYTES
        bne     rem, len, 1b
         ADD    t1, dst, len    # t1 is just past last byte of dst
        li      bits, 8*NBYTES
        SLL     rem, len, 3     # rem = number of bits to keep
-EXC(   LOAD    t0, 0(src),             .Ll_exc)
+       LOAD(t0, 0(src), .Ll_exc)
        SUB     bits, bits, rem # bits = number of bits to discard
        SHIFT_DISCARD t0, t0, bits
-EXC(   STREST  t0, -1(t1),             .Ls_exc)
+       STREST(t0, -1(t1), .Ls_exc)
        jr      ra
         move   len, zero
 .Ldst_unaligned:
         * Set match = (src and dst have same alignment)
         */
 #define match rem
-EXC(   LDFIRST t3, FIRST(0)(src),      .Ll_exc)
+       LDFIRST(t3, FIRST(0)(src), .Ll_exc)
        ADD     t2, zero, NBYTES
-EXC(   LDREST  t3, REST(0)(src),       .Ll_exc_copy)
+       LDREST(t3, REST(0)(src), .Ll_exc_copy)
        SUB     t2, t2, t1      # t2 = number of bytes copied
        xor     match, t0, t1
        R10KCBARRIER(0(ra))
-EXC(   STFIRST t3, FIRST(0)(dst),      .Ls_exc)
+       STFIRST(t3, FIRST(0)(dst), .Ls_exc)
        beq     len, t2, .Ldone
         SUB    len, len, t2
        ADD     dst, dst, t2
  * are to the same unit (unless src is aligned, but it's not).
  */
        R10KCBARRIER(0(ra))
-EXC(   LDFIRST t0, FIRST(0)(src),      .Ll_exc)
-EXC(   LDFIRST t1, FIRST(1)(src),      .Ll_exc_copy)
+       LDFIRST(t0, FIRST(0)(src), .Ll_exc)
+       LDFIRST(t1, FIRST(1)(src), .Ll_exc_copy)
        SUB     len, len, 4*NBYTES
-EXC(   LDREST  t0, REST(0)(src),       .Ll_exc_copy)
-EXC(   LDREST  t1, REST(1)(src),       .Ll_exc_copy)
-EXC(   LDFIRST t2, FIRST(2)(src),      .Ll_exc_copy)
-EXC(   LDFIRST t3, FIRST(3)(src),      .Ll_exc_copy)
-EXC(   LDREST  t2, REST(2)(src),       .Ll_exc_copy)
-EXC(   LDREST  t3, REST(3)(src),       .Ll_exc_copy)
+       LDREST(t0, REST(0)(src), .Ll_exc_copy)
+       LDREST(t1, REST(1)(src), .Ll_exc_copy)
+       LDFIRST(t2, FIRST(2)(src), .Ll_exc_copy)
+       LDFIRST(t3, FIRST(3)(src), .Ll_exc_copy)
+       LDREST(t2, REST(2)(src), .Ll_exc_copy)
+       LDREST(t3, REST(3)(src), .Ll_exc_copy)
        PREF(   0, 9*32(src) )          # 0 is PREF_LOAD  (not streamed)
        ADD     src, src, 4*NBYTES
 #ifdef CONFIG_CPU_SB1
        nop                             # improves slotting
 #endif
-EXC(   STORE   t0, UNIT(0)(dst),       .Ls_exc_p4u)
-EXC(   STORE   t1, UNIT(1)(dst),       .Ls_exc_p3u)
-EXC(   STORE   t2, UNIT(2)(dst),       .Ls_exc_p2u)
-EXC(   STORE   t3, UNIT(3)(dst),       .Ls_exc_p1u)
+       STORE(t0, UNIT(0)(dst), .Ls_exc_p4u)
+       STORE(t1, UNIT(1)(dst), .Ls_exc_p3u)
+       STORE(t2, UNIT(2)(dst), .Ls_exc_p2u)
+       STORE(t3, UNIT(3)(dst), .Ls_exc_p1u)
        PREF(   1, 9*32(dst) )          # 1 is PREF_STORE (not streamed)
        .set    reorder                         /* DADDI_WAR */
        ADD     dst, dst, 4*NBYTES
         nop
 1:
        R10KCBARRIER(0(ra))
-EXC(   LDFIRST t0, FIRST(0)(src),      .Ll_exc)
-EXC(   LDREST  t0, REST(0)(src),       .Ll_exc_copy)
+       LDFIRST(t0, FIRST(0)(src), .Ll_exc)
+       LDREST(t0, REST(0)(src), .Ll_exc_copy)
        ADD     src, src, NBYTES
        SUB     len, len, NBYTES
-EXC(   STORE   t0, 0(dst),             .Ls_exc_p1u)
+       STORE(t0, 0(dst), .Ls_exc_p1u)
        .set    reorder                         /* DADDI_WAR */
        ADD     dst, dst, NBYTES
        bne     len, rem, 1b
        /* 0 < len < NBYTES  */
        R10KCBARRIER(0(ra))
 #define COPY_BYTE(N)                   \
-EXC(   lb      t0, N(src), .Ll_exc);   \
+       LOADB(t0, N(src), .Ll_exc);     \
        SUB     len, len, 1;            \
        beqz    len, .Ldone;            \
-EXC(    sb     t0, N(dst), .Ls_exc_p1)
+       STOREB(t0, N(dst), .Ls_exc_p1)
 
        COPY_BYTE(0)
        COPY_BYTE(1)
        COPY_BYTE(4)
        COPY_BYTE(5)
 #endif
-EXC(   lb      t0, NBYTES-2(src), .Ll_exc)
+       LOADB(t0, NBYTES-2(src), .Ll_exc)
        SUB     len, len, 1
        jr      ra
-EXC(    sb     t0, NBYTES-2(dst), .Ls_exc_p1)
+       STOREB(t0, NBYTES-2(dst), .Ls_exc_p1)
 .Ldone:
        jr      ra
         nop
         *
         * Assumes src < THREAD_BUADDR($28)
         */
-       LOAD    t0, TI_TASK($28)
+       LOADK   t0, TI_TASK($28)
         nop
-       LOAD    t0, THREAD_BUADDR(t0)
+       LOADK   t0, THREAD_BUADDR(t0)
 1:
-EXC(   lb      t1, 0(src),     .Ll_exc)
+       LOADB(t1, 0(src), .Ll_exc)
        ADD     src, src, 1
        sb      t1, 0(dst)      # can't fault -- we're copy_from_user
        .set    reorder                         /* DADDI_WAR */
        bne     src, t0, 1b
        .set    noreorder
 .Ll_exc:
-       LOAD    t0, TI_TASK($28)
+       LOADK   t0, TI_TASK($28)
         nop
-       LOAD    t0, THREAD_BUADDR(t0)   # t0 is just past last good address
+       LOADK   t0, THREAD_BUADDR(t0)   # t0 is just past last good address
         nop
        SUB     len, AT, t0             # len number of uncopied bytes
        bnez    t6, .Ldone      /* Skip the zeroing part if inatomic */