GNU Linux-libre 4.14.266-gnu1
[releases.git] / arch / metag / mm / maccess.c
1 // SPDX-License-Identifier: GPL-2.0
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