Merge tag 'fixes-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/arm...
[linux-2.6.git] / arch / metag / mm / maccess.c
blobeba2cfc935b10fc4a731d4b97f87df186cea86bd
1 /*
2 * safe read and write memory routines callable while atomic
4 * Copyright 2012 Imagination Technologies
5 */
7 #include <linux/uaccess.h>
8 #include <asm/io.h>
11 * The generic probe_kernel_write() uses the user copy code which can split the
12 * writes if the source is unaligned, and repeats writes to make exceptions
13 * precise. We override it here to avoid these things happening to memory mapped
14 * IO memory where they could have undesired effects.
15 * Due to the use of CACHERD instruction this only works on Meta2 onwards.
17 #ifdef CONFIG_METAG_META21
18 long probe_kernel_write(void *dst, const void *src, size_t size)
20 unsigned long ldst = (unsigned long)dst;
21 void __iomem *iodst = (void __iomem *)dst;
22 unsigned long lsrc = (unsigned long)src;
23 const u8 *psrc = (u8 *)src;
24 unsigned int pte, i;
25 u8 bounce[8] __aligned(8);
27 if (!size)
28 return 0;
30 /* Use the write combine bit to decide is the destination is MMIO. */
31 pte = __builtin_meta2_cacherd(dst);
33 /* Check the mapping is valid and writeable. */
34 if ((pte & (MMCU_ENTRY_WR_BIT | MMCU_ENTRY_VAL_BIT))
35 != (MMCU_ENTRY_WR_BIT | MMCU_ENTRY_VAL_BIT))
36 return -EFAULT;
38 /* Fall back to generic version for cases we're not interested in. */
39 if (pte & MMCU_ENTRY_WRC_BIT || /* write combined memory */
40 (ldst & (size - 1)) || /* destination unaligned */
41 size > 8 || /* more than max write size */
42 (size & (size - 1))) /* non power of 2 size */
43 return __probe_kernel_write(dst, src, size);
45 /* If src is unaligned, copy to the aligned bounce buffer first. */
46 if (lsrc & (size - 1)) {
47 for (i = 0; i < size; ++i)
48 bounce[i] = psrc[i];
49 psrc = bounce;
52 switch (size) {
53 case 1:
54 writeb(*psrc, iodst);
55 break;
56 case 2:
57 writew(*(const u16 *)psrc, iodst);
58 break;
59 case 4:
60 writel(*(const u32 *)psrc, iodst);
61 break;
62 case 8:
63 writeq(*(const u64 *)psrc, iodst);
64 break;
66 return 0;
68 #endif