1 /*
2  * Copyright (c) 2021 Carlo Caione, <ccaione@baylibre.com>
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/kernel.h>
8 #include <zephyr/device.h>
9 #include <zephyr/sys/sys_heap.h>
10 #include <zephyr/mem_mgmt/mem_attr.h>
11 #include <zephyr/sys/multi_heap.h>
12 #include <zephyr/dt-bindings/memory-attr/memory-attr.h>
13 #include <zephyr/dt-bindings/memory-attr/memory-attr-sw.h>
14 
15 struct ma_heap {
16 	struct sys_heap heap;
17 	uint32_t attr;
18 };
19 
20 struct {
21 	struct ma_heap ma_heaps[MAX_MULTI_HEAPS];
22 	struct sys_multi_heap multi_heap;
23 	int nheaps;
24 } mah_data;
25 
mah_choice(struct sys_multi_heap * m_heap,void * cfg,size_t align,size_t size)26 static void *mah_choice(struct sys_multi_heap *m_heap, void *cfg, size_t align, size_t size)
27 {
28 	uint32_t attr;
29 	void *block;
30 
31 	if (size == 0) {
32 		return NULL;
33 	}
34 
35 	attr = (uint32_t)(long) cfg;
36 
37 	/* Set in case the user requested a non-existing attr */
38 	block = NULL;
39 
40 	for (size_t hdx = 0; hdx < mah_data.nheaps; hdx++) {
41 		struct ma_heap *h;
42 
43 		h = &mah_data.ma_heaps[hdx];
44 
45 		if (h->attr != attr) {
46 			continue;
47 		}
48 
49 		block = sys_heap_aligned_alloc(&h->heap, align, size);
50 		if (block != NULL) {
51 			break;
52 		}
53 	}
54 
55 	return block;
56 }
57 
mem_attr_heap_free(void * block)58 void mem_attr_heap_free(void *block)
59 {
60 	sys_multi_heap_free(&mah_data.multi_heap, block);
61 }
62 
mem_attr_heap_alloc(uint32_t attr,size_t bytes)63 void *mem_attr_heap_alloc(uint32_t attr, size_t bytes)
64 {
65 	return sys_multi_heap_alloc(&mah_data.multi_heap,
66 				    (void *)(long) attr, bytes);
67 }
68 
mem_attr_heap_aligned_alloc(uint32_t attr,size_t align,size_t bytes)69 void *mem_attr_heap_aligned_alloc(uint32_t attr, size_t align, size_t bytes)
70 {
71 	return sys_multi_heap_aligned_alloc(&mah_data.multi_heap,
72 					    (void *)(long) attr, align, bytes);
73 }
74 
mem_attr_heap_get_region(void * addr)75 const struct mem_attr_region_t *mem_attr_heap_get_region(void *addr)
76 {
77 	const struct sys_multi_heap_rec *heap_rec;
78 
79 	heap_rec = sys_multi_heap_get_heap(&mah_data.multi_heap, addr);
80 
81 	return (const struct mem_attr_region_t *) heap_rec->user_data;
82 }
83 
ma_heap_add(const struct mem_attr_region_t * region,uint32_t attr)84 static int ma_heap_add(const struct mem_attr_region_t *region, uint32_t attr)
85 {
86 	struct ma_heap *mh;
87 	struct sys_heap *h;
88 
89 	/* No more heaps available */
90 	if (mah_data.nheaps >= MAX_MULTI_HEAPS) {
91 		return -ENOMEM;
92 	}
93 
94 	mh = &mah_data.ma_heaps[mah_data.nheaps++];
95 	h = &mh->heap;
96 
97 	mh->attr = attr;
98 
99 	sys_heap_init(h, (void *) region->dt_addr, region->dt_size);
100 	sys_multi_heap_add_heap(&mah_data.multi_heap, h, (void *) region);
101 
102 	return 0;
103 }
104 
105 
mem_attr_heap_pool_init(void)106 int mem_attr_heap_pool_init(void)
107 {
108 	const struct mem_attr_region_t *regions;
109 	static atomic_t state;
110 	size_t num_regions;
111 
112 	if (!atomic_cas(&state, 0, 1)) {
113 		return -EALREADY;
114 	}
115 
116 	sys_multi_heap_init(&mah_data.multi_heap, mah_choice);
117 
118 	num_regions = mem_attr_get_regions(&regions);
119 
120 	for (size_t idx = 0; idx < num_regions; idx++) {
121 		uint32_t sw_attr;
122 
123 		sw_attr = DT_MEM_SW_ATTR_GET(regions[idx].dt_attr);
124 
125 		/* No SW attribute is present */
126 		if (!sw_attr) {
127 			continue;
128 		}
129 
130 		if (ma_heap_add(&regions[idx], sw_attr)) {
131 			return -ENOMEM;
132 		}
133 	}
134 
135 	return 0;
136 }
137