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
|
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2022, Liuw <lw312886@alibaba-inc.com>
*
* U-Boot syscon driver for Thead's Platform Level Interrupt Controller (PLIC)
*/
#include <common.h>
#include <dm.h>
#include <dm/device-internal.h>
#include <dm/lists.h>
#include <dm/uclass-internal.h>
#include <regmap.h>
#include <syscon.h>
#include <asm/csr.h>
#include <asm/io.h>
#include <asm/syscon.h>
#include <asm/global_data.h>
#include <cpu.h>
#include <linux/err.h>
#include <asm/arch-thead/light-plic.h>
DECLARE_GLOBAL_DATA_PTR;
#define PLIC_BASE_GET(void) \
do { \
long *ret; \
\
if (!gd->arch.plic) { \
ret = syscon_get_first_range(RISCV_SYSCON_PLIC); \
gd->arch.plic = ret; \
} \
} while (0)
irq_handler_t irq_table[MAX_IRQ_NUM];
void __iomem *plic_base = NULL;
void external_interrupt(struct pt_regs *regs)
{
void __iomem *claim;
irq_handler_t handler;
u32 irq_num;
debug("[%s,%d]\n", __func__, __LINE__);
if (!plic_base)
return;
debug("[%s,%d]\n", __func__, __LINE__);
claim = PLIC_CLAIM_REG(plic_base, 0);
while ((irq_num = readl(claim))) {
if (irq_num >= MAX_IRQ_NUM)
debug("Cannot find irq:%d\n", irq_num);
else {
handler = irq_table[irq_num];
if (handler)
handler();
writel(irq_num, claim);
}
}
debug("[%s,%d]\n", __func__, __LINE__);
}
static void plic_toggle(void __iomem *enable_base, int hwirq, int enable)
{
u32 __iomem *reg = enable_base + (hwirq / 32) * sizeof(u32);
u32 hwirq_mask = 1 << (hwirq % 32);
if (enable)
writel(readl(reg) | hwirq_mask, reg);
else
writel(readl(reg) & ~hwirq_mask, reg);
debug("[%s,%d][0x%lx] = 0x%x\n", __func__, __LINE__,
(unsigned long)reg, readl(reg));
}
static void plic_set_threshold(void __iomem *thre_base, u32 threshold)
{
writel(threshold, thre_base);
debug("[%s,%d][0x%lx] = 0x%x\n", __func__, __LINE__,
(unsigned long)thre_base, readl(thre_base));
}
static void plic_set_irq_priority(void __iomem *prio_base, int prio)
{
writel(prio, prio_base);
}
int irq_handler_register(int irq, irq_handler_t handler)
{
if (irq < 0 || irq >= MAX_IRQ_NUM) {
debug("invalid irq number to register\n");
return -EINVAL;
}
irq_table[irq] = handler;
return 0;
}
void arch_local_irq_enable(void)
{
csr_set(CSR_MIE, MIE_MEIE);
csr_set(CSR_MSTATUS, SR_MIE);
}
void arch_local_irq_disable(void)
{
csr_clear(CSR_MIE, MIE_MEIE);
csr_clear(CSR_MSTATUS, SR_MIE);
}
void irq_priority_set(int irq_id)
{
plic_set_irq_priority(PLIC_PRIO_REG(gd->arch.plic, irq_id), 4);
}
void irq_enable(int hwirq)
{
plic_toggle(PLIC_ENABLE_REG(gd->arch.plic, 0), hwirq, 1);
}
void irq_disable(int hwirq)
{
plic_toggle(PLIC_ENABLE_REG(gd->arch.plic, 0), hwirq, 0);
}
int plic_init()
{
PLIC_BASE_GET();
if (IS_ERR(gd->arch.plic))
return PTR_ERR(gd->arch.plic);
plic_base = gd->arch.plic;
debug("THEAD PLIC BASE: 0x%lx\n", (unsigned long)gd->arch.plic);
plic_set_threshold(PLIC_THRESHOLD_REG(gd->arch.plic, 0), 0);
arch_local_irq_enable(); //enale the global interrupt
return 0;
}
static const struct udevice_id thead_plic_ids[] = {
{ .compatible = "riscv,plic0", .data = RISCV_SYSCON_PLIC},
{ }
};
U_BOOT_DRIVER(thead_plic) = {
.name = "thead_light_plic",
.id = UCLASS_SYSCON,
.of_match = thead_plic_ids,
};
|