1 /*
2  * Copyright (c) 2014 Wind River Systems, Inc.
3  * Copyright (c) 2020 Synopsys.
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 /**
9  * @file
10  * @brief ARCv2 Interrupt Unit device driver
11  *
12  * The ARCv2 interrupt unit has 16 allocated exceptions associated with
13  * vectors 0 to 15 and 240 interrupts associated with vectors 16 to 255.
14  * The interrupt unit is optional in the ARCv2-based processors. When
15  * building a processor, you can configure the processor to include an
16  * interrupt unit. The ARCv2 interrupt unit is highly programmable.
17  */
18 
19 #include <kernel.h>
20 #include <arch/cpu.h>
21 #include <device.h>
22 #include <init.h>
23 
24 extern void *_VectorTable;
25 
26 #ifdef CONFIG_PM_DEVICE
27 #include <pm/device.h>
28 #include <kernel_structs.h>
29 
30 #ifdef CONFIG_ARC_SECURE_FIRMWARE
31 #undef _ARC_V2_IRQ_VECT_BASE
32 #define _ARC_V2_IRQ_VECT_BASE _ARC_V2_IRQ_VECT_BASE_S
33 #endif
34 
35 struct arc_v2_irq_unit_ctx {
36 	uint32_t irq_ctrl; /* Interrupt Context Saving Control Register. */
37 	uint32_t irq_vect_base; /* Interrupt Vector Base. */
38 
39 	/*
40 	 * IRQ configuration:
41 	 * - IRQ Priority:BIT(6):BIT(2)
42 	 * - IRQ Trigger:BIT(1)
43 	 * - IRQ Enable:BIT(0)
44 	 */
45 	uint8_t irq_config[CONFIG_NUM_IRQS - 16];
46 };
47 static struct arc_v2_irq_unit_ctx ctx;
48 #endif
49 
50 /*
51  * @brief Initialize the interrupt unit device driver
52  *
53  * Initializes the interrupt unit device driver and the device
54  * itself.
55  *
56  * Interrupts are still locked at this point, so there is no need to protect
57  * the window between a write to IRQ_SELECT and subsequent writes to the
58  * selected IRQ's registers.
59  *
60  * @return 0 for success
61  */
arc_v2_irq_unit_init(const struct device * unused)62 static int arc_v2_irq_unit_init(const struct device *unused)
63 {
64 	ARG_UNUSED(unused);
65 	int irq; /* the interrupt index */
66 
67 	/* Interrupts from 0 to 15 are exceptions and they are ignored
68 	 * by IRQ auxiliary registers. For that reason we skip those
69 	 * values in this loop.
70 	 */
71 	for (irq = 16; irq < CONFIG_NUM_IRQS; irq++) {
72 
73 		z_arc_v2_aux_reg_write(_ARC_V2_IRQ_SELECT, irq);
74 #ifdef CONFIG_ARC_SECURE_FIRMWARE
75 		z_arc_v2_aux_reg_write(_ARC_V2_IRQ_PRIORITY,
76 			 (CONFIG_NUM_IRQ_PRIO_LEVELS-1) |
77 			 _ARC_V2_IRQ_PRIORITY_SECURE); /* lowest priority */
78 #else
79 		z_arc_v2_aux_reg_write(_ARC_V2_IRQ_PRIORITY,
80 			 (CONFIG_NUM_IRQ_PRIO_LEVELS-1)); /* lowest priority */
81 #endif
82 		z_arc_v2_aux_reg_write(_ARC_V2_IRQ_ENABLE, _ARC_V2_INT_DISABLE);
83 		z_arc_v2_aux_reg_write(_ARC_V2_IRQ_TRIGGER, _ARC_V2_INT_LEVEL);
84 	}
85 
86 	return 0;
87 }
88 
89 #ifdef CONFIG_PM_DEVICE
90 
91 /*
92  * @brief Suspend the interrupt unit device driver
93  *
94  * Suspends the interrupt unit device driver and the device
95  * itself.
96  *
97  * @return 0 for success
98  */
arc_v2_irq_unit_suspend(const struct device * dev)99 static int arc_v2_irq_unit_suspend(const struct device *dev)
100 {
101 	uint8_t irq;
102 
103 	ARG_UNUSED(dev);
104 
105 	/* Interrupts from 0 to 15 are exceptions and they are ignored
106 	 * by IRQ auxiliary registers. For that reason we skip those
107 	 * values in this loop.
108 	 */
109 	for (irq = 16U; irq < CONFIG_NUM_IRQS; irq++) {
110 		z_arc_v2_aux_reg_write(_ARC_V2_IRQ_SELECT, irq);
111 		ctx.irq_config[irq - 16] =
112 			z_arc_v2_aux_reg_read(_ARC_V2_IRQ_PRIORITY) << 2;
113 		ctx.irq_config[irq - 16] |=
114 			z_arc_v2_aux_reg_read(_ARC_V2_IRQ_TRIGGER) << 1;
115 		ctx.irq_config[irq - 16] |=
116 			z_arc_v2_aux_reg_read(_ARC_V2_IRQ_ENABLE);
117 	}
118 
119 	ctx.irq_ctrl = z_arc_v2_aux_reg_read(_ARC_V2_AUX_IRQ_CTRL);
120 	ctx.irq_vect_base = z_arc_v2_aux_reg_read(_ARC_V2_IRQ_VECT_BASE);
121 
122 	return 0;
123 }
124 
125 /*
126  * @brief Resume the interrupt unit device driver
127  *
128  * Resume the interrupt unit device driver and the device
129  * itself.
130  *
131  * @return 0 for success
132  */
arc_v2_irq_unit_resume(const struct device * dev)133 static int arc_v2_irq_unit_resume(const struct device *dev)
134 {
135 	uint8_t irq;
136 
137 	ARG_UNUSED(dev);
138 
139 	/* Interrupts from 0 to 15 are exceptions and they are ignored
140 	 * by IRQ auxiliary registers. For that reason we skip those
141 	 * values in this loop.
142 	 */
143 	for (irq = 16U; irq < CONFIG_NUM_IRQS; irq++) {
144 		z_arc_v2_aux_reg_write(_ARC_V2_IRQ_SELECT, irq);
145 #ifdef CONFIG_ARC_SECURE_FIRMWARE
146 		z_arc_v2_aux_reg_write(_ARC_V2_IRQ_PRIORITY,
147 				ctx.irq_config[irq - 16] >> 2 |
148 				_ARC_V2_IRQ_PRIORITY_SECURE);
149 #else
150 		z_arc_v2_aux_reg_write(_ARC_V2_IRQ_PRIORITY,
151 				ctx.irq_config[irq - 16] >> 2);
152 #endif
153 		z_arc_v2_aux_reg_write(_ARC_V2_IRQ_TRIGGER,
154 				(ctx.irq_config[irq - 16] >> 1) & BIT(0));
155 		z_arc_v2_aux_reg_write(_ARC_V2_IRQ_ENABLE,
156 				ctx.irq_config[irq - 16] & BIT(0));
157 	}
158 
159 #ifdef CONFIG_ARC_NORMAL_FIRMWARE
160 	/* \todo use sjli instruction to access irq_ctrl */
161 #else
162 	z_arc_v2_aux_reg_write(_ARC_V2_AUX_IRQ_CTRL, ctx.irq_ctrl);
163 #endif
164 	z_arc_v2_aux_reg_write(_ARC_V2_IRQ_VECT_BASE, ctx.irq_vect_base);
165 
166 	return 0;
167 }
168 
169 /*
170  * @brief  Implement the driver control of interrupt unit
171  *
172  * The operation on interrupt unit requires interrupt lock.
173  * The *context may include IN data or/and OUT data
174  *
175  * @return operation result
176  */
arc_v2_irq_unit_device_ctrl(const struct device * dev,enum pm_device_action action)177 static int arc_v2_irq_unit_device_ctrl(const struct device *dev,
178 				       enum pm_device_action action)
179 {
180 	int ret = 0;
181 	unsigned int key = arch_irq_lock();
182 
183 	switch (action) {
184 	case PM_DEVICE_ACTION_SUSPEND:
185 		ret = arc_v2_irq_unit_suspend(dev);
186 		break;
187 	case PM_DEVICE_ACTION_RESUME:
188 		ret = arc_v2_irq_unit_resume(dev);
189 		break;
190 	default:
191 		ret = -ENOTSUP;
192 		break;
193 	}
194 
195 	arch_irq_unlock(key);
196 
197 	return ret;
198 }
199 
200 SYS_DEVICE_DEFINE("arc_v2_irq_unit", arc_v2_irq_unit_init,
201 		  arc_v2_irq_unit_device_ctrl, PRE_KERNEL_1, 0);
202 #else
203 SYS_INIT(arc_v2_irq_unit_init, PRE_KERNEL_1, 0);
204 #endif   /* CONFIG_PM_DEVICE */
205