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