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