1 /*
2  * Copyright (c) 2023-2024, Arm Limited. All rights reserved.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  *
6  */
7 
8 #include "discovery/ni_tower_discovery_drv.h"
9 #include "ni_tower_psam_drv.h"
10 #include "ni_tower_psam_reg.h"
11 #include "util/ni_tower_util.h"
12 
13 #include <stddef.h>
14 
15 #define NI_TOWER_PSAM_ADDRESS_GRAN      (1ULL << 12)
16 #define NI_TOWER_PSAM_ADDRESS_MASK      (~(NI_TOWER_PSAM_ADDRESS_GRAN - 1))
17 #define NI_TOWER_PSAM_ADDRESS_H(addr)   ((addr) >> 32)
18 #define NI_TOWER_PSAM_ADDRESS_L(addr)   ((addr) & NI_TOWER_PSAM_ADDRESS_MASK)
19 
20 #define NI_TOWER_PSAM_GET64_BASE_ADDRESS(addr, high, low)   \
21     addr = (((uint64_t)(high) << 32) | (low)) & NI_TOWER_PSAM_ADDRESS_MASK
22 
23 #define NI_TOWER_PSAM_GET64_END_ADDRESS(addr, high, low)    \
24     addr = (((uint64_t)(high) << 32) | (low)) |             \
25             (NI_TOWER_PSAM_ADDRESS_GRAN - 1)
26 
ni_tower_psam_dev_init(const struct ni_tower_dev * ni_tower_dev,const struct ni_tower_component_node * component,const uint64_t region_mapping_offset,struct ni_tower_psam_dev * dev)27 enum ni_tower_err ni_tower_psam_dev_init(
28     const struct ni_tower_dev *ni_tower_dev,
29     const struct ni_tower_component_node* component,
30     const uint64_t region_mapping_offset,
31     struct ni_tower_psam_dev *dev)
32 {
33     enum ni_tower_err err;
34     uint32_t off_addr;
35     struct ni_tower_discovery_node root = {
36         .node_type = NI_TOWER_CFGNI,
37         .node_id = 0,
38         .node_off_addr = 0x0
39     };
40 
41     if (ni_tower_dev == NULL || ni_tower_dev->periphbase == (uintptr_t)NULL) {
42         return NI_TOWER_ERR_INVALID_ARG;
43     }
44 
45     if (component == NULL || dev == NULL) {
46         return NI_TOWER_ERR_INVALID_ARG;
47     }
48 
49     /* Discover offset address for the PSAM */
50     err = ni_tower_discover_offset(
51             ni_tower_dev, &root,
52             component->type,
53             component->id,
54             NI_TOWER_PSAM, &off_addr);
55     if (err != NI_TOWER_SUCCESS) {
56         return err;
57     }
58 
59     dev->base = ni_tower_dev->periphbase + off_addr;
60     dev->region_mapping_offset = region_mapping_offset;
61 
62     return NI_TOWER_SUCCESS;
63 }
64 
ni_tower_psam_configure_nhregion(const struct ni_tower_psam_dev * dev,const struct ni_tower_psam_reg_cfg_info * cfg_info,const uint32_t region)65 enum ni_tower_err ni_tower_psam_configure_nhregion(
66     const struct ni_tower_psam_dev *dev,
67     const struct ni_tower_psam_reg_cfg_info *cfg_info,
68     const uint32_t region)
69 {
70     struct ni_tower_psam_reg_map* reg;
71     uint64_t base_addr, end_addr;
72     uint64_t temp_base_addr, temp_end_addr;
73     uint32_t r_idx;
74 
75     if (dev == NULL || dev->base == (uintptr_t)NULL) {
76         return NI_TOWER_ERR_INVALID_ARG;
77     }
78 
79     reg = (struct ni_tower_psam_reg_map*)dev->base;
80 
81     if (cfg_info == NULL) {
82         return NI_TOWER_ERR_INVALID_ARG;
83     }
84 
85     base_addr = cfg_info->base_addr + dev->region_mapping_offset;
86     end_addr = cfg_info->end_addr + dev->region_mapping_offset;
87 
88     /* Checking alignment of base and end addresses */
89     if (((base_addr & (NI_TOWER_PSAM_ADDRESS_GRAN - 1)) != 0) ||
90         ((~end_addr & (NI_TOWER_PSAM_ADDRESS_GRAN - 1)) != 0)) {
91         return NI_TOWER_ERR_INVALID_ARG;
92     }
93 
94     /* Disable region */
95     reg->nh_region[region].cfg0 &= ~NI_TOWER_NH_REGION_REGION_VALID;
96 
97     /* Check whether region overlaps with another valid region */
98     for (r_idx = 0; r_idx < NI_TOWER_MAX_NH_REGIONS; ++r_idx) {
99         if (reg->nh_region[r_idx].cfg0 & NI_TOWER_NH_REGION_REGION_VALID) {
100             NI_TOWER_PSAM_GET64_BASE_ADDRESS(temp_base_addr,
101                                              reg->nh_region[r_idx].cfg1,
102                                              reg->nh_region[r_idx].cfg0);
103 
104             NI_TOWER_PSAM_GET64_END_ADDRESS(temp_end_addr,
105                                             reg->nh_region[r_idx].cfg3,
106                                             reg->nh_region[r_idx].cfg2);
107 
108             if (ni_tower_check_region_overlaps(base_addr, end_addr,
109                     temp_base_addr, temp_end_addr) !=
110                 NI_TOWER_SUCCESS) {
111                 return NI_TOWER_ERR_REGION_OVERLAPS;
112             }
113         }
114     }
115 
116     /* Set base address */
117     reg->nh_region[region].cfg0 = NI_TOWER_PSAM_ADDRESS_L(base_addr);
118     reg->nh_region[region].cfg1 = NI_TOWER_PSAM_ADDRESS_H(base_addr);
119     /* Set end address */
120     reg->nh_region[region].cfg2 = NI_TOWER_PSAM_ADDRESS_L(end_addr);
121     reg->nh_region[region].cfg3 = NI_TOWER_PSAM_ADDRESS_H(end_addr);
122     /* Set ID for the Target interface. */
123     reg->nh_region[region].cfg2 |= (cfg_info->target_id &
124                                     NI_TOWER_NH_REGION_TGT_ID_MSK);
125     /* Set region valid */
126     reg->nh_region[region].cfg0 |= NI_TOWER_NH_REGION_REGION_VALID;
127 
128     return NI_TOWER_SUCCESS;
129 }
130 
get_next_available_region(const struct ni_tower_psam_dev * dev,uint32_t * region)131 static enum ni_tower_err get_next_available_region(
132     const struct ni_tower_psam_dev *dev,
133     uint32_t *region)
134 {
135     struct ni_tower_psam_reg_map* reg;
136     uint32_t r_idx;
137 
138     if (dev == NULL || dev->base == (uintptr_t)NULL) {
139         return NI_TOWER_ERR_INVALID_ARG;
140     }
141 
142     reg = (struct ni_tower_psam_reg_map*)dev->base;
143 
144     for (r_idx = 0; r_idx < NI_TOWER_MAX_NH_REGIONS; ++r_idx) {
145         if (!(reg->nh_region[r_idx].cfg0 & NI_TOWER_NH_REGION_REGION_VALID)) {
146             *region = r_idx;
147             return NI_TOWER_SUCCESS;
148         }
149     }
150 
151     return NI_TOWER_ERR;
152 }
153 
ni_tower_psam_configure_next_available_nhregion(const struct ni_tower_psam_dev * dev,const struct ni_tower_psam_reg_cfg_info * cfg_info)154 enum ni_tower_err ni_tower_psam_configure_next_available_nhregion(
155     const struct ni_tower_psam_dev *dev,
156     const struct ni_tower_psam_reg_cfg_info *cfg_info)
157 {
158     enum ni_tower_err err;
159     uint32_t next_available_region;
160 
161     err = get_next_available_region(dev, &next_available_region);
162     if (err != NI_TOWER_SUCCESS) {
163         return err;
164     }
165 
166     return ni_tower_psam_configure_nhregion(dev, cfg_info,
167             next_available_region);
168 }
169 
ni_tower_psam_enable(const struct ni_tower_psam_dev * dev)170 enum ni_tower_err ni_tower_psam_enable(const struct ni_tower_psam_dev *dev)
171 {
172     struct ni_tower_psam_reg_map* reg;
173 
174     if (dev == NULL || dev->base == (uintptr_t)NULL) {
175         return NI_TOWER_ERR_INVALID_ARG;
176     }
177 
178     reg = (struct ni_tower_psam_reg_map*)dev->base;
179 
180     reg->sam_status |= NI_TOWER_SAM_STATUS_SETUP_COMPLETE;
181 
182     return NI_TOWER_SUCCESS;
183 }
184 
ni_tower_psam_disable(const struct ni_tower_psam_dev * dev)185 enum ni_tower_err ni_tower_psam_disable(const struct ni_tower_psam_dev *dev)
186 {
187     struct ni_tower_psam_reg_map* reg;
188 
189     if (dev == NULL || dev->base == (uintptr_t)NULL) {
190         return NI_TOWER_ERR_INVALID_ARG;
191     }
192 
193     reg = (struct ni_tower_psam_reg_map*)dev->base;
194 
195     reg->sam_status &= ~NI_TOWER_SAM_STATUS_SETUP_COMPLETE;
196 
197     return NI_TOWER_SUCCESS;
198 }
199