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