Skip to content

Commit 58d8afb

Browse files
Marti Bolivarnashif
authored andcommitted
interrupt_controller: RV32M1: add intmux driver / DT bindings
Add a level 2 interrupt controller for the RV32M1 SoC. This uses the INTMUX peripheral. As a first customer, convert the timer driver over to using this, adding nodes for the LPTMR peripherals. This lets users select the timer instance they want to use, and what intmux channel they want to route its interrupt to, using DT overlays. Signed-off-by: Marti Bolivar <[email protected]> Signed-off-by: Mike Scott <[email protected]>
1 parent 521f477 commit 58d8afb

File tree

14 files changed

+758
-54
lines changed

14 files changed

+758
-54
lines changed

drivers/interrupt_controller/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@ zephyr_sources_ifdef(CONFIG_SHARED_IRQ shared_irq.c)
99
zephyr_sources_ifdef(CONFIG_SOC_FAMILY_STM32 exti_stm32.c)
1010
zephyr_sources_ifdef(CONFIG_CAVS_ICTL cavs_ictl.c)
1111
zephyr_sources_ifdef(CONFIG_DW_ICTL dw_ictl.c)
12+
zephyr_sources_ifdef(CONFIG_RV32M1_INTMUX rv32m1_intmux.c)

drivers/interrupt_controller/Kconfig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,4 +162,6 @@ source "drivers/interrupt_controller/Kconfig.multilevel"
162162

163163
source "drivers/interrupt_controller/Kconfig.s1000"
164164

165+
source "drivers/interrupt_controller/Kconfig.rv32m1"
166+
165167
endmenu
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# Kconfig - RV32M1 INTMUX config
2+
#
3+
# Copyright (c) 2018 Foundries.io
4+
#
5+
# SPDX-License-Identifier: Apache-2.0
6+
7+
config RV32M1_INTMUX
8+
bool "OpenISA RV32M1 INTMUX interrupt controller support"
9+
depends on SOC_OPENISA_RV32M1_RISCV32 && MULTI_LEVEL_INTERRUPTS
10+
help
11+
Select this option to enable support for the RV32M1 INTMUX
12+
driver. This provides a level 2 interrupt controller for the SoC.
13+
The INTMUX peripheral combines level 2 interrupts into
14+
eight channels; each channel has its own level 1 interrupt to
15+
the core.
16+
17+
if RV32M1_INTMUX
18+
19+
config RV32M1_INTMUX_INIT_PRIORITY
20+
int "INTMUX driver initialization priority"
21+
default 60
22+
help
23+
Boot time initialization priority for INTMUX driver.
24+
Don't change the default unless you know what you are doing.
25+
26+
config RV32M1_INTMUX_CHANNEL_0
27+
bool "INTMUX channel 0"
28+
help
29+
Enable support for INTMUX channel 0.
30+
31+
config RV32M1_INTMUX_CHANNEL_1
32+
bool "INTMUX channel 1"
33+
help
34+
Enable support for INTMUX channel 1.
35+
36+
config RV32M1_INTMUX_CHANNEL_2
37+
bool "INTMUX channel 2"
38+
help
39+
Enable support for INTMUX channel 2.
40+
41+
config RV32M1_INTMUX_CHANNEL_3
42+
bool "INTMUX channel 3"
43+
help
44+
Enable support for INTMUX channel 3.
45+
46+
config RV32M1_INTMUX_CHANNEL_4
47+
bool "INTMUX channel 4"
48+
help
49+
Enable support for INTMUX channel 4.
50+
51+
config RV32M1_INTMUX_CHANNEL_5
52+
bool "INTMUX channel 5"
53+
help
54+
Enable support for INTMUX channel 5.
55+
56+
config RV32M1_INTMUX_CHANNEL_6
57+
bool "INTMUX channel 6"
58+
help
59+
Enable support for INTMUX channel 6.
60+
61+
config RV32M1_INTMUX_CHANNEL_7
62+
bool "INTMUX channel 7"
63+
help
64+
Enable support for INTMUX channel 7.
65+
66+
endif # RV32M1_INTMUX
Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
/*
2+
* Copyright (c) 2018 Foundries.io
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
/**
8+
* @file
9+
* @brief RV32M1 INTMUX (interrupt multiplexer) driver
10+
*
11+
* This driver provides support for level 2 interrupts on the RV32M1
12+
* SoC using the INTMUX peripheral.
13+
*
14+
* Each of the RI5CY and ZERO-RISCY cores has an INTMUX peripheral;
15+
* INTMUX0 is wired to the RI5CY event unit interrupt table, while
16+
* INTMUX1 is used with ZERO-RISCY.
17+
*
18+
* For this reason, only a single intmux device is declared here. The
19+
* dtsi for each core needs to set up the intmux device and any
20+
* associated IRQ numbers to work with this driver.
21+
*/
22+
23+
#include <kernel.h>
24+
#include <clock_control.h>
25+
#include <init.h>
26+
#include <irq.h>
27+
#include <irq_nextlevel.h>
28+
#include <sw_isr_table.h>
29+
#include <soc.h>
30+
#include <dt-bindings/interrupt-controller/openisa-intmux.h>
31+
32+
/*
33+
* CHn_VEC registers are offset by a value that is convenient if
34+
* you're dealing with a Cortex-M NVIC vector table; we're not, so it
35+
* needs to be subtracted out to get a useful value.
36+
*/
37+
#define VECN_OFFSET 48U
38+
39+
struct rv32m1_intmux_config {
40+
INTMUX_Type *regs;
41+
char *clock_name;
42+
clock_control_subsys_t clock_subsys;
43+
struct _isr_table_entry *isr_base;
44+
};
45+
46+
#define DEV_CFG(dev) \
47+
((struct rv32m1_intmux_config *)(dev->config->config_info))
48+
49+
#define DEV_REGS(dev) (DEV_CFG(dev)->regs)
50+
51+
DEVICE_DECLARE(intmux);
52+
53+
/*
54+
* <irq_nextlevel.h> API
55+
*/
56+
57+
static void rv32m1_intmux_irq_enable(struct device *dev, u32_t irq)
58+
{
59+
INTMUX_Type *regs = DEV_REGS(dev);
60+
u32_t channel = rv32m1_intmux_channel(irq);
61+
u32_t line = rv32m1_intmux_line(irq);
62+
63+
regs->CHANNEL[channel].CHn_IER_31_0 |= BIT(line);
64+
}
65+
66+
static void rv32m1_intmux_irq_disable(struct device *dev, u32_t irq)
67+
{
68+
INTMUX_Type *regs = DEV_REGS(dev);
69+
u32_t channel = rv32m1_intmux_channel(irq);
70+
u32_t line = rv32m1_intmux_line(irq);
71+
72+
regs->CHANNEL[channel].CHn_IER_31_0 &= ~BIT(line);
73+
}
74+
75+
static u32_t rv32m1_intmux_get_state(struct device *dev)
76+
{
77+
INTMUX_Type *regs = DEV_REGS(dev);
78+
size_t i;
79+
80+
for (i = 0; i < INTMUX_CHn_IER_31_0_COUNT; i++) {
81+
if (regs->CHANNEL[i].CHn_IER_31_0) {
82+
return 1;
83+
}
84+
}
85+
86+
return 0;
87+
}
88+
89+
/*
90+
* IRQ handling.
91+
*/
92+
93+
#define ISR_ENTRY(channel, line) \
94+
((channel) * CONFIG_MAX_IRQ_PER_AGGREGATOR + line)
95+
96+
static void rv32m1_intmux_isr(void *arg)
97+
{
98+
struct device *dev = DEVICE_GET(intmux);
99+
INTMUX_Type *regs = DEV_REGS(dev);
100+
u32_t channel = POINTER_TO_UINT(arg);
101+
u32_t line = (regs->CHANNEL[channel].CHn_VEC >> 2) - VECN_OFFSET;
102+
struct _isr_table_entry *isr_base = DEV_CFG(dev)->isr_base;
103+
struct _isr_table_entry *entry = &isr_base[ISR_ENTRY(channel, line)];
104+
105+
entry->isr(entry->arg);
106+
}
107+
108+
/*
109+
* Instance and initialization
110+
*/
111+
112+
static const struct irq_next_level_api rv32m1_intmux_apis = {
113+
.intr_enable = rv32m1_intmux_irq_enable,
114+
.intr_disable = rv32m1_intmux_irq_disable,
115+
.intr_get_state = rv32m1_intmux_get_state,
116+
};
117+
118+
static const struct rv32m1_intmux_config rv32m1_intmux_cfg = {
119+
.regs = (INTMUX_Type *)INTMUX_BASE_ADDRESS,
120+
.clock_name = INTMUX_CLOCK_CONTROLLER,
121+
.clock_subsys = UINT_TO_POINTER(INTMUX_CLOCK_NAME),
122+
.isr_base = &_sw_isr_table[CONFIG_2ND_LVL_ISR_TBL_OFFSET],
123+
};
124+
125+
static int rv32m1_intmux_init(struct device *dev)
126+
{
127+
const struct rv32m1_intmux_config *config = DEV_CFG(dev);
128+
INTMUX_Type *regs = DEV_REGS(dev);
129+
struct device *clock_dev = device_get_binding(config->clock_name);
130+
size_t i;
131+
132+
if (!clock_dev) {
133+
return -ENODEV;
134+
}
135+
136+
/* Enable INTMUX clock. */
137+
clock_control_on(clock_dev, config->clock_subsys);
138+
139+
/*
140+
* Reset all channels, not just the ones we're configured to
141+
* support. We don't want to continue to take level 2 IRQs
142+
* enabled by bootloaders, for example.
143+
*/
144+
for (i = 0; i < INTMUX_CHn_CSR_COUNT; i++) {
145+
regs->CHANNEL[i].CHn_CSR |= INTMUX_CHn_CSR_RST_MASK;
146+
}
147+
148+
/* Connect and enable level 1 (channel) interrupts. */
149+
#ifdef CONFIG_RV32M1_INTMUX_CHANNEL_0
150+
IRQ_CONNECT(INTMUX_CH0_IRQ, 0, rv32m1_intmux_isr,
151+
UINT_TO_POINTER(0), 0);
152+
irq_enable(INTMUX_CH0_IRQ);
153+
#endif
154+
#ifdef CONFIG_RV32M1_INTMUX_CHANNEL_1
155+
IRQ_CONNECT(INTMUX_CH1_IRQ, 0, rv32m1_intmux_isr,
156+
UINT_TO_POINTER(1), 0);
157+
irq_enable(INTMUX_CH1_IRQ);
158+
#endif
159+
#ifdef CONFIG_RV32M1_INTMUX_CHANNEL_2
160+
IRQ_CONNECT(INTMUX_CH2_IRQ, 0, rv32m1_intmux_isr,
161+
UINT_TO_POINTER(2), 0);
162+
irq_enable(INTMUX_CH2_IRQ);
163+
#endif
164+
#ifdef CONFIG_RV32M1_INTMUX_CHANNEL_3
165+
IRQ_CONNECT(INTMUX_CH3_IRQ, 0, rv32m1_intmux_isr,
166+
UINT_TO_POINTER(3), 0);
167+
irq_enable(INTMUX_CH3_IRQ);
168+
#endif
169+
#ifdef CONFIG_RV32M1_INTMUX_CHANNEL_4
170+
IRQ_CONNECT(INTMUX_CH4_IRQ, 0, rv32m1_intmux_isr,
171+
UINT_TO_POINTER(4), 0);
172+
irq_enable(INTMUX_CH4_IRQ);
173+
#endif
174+
#ifdef CONFIG_RV32M1_INTMUX_CHANNEL_5
175+
IRQ_CONNECT(INTMUX_CH5_IRQ, 0, rv32m1_intmux_isr,
176+
UINT_TO_POINTER(5), 0);
177+
irq_enable(INTMUX_CH5_IRQ);
178+
#endif
179+
#ifdef CONFIG_RV32M1_INTMUX_CHANNEL_6
180+
IRQ_CONNECT(INTMUX_CH6_IRQ, 0, rv32m1_intmux_isr,
181+
UINT_TO_POINTER(6), 0);
182+
irq_enable(INTMUX_CH6_IRQ);
183+
#endif
184+
#ifdef CONFIG_RV32M1_INTMUX_CHANNEL_7
185+
IRQ_CONNECT(INTMUX_CH7_IRQ, 0, rv32m1_intmux_isr,
186+
UINT_TO_POINTER(7), 0);
187+
irq_enable(INTMUX_CH7_IRQ);
188+
#endif
189+
190+
return 0;
191+
}
192+
193+
DEVICE_AND_API_INIT(intmux, INTMUX_LABEL, &rv32m1_intmux_init, NULL,
194+
&rv32m1_intmux_cfg, PRE_KERNEL_1,
195+
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &rv32m1_intmux_apis);

drivers/timer/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ config RV32M1_LPTMR_TIMER
139139
default y
140140
depends on SOC_OPENISA_RV32M1_RISCV32
141141
depends on !TICKLESS_IDLE
142+
depends on RV32M1_INTMUX
142143
help
143144
This module implements a kernel device driver for using the LPTMR
144145
peripheral as the system clock. It provides the standard "system clock

0 commit comments

Comments
 (0)