summaryrefslogtreecommitdiff
path: root/drivers/arm/gic/v3/gic600_multichip.c
blob: 7f0735d42caea5d0791959ef37e90d70b1bd89c9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
/*
 * Copyright (c) 2019, Arm Limited. All rights reserved.
 * Copyright (c) 2022-2023, NVIDIA Corporation. All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

/*
 * GIC-600 driver extension for multichip setup
 */

#include <assert.h>

#include <common/debug.h>
#include <drivers/arm/arm_gicv3_common.h>
#include <drivers/arm/gic600_multichip.h>
#include <drivers/arm/gicv3.h>

#include "../common/gic_common_private.h"
#include "gic600_multichip_private.h"

static struct gic600_multichip_data *plat_gic_multichip_data;

/*******************************************************************************
 * Retrieve the address of the chip owner for a given SPI ID
 ******************************************************************************/
uintptr_t gic600_multichip_gicd_base_for_spi(uint32_t spi_id)
{
	unsigned int i;

	/* Find the multichip instance */
	for (i = 0U; i < GIC600_MAX_MULTICHIP; i++) {
		if ((spi_id <= plat_gic_multichip_data->spi_ids[i].spi_id_max) &&
		     (spi_id >= plat_gic_multichip_data->spi_ids[i].spi_id_min)) {
			break;
		}
	}

	/* Ensure that plat_gic_multichip_data contains valid values */
	assert(i < GIC600_MAX_MULTICHIP);

	return plat_gic_multichip_data->spi_ids[i].gicd_base;
}

/*******************************************************************************
 * GIC-600 multichip operation related helper functions
 ******************************************************************************/
static void gicd_dchipr_wait_for_power_update_progress(uintptr_t base)
{
	unsigned int retry = GICD_PUP_UPDATE_RETRIES;

	while ((read_gicd_dchipr(base) & GICD_DCHIPR_PUP_BIT) != 0U) {
		if (retry-- == 0U) {
			ERROR("GIC-600 connection to Routing Table Owner timed "
					 "out\n");
			panic();
		}
	}
}

/*******************************************************************************
 * Sets up the routing table owner.
 ******************************************************************************/
static void set_gicd_dchipr_rt_owner(uintptr_t base, unsigned int rt_owner)
{
	/*
	 * Ensure that Group enables in GICD_CTLR are disabled and no pending
	 * register writes to GICD_CTLR.
	 */
	if ((gicd_read_ctlr(base) &
			(CTLR_ENABLE_G0_BIT | CTLR_ENABLE_G1S_BIT |
			 CTLR_ENABLE_G1NS_BIT | GICD_CTLR_RWP_BIT)) != 0) {
		ERROR("GICD_CTLR group interrupts are either enabled or have "
				"pending writes. Cannot set RT owner.\n");
		panic();
	}

	/* Poll till PUP is zero before intiating write */
	gicd_dchipr_wait_for_power_update_progress(base);

	write_gicd_dchipr(base, read_gicd_dchipr(base) |
			(rt_owner << GICD_DCHIPR_RT_OWNER_SHIFT));

	/* Poll till PUP is zero to ensure write is complete */
	gicd_dchipr_wait_for_power_update_progress(base);
}

/*******************************************************************************
 * Configures the Chip Register to make connections to GICDs on
 * a multichip platform.
 ******************************************************************************/
static void set_gicd_chipr_n(uintptr_t base,
				unsigned int chip_id,
				uint64_t chip_addr,
				unsigned int spi_id_min,
				unsigned int spi_id_max)
{
	unsigned int spi_block_min, spi_blocks;
	unsigned int gicd_iidr_val = gicd_read_iidr(base);
	uint64_t chipr_n_val;

	/*
	 * Ensure that group enables in GICD_CTLR are disabled and no pending
	 * register writes to GICD_CTLR.
	 */
	if ((gicd_read_ctlr(base) &
			(CTLR_ENABLE_G0_BIT | CTLR_ENABLE_G1S_BIT |
			 CTLR_ENABLE_G1NS_BIT | GICD_CTLR_RWP_BIT)) != 0) {
		ERROR("GICD_CTLR group interrupts are either enabled or have "
				"pending writes. Cannot set CHIPR register.\n");
		panic();
	}

	/*
	 * spi_id_min and spi_id_max of value 0 is used to intidicate that the
	 * chip doesn't own any SPI block. Re-assign min and max values as SPI
	 * id starts from 32.
	 */
	if (spi_id_min == 0 && spi_id_max == 0) {
		spi_id_min = GIC600_SPI_ID_MIN;
		spi_id_max = GIC600_SPI_ID_MIN;
	}

	switch ((gicd_iidr_val & IIDR_MODEL_MASK)) {
	case IIDR_MODEL_ARM_GIC_600:
		spi_block_min = SPI_BLOCK_MIN_VALUE(spi_id_min);
		spi_blocks    = SPI_BLOCKS_VALUE(spi_id_min, spi_id_max);

		chipr_n_val = GICD_CHIPR_VALUE_GIC_600(chip_addr,
						       spi_block_min,
						       spi_blocks);
		break;
	case IIDR_MODEL_ARM_GIC_700:
		/* Calculate the SPI_ID_MIN value for ESPI */
		if (spi_id_min >= GIC700_ESPI_ID_MIN) {
			spi_block_min = ESPI_BLOCK_MIN_VALUE(spi_id_min);
			spi_block_min += SPI_BLOCKS_VALUE(GIC700_SPI_ID_MIN,
				GIC700_SPI_ID_MAX);
		} else {
			spi_block_min = SPI_BLOCK_MIN_VALUE(spi_id_min);
		}

		/* Calculate the total number of blocks */
		spi_blocks = SPI_BLOCKS_VALUE(spi_id_min, spi_id_max);

		chipr_n_val = GICD_CHIPR_VALUE_GIC_700(chip_addr,
						       spi_block_min,
						       spi_blocks);
		break;
	default:
		ERROR("Unsupported GIC model 0x%x for multichip setup.\n",
		      gicd_iidr_val);
		panic();
		break;
	}
	chipr_n_val |= GICD_CHIPRx_SOCKET_STATE;

	/*
	 * Wait for DCHIPR.PUP to be zero before commencing writes to
	 * GICD_CHIPRx.
	 */
	gicd_dchipr_wait_for_power_update_progress(base);

	/*
	 * Assign chip addr, spi min block, number of spi blocks and bring chip
	 * online by setting SocketState.
	 */
	write_gicd_chipr_n(base, chip_id, chipr_n_val);

	/*
	 * Poll until DCHIP.PUP is zero to verify connection to rt_owner chip
	 * is complete.
	 */
	gicd_dchipr_wait_for_power_update_progress(base);

	/*
	 * Ensure that write to GICD_CHIPRx is successful and the chip_n came
	 * online.
	 */
	if (read_gicd_chipr_n(base, chip_id) != chipr_n_val) {
		ERROR("GICD_CHIPR%u write failed\n", chip_id);
		panic();
	}

	/* Ensure that chip is in consistent state */
	if (((read_gicd_chipsr(base) & GICD_CHIPSR_RTS_MASK) >>
				GICD_CHIPSR_RTS_SHIFT) !=
			GICD_CHIPSR_RTS_STATE_CONSISTENT) {
		ERROR("Chip %u routing table is not in consistent state\n",
				chip_id);
		panic();
	}
}

/*******************************************************************************
 * Validates the GIC-600 Multichip data structure passed by the platform.
 ******************************************************************************/
static void gic600_multichip_validate_data(
		struct gic600_multichip_data *multichip_data)
{
	unsigned int i, spi_id_min, spi_id_max, blocks_of_32;
	unsigned int multichip_spi_blocks = 0;

	assert(multichip_data != NULL);

	if (multichip_data->chip_count > GIC600_MAX_MULTICHIP) {
		ERROR("GIC-600 Multichip count should not exceed %d\n",
				GIC600_MAX_MULTICHIP);
		panic();
	}

	for (i = 0U; i < multichip_data->chip_count; i++) {
		spi_id_min = multichip_data->spi_ids[i].spi_id_min;
		spi_id_max = multichip_data->spi_ids[i].spi_id_max;

		if ((spi_id_min != 0U) || (spi_id_max != 0U)) {

			/* SPI IDs range check */
			if (!(spi_id_min >= GIC600_SPI_ID_MIN) ||
			    !(spi_id_max < GIC600_SPI_ID_MAX) ||
			    !(spi_id_min <= spi_id_max) ||
			    !((spi_id_max - spi_id_min + 1) % 32 == 0)) {
				ERROR("Invalid SPI IDs {%u, %u} passed for "
						"Chip %u\n", spi_id_min,
						spi_id_max, i);
				panic();
			}

			/* SPI IDs overlap check */
			blocks_of_32 = BLOCKS_OF_32(spi_id_min, spi_id_max);
			if ((multichip_spi_blocks & blocks_of_32) != 0) {
				ERROR("SPI IDs of Chip %u overlapping\n", i);
				panic();
			}
			multichip_spi_blocks |= blocks_of_32;
		}
	}
}

/*******************************************************************************
 * Validates the GIC-700 Multichip data structure passed by the platform.
 ******************************************************************************/
static void gic700_multichip_validate_data(
		struct gic600_multichip_data *multichip_data)
{
	unsigned int i, spi_id_min, spi_id_max, blocks_of_32;
	unsigned int multichip_spi_blocks = 0U, multichip_espi_blocks = 0U;

	assert(multichip_data != NULL);

	if (multichip_data->chip_count > GIC600_MAX_MULTICHIP) {
		ERROR("GIC-700 Multichip count (%u) should not exceed %u\n",
				multichip_data->chip_count, GIC600_MAX_MULTICHIP);
		panic();
	}

	for (i = 0U; i < multichip_data->chip_count; i++) {
		spi_id_min = multichip_data->spi_ids[i].spi_id_min;
		spi_id_max = multichip_data->spi_ids[i].spi_id_max;

		if ((spi_id_min == 0U) || (spi_id_max == 0U)) {
			continue;
		}

		/* MIN SPI ID check */
		if ((spi_id_min < GIC700_SPI_ID_MIN) ||
		    ((spi_id_min >= GIC700_SPI_ID_MAX) &&
		     (spi_id_min < GIC700_ESPI_ID_MIN))) {
			ERROR("Invalid MIN SPI ID {%u} passed for "
					"Chip %u\n", spi_id_min, i);
			panic();
		}

		if ((spi_id_min > spi_id_max) ||
		    ((spi_id_max - spi_id_min + 1) % 32 != 0)) {
			ERROR("Unaligned SPI IDs {%u, %u} passed for "
					"Chip %u\n", spi_id_min,
					spi_id_max, i);
			panic();
		}

		/* ESPI IDs range check */
		if ((spi_id_min >= GIC700_ESPI_ID_MIN) &&
		    (spi_id_max > GIC700_ESPI_ID_MAX)) {
			ERROR("Invalid ESPI IDs {%u, %u} passed for "
					"Chip %u\n", spi_id_min,
					spi_id_max, i);
			panic();

		}

		/* SPI IDs range check */
		if (((spi_id_min < GIC700_SPI_ID_MAX) &&
		     (spi_id_max > GIC700_SPI_ID_MAX))) {
			ERROR("Invalid SPI IDs {%u, %u} passed for "
					"Chip %u\n", spi_id_min,
					spi_id_max, i);
			panic();
		}

		/* SPI IDs overlap check */
		if (spi_id_max < GIC700_SPI_ID_MAX) {
			blocks_of_32 = BLOCKS_OF_32(spi_id_min, spi_id_max);
			if ((multichip_spi_blocks & blocks_of_32) != 0) {
				ERROR("SPI IDs of Chip %u overlapping\n", i);
				panic();
			}
			multichip_spi_blocks |= blocks_of_32;
		}

		/* ESPI IDs overlap check */
		if (spi_id_max > GIC700_ESPI_ID_MIN) {
			blocks_of_32 = BLOCKS_OF_32(spi_id_min - GIC700_ESPI_ID_MIN,
					spi_id_max - GIC700_ESPI_ID_MIN);
			if ((multichip_espi_blocks & blocks_of_32) != 0) {
				ERROR("SPI IDs of Chip %u overlapping\n", i);
				panic();
			}
			multichip_espi_blocks |= blocks_of_32;
		}
	}
}

/*******************************************************************************
 * Initialize GIC-600 and GIC-700 Multichip operation.
 ******************************************************************************/
void gic600_multichip_init(struct gic600_multichip_data *multichip_data)
{
	unsigned int i;
	uint32_t gicd_iidr_val = gicd_read_iidr(multichip_data->rt_owner_base);

	if ((gicd_iidr_val & IIDR_MODEL_MASK) == IIDR_MODEL_ARM_GIC_600) {
		gic600_multichip_validate_data(multichip_data);
	}

	if ((gicd_iidr_val & IIDR_MODEL_MASK) == IIDR_MODEL_ARM_GIC_700) {
		gic700_multichip_validate_data(multichip_data);
	}

	/*
	 * Ensure that G0/G1S/G1NS interrupts are disabled. This also ensures
	 * that GIC-600 Multichip configuration is done first.
	 */
	if ((gicd_read_ctlr(multichip_data->rt_owner_base) &
			(CTLR_ENABLE_G0_BIT | CTLR_ENABLE_G1S_BIT |
			 CTLR_ENABLE_G1NS_BIT | GICD_CTLR_RWP_BIT)) != 0) {
		ERROR("GICD_CTLR group interrupts are either enabled or have "
				"pending writes.\n");
		panic();
	}

	/* Ensure that the routing table owner is in disconnected state */
	if (((read_gicd_chipsr(multichip_data->rt_owner_base) &
		GICD_CHIPSR_RTS_MASK) >> GICD_CHIPSR_RTS_SHIFT) !=
			GICD_CHIPSR_RTS_STATE_DISCONNECTED) {
		ERROR("GIC-600 routing table owner is not in disconnected "
				"state to begin multichip configuration\n");
		panic();
	}

	/* Initialize the GICD which is marked as routing table owner first */
	set_gicd_dchipr_rt_owner(multichip_data->rt_owner_base,
			multichip_data->rt_owner);

	set_gicd_chipr_n(multichip_data->rt_owner_base, multichip_data->rt_owner,
			multichip_data->chip_addrs[multichip_data->rt_owner],
			multichip_data->
			spi_ids[multichip_data->rt_owner].spi_id_min,
			multichip_data->
			spi_ids[multichip_data->rt_owner].spi_id_max);

	for (i = 0; i < multichip_data->chip_count; i++) {
		if (i == multichip_data->rt_owner)
			continue;

		set_gicd_chipr_n(multichip_data->rt_owner_base, i,
				multichip_data->chip_addrs[i],
				multichip_data->spi_ids[i].spi_id_min,
				multichip_data->spi_ids[i].spi_id_max);
	}

	plat_gic_multichip_data = multichip_data;
}

/*******************************************************************************
 * Allow a way to query the status of the GIC600 multichip driver
 ******************************************************************************/
bool gic600_multichip_is_initialized(void)
{
	return (plat_gic_multichip_data != NULL);
}