blob: c22755165df999aa0d4d660bb9b52e6ed0b3d0af [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
/*
* safe read and write memory routines callable while atomic
*
* Copyright 2012 Imagination Technologies
*/
#include <linux/uaccess.h>
#include <asm/io.h>
/*
* The generic probe_kernel_write() uses the user copy code which can split the
* writes if the source is unaligned, and repeats writes to make exceptions
* precise. We override it here to avoid these things happening to memory mapped
* IO memory where they could have undesired effects.
* Due to the use of CACHERD instruction this only works on Meta2 onwards.
*/
#ifdef CONFIG_METAG_META21
long probe_kernel_write(void *dst, const void *src, size_t size)
{
unsigned long ldst = (unsigned long)dst;
void __iomem *iodst = (void __iomem *)dst;
unsigned long lsrc = (unsigned long)src;
const u8 *psrc = (u8 *)src;
unsigned int pte, i;
u8 bounce[8] __aligned(8);
if (!size)
return 0;
/* Use the write combine bit to decide is the destination is MMIO. */
pte = __builtin_meta2_cacherd(dst);
/* Check the mapping is valid and writeable. */
if ((pte & (MMCU_ENTRY_WR_BIT | MMCU_ENTRY_VAL_BIT))
!= (MMCU_ENTRY_WR_BIT | MMCU_ENTRY_VAL_BIT))
return -EFAULT;
/* Fall back to generic version for cases we're not interested in. */
if (pte & MMCU_ENTRY_WRC_BIT || /* write combined memory */
(ldst & (size - 1)) || /* destination unaligned */
size > 8 || /* more than max write size */
(size & (size - 1))) /* non power of 2 size */
return __probe_kernel_write(dst, src, size);
/* If src is unaligned, copy to the aligned bounce buffer first. */
if (lsrc & (size - 1)) {
for (i = 0; i < size; ++i)
bounce[i] = psrc[i];
psrc = bounce;
}
switch (size) {
case 1:
writeb(*psrc, iodst);
break;
case 2:
writew(*(const u16 *)psrc, iodst);
break;
case 4:
writel(*(const u32 *)psrc, iodst);
break;
case 8:
writeq(*(const u64 *)psrc, iodst);
break;
}
return 0;
}
#endif