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