1 /*
2  * Copyright 2025 NXP
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/init.h>
8 #include <zephyr/logging/log.h>
9 #include <addr_translation.h>
10 
11 #ifdef CONFIG_OPENAMP_VENDOR_ADDR_TRANSLATION_FILE
12 /*
13  * In this file get_phys_pages() needs to be defined,
14  * see example in nxp_addr_translation.h
15  */
16 #include CONFIG_OPENAMP_VENDOR_ADDR_TRANSLATION_FILE
17 #else
18 /*
19  * Return table of base physical addresses for the I/O region
20  * that starts with the specified physical base address (phys)
21  */
get_phys_map(metal_phys_addr_t phys)22 static inline const struct phys_pages *get_phys_map(metal_phys_addr_t phys)
23 {
24 	(void)phys;
25 	return NULL;
26 }
27 #endif
28 
29 static const struct phys_pages *phys_pages;
30 
31 /**
32  * @brief Translates an offset within an I/O region to a physical address.
33  *
34  * This function first attempts to translate the offset using the driver's
35  * physical address map. If no valid mapping is found, it falls back to the
36  * device physical address map.
37  *
38  * @param io Pointer to the I/O region.
39  * @param offset Offset within the I/O region.
40  *
41  * @return physical address if valid, otherwise METAL_BAD_PHYS.
42  */
translate_offset_to_phys(struct metal_io_region * io,unsigned long offset)43 static metal_phys_addr_t translate_offset_to_phys(struct metal_io_region *io,
44 						  unsigned long offset)
45 {
46 	(void)io;
47 	size_t i;
48 	metal_phys_addr_t tmp_addr;
49 	size_t tmp_size;
50 	unsigned long tmp_offset = 0;
51 
52 	if (offset > io->size) {
53 		return METAL_BAD_PHYS;
54 	}
55 
56 	for (i = 0; i < phys_pages->no_pages; i++) {
57 		tmp_addr = phys_pages->map[i].addr;
58 		tmp_size = phys_pages->map[i].size;
59 		if ((tmp_offset <= offset) && (offset < tmp_offset + tmp_size)) {
60 			return tmp_addr + (offset - tmp_offset);
61 		}
62 		tmp_offset += tmp_size;
63 	}
64 
65 	return METAL_BAD_PHYS;
66 }
67 
68 /**
69  * @brief Translates a physical address to an offset within an I/O region.
70  *
71  * This function first attempts to translate the physical address using the
72  * driver's address map. If no valid mapping is found, it falls back to the
73  * device address map.
74  *
75  * @param io Pointer to the I/O region.
76  * @param phys Physical address to translate.
77  *
78  * @return offset if valid, otherwise METAL_BAD_OFFSET.
79  */
translate_phys_to_offset(struct metal_io_region * io,metal_phys_addr_t phys)80 static unsigned long translate_phys_to_offset(struct metal_io_region *io,
81 					      metal_phys_addr_t phys)
82 {
83 	(void)io;
84 	size_t i;
85 	metal_phys_addr_t tmp_addr;
86 	size_t tmp_size;
87 	unsigned long offset = 0;
88 
89 	for (i = 0; i < phys_pages->no_pages; i++) {
90 		tmp_addr = phys_pages->map[i].addr;
91 		tmp_size = phys_pages->map[i].size;
92 		if ((tmp_addr <= phys) && (phys < tmp_addr + tmp_size)) {
93 			offset += (phys - tmp_addr);
94 			return (offset < io->size ? offset : METAL_BAD_OFFSET);
95 		}
96 		offset += tmp_size;
97 	}
98 
99 	/* if not found in local addr, search in remote addr */
100 	offset = 0;
101 	for (i = 0; i < phys_pages->no_pages; i++) {
102 		tmp_addr = phys_pages->map[i].remote_addr;
103 		tmp_size = phys_pages->map[i].size;
104 		if ((phys >= tmp_addr) && (phys < tmp_addr + tmp_size)) {
105 			offset += (phys - tmp_addr);
106 			return (offset < io->size ? offset : METAL_BAD_OFFSET);
107 		}
108 		offset += tmp_size;
109 	}
110 
111 	return METAL_BAD_OFFSET;
112 }
113 
114 /* Address translation operations for OpenAMP */
115 static const struct metal_io_ops openamp_addr_translation_ops = {
116 	.phys_to_offset = translate_phys_to_offset,
117 	.offset_to_phys = translate_offset_to_phys,
118 };
119 
120 /**
121  * @brief Return generic I/O operations
122  *
123  * @param	phys	Physical base address of the I/O region
124  * @return	metal_io_ops struct
125  */
addr_translation_get_ops(metal_phys_addr_t phys)126 const struct metal_io_ops *addr_translation_get_ops(metal_phys_addr_t phys)
127 {
128 	phys_pages = get_phys_map(phys);
129 	if (!phys_pages) {
130 		return NULL;
131 	}
132 
133 	return &openamp_addr_translation_ops;
134 }
135