xref: /linux/lib/logic_iomem.c (revision 2a6852cb)
1ca2e3342SJohannes Berg // SPDX-License-Identifier: GPL-2.0
2ca2e3342SJohannes Berg /*
3ca2e3342SJohannes Berg  * Copyright (C) 2021 Intel Corporation
4ca2e3342SJohannes Berg  * Author: Johannes Berg <johannes@sipsolutions.net>
5ca2e3342SJohannes Berg  */
6ca2e3342SJohannes Berg #include <linux/types.h>
7ca2e3342SJohannes Berg #include <linux/slab.h>
8ca2e3342SJohannes Berg #include <linux/logic_iomem.h>
968fdb644SJohannes Berg #include <asm/io.h>
10ca2e3342SJohannes Berg 
11ca2e3342SJohannes Berg struct logic_iomem_region {
12ca2e3342SJohannes Berg 	const struct resource *res;
13ca2e3342SJohannes Berg 	const struct logic_iomem_region_ops *ops;
14ca2e3342SJohannes Berg 	struct list_head list;
15ca2e3342SJohannes Berg };
16ca2e3342SJohannes Berg 
17ca2e3342SJohannes Berg struct logic_iomem_area {
18ca2e3342SJohannes Berg 	const struct logic_iomem_ops *ops;
19ca2e3342SJohannes Berg 	void *priv;
20ca2e3342SJohannes Berg };
21ca2e3342SJohannes Berg 
22ca2e3342SJohannes Berg #define AREA_SHIFT	24
23ca2e3342SJohannes Berg #define MAX_AREA_SIZE	(1 << AREA_SHIFT)
244e8a5edaSJohannes Berg #define MAX_AREAS	((1U << 31) / MAX_AREA_SIZE)
25ca2e3342SJohannes Berg #define AREA_BITS	((MAX_AREAS - 1) << AREA_SHIFT)
26ca2e3342SJohannes Berg #define AREA_MASK	(MAX_AREA_SIZE - 1)
27ca2e3342SJohannes Berg #ifdef CONFIG_64BIT
28ca2e3342SJohannes Berg #define IOREMAP_BIAS	0xDEAD000000000000UL
29ca2e3342SJohannes Berg #define IOREMAP_MASK	0xFFFFFFFF00000000UL
30ca2e3342SJohannes Berg #else
314e8a5edaSJohannes Berg #define IOREMAP_BIAS	0x80000000UL
324e8a5edaSJohannes Berg #define IOREMAP_MASK	0x80000000UL
33ca2e3342SJohannes Berg #endif
34ca2e3342SJohannes Berg 
35ca2e3342SJohannes Berg static DEFINE_MUTEX(regions_mtx);
36ca2e3342SJohannes Berg static LIST_HEAD(regions_list);
37ca2e3342SJohannes Berg static struct logic_iomem_area mapped_areas[MAX_AREAS];
38ca2e3342SJohannes Berg 
logic_iomem_add_region(struct resource * resource,const struct logic_iomem_region_ops * ops)39ca2e3342SJohannes Berg int logic_iomem_add_region(struct resource *resource,
40ca2e3342SJohannes Berg 			   const struct logic_iomem_region_ops *ops)
41ca2e3342SJohannes Berg {
42ca2e3342SJohannes Berg 	struct logic_iomem_region *rreg;
43ca2e3342SJohannes Berg 	int err;
44ca2e3342SJohannes Berg 
45ca2e3342SJohannes Berg 	if (WARN_ON(!resource || !ops))
46ca2e3342SJohannes Berg 		return -EINVAL;
47ca2e3342SJohannes Berg 
48ca2e3342SJohannes Berg 	if (WARN_ON((resource->flags & IORESOURCE_TYPE_BITS) != IORESOURCE_MEM))
49ca2e3342SJohannes Berg 		return -EINVAL;
50ca2e3342SJohannes Berg 
51ca2e3342SJohannes Berg 	rreg = kzalloc(sizeof(*rreg), GFP_KERNEL);
52ca2e3342SJohannes Berg 	if (!rreg)
53ca2e3342SJohannes Berg 		return -ENOMEM;
54ca2e3342SJohannes Berg 
55ca2e3342SJohannes Berg 	err = request_resource(&iomem_resource, resource);
56ca2e3342SJohannes Berg 	if (err) {
57ca2e3342SJohannes Berg 		kfree(rreg);
58ca2e3342SJohannes Berg 		return -ENOMEM;
59ca2e3342SJohannes Berg 	}
60ca2e3342SJohannes Berg 
61ca2e3342SJohannes Berg 	mutex_lock(&regions_mtx);
62ca2e3342SJohannes Berg 	rreg->res = resource;
63ca2e3342SJohannes Berg 	rreg->ops = ops;
64ca2e3342SJohannes Berg 	list_add_tail(&rreg->list, &regions_list);
65ca2e3342SJohannes Berg 	mutex_unlock(&regions_mtx);
66ca2e3342SJohannes Berg 
67ca2e3342SJohannes Berg 	return 0;
68ca2e3342SJohannes Berg }
69ca2e3342SJohannes Berg EXPORT_SYMBOL(logic_iomem_add_region);
70ca2e3342SJohannes Berg 
712a6852cbSJohannes Berg #ifndef CONFIG_INDIRECT_IOMEM_FALLBACK
real_ioremap(phys_addr_t offset,size_t size)72ca2e3342SJohannes Berg static void __iomem *real_ioremap(phys_addr_t offset, size_t size)
73ca2e3342SJohannes Berg {
74ca2e3342SJohannes Berg 	WARN(1, "invalid ioremap(0x%llx, 0x%zx)\n",
75ca2e3342SJohannes Berg 	     (unsigned long long)offset, size);
76ca2e3342SJohannes Berg 	return NULL;
77ca2e3342SJohannes Berg }
78ca2e3342SJohannes Berg 
real_iounmap(volatile void __iomem * addr)795f174ec3SAl Viro static void real_iounmap(volatile void __iomem *addr)
80ca2e3342SJohannes Berg {
81ca2e3342SJohannes Berg 	WARN(1, "invalid iounmap for addr 0x%llx\n",
824e84139eSJohannes Berg 	     (unsigned long long)(uintptr_t __force)addr);
83ca2e3342SJohannes Berg }
842a6852cbSJohannes Berg #endif /* CONFIG_INDIRECT_IOMEM_FALLBACK */
85ca2e3342SJohannes Berg 
ioremap(phys_addr_t offset,size_t size)86ca2e3342SJohannes Berg void __iomem *ioremap(phys_addr_t offset, size_t size)
87ca2e3342SJohannes Berg {
88ca2e3342SJohannes Berg 	void __iomem *ret = NULL;
89ca2e3342SJohannes Berg 	struct logic_iomem_region *rreg, *found = NULL;
90ca2e3342SJohannes Berg 	int i;
91ca2e3342SJohannes Berg 
92ca2e3342SJohannes Berg 	mutex_lock(&regions_mtx);
93ca2e3342SJohannes Berg 	list_for_each_entry(rreg, &regions_list, list) {
94ca2e3342SJohannes Berg 		if (rreg->res->start > offset)
95ca2e3342SJohannes Berg 			continue;
96ca2e3342SJohannes Berg 		if (rreg->res->end < offset + size - 1)
97ca2e3342SJohannes Berg 			continue;
98ca2e3342SJohannes Berg 		found = rreg;
99ca2e3342SJohannes Berg 		break;
100ca2e3342SJohannes Berg 	}
101ca2e3342SJohannes Berg 
102ca2e3342SJohannes Berg 	if (!found)
103ca2e3342SJohannes Berg 		goto out;
104ca2e3342SJohannes Berg 
105ca2e3342SJohannes Berg 	for (i = 0; i < MAX_AREAS; i++) {
106ca2e3342SJohannes Berg 		long offs;
107ca2e3342SJohannes Berg 
108ca2e3342SJohannes Berg 		if (mapped_areas[i].ops)
109ca2e3342SJohannes Berg 			continue;
110ca2e3342SJohannes Berg 
111ca2e3342SJohannes Berg 		offs = rreg->ops->map(offset - found->res->start,
112ca2e3342SJohannes Berg 				      size, &mapped_areas[i].ops,
113ca2e3342SJohannes Berg 				      &mapped_areas[i].priv);
114ca2e3342SJohannes Berg 		if (offs < 0) {
115ca2e3342SJohannes Berg 			mapped_areas[i].ops = NULL;
116ca2e3342SJohannes Berg 			break;
117ca2e3342SJohannes Berg 		}
118ca2e3342SJohannes Berg 
119ca2e3342SJohannes Berg 		if (WARN_ON(!mapped_areas[i].ops)) {
120ca2e3342SJohannes Berg 			mapped_areas[i].ops = NULL;
121ca2e3342SJohannes Berg 			break;
122ca2e3342SJohannes Berg 		}
123ca2e3342SJohannes Berg 
124ca2e3342SJohannes Berg 		ret = (void __iomem *)(IOREMAP_BIAS + (i << AREA_SHIFT) + offs);
125ca2e3342SJohannes Berg 		break;
126ca2e3342SJohannes Berg 	}
127ca2e3342SJohannes Berg out:
128ca2e3342SJohannes Berg 	mutex_unlock(&regions_mtx);
129ca2e3342SJohannes Berg 	if (ret)
130ca2e3342SJohannes Berg 		return ret;
131ca2e3342SJohannes Berg 	return real_ioremap(offset, size);
132ca2e3342SJohannes Berg }
133ca2e3342SJohannes Berg EXPORT_SYMBOL(ioremap);
134ca2e3342SJohannes Berg 
135ca2e3342SJohannes Berg static inline struct logic_iomem_area *
get_area(const volatile void __iomem * addr)136ca2e3342SJohannes Berg get_area(const volatile void __iomem *addr)
137ca2e3342SJohannes Berg {
138ca2e3342SJohannes Berg 	unsigned long a = (unsigned long)addr;
139ca2e3342SJohannes Berg 	unsigned int idx;
140ca2e3342SJohannes Berg 
141ca2e3342SJohannes Berg 	if (WARN_ON((a & IOREMAP_MASK) != IOREMAP_BIAS))
142ca2e3342SJohannes Berg 		return NULL;
143ca2e3342SJohannes Berg 
144ca2e3342SJohannes Berg 	idx = (a & AREA_BITS) >> AREA_SHIFT;
145ca2e3342SJohannes Berg 
146ca2e3342SJohannes Berg 	if (mapped_areas[idx].ops)
147ca2e3342SJohannes Berg 		return &mapped_areas[idx];
148ca2e3342SJohannes Berg 
149ca2e3342SJohannes Berg 	return NULL;
150ca2e3342SJohannes Berg }
151ca2e3342SJohannes Berg 
iounmap(volatile void __iomem * addr)1525f174ec3SAl Viro void iounmap(volatile void __iomem *addr)
153ca2e3342SJohannes Berg {
154ca2e3342SJohannes Berg 	struct logic_iomem_area *area = get_area(addr);
155ca2e3342SJohannes Berg 
156ca2e3342SJohannes Berg 	if (!area) {
157ca2e3342SJohannes Berg 		real_iounmap(addr);
158ca2e3342SJohannes Berg 		return;
159ca2e3342SJohannes Berg 	}
160ca2e3342SJohannes Berg 
161ca2e3342SJohannes Berg 	if (area->ops->unmap)
162ca2e3342SJohannes Berg 		area->ops->unmap(area->priv);
163ca2e3342SJohannes Berg 
164ca2e3342SJohannes Berg 	mutex_lock(&regions_mtx);
165ca2e3342SJohannes Berg 	area->ops = NULL;
166ca2e3342SJohannes Berg 	area->priv = NULL;
167ca2e3342SJohannes Berg 	mutex_unlock(&regions_mtx);
168ca2e3342SJohannes Berg }
169ca2e3342SJohannes Berg EXPORT_SYMBOL(iounmap);
170ca2e3342SJohannes Berg 
1712a6852cbSJohannes Berg #ifndef CONFIG_INDIRECT_IOMEM_FALLBACK
172ca2e3342SJohannes Berg #define MAKE_FALLBACK(op, sz) 						\
173ca2e3342SJohannes Berg static u##sz real_raw_read ## op(const volatile void __iomem *addr)	\
174ca2e3342SJohannes Berg {									\
175ca2e3342SJohannes Berg 	WARN(1, "Invalid read" #op " at address %llx\n",		\
1764e84139eSJohannes Berg 	     (unsigned long long)(uintptr_t __force)addr);		\
177ca2e3342SJohannes Berg 	return (u ## sz)~0ULL;						\
178ca2e3342SJohannes Berg }									\
179ca2e3342SJohannes Berg 									\
18068fdb644SJohannes Berg static void real_raw_write ## op(u ## sz val,				\
18168fdb644SJohannes Berg 				 volatile void __iomem *addr)		\
182ca2e3342SJohannes Berg {									\
183ca2e3342SJohannes Berg 	WARN(1, "Invalid writeq" #op " of 0x%llx at address %llx\n",	\
1844e84139eSJohannes Berg 	     (unsigned long long)val,					\
1854e84139eSJohannes Berg 	     (unsigned long long)(uintptr_t __force)addr);\
186ca2e3342SJohannes Berg }									\
187ca2e3342SJohannes Berg 
188ca2e3342SJohannes Berg MAKE_FALLBACK(b, 8);
189ca2e3342SJohannes Berg MAKE_FALLBACK(w, 16);
190ca2e3342SJohannes Berg MAKE_FALLBACK(l, 32);
191ca2e3342SJohannes Berg #ifdef CONFIG_64BIT
192ca2e3342SJohannes Berg MAKE_FALLBACK(q, 64);
193ca2e3342SJohannes Berg #endif
194ca2e3342SJohannes Berg 
real_memset_io(volatile void __iomem * addr,int value,size_t size)195ca2e3342SJohannes Berg static void real_memset_io(volatile void __iomem *addr, int value, size_t size)
196ca2e3342SJohannes Berg {
197ca2e3342SJohannes Berg 	WARN(1, "Invalid memset_io at address 0x%llx\n",
1984e84139eSJohannes Berg 	     (unsigned long long)(uintptr_t __force)addr);
199ca2e3342SJohannes Berg }
200ca2e3342SJohannes Berg 
real_memcpy_fromio(void * buffer,const volatile void __iomem * addr,size_t size)201ca2e3342SJohannes Berg static void real_memcpy_fromio(void *buffer, const volatile void __iomem *addr,
202ca2e3342SJohannes Berg 			       size_t size)
203ca2e3342SJohannes Berg {
204ca2e3342SJohannes Berg 	WARN(1, "Invalid memcpy_fromio at address 0x%llx\n",
2054e84139eSJohannes Berg 	     (unsigned long long)(uintptr_t __force)addr);
206ca2e3342SJohannes Berg 
207ca2e3342SJohannes Berg 	memset(buffer, 0xff, size);
208ca2e3342SJohannes Berg }
209ca2e3342SJohannes Berg 
real_memcpy_toio(volatile void __iomem * addr,const void * buffer,size_t size)210ca2e3342SJohannes Berg static void real_memcpy_toio(volatile void __iomem *addr, const void *buffer,
211ca2e3342SJohannes Berg 			     size_t size)
212ca2e3342SJohannes Berg {
213ca2e3342SJohannes Berg 	WARN(1, "Invalid memcpy_toio at address 0x%llx\n",
2144e84139eSJohannes Berg 	     (unsigned long long)(uintptr_t __force)addr);
215ca2e3342SJohannes Berg }
2162a6852cbSJohannes Berg #endif /* CONFIG_INDIRECT_IOMEM_FALLBACK */
217ca2e3342SJohannes Berg 
218ca2e3342SJohannes Berg #define MAKE_OP(op, sz) 						\
219ca2e3342SJohannes Berg u##sz __raw_read ## op(const volatile void __iomem *addr)		\
220ca2e3342SJohannes Berg {									\
221ca2e3342SJohannes Berg 	struct logic_iomem_area *area = get_area(addr);			\
222ca2e3342SJohannes Berg 									\
223ca2e3342SJohannes Berg 	if (!area)							\
224ca2e3342SJohannes Berg 		return real_raw_read ## op(addr);			\
225ca2e3342SJohannes Berg 									\
226ca2e3342SJohannes Berg 	return (u ## sz) area->ops->read(area->priv,			\
227ca2e3342SJohannes Berg 					 (unsigned long)addr & AREA_MASK,\
228ca2e3342SJohannes Berg 					 sz / 8);			\
229ca2e3342SJohannes Berg }									\
230ca2e3342SJohannes Berg EXPORT_SYMBOL(__raw_read ## op);					\
231ca2e3342SJohannes Berg 									\
232ca2e3342SJohannes Berg void __raw_write ## op(u ## sz val, volatile void __iomem *addr)	\
233ca2e3342SJohannes Berg {									\
234ca2e3342SJohannes Berg 	struct logic_iomem_area *area = get_area(addr);			\
235ca2e3342SJohannes Berg 									\
236ca2e3342SJohannes Berg 	if (!area) {							\
237ca2e3342SJohannes Berg 		real_raw_write ## op(val, addr);			\
238ca2e3342SJohannes Berg 		return;							\
239ca2e3342SJohannes Berg 	}								\
240ca2e3342SJohannes Berg 									\
241ca2e3342SJohannes Berg 	area->ops->write(area->priv,					\
242ca2e3342SJohannes Berg 			 (unsigned long)addr & AREA_MASK,		\
243ca2e3342SJohannes Berg 			 sz / 8, val);					\
244ca2e3342SJohannes Berg }									\
245ca2e3342SJohannes Berg EXPORT_SYMBOL(__raw_write ## op)
246ca2e3342SJohannes Berg 
247ca2e3342SJohannes Berg MAKE_OP(b, 8);
248ca2e3342SJohannes Berg MAKE_OP(w, 16);
249ca2e3342SJohannes Berg MAKE_OP(l, 32);
250ca2e3342SJohannes Berg #ifdef CONFIG_64BIT
251ca2e3342SJohannes Berg MAKE_OP(q, 64);
252ca2e3342SJohannes Berg #endif
253ca2e3342SJohannes Berg 
memset_io(volatile void __iomem * addr,int value,size_t size)254ca2e3342SJohannes Berg void memset_io(volatile void __iomem *addr, int value, size_t size)
255ca2e3342SJohannes Berg {
256ca2e3342SJohannes Berg 	struct logic_iomem_area *area = get_area(addr);
257ca2e3342SJohannes Berg 	unsigned long offs, start;
258ca2e3342SJohannes Berg 
259ca2e3342SJohannes Berg 	if (!area) {
260ca2e3342SJohannes Berg 		real_memset_io(addr, value, size);
261ca2e3342SJohannes Berg 		return;
262ca2e3342SJohannes Berg 	}
263ca2e3342SJohannes Berg 
264ca2e3342SJohannes Berg 	start = (unsigned long)addr & AREA_MASK;
265ca2e3342SJohannes Berg 
266ca2e3342SJohannes Berg 	if (area->ops->set) {
267ca2e3342SJohannes Berg 		area->ops->set(area->priv, start, value, size);
268ca2e3342SJohannes Berg 		return;
269ca2e3342SJohannes Berg 	}
270ca2e3342SJohannes Berg 
271ca2e3342SJohannes Berg 	for (offs = 0; offs < size; offs++)
272ca2e3342SJohannes Berg 		area->ops->write(area->priv, start + offs, 1, value);
273ca2e3342SJohannes Berg }
274ca2e3342SJohannes Berg EXPORT_SYMBOL(memset_io);
275ca2e3342SJohannes Berg 
memcpy_fromio(void * buffer,const volatile void __iomem * addr,size_t size)276ca2e3342SJohannes Berg void memcpy_fromio(void *buffer, const volatile void __iomem *addr,
277ca2e3342SJohannes Berg                    size_t size)
278ca2e3342SJohannes Berg {
279ca2e3342SJohannes Berg 	struct logic_iomem_area *area = get_area(addr);
280ca2e3342SJohannes Berg 	u8 *buf = buffer;
281ca2e3342SJohannes Berg 	unsigned long offs, start;
282ca2e3342SJohannes Berg 
283ca2e3342SJohannes Berg 	if (!area) {
284ca2e3342SJohannes Berg 		real_memcpy_fromio(buffer, addr, size);
285ca2e3342SJohannes Berg 		return;
286ca2e3342SJohannes Berg 	}
287ca2e3342SJohannes Berg 
288ca2e3342SJohannes Berg 	start = (unsigned long)addr & AREA_MASK;
289ca2e3342SJohannes Berg 
290ca2e3342SJohannes Berg 	if (area->ops->copy_from) {
291ca2e3342SJohannes Berg 		area->ops->copy_from(area->priv, buffer, start, size);
292ca2e3342SJohannes Berg 		return;
293ca2e3342SJohannes Berg 	}
294ca2e3342SJohannes Berg 
295ca2e3342SJohannes Berg 	for (offs = 0; offs < size; offs++)
296ca2e3342SJohannes Berg 		buf[offs] = area->ops->read(area->priv, start + offs, 1);
297ca2e3342SJohannes Berg }
298ca2e3342SJohannes Berg EXPORT_SYMBOL(memcpy_fromio);
299ca2e3342SJohannes Berg 
memcpy_toio(volatile void __iomem * addr,const void * buffer,size_t size)300ca2e3342SJohannes Berg void memcpy_toio(volatile void __iomem *addr, const void *buffer, size_t size)
301ca2e3342SJohannes Berg {
302ca2e3342SJohannes Berg 	struct logic_iomem_area *area = get_area(addr);
303ca2e3342SJohannes Berg 	const u8 *buf = buffer;
304ca2e3342SJohannes Berg 	unsigned long offs, start;
305ca2e3342SJohannes Berg 
306ca2e3342SJohannes Berg 	if (!area) {
307ca2e3342SJohannes Berg 		real_memcpy_toio(addr, buffer, size);
308ca2e3342SJohannes Berg 		return;
309ca2e3342SJohannes Berg 	}
310ca2e3342SJohannes Berg 
311ca2e3342SJohannes Berg 	start = (unsigned long)addr & AREA_MASK;
312ca2e3342SJohannes Berg 
313ca2e3342SJohannes Berg 	if (area->ops->copy_to) {
314ca2e3342SJohannes Berg 		area->ops->copy_to(area->priv, start, buffer, size);
315ca2e3342SJohannes Berg 		return;
316ca2e3342SJohannes Berg 	}
317ca2e3342SJohannes Berg 
318ca2e3342SJohannes Berg 	for (offs = 0; offs < size; offs++)
319ca2e3342SJohannes Berg 		area->ops->write(area->priv, start + offs, 1, buf[offs]);
320ca2e3342SJohannes Berg }
321ca2e3342SJohannes Berg EXPORT_SYMBOL(memcpy_toio);
322