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