1 /*
2  * Copyright (c) 2023 Texas Instruments Incorporated
3  * Copyright (c) 2023 L Lakshmanan
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 /**
9  * @file
10  * @brief Driver handling Region based Address Translation (RAT)
11  * related functions
12  *
13  * RAT is a module that is used by certain Texas Instruments SoCs
14  * to allow some cores with a 32 bit address space to access
15  * the full 48 bit SoC address space. This is required for the
16  * core to be able to use peripherals.
17  *
18  * The driver uses the sys_mm_drv_page_phys_get() API to access
19  * the address space.
20  */
21 
22 #include <zephyr/kernel.h>
23 #include <zephyr/drivers/mm/rat.h>
24 #include <zephyr/drivers/mm/system_mm.h>
25 #include <zephyr/sys/__assert.h>
26 
27 static struct address_trans_params translate_config;
28 
29 /**
30  * @brief Set registers for the address regions being used
31  *
32  * @param addr_translate_config Pointer to config struct for the RAT module
33  * @param region_num Number of regions being initialised
34  * @param enable Region status
35  */
36 
address_trans_set_region(struct address_trans_params * addr_translate_config,uint16_t region_num,uint32_t enable)37 static void address_trans_set_region(struct address_trans_params *addr_translate_config,
38 			uint16_t region_num, uint32_t enable)
39 {
40 	uint32_t rat_base_addr = addr_translate_config->rat_base_addr;
41 	uint64_t system_addr = addr_translate_config->region_config[region_num].system_addr;
42 	uint32_t local_addr = addr_translate_config->region_config[region_num].local_addr;
43 	uint32_t size = addr_translate_config->region_config[region_num].size;
44 	uint32_t system_addrL, system_addrH;
45 
46 	if (size > address_trans_region_size_4G) {
47 		size = address_trans_region_size_4G;
48 	}
49 	system_addrL = (uint32_t)(system_addr & ~((uint32_t)((BIT64_MASK(size)))));
50 	system_addrH = (uint32_t)((system_addr >> 32) & 0xFFFF);
51 	local_addr = local_addr & ~((uint32_t)(BIT64_MASK(size)));
52 
53 	sys_write32(0, RAT_CTRL(rat_base_addr, region_num));
54 	sys_write32(local_addr, RAT_BASE(rat_base_addr, region_num));
55 	sys_write32(system_addrL, RAT_TRANS_L(rat_base_addr, region_num));
56 	sys_write32(system_addrH, RAT_TRANS_H(rat_base_addr, region_num));
57 	sys_write32(RAT_CTRL_W(enable, size), RAT_CTRL(rat_base_addr, region_num));
58 }
59 
address_trans_init(struct address_trans_params * params)60 static void address_trans_init(struct address_trans_params *params)
61 {
62 	uint32_t i;
63 
64 	if (params != NULL) {
65 		translate_config = *params;
66 	}
67 
68 	__ASSERT(translate_config.num_regions < ADDR_TRANSLATE_MAX_REGIONS,
69 		 "Exceeding maximum number of regions");
70 
71 	for (i = 0; i < translate_config.num_regions; i++) {
72 		__ASSERT(translate_config.rat_base_addr != 0, "RAT base address cannot be 0");
73 		__ASSERT(translate_config.region_config != NULL,
74 			 "RAT region config cannot be NULL");
75 
76 		/* enable regions setup by user */
77 		address_trans_set_region(&translate_config, i, 1);
78 	}
79 }
80 
81 /**
82  * @brief Initialise RAT module
83  *
84  * @param region_config Pointer to config struct for the regions
85  * @param rat_base_addr Base address for the RAT module
86  * @param translate_regions Number of regions being initialised
87  */
88 
sys_mm_drv_ti_rat_init(void * region_config,uint64_t rat_base_addr,uint8_t translate_regions)89 void sys_mm_drv_ti_rat_init(void *region_config, uint64_t rat_base_addr, uint8_t translate_regions)
90 {
91 	translate_config.num_regions = translate_regions;
92 	translate_config.rat_base_addr = rat_base_addr;
93 	translate_config.region_config = (struct address_trans_region_config *)region_config;
94 
95 	address_trans_init(&translate_config);
96 }
97 
sys_mm_drv_page_phys_get(void * virt,uintptr_t * phys)98 int sys_mm_drv_page_phys_get(void *virt, uintptr_t *phys)
99 {
100 	if (virt == NULL) {
101 		return -EINVAL;
102 	}
103 	uintptr_t pa = (uintptr_t) virt;
104 	uintptr_t *va = phys;
105 
106 	uint32_t found, regionId;
107 
108 	__ASSERT(translate_config.num_regions < ADDR_TRANSLATE_MAX_REGIONS,
109 		 "Exceeding maximum number of regions");
110 
111 	found = 0;
112 
113 	for (regionId = 0; regionId < translate_config.num_regions; regionId++) {
114 		uint64_t start_addr, end_addr;
115 		uint32_t size_mask;
116 
117 		size_mask =
118 			((uint32_t)((BIT64_MASK(translate_config.region_config[regionId].size))));
119 
120 		start_addr = translate_config.region_config[regionId].system_addr;
121 
122 		end_addr = start_addr + size_mask;
123 
124 		if (pa >= start_addr && pa <= end_addr) {
125 			found = 1;
126 			break;
127 		}
128 	}
129 	if (found) {
130 		/* translate input address to output address */
131 		uint32_t offset =
132 			pa - translate_config.region_config[regionId].system_addr;
133 
134 		*va = (translate_config.region_config[regionId].local_addr + offset);
135 	} else {
136 		/* no mapping found, set output = input with 32b truncation */
137 		*va = pa;
138 	}
139 
140 	if (va == NULL) {
141 		return -EFAULT;
142 	}
143 	return 0;
144 }
145