Commit | Line | Data |
---|---|---|
373cd784 JH |
1 | /* |
2 | * safe read and write memory routines callable while atomic | |
3 | * | |
4 | * Copyright 2012 Imagination Technologies | |
5 | */ | |
6 | ||
7 | #include <linux/uaccess.h> | |
8 | #include <asm/io.h> | |
9 | ||
10 | /* | |
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. | |
16 | */ | |
17 | #ifdef CONFIG_METAG_META21 | |
18 | long probe_kernel_write(void *dst, const void *src, size_t size) | |
19 | { | |
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); | |
26 | ||
27 | if (!size) | |
28 | return 0; | |
29 | ||
30 | /* Use the write combine bit to decide is the destination is MMIO. */ | |
31 | pte = __builtin_meta2_cacherd(dst); | |
32 | ||
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; | |
37 | ||
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); | |
44 | ||
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; | |
50 | } | |
51 | ||
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; | |
65 | } | |
66 | return 0; | |
67 | } | |
68 | #endif |