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