mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-01 00:58:39 +02:00 
			
		
		
		
	lib: add iomem emulation (logic_iomem)
Add IO memory emulation that uses callbacks for read/write to the allocated regions. The callbacks can be registered by the users using logic_iomem_alloc(). To use, an architecture must 'select LOGIC_IOMEM' in Kconfig and then include <asm-generic/logic_io.h> into asm/io.h to get the __raw_read*/__raw_write* functions. Optionally, an architecture may 'select LOGIC_IOMEM_FALLBACK' in which case non-emulated regions will 'fall back' to the various real_* functions that must then be provided. Cc: Arnd Bergmann <arnd@kernel.org> Signed-off-by: Johannes Berg <johannes.berg@intel.com> Signed-off-by: Richard Weinberger <richard@nod.at>
This commit is contained in:
		
							parent
							
								
									0bbadafdc4
								
							
						
					
					
						commit
						ca2e334232
					
				
					 5 changed files with 474 additions and 0 deletions
				
			
		
							
								
								
									
										78
									
								
								include/asm-generic/logic_io.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								include/asm-generic/logic_io.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,78 @@ | ||||||
|  | /* SPDX-License-Identifier: GPL-2.0 */ | ||||||
|  | /*
 | ||||||
|  |  * Copyright (C) 2021 Intel Corporation | ||||||
|  |  * Author: johannes@sipsolutions.net | ||||||
|  |  */ | ||||||
|  | #ifndef _LOGIC_IO_H | ||||||
|  | #define _LOGIC_IO_H | ||||||
|  | #include <linux/types.h> | ||||||
|  | 
 | ||||||
|  | /* include this file into asm/io.h */ | ||||||
|  | 
 | ||||||
|  | #ifdef CONFIG_INDIRECT_IOMEM | ||||||
|  | 
 | ||||||
|  | #ifdef CONFIG_INDIRECT_IOMEM_FALLBACK | ||||||
|  | /*
 | ||||||
|  |  * If you want emulated IO memory to fall back to 'normal' IO memory | ||||||
|  |  * if a region wasn't registered as emulated, then you need to have | ||||||
|  |  * all of the real_* functions implemented. | ||||||
|  |  */ | ||||||
|  | #if !defined(real_ioremap) || !defined(real_iounmap) || \ | ||||||
|  |     !defined(real_raw_readb) || !defined(real_raw_writeb) || \ | ||||||
|  |     !defined(real_raw_readw) || !defined(real_raw_writew) || \ | ||||||
|  |     !defined(real_raw_readl) || !defined(real_raw_writel) || \ | ||||||
|  |     (defined(CONFIG_64BIT) && \ | ||||||
|  |      (!defined(real_raw_readq) || !defined(real_raw_writeq))) || \ | ||||||
|  |     !defined(real_memset_io) || \ | ||||||
|  |     !defined(real_memcpy_fromio) || \ | ||||||
|  |     !defined(real_memcpy_toio) | ||||||
|  | #error "Must provide fallbacks for real IO memory access" | ||||||
|  | #endif /* defined ... */ | ||||||
|  | #endif /* CONFIG_INDIRECT_IOMEM_FALLBACK */ | ||||||
|  | 
 | ||||||
|  | #define ioremap ioremap | ||||||
|  | void __iomem *ioremap(phys_addr_t offset, size_t size); | ||||||
|  | 
 | ||||||
|  | #define iounmap iounmap | ||||||
|  | void iounmap(void __iomem *addr); | ||||||
|  | 
 | ||||||
|  | #define __raw_readb __raw_readb | ||||||
|  | u8 __raw_readb(const volatile void __iomem *addr); | ||||||
|  | 
 | ||||||
|  | #define __raw_readw __raw_readw | ||||||
|  | u16 __raw_readw(const volatile void __iomem *addr); | ||||||
|  | 
 | ||||||
|  | #define __raw_readl __raw_readl | ||||||
|  | u32 __raw_readl(const volatile void __iomem *addr); | ||||||
|  | 
 | ||||||
|  | #ifdef CONFIG_64BIT | ||||||
|  | #define __raw_readq __raw_readq | ||||||
|  | u64 __raw_readq(const volatile void __iomem *addr); | ||||||
|  | #endif /* CONFIG_64BIT */ | ||||||
|  | 
 | ||||||
|  | #define __raw_writeb __raw_writeb | ||||||
|  | void __raw_writeb(u8 value, volatile void __iomem *addr); | ||||||
|  | 
 | ||||||
|  | #define __raw_writew __raw_writew | ||||||
|  | void __raw_writew(u16 value, volatile void __iomem *addr); | ||||||
|  | 
 | ||||||
|  | #define __raw_writel __raw_writel | ||||||
|  | void __raw_writel(u32 value, volatile void __iomem *addr); | ||||||
|  | 
 | ||||||
|  | #ifdef CONFIG_64BIT | ||||||
|  | #define __raw_writeq __raw_writeq | ||||||
|  | void __raw_writeq(u64 value, volatile void __iomem *addr); | ||||||
|  | #endif /* CONFIG_64BIT */ | ||||||
|  | 
 | ||||||
|  | #define memset_io memset_io | ||||||
|  | void memset_io(volatile void __iomem *addr, int value, size_t size); | ||||||
|  | 
 | ||||||
|  | #define memcpy_fromio memcpy_fromio | ||||||
|  | void memcpy_fromio(void *buffer, const volatile void __iomem *addr, | ||||||
|  | 		   size_t size); | ||||||
|  | 
 | ||||||
|  | #define memcpy_toio memcpy_toio | ||||||
|  | void memcpy_toio(volatile void __iomem *addr, const void *buffer, size_t size); | ||||||
|  | 
 | ||||||
|  | #endif /* CONFIG_INDIRECT_IOMEM */ | ||||||
|  | #endif /* _LOGIC_IO_H */ | ||||||
							
								
								
									
										62
									
								
								include/linux/logic_iomem.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								include/linux/logic_iomem.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,62 @@ | ||||||
|  | /* SPDX-License-Identifier: GPL-2.0 */ | ||||||
|  | /*
 | ||||||
|  |  * Copyright (C) 2021 Intel Corporation | ||||||
|  |  * Author: johannes@sipsolutions.net | ||||||
|  |  */ | ||||||
|  | #ifndef __LOGIC_IOMEM_H | ||||||
|  | #define __LOGIC_IOMEM_H | ||||||
|  | #include <linux/types.h> | ||||||
|  | #include <linux/ioport.h> | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * struct logic_iomem_ops - emulated IO memory ops | ||||||
|  |  * @read: read an 8, 16, 32 or 64 bit quantity from the given offset, | ||||||
|  |  *	size is given in bytes (1, 2, 4 or 8) | ||||||
|  |  *	(64-bit only necessary if CONFIG_64BIT is set) | ||||||
|  |  * @write: write an 8, 16 32 or 64 bit quantity to the given offset, | ||||||
|  |  *	size is given in bytes (1, 2, 4 or 8) | ||||||
|  |  *	(64-bit only necessary if CONFIG_64BIT is set) | ||||||
|  |  * @set: optional, for memset_io() | ||||||
|  |  * @copy_from: optional, for memcpy_fromio() | ||||||
|  |  * @copy_to: optional, for memcpy_toio() | ||||||
|  |  * @unmap: optional, this region is getting unmapped | ||||||
|  |  */ | ||||||
|  | struct logic_iomem_ops { | ||||||
|  | 	unsigned long (*read)(void *priv, unsigned int offset, int size); | ||||||
|  | 	void (*write)(void *priv, unsigned int offset, int size, | ||||||
|  | 		      unsigned long val); | ||||||
|  | 
 | ||||||
|  | 	void (*set)(void *priv, unsigned int offset, u8 value, int size); | ||||||
|  | 	void (*copy_from)(void *priv, void *buffer, unsigned int offset, | ||||||
|  | 			  int size); | ||||||
|  | 	void (*copy_to)(void *priv, unsigned int offset, const void *buffer, | ||||||
|  | 			int size); | ||||||
|  | 
 | ||||||
|  | 	void (*unmap)(void *priv); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * struct logic_iomem_region_ops - ops for an IO memory handler | ||||||
|  |  * @map: map a range in the registered IO memory region, must | ||||||
|  |  *	fill *ops with the ops and may fill *priv to be passed | ||||||
|  |  *	to the ops. The offset is given as the offset into the | ||||||
|  |  *	registered resource region. | ||||||
|  |  *	The return value is negative for errors, or >= 0 for | ||||||
|  |  *	success. On success, the return value is added to the | ||||||
|  |  *	offset for later ops, to allow for partial mappings. | ||||||
|  |  */ | ||||||
|  | struct logic_iomem_region_ops { | ||||||
|  | 	long (*map)(unsigned long offset, size_t size, | ||||||
|  | 		    const struct logic_iomem_ops **ops, | ||||||
|  | 		    void **priv); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * logic_iomem_add_region - register an IO memory region | ||||||
|  |  * @resource: the resource description for this region | ||||||
|  |  * @ops: the IO memory mapping ops for this resource | ||||||
|  |  */ | ||||||
|  | int logic_iomem_add_region(struct resource *resource, | ||||||
|  | 			   const struct logic_iomem_region_ops *ops); | ||||||
|  | 
 | ||||||
|  | #endif /* __LOGIC_IOMEM_H */ | ||||||
							
								
								
									
										14
									
								
								lib/Kconfig
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								lib/Kconfig
									
									
									
									
									
								
							|  | @ -102,6 +102,20 @@ config INDIRECT_PIO | ||||||
| 
 | 
 | ||||||
| 	  When in doubt, say N. | 	  When in doubt, say N. | ||||||
| 
 | 
 | ||||||
|  | config INDIRECT_IOMEM | ||||||
|  | 	bool | ||||||
|  | 	help | ||||||
|  | 	  This is selected by other options/architectures to provide the | ||||||
|  | 	  emulated iomem accessors. | ||||||
|  | 
 | ||||||
|  | config INDIRECT_IOMEM_FALLBACK | ||||||
|  | 	bool | ||||||
|  | 	depends on INDIRECT_IOMEM | ||||||
|  | 	help | ||||||
|  | 	  If INDIRECT_IOMEM is selected, this enables falling back to plain | ||||||
|  | 	  mmio accesses when the IO memory address is not a registered | ||||||
|  | 	  emulated region. | ||||||
|  | 
 | ||||||
| config CRC_CCITT | config CRC_CCITT | ||||||
| 	tristate "CRC-CCITT functions" | 	tristate "CRC-CCITT functions" | ||||||
| 	help | 	help | ||||||
|  |  | ||||||
|  | @ -147,6 +147,8 @@ obj-$(CONFIG_DEBUG_LOCKING_API_SELFTESTS) += locking-selftest.o | ||||||
| 
 | 
 | ||||||
| lib-y += logic_pio.o | lib-y += logic_pio.o | ||||||
| 
 | 
 | ||||||
|  | lib-$(CONFIG_INDIRECT_IOMEM) += logic_iomem.o | ||||||
|  | 
 | ||||||
| obj-$(CONFIG_GENERIC_HWEIGHT) += hweight.o | obj-$(CONFIG_GENERIC_HWEIGHT) += hweight.o | ||||||
| 
 | 
 | ||||||
| obj-$(CONFIG_BTREE) += btree.o | obj-$(CONFIG_BTREE) += btree.o | ||||||
|  |  | ||||||
							
								
								
									
										318
									
								
								lib/logic_iomem.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										318
									
								
								lib/logic_iomem.c
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,318 @@ | ||||||
|  | // SPDX-License-Identifier: GPL-2.0
 | ||||||
|  | /*
 | ||||||
|  |  * Copyright (C) 2021 Intel Corporation | ||||||
|  |  * Author: Johannes Berg <johannes@sipsolutions.net> | ||||||
|  |  */ | ||||||
|  | #include <linux/types.h> | ||||||
|  | #include <linux/slab.h> | ||||||
|  | #include <linux/logic_iomem.h> | ||||||
|  | 
 | ||||||
|  | struct logic_iomem_region { | ||||||
|  | 	const struct resource *res; | ||||||
|  | 	const struct logic_iomem_region_ops *ops; | ||||||
|  | 	struct list_head list; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct logic_iomem_area { | ||||||
|  | 	const struct logic_iomem_ops *ops; | ||||||
|  | 	void *priv; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | #define AREA_SHIFT	24 | ||||||
|  | #define MAX_AREA_SIZE	(1 << AREA_SHIFT) | ||||||
|  | #define MAX_AREAS	((1ULL<<32) / MAX_AREA_SIZE) | ||||||
|  | #define AREA_BITS	((MAX_AREAS - 1) << AREA_SHIFT) | ||||||
|  | #define AREA_MASK	(MAX_AREA_SIZE - 1) | ||||||
|  | #ifdef CONFIG_64BIT | ||||||
|  | #define IOREMAP_BIAS	0xDEAD000000000000UL | ||||||
|  | #define IOREMAP_MASK	0xFFFFFFFF00000000UL | ||||||
|  | #else | ||||||
|  | #define IOREMAP_BIAS	0 | ||||||
|  | #define IOREMAP_MASK	0 | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | static DEFINE_MUTEX(regions_mtx); | ||||||
|  | static LIST_HEAD(regions_list); | ||||||
|  | static struct logic_iomem_area mapped_areas[MAX_AREAS]; | ||||||
|  | 
 | ||||||
|  | int logic_iomem_add_region(struct resource *resource, | ||||||
|  | 			   const struct logic_iomem_region_ops *ops) | ||||||
|  | { | ||||||
|  | 	struct logic_iomem_region *rreg; | ||||||
|  | 	int err; | ||||||
|  | 
 | ||||||
|  | 	if (WARN_ON(!resource || !ops)) | ||||||
|  | 		return -EINVAL; | ||||||
|  | 
 | ||||||
|  | 	if (WARN_ON((resource->flags & IORESOURCE_TYPE_BITS) != IORESOURCE_MEM)) | ||||||
|  | 		return -EINVAL; | ||||||
|  | 
 | ||||||
|  | 	rreg = kzalloc(sizeof(*rreg), GFP_KERNEL); | ||||||
|  | 	if (!rreg) | ||||||
|  | 		return -ENOMEM; | ||||||
|  | 
 | ||||||
|  | 	err = request_resource(&iomem_resource, resource); | ||||||
|  | 	if (err) { | ||||||
|  | 		kfree(rreg); | ||||||
|  | 		return -ENOMEM; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	mutex_lock(®ions_mtx); | ||||||
|  | 	rreg->res = resource; | ||||||
|  | 	rreg->ops = ops; | ||||||
|  | 	list_add_tail(&rreg->list, ®ions_list); | ||||||
|  | 	mutex_unlock(®ions_mtx); | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | EXPORT_SYMBOL(logic_iomem_add_region); | ||||||
|  | 
 | ||||||
|  | #ifndef CONFIG_LOGIC_IOMEM_FALLBACK | ||||||
|  | static void __iomem *real_ioremap(phys_addr_t offset, size_t size) | ||||||
|  | { | ||||||
|  | 	WARN(1, "invalid ioremap(0x%llx, 0x%zx)\n", | ||||||
|  | 	     (unsigned long long)offset, size); | ||||||
|  | 	return NULL; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void real_iounmap(void __iomem *addr) | ||||||
|  | { | ||||||
|  | 	WARN(1, "invalid iounmap for addr 0x%llx\n", | ||||||
|  | 	     (unsigned long long)addr); | ||||||
|  | } | ||||||
|  | #endif /* CONFIG_LOGIC_IOMEM_FALLBACK */ | ||||||
|  | 
 | ||||||
|  | void __iomem *ioremap(phys_addr_t offset, size_t size) | ||||||
|  | { | ||||||
|  | 	void __iomem *ret = NULL; | ||||||
|  | 	struct logic_iomem_region *rreg, *found = NULL; | ||||||
|  | 	int i; | ||||||
|  | 
 | ||||||
|  | 	mutex_lock(®ions_mtx); | ||||||
|  | 	list_for_each_entry(rreg, ®ions_list, list) { | ||||||
|  | 		if (rreg->res->start > offset) | ||||||
|  | 			continue; | ||||||
|  | 		if (rreg->res->end < offset + size - 1) | ||||||
|  | 			continue; | ||||||
|  | 		found = rreg; | ||||||
|  | 		break; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (!found) | ||||||
|  | 		goto out; | ||||||
|  | 
 | ||||||
|  | 	for (i = 0; i < MAX_AREAS; i++) { | ||||||
|  | 		long offs; | ||||||
|  | 
 | ||||||
|  | 		if (mapped_areas[i].ops) | ||||||
|  | 			continue; | ||||||
|  | 
 | ||||||
|  | 		offs = rreg->ops->map(offset - found->res->start, | ||||||
|  | 				      size, &mapped_areas[i].ops, | ||||||
|  | 				      &mapped_areas[i].priv); | ||||||
|  | 		if (offs < 0) { | ||||||
|  | 			mapped_areas[i].ops = NULL; | ||||||
|  | 			break; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if (WARN_ON(!mapped_areas[i].ops)) { | ||||||
|  | 			mapped_areas[i].ops = NULL; | ||||||
|  | 			break; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		ret = (void __iomem *)(IOREMAP_BIAS + (i << AREA_SHIFT) + offs); | ||||||
|  | 		break; | ||||||
|  | 	} | ||||||
|  | out: | ||||||
|  | 	mutex_unlock(®ions_mtx); | ||||||
|  | 	if (ret) | ||||||
|  | 		return ret; | ||||||
|  | 	return real_ioremap(offset, size); | ||||||
|  | } | ||||||
|  | EXPORT_SYMBOL(ioremap); | ||||||
|  | 
 | ||||||
|  | static inline struct logic_iomem_area * | ||||||
|  | get_area(const volatile void __iomem *addr) | ||||||
|  | { | ||||||
|  | 	unsigned long a = (unsigned long)addr; | ||||||
|  | 	unsigned int idx; | ||||||
|  | 
 | ||||||
|  | 	if (WARN_ON((a & IOREMAP_MASK) != IOREMAP_BIAS)) | ||||||
|  | 		return NULL; | ||||||
|  | 
 | ||||||
|  | 	idx = (a & AREA_BITS) >> AREA_SHIFT; | ||||||
|  | 
 | ||||||
|  | 	if (mapped_areas[idx].ops) | ||||||
|  | 		return &mapped_areas[idx]; | ||||||
|  | 
 | ||||||
|  | 	return NULL; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void iounmap(void __iomem *addr) | ||||||
|  | { | ||||||
|  | 	struct logic_iomem_area *area = get_area(addr); | ||||||
|  | 
 | ||||||
|  | 	if (!area) { | ||||||
|  | 		real_iounmap(addr); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (area->ops->unmap) | ||||||
|  | 		area->ops->unmap(area->priv); | ||||||
|  | 
 | ||||||
|  | 	mutex_lock(®ions_mtx); | ||||||
|  | 	area->ops = NULL; | ||||||
|  | 	area->priv = NULL; | ||||||
|  | 	mutex_unlock(®ions_mtx); | ||||||
|  | } | ||||||
|  | EXPORT_SYMBOL(iounmap); | ||||||
|  | 
 | ||||||
|  | #ifndef CONFIG_LOGIC_IOMEM_FALLBACK | ||||||
|  | #define MAKE_FALLBACK(op, sz) 						\ | ||||||
|  | static u##sz real_raw_read ## op(const volatile void __iomem *addr)	\ | ||||||
|  | {									\ | ||||||
|  | 	WARN(1, "Invalid read" #op " at address %llx\n",		\ | ||||||
|  | 	     (unsigned long long)addr);					\ | ||||||
|  | 	return (u ## sz)~0ULL;						\ | ||||||
|  | }									\ | ||||||
|  | 									\ | ||||||
|  | void real_raw_write ## op(u ## sz val, volatile void __iomem *addr)	\ | ||||||
|  | {									\ | ||||||
|  | 	WARN(1, "Invalid writeq" #op " of 0x%llx at address %llx\n",	\ | ||||||
|  | 	     (unsigned long long)val, (unsigned long long)addr);	\ | ||||||
|  | }									\ | ||||||
|  | 
 | ||||||
|  | MAKE_FALLBACK(b, 8); | ||||||
|  | MAKE_FALLBACK(w, 16); | ||||||
|  | MAKE_FALLBACK(l, 32); | ||||||
|  | #ifdef CONFIG_64BIT | ||||||
|  | MAKE_FALLBACK(q, 64); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | static void real_memset_io(volatile void __iomem *addr, int value, size_t size) | ||||||
|  | { | ||||||
|  | 	WARN(1, "Invalid memset_io at address 0x%llx\n", | ||||||
|  | 	     (unsigned long long)addr); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void real_memcpy_fromio(void *buffer, const volatile void __iomem *addr, | ||||||
|  | 			       size_t size) | ||||||
|  | { | ||||||
|  | 	WARN(1, "Invalid memcpy_fromio at address 0x%llx\n", | ||||||
|  | 	     (unsigned long long)addr); | ||||||
|  | 
 | ||||||
|  | 	memset(buffer, 0xff, size); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void real_memcpy_toio(volatile void __iomem *addr, const void *buffer, | ||||||
|  | 			     size_t size) | ||||||
|  | { | ||||||
|  | 	WARN(1, "Invalid memcpy_toio at address 0x%llx\n", | ||||||
|  | 	     (unsigned long long)addr); | ||||||
|  | } | ||||||
|  | #endif /* CONFIG_LOGIC_IOMEM_FALLBACK */ | ||||||
|  | 
 | ||||||
|  | #define MAKE_OP(op, sz) 						\ | ||||||
|  | u##sz __raw_read ## op(const volatile void __iomem *addr)		\ | ||||||
|  | {									\ | ||||||
|  | 	struct logic_iomem_area *area = get_area(addr);			\ | ||||||
|  | 									\ | ||||||
|  | 	if (!area)							\ | ||||||
|  | 		return real_raw_read ## op(addr);			\ | ||||||
|  | 									\ | ||||||
|  | 	return (u ## sz) area->ops->read(area->priv,			\ | ||||||
|  | 					 (unsigned long)addr & AREA_MASK,\ | ||||||
|  | 					 sz / 8);			\ | ||||||
|  | }									\ | ||||||
|  | EXPORT_SYMBOL(__raw_read ## op);					\ | ||||||
|  | 									\ | ||||||
|  | void __raw_write ## op(u ## sz val, volatile void __iomem *addr)	\ | ||||||
|  | {									\ | ||||||
|  | 	struct logic_iomem_area *area = get_area(addr);			\ | ||||||
|  | 									\ | ||||||
|  | 	if (!area) {							\ | ||||||
|  | 		real_raw_write ## op(val, addr);			\ | ||||||
|  | 		return;							\ | ||||||
|  | 	}								\ | ||||||
|  | 									\ | ||||||
|  | 	area->ops->write(area->priv,					\ | ||||||
|  | 			 (unsigned long)addr & AREA_MASK,		\ | ||||||
|  | 			 sz / 8, val);					\ | ||||||
|  | }									\ | ||||||
|  | EXPORT_SYMBOL(__raw_write ## op) | ||||||
|  | 
 | ||||||
|  | MAKE_OP(b, 8); | ||||||
|  | MAKE_OP(w, 16); | ||||||
|  | MAKE_OP(l, 32); | ||||||
|  | #ifdef CONFIG_64BIT | ||||||
|  | MAKE_OP(q, 64); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | void memset_io(volatile void __iomem *addr, int value, size_t size) | ||||||
|  | { | ||||||
|  | 	struct logic_iomem_area *area = get_area(addr); | ||||||
|  | 	unsigned long offs, start; | ||||||
|  | 
 | ||||||
|  | 	if (!area) { | ||||||
|  | 		real_memset_io(addr, value, size); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	start = (unsigned long)addr & AREA_MASK; | ||||||
|  | 
 | ||||||
|  | 	if (area->ops->set) { | ||||||
|  | 		area->ops->set(area->priv, start, value, size); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for (offs = 0; offs < size; offs++) | ||||||
|  | 		area->ops->write(area->priv, start + offs, 1, value); | ||||||
|  | } | ||||||
|  | EXPORT_SYMBOL(memset_io); | ||||||
|  | 
 | ||||||
|  | void memcpy_fromio(void *buffer, const volatile void __iomem *addr, | ||||||
|  |                    size_t size) | ||||||
|  | { | ||||||
|  | 	struct logic_iomem_area *area = get_area(addr); | ||||||
|  | 	u8 *buf = buffer; | ||||||
|  | 	unsigned long offs, start; | ||||||
|  | 
 | ||||||
|  | 	if (!area) { | ||||||
|  | 		real_memcpy_fromio(buffer, addr, size); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	start = (unsigned long)addr & AREA_MASK; | ||||||
|  | 
 | ||||||
|  | 	if (area->ops->copy_from) { | ||||||
|  | 		area->ops->copy_from(area->priv, buffer, start, size); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for (offs = 0; offs < size; offs++) | ||||||
|  | 		buf[offs] = area->ops->read(area->priv, start + offs, 1); | ||||||
|  | } | ||||||
|  | EXPORT_SYMBOL(memcpy_fromio); | ||||||
|  | 
 | ||||||
|  | void memcpy_toio(volatile void __iomem *addr, const void *buffer, size_t size) | ||||||
|  | { | ||||||
|  | 	struct logic_iomem_area *area = get_area(addr); | ||||||
|  | 	const u8 *buf = buffer; | ||||||
|  | 	unsigned long offs, start; | ||||||
|  | 
 | ||||||
|  | 	if (!area) { | ||||||
|  | 		real_memcpy_toio(addr, buffer, size); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	start = (unsigned long)addr & AREA_MASK; | ||||||
|  | 
 | ||||||
|  | 	if (area->ops->copy_to) { | ||||||
|  | 		area->ops->copy_to(area->priv, start, buffer, size); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for (offs = 0; offs < size; offs++) | ||||||
|  | 		area->ops->write(area->priv, start + offs, 1, buf[offs]); | ||||||
|  | } | ||||||
|  | EXPORT_SYMBOL(memcpy_toio); | ||||||
		Loading…
	
		Reference in a new issue
	
	 Johannes Berg
						Johannes Berg