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