summaryrefslogtreecommitdiff
path: root/lib/utils/irqchip/fdt_irqchip_plic.c
blob: 89b12d40c05b65d07add3907b4445fcb36d21ba8 (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
/*
 * SPDX-License-Identifier: BSD-2-Clause
 *
 * Copyright (c) 2020 Western Digital Corporation or its affiliates.
 *
 * Authors:
 *   Anup Patel <anup.patel@wdc.com>
 */

#include <libfdt.h>
#include <sbi/riscv_asm.h>
#include <sbi/riscv_io.h>
#include <sbi/sbi_error.h>
#include <sbi/sbi_hartmask.h>
#include <sbi_utils/fdt/fdt_helper.h>
#include <sbi_utils/irqchip/fdt_irqchip.h>
#include <sbi_utils/irqchip/plic.h>

#define PLIC_MAX_NR			16

static unsigned long plic_count = 0;
static struct plic_data plic[PLIC_MAX_NR];

static struct plic_data *plic_hartid2data[SBI_HARTMASK_MAX_BITS];
static int plic_hartid2context[SBI_HARTMASK_MAX_BITS][2];

void fdt_plic_priority_save(u8 *priority)
{
	struct plic_data *plic = plic_hartid2data[current_hartid()];

	plic_priority_save(plic, priority);
}

void fdt_plic_priority_restore(const u8 *priority)
{
	struct plic_data *plic = plic_hartid2data[current_hartid()];

	plic_priority_restore(plic, priority);
}

void fdt_plic_context_save(bool smode, u32 *enable, u32 *threshold)
{
	u32 hartid = current_hartid();

	plic_context_save(plic_hartid2data[hartid],
			  plic_hartid2context[hartid][smode],
			  enable, threshold);
}

void fdt_plic_context_restore(bool smode, const u32 *enable, u32 threshold)
{
	u32 hartid = current_hartid();

	plic_context_restore(plic_hartid2data[hartid],
			     plic_hartid2context[hartid][smode],
			     enable, threshold);
}

static int irqchip_plic_warm_init(void)
{
	u32 hartid = current_hartid();

	return plic_warm_irqchip_init(plic_hartid2data[hartid],
				      plic_hartid2context[hartid][0],
				      plic_hartid2context[hartid][1]);
}

static int irqchip_plic_update_hartid_table(void *fdt, int nodeoff,
					    struct plic_data *pd)
{
	const fdt32_t *val;
	u32 phandle, hwirq, hartid;
	int i, err, count, cpu_offset, cpu_intc_offset;

	val = fdt_getprop(fdt, nodeoff, "interrupts-extended", &count);
	if (!val || count < sizeof(fdt32_t))
		return SBI_EINVAL;
	count = count / sizeof(fdt32_t);

	for (i = 0; i < count; i += 2) {
		phandle = fdt32_to_cpu(val[i]);
		hwirq = fdt32_to_cpu(val[i + 1]);

		cpu_intc_offset = fdt_node_offset_by_phandle(fdt, phandle);
		if (cpu_intc_offset < 0)
			continue;

		cpu_offset = fdt_parent_offset(fdt, cpu_intc_offset);
		if (cpu_intc_offset < 0)
			continue;

		err = fdt_parse_hart_id(fdt, cpu_offset, &hartid);
		if (err)
			continue;

		if (SBI_HARTMASK_MAX_BITS <= hartid)
			continue;

		plic_hartid2data[hartid] = pd;
		switch (hwirq) {
		case IRQ_M_EXT:
			plic_hartid2context[hartid][0] = i / 2;
			break;
		case IRQ_S_EXT:
			plic_hartid2context[hartid][1] = i / 2;
			break;
		}
	}

	return 0;
}

static int irqchip_plic_cold_init(void *fdt, int nodeoff,
				  const struct fdt_match *match)
{
	int i, rc;
	struct plic_data *pd;

	if (PLIC_MAX_NR <= plic_count)
		return SBI_ENOSPC;
	pd = &plic[plic_count++];

	rc = fdt_parse_plic_node(fdt, nodeoff, pd);
	if (rc)
		return rc;

	if (match->data) {
		void (*plic_plat_init)(struct plic_data *) = match->data;
		plic_plat_init(pd);
	}

	rc = plic_cold_irqchip_init(pd);
	if (rc)
		return rc;

	if (plic_count == 1) {
		for (i = 0; i < SBI_HARTMASK_MAX_BITS; i++) {
			plic_hartid2data[i] = NULL;
			plic_hartid2context[i][0] = -1;
			plic_hartid2context[i][1] = -1;
		}
	}

	return irqchip_plic_update_hartid_table(fdt, nodeoff, pd);
}

#define THEAD_PLIC_CTRL_REG 0x1ffffc

static void thead_plic_plat_init(struct plic_data *pd)
{
	writel_relaxed(BIT(0), (char *)pd->addr + THEAD_PLIC_CTRL_REG);
}

static const struct fdt_match irqchip_plic_match[] = {
	{ .compatible = "riscv,plic0" },
	{ .compatible = "sifive,plic-1.0.0" },
	{ .compatible = "thead,c900-plic",
	  .data = thead_plic_plat_init },
	{ },
};

struct fdt_irqchip fdt_irqchip_plic = {
	.match_table = irqchip_plic_match,
	.cold_init = irqchip_plic_cold_init,
	.warm_init = irqchip_plic_warm_init,
	.exit = NULL,
};