1 /* 2 * Copyright (C) 2018 Marvell International Ltd. 3 * 4 * SPDX-License-Identifier: BSD-3-Clause 5 * https://spdx.org/licenses 6 */ 7 8 /* GWIN unit device driver for Marvell AP810 SoC */ 9 10 #include <a8k_common.h> 11 #include <debug.h> 12 #include <gwin.h> 13 #include <mmio.h> 14 #include <mvebu.h> 15 #include <mvebu_def.h> 16 17 #if LOG_LEVEL >= LOG_LEVEL_INFO 18 #define DEBUG_ADDR_MAP 19 #endif 20 21 /* common defines */ 22 #define WIN_ENABLE_BIT (0x1) 23 #define WIN_TARGET_MASK (0xF) 24 #define WIN_TARGET_SHIFT (0x8) 25 #define WIN_TARGET(tgt) (((tgt) & WIN_TARGET_MASK) \ 26 << WIN_TARGET_SHIFT) 27 28 /* Bits[43:26] of the physical address are the window base, 29 * which is aligned to 64MB 30 */ 31 #define ADDRESS_RSHIFT (26) 32 #define ADDRESS_LSHIFT (10) 33 #define GWIN_ALIGNMENT_64M (0x4000000) 34 35 /* AP registers */ 36 #define GWIN_CR_OFFSET(ap, win) (MVEBU_GWIN_BASE(ap) + 0x0 + \ 37 (0x10 * (win))) 38 #define GWIN_ALR_OFFSET(ap, win) (MVEBU_GWIN_BASE(ap) + 0x8 + \ 39 (0x10 * (win))) 40 #define GWIN_AHR_OFFSET(ap, win) (MVEBU_GWIN_BASE(ap) + 0xc + \ 41 (0x10 * (win))) 42 43 #define CCU_GRU_CR_OFFSET(ap) (MVEBU_CCU_GRU_BASE(ap)) 44 #define CCR_GRU_CR_GWIN_MBYPASS (1 << 1) 45 46 static void gwin_check(struct addr_map_win *win) 47 { 48 /* The base is always 64M aligned */ 49 if (IS_NOT_ALIGN(win->base_addr, GWIN_ALIGNMENT_64M)) { 50 win->base_addr &= ~(GWIN_ALIGNMENT_64M - 1); 51 NOTICE("%s: Align the base address to 0x%llx\n", 52 __func__, win->base_addr); 53 } 54 55 /* size parameter validity check */ 56 if (IS_NOT_ALIGN(win->win_size, GWIN_ALIGNMENT_64M)) { 57 win->win_size = ALIGN_UP(win->win_size, GWIN_ALIGNMENT_64M); 58 NOTICE("%s: Aligning window size to 0x%llx\n", 59 __func__, win->win_size); 60 } 61 } 62 63 static void gwin_enable_window(int ap_index, struct addr_map_win *win, 64 uint32_t win_num) 65 { 66 uint32_t alr, ahr; 67 uint64_t end_addr; 68 69 if ((win->target_id & WIN_TARGET_MASK) != win->target_id) { 70 ERROR("target ID = %d, is invalid\n", win->target_id); 71 return; 72 } 73 74 /* calculate 64bit end-address */ 75 end_addr = (win->base_addr + win->win_size - 1); 76 77 alr = (uint32_t)((win->base_addr >> ADDRESS_RSHIFT) << ADDRESS_LSHIFT); 78 ahr = (uint32_t)((end_addr >> ADDRESS_RSHIFT) << ADDRESS_LSHIFT); 79 80 /* write start address and end address for GWIN */ 81 mmio_write_32(GWIN_ALR_OFFSET(ap_index, win_num), alr); 82 mmio_write_32(GWIN_AHR_OFFSET(ap_index, win_num), ahr); 83 84 /* write the target ID and enable the window */ 85 mmio_write_32(GWIN_CR_OFFSET(ap_index, win_num), 86 WIN_TARGET(win->target_id) | WIN_ENABLE_BIT); 87 } 88 89 static void gwin_disable_window(int ap_index, uint32_t win_num) 90 { 91 uint32_t win_reg; 92 93 win_reg = mmio_read_32(GWIN_CR_OFFSET(ap_index, win_num)); 94 win_reg &= ~WIN_ENABLE_BIT; 95 mmio_write_32(GWIN_CR_OFFSET(ap_index, win_num), win_reg); 96 } 97 98 /* Insert/Remove temporary window for using the out-of reset default 99 * CPx base address to access the CP configuration space prior to 100 * the further base address update in accordance with address mapping 101 * design. 102 * 103 * NOTE: Use the same window array for insertion and removal of 104 * temporary windows. 105 */ 106 void gwin_temp_win_insert(int ap_index, struct addr_map_win *win, int size) 107 { 108 uint32_t win_id; 109 110 for (int i = 0; i < size; i++) { 111 win_id = MVEBU_GWIN_MAX_WINS - i - 1; 112 gwin_check(win); 113 gwin_enable_window(ap_index, win, win_id); 114 win++; 115 } 116 } 117 118 /* 119 * NOTE: Use the same window array for insertion and removal of 120 * temporary windows. 121 */ 122 void gwin_temp_win_remove(int ap_index, struct addr_map_win *win, int size) 123 { 124 uint32_t win_id; 125 126 for (int i = 0; i < size; i++) { 127 uint64_t base; 128 uint32_t target; 129 130 win_id = MVEBU_GWIN_MAX_WINS - i - 1; 131 132 target = mmio_read_32(GWIN_CR_OFFSET(ap_index, win_id)); 133 target >>= WIN_TARGET_SHIFT; 134 target &= WIN_TARGET_MASK; 135 136 base = mmio_read_32(GWIN_ALR_OFFSET(ap_index, win_id)); 137 base >>= ADDRESS_LSHIFT; 138 base <<= ADDRESS_RSHIFT; 139 140 if (win->target_id != target) { 141 ERROR("%s: Trying to remove bad window-%d!\n", 142 __func__, win_id); 143 continue; 144 } 145 gwin_disable_window(ap_index, win_id); 146 win++; 147 } 148 } 149 150 #ifdef DEBUG_ADDR_MAP 151 static void dump_gwin(int ap_index) 152 { 153 uint32_t win_num; 154 155 /* Dump all GWIN windows */ 156 tf_printf("\tbank target start end\n"); 157 tf_printf("\t----------------------------------------------------\n"); 158 for (win_num = 0; win_num < MVEBU_GWIN_MAX_WINS; win_num++) { 159 uint32_t cr; 160 uint64_t alr, ahr; 161 162 cr = mmio_read_32(GWIN_CR_OFFSET(ap_index, win_num)); 163 /* Window enabled */ 164 if (cr & WIN_ENABLE_BIT) { 165 alr = mmio_read_32(GWIN_ALR_OFFSET(ap_index, win_num)); 166 alr = (alr >> ADDRESS_LSHIFT) << ADDRESS_RSHIFT; 167 ahr = mmio_read_32(GWIN_AHR_OFFSET(ap_index, win_num)); 168 ahr = (ahr >> ADDRESS_LSHIFT) << ADDRESS_RSHIFT; 169 tf_printf("\tgwin %d 0x%016llx 0x%016llx\n", 170 (cr >> 8) & 0xF, alr, ahr); 171 } 172 } 173 } 174 #endif 175 176 int init_gwin(int ap_index) 177 { 178 struct addr_map_win *win; 179 uint32_t win_id; 180 uint32_t win_count; 181 uint32_t win_reg; 182 183 INFO("Initializing GWIN Address decoding\n"); 184 185 /* Get the array of the windows and its size */ 186 marvell_get_gwin_memory_map(ap_index, &win, &win_count); 187 if (win_count <= 0) { 188 INFO("no windows configurations found\n"); 189 return 0; 190 } 191 192 if (win_count > MVEBU_GWIN_MAX_WINS) { 193 ERROR("number of windows is bigger than %d\n", 194 MVEBU_GWIN_MAX_WINS); 195 return 0; 196 } 197 198 /* disable all windows */ 199 for (win_id = 0; win_id < MVEBU_GWIN_MAX_WINS; win_id++) 200 gwin_disable_window(ap_index, win_id); 201 202 /* enable relevant windows */ 203 for (win_id = 0; win_id < win_count; win_id++, win++) { 204 gwin_check(win); 205 gwin_enable_window(ap_index, win, win_id); 206 } 207 208 /* GWIN Miss feature has not verified, therefore any access towards 209 * remote AP should be accompanied with proper configuration to 210 * GWIN registers group and therefore the GWIN Miss feature 211 * should be set into Bypass mode, need to make sure all GWIN regions 212 * are defined correctly that will assure no GWIN miss occurrance 213 * JIRA-AURORA2-1630 214 */ 215 INFO("Update GWIN miss bypass\n"); 216 win_reg = mmio_read_32(CCU_GRU_CR_OFFSET(ap_index)); 217 win_reg |= CCR_GRU_CR_GWIN_MBYPASS; 218 mmio_write_32(CCU_GRU_CR_OFFSET(ap_index), win_reg); 219 220 #ifdef DEBUG_ADDR_MAP 221 dump_gwin(ap_index); 222 #endif 223 224 INFO("Done GWIN Address decoding Initializing\n"); 225 226 return 0; 227 } 228