summaryrefslogtreecommitdiff
path: root/plat/marvell/common/marvell_gicv2.c
blob: 5251854664ad130317876cbcd39e0c257ed70329 (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
/*
 * Copyright (C) 2018 Marvell International Ltd.
 *
 * SPDX-License-Identifier:     BSD-3-Clause
 * https://spdx.org/licenses
 */

#include <bakery_lock.h>
#include <debug.h>
#include <gicv2.h>
#include <interrupt_mgmt.h>
#include <mmio.h>
#include <plat_marvell.h>
#include <platform.h>
#include <platform_def.h>

/*
 * The following functions are defined as weak to allow a platform to override
 * the way the GICv2 driver is initialised and used.
 */
#pragma weak plat_marvell_gic_driver_init
#pragma weak plat_marvell_gic_init

#define A7K8K_PIC_CAUSE_REG		0xf03f0100
#define A7K8K_PIC0_MASK_REG		0xf03f0108

#define A7K8K_PIC_PMUOF_IRQ_MASK	(1 << 17)

#define A7K8K_PIC_MAX_IRQS		32
#define A7K8K_PIC_MAX_IRQ_MASK		((1UL << A7K8K_PIC_MAX_IRQS) - 1)

#define A7K8K_ODMIN_SET_REG		0xf0300040
#define A7K8K_ODMI_PMU_IRQ(idx)		((2 + idx) << 12)

#define A7K8K_ODMI_PMU_GIC_IRQ(idx)	(130 + idx)

static DEFINE_BAKERY_LOCK(a7k8k_irq_lock);

/*
 * On a GICv2 system, the Group 1 secure interrupts are treated as Group 0
 * interrupts.
 */
static const interrupt_prop_t marvell_interrupt_props[] = {
	PLAT_MARVELL_G1S_IRQ_PROPS(GICV2_INTR_GROUP0),
	PLAT_MARVELL_G0_IRQ_PROPS(GICV2_INTR_GROUP0)
};

static unsigned int target_mask_array[PLATFORM_CORE_COUNT];

/*
 * Ideally `marvell_gic_data` structure definition should be a `const` but it is
 * kept as modifiable for overwriting with different GICD and GICC base when
 * running on FVP with VE memory map.
 */
static gicv2_driver_data_t marvell_gic_data = {
	.gicd_base = PLAT_MARVELL_GICD_BASE,
	.gicc_base = PLAT_MARVELL_GICC_BASE,
	.interrupt_props = marvell_interrupt_props,
	.interrupt_props_num = ARRAY_SIZE(marvell_interrupt_props),
	.target_masks = target_mask_array,
	.target_masks_num = ARRAY_SIZE(target_mask_array),
};

/*
 * ARM common helper to initialize the GICv2 only driver.
 */
void plat_marvell_gic_driver_init(void)
{
	gicv2_driver_init(&marvell_gic_data);
}

static uint64_t a7k8k_pmu_interrupt_handler(uint32_t id,
					  uint32_t flags,
					  void *handle,
					  void *cookie)
{
	unsigned int idx = plat_my_core_pos();
	uint32_t irq;

	bakery_lock_get(&a7k8k_irq_lock);

	/* Acknowledge IRQ */
	irq = plat_ic_acknowledge_interrupt();

	plat_ic_end_of_interrupt(irq);

	if (irq != MARVELL_IRQ_PIC0) {
		bakery_lock_release(&a7k8k_irq_lock);
		return 0;
	}

	/* Acknowledge PMU overflow IRQ in PIC0 */
	mmio_setbits_32(A7K8K_PIC_CAUSE_REG, A7K8K_PIC_PMUOF_IRQ_MASK);

	/* Trigger ODMI Frame IRQ as edge triggered */
	gicv2_interrupt_set_edge_triggered(A7K8K_ODMI_PMU_GIC_IRQ(idx));
	mmio_write_32(A7K8K_ODMIN_SET_REG, A7K8K_ODMI_PMU_IRQ(idx));

	bakery_lock_release(&a7k8k_irq_lock);

	return 0;
}

void mvebu_pmu_interrupt_enable(void)
{
	uint32_t flags;
	int32_t rc;

	/* Reset PIC */
	mmio_write_32(A7K8K_PIC_CAUSE_REG, A7K8K_PIC_MAX_IRQ_MASK);
	/* Unmask PMU overflow IRQ in PIC0 */
	mmio_clrbits_32(A7K8K_PIC0_MASK_REG, A7K8K_PIC_PMUOF_IRQ_MASK);

	/*
	 * Register IRQ handler as INTR_TYPE_S_EL1 as its the only valid type
	 * for GICv2 in ARM-TF.
	 */
	flags = 0U;
	set_interrupt_rm_flag((flags), (NON_SECURE));
	rc = register_interrupt_type_handler(INTR_TYPE_S_EL1,
					     a7k8k_pmu_interrupt_handler,
					     flags);
	if (rc != 0)
		panic();
}

void mvebu_pmu_interrupt_disable(void)
{
	/* Reset PIC */
	mmio_write_32(A7K8K_PIC_CAUSE_REG, A7K8K_PIC_MAX_IRQ_MASK);
	/* Mask PMU overflow IRQ in PIC0 */
	mmio_setbits_32(A7K8K_PIC0_MASK_REG, A7K8K_PIC_PMUOF_IRQ_MASK);
}

void plat_marvell_gic_init(void)
{
	gicv2_distif_init();
	gicv2_pcpu_distif_init();
	gicv2_set_pe_target_mask(plat_my_core_pos());
	gicv2_cpuif_enable();
}