2015-04-10 16:44:37 -07:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2014 Wind River Systems, Inc.
|
2020-04-06 11:56:08 +08:00
|
|
|
* Copyright (c) 2020 Synopsys.
|
2015-04-10 16:44:37 -07:00
|
|
|
*
|
2017-01-18 17:01:01 -08:00
|
|
|
* SPDX-License-Identifier: Apache-2.0
|
2015-04-10 16:44:37 -07:00
|
|
|
*/
|
|
|
|
|
2015-12-04 10:09:39 -05:00
|
|
|
/**
|
|
|
|
* @file
|
|
|
|
* @brief ARCv2 Interrupt Unit device driver
|
|
|
|
*
|
2015-04-10 16:44:37 -07:00
|
|
|
* The ARCv2 interrupt unit has 16 allocated exceptions associated with
|
|
|
|
* vectors 0 to 15 and 240 interrupts associated with vectors 16 to 255.
|
|
|
|
* The interrupt unit is optional in the ARCv2-based processors. When
|
|
|
|
* building a processor, you can configure the processor to include an
|
|
|
|
* interrupt unit. The ARCv2 interrupt unit is highly programmable.
|
|
|
|
*/
|
|
|
|
|
2016-12-04 14:59:37 -06:00
|
|
|
#include <kernel.h>
|
2015-05-28 10:56:47 -07:00
|
|
|
#include <arch/cpu.h>
|
2016-11-03 10:45:33 +00:00
|
|
|
#include <device.h>
|
2016-11-15 15:19:00 +00:00
|
|
|
#include <init.h>
|
2016-12-04 14:59:37 -06:00
|
|
|
|
2015-10-14 16:04:48 -07:00
|
|
|
extern void *_VectorTable;
|
2015-04-10 16:44:37 -07:00
|
|
|
|
2020-09-01 18:31:40 -04:00
|
|
|
#ifdef CONFIG_PM_DEVICE
|
2021-05-03 17:26:38 +02:00
|
|
|
#include <pm/device.h>
|
2016-11-03 10:45:33 +00:00
|
|
|
#include <kernel_structs.h>
|
|
|
|
|
2019-08-01 12:39:35 +08:00
|
|
|
#ifdef CONFIG_ARC_SECURE_FIRMWARE
|
2017-11-29 17:24:09 +08:00
|
|
|
#undef _ARC_V2_IRQ_VECT_BASE
|
|
|
|
#define _ARC_V2_IRQ_VECT_BASE _ARC_V2_IRQ_VECT_BASE_S
|
|
|
|
#endif
|
|
|
|
|
2021-05-07 14:18:57 -07:00
|
|
|
static uint32_t _arc_v2_irq_unit_device_power_state = PM_DEVICE_STATE_ACTIVE;
|
2016-11-03 10:45:33 +00:00
|
|
|
struct arc_v2_irq_unit_ctx {
|
2020-05-27 11:26:57 -05:00
|
|
|
uint32_t irq_ctrl; /* Interrupt Context Saving Control Register. */
|
|
|
|
uint32_t irq_vect_base; /* Interrupt Vector Base. */
|
2016-11-03 10:45:33 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* IRQ configuration:
|
|
|
|
* - IRQ Priority:BIT(6):BIT(2)
|
|
|
|
* - IRQ Trigger:BIT(1)
|
|
|
|
* - IRQ Enable:BIT(0)
|
|
|
|
*/
|
2020-05-27 11:26:57 -05:00
|
|
|
uint8_t irq_config[CONFIG_NUM_IRQS - 16];
|
2016-11-03 10:45:33 +00:00
|
|
|
};
|
|
|
|
static struct arc_v2_irq_unit_ctx ctx;
|
|
|
|
#endif
|
|
|
|
|
2016-11-15 15:19:00 +00:00
|
|
|
/*
|
|
|
|
* @brief Initialize the interrupt unit device driver
|
|
|
|
*
|
|
|
|
* Initializes the interrupt unit device driver and the device
|
|
|
|
* itself.
|
|
|
|
*
|
|
|
|
* Interrupts are still locked at this point, so there is no need to protect
|
|
|
|
* the window between a write to IRQ_SELECT and subsequent writes to the
|
|
|
|
* selected IRQ's registers.
|
|
|
|
*
|
2020-04-06 11:56:08 +08:00
|
|
|
* @return 0 for success
|
2016-11-15 15:19:00 +00:00
|
|
|
*/
|
2020-04-30 20:33:38 +02:00
|
|
|
static int arc_v2_irq_unit_init(const struct device *unused)
|
2015-04-10 16:44:37 -07:00
|
|
|
{
|
2016-11-15 15:19:00 +00:00
|
|
|
ARG_UNUSED(unused);
|
2015-04-10 16:44:37 -07:00
|
|
|
int irq; /* the interrupt index */
|
|
|
|
|
2016-11-03 10:45:33 +00:00
|
|
|
/* Interrupts from 0 to 15 are exceptions and they are ignored
|
|
|
|
* by IRQ auxiliary registers. For that reason we skip those
|
|
|
|
* values in this loop.
|
|
|
|
*/
|
2016-04-26 15:41:13 -07:00
|
|
|
for (irq = 16; irq < CONFIG_NUM_IRQS; irq++) {
|
2019-08-01 12:39:35 +08:00
|
|
|
|
2019-03-08 14:19:05 -07:00
|
|
|
z_arc_v2_aux_reg_write(_ARC_V2_IRQ_SELECT, irq);
|
2019-08-01 12:39:35 +08:00
|
|
|
#ifdef CONFIG_ARC_SECURE_FIRMWARE
|
2019-03-08 14:19:05 -07:00
|
|
|
z_arc_v2_aux_reg_write(_ARC_V2_IRQ_PRIORITY,
|
2017-11-29 17:24:09 +08:00
|
|
|
(CONFIG_NUM_IRQ_PRIO_LEVELS-1) |
|
|
|
|
_ARC_V2_IRQ_PRIORITY_SECURE); /* lowest priority */
|
|
|
|
#else
|
2019-03-08 14:19:05 -07:00
|
|
|
z_arc_v2_aux_reg_write(_ARC_V2_IRQ_PRIORITY,
|
2016-06-01 17:52:11 -07:00
|
|
|
(CONFIG_NUM_IRQ_PRIO_LEVELS-1)); /* lowest priority */
|
2017-11-29 17:24:09 +08:00
|
|
|
#endif
|
2019-03-08 14:19:05 -07:00
|
|
|
z_arc_v2_aux_reg_write(_ARC_V2_IRQ_ENABLE, _ARC_V2_INT_DISABLE);
|
|
|
|
z_arc_v2_aux_reg_write(_ARC_V2_IRQ_TRIGGER, _ARC_V2_INT_LEVEL);
|
2015-04-10 16:44:37 -07:00
|
|
|
}
|
2016-11-15 15:19:00 +00:00
|
|
|
|
|
|
|
return 0;
|
2015-04-10 16:44:37 -07:00
|
|
|
}
|
|
|
|
|
2020-09-01 18:31:40 -04:00
|
|
|
#ifdef CONFIG_PM_DEVICE
|
2016-11-03 10:45:33 +00:00
|
|
|
|
2020-04-06 11:56:08 +08:00
|
|
|
/*
|
|
|
|
* @brief Suspend the interrupt unit device driver
|
|
|
|
*
|
|
|
|
* Suspends the interrupt unit device driver and the device
|
|
|
|
* itself.
|
|
|
|
*
|
|
|
|
* @return 0 for success
|
|
|
|
*/
|
2020-04-30 20:33:38 +02:00
|
|
|
static int arc_v2_irq_unit_suspend(const struct device *dev)
|
2016-11-03 10:45:33 +00:00
|
|
|
{
|
2020-05-27 11:26:57 -05:00
|
|
|
uint8_t irq;
|
2016-11-03 10:45:33 +00:00
|
|
|
|
|
|
|
ARG_UNUSED(dev);
|
|
|
|
|
|
|
|
/* Interrupts from 0 to 15 are exceptions and they are ignored
|
|
|
|
* by IRQ auxiliary registers. For that reason we skip those
|
|
|
|
* values in this loop.
|
|
|
|
*/
|
2018-11-29 11:12:22 -08:00
|
|
|
for (irq = 16U; irq < CONFIG_NUM_IRQS; irq++) {
|
2019-03-08 14:19:05 -07:00
|
|
|
z_arc_v2_aux_reg_write(_ARC_V2_IRQ_SELECT, irq);
|
2016-11-03 10:45:33 +00:00
|
|
|
ctx.irq_config[irq - 16] =
|
2019-03-08 14:19:05 -07:00
|
|
|
z_arc_v2_aux_reg_read(_ARC_V2_IRQ_PRIORITY) << 2;
|
2016-11-03 10:45:33 +00:00
|
|
|
ctx.irq_config[irq - 16] |=
|
2019-03-08 14:19:05 -07:00
|
|
|
z_arc_v2_aux_reg_read(_ARC_V2_IRQ_TRIGGER) << 1;
|
2016-11-03 10:45:33 +00:00
|
|
|
ctx.irq_config[irq - 16] |=
|
2019-03-08 14:19:05 -07:00
|
|
|
z_arc_v2_aux_reg_read(_ARC_V2_IRQ_ENABLE);
|
2016-11-03 10:45:33 +00:00
|
|
|
}
|
|
|
|
|
2019-03-08 14:19:05 -07:00
|
|
|
ctx.irq_ctrl = z_arc_v2_aux_reg_read(_ARC_V2_AUX_IRQ_CTRL);
|
|
|
|
ctx.irq_vect_base = z_arc_v2_aux_reg_read(_ARC_V2_IRQ_VECT_BASE);
|
2016-11-03 10:45:33 +00:00
|
|
|
|
2021-05-07 14:18:57 -07:00
|
|
|
_arc_v2_irq_unit_device_power_state = PM_DEVICE_STATE_SUSPEND;
|
2016-11-03 10:45:33 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-04-06 11:56:08 +08:00
|
|
|
/*
|
|
|
|
* @brief Resume the interrupt unit device driver
|
|
|
|
*
|
|
|
|
* Resume the interrupt unit device driver and the device
|
|
|
|
* itself.
|
|
|
|
*
|
|
|
|
* @return 0 for success
|
|
|
|
*/
|
2020-04-30 20:33:38 +02:00
|
|
|
static int arc_v2_irq_unit_resume(const struct device *dev)
|
2016-11-03 10:45:33 +00:00
|
|
|
{
|
2020-05-27 11:26:57 -05:00
|
|
|
uint8_t irq;
|
2016-11-03 10:45:33 +00:00
|
|
|
|
|
|
|
ARG_UNUSED(dev);
|
|
|
|
|
|
|
|
/* Interrupts from 0 to 15 are exceptions and they are ignored
|
|
|
|
* by IRQ auxiliary registers. For that reason we skip those
|
|
|
|
* values in this loop.
|
|
|
|
*/
|
2018-11-29 11:12:22 -08:00
|
|
|
for (irq = 16U; irq < CONFIG_NUM_IRQS; irq++) {
|
2019-03-08 14:19:05 -07:00
|
|
|
z_arc_v2_aux_reg_write(_ARC_V2_IRQ_SELECT, irq);
|
2019-08-01 12:39:35 +08:00
|
|
|
#ifdef CONFIG_ARC_SECURE_FIRMWARE
|
2019-03-08 14:19:05 -07:00
|
|
|
z_arc_v2_aux_reg_write(_ARC_V2_IRQ_PRIORITY,
|
2017-11-29 17:24:09 +08:00
|
|
|
ctx.irq_config[irq - 16] >> 2 |
|
|
|
|
_ARC_V2_IRQ_PRIORITY_SECURE);
|
|
|
|
#else
|
2019-03-08 14:19:05 -07:00
|
|
|
z_arc_v2_aux_reg_write(_ARC_V2_IRQ_PRIORITY,
|
2016-11-03 10:45:33 +00:00
|
|
|
ctx.irq_config[irq - 16] >> 2);
|
2017-11-29 17:24:09 +08:00
|
|
|
#endif
|
2019-03-08 14:19:05 -07:00
|
|
|
z_arc_v2_aux_reg_write(_ARC_V2_IRQ_TRIGGER,
|
2016-11-03 10:45:33 +00:00
|
|
|
(ctx.irq_config[irq - 16] >> 1) & BIT(0));
|
2019-03-08 14:19:05 -07:00
|
|
|
z_arc_v2_aux_reg_write(_ARC_V2_IRQ_ENABLE,
|
2016-11-03 10:45:33 +00:00
|
|
|
ctx.irq_config[irq - 16] & BIT(0));
|
|
|
|
}
|
|
|
|
|
2019-08-01 12:39:35 +08:00
|
|
|
#ifdef CONFIG_ARC_NORMAL_FIRMWARE
|
|
|
|
/* \todo use sjli instruction to access irq_ctrl */
|
|
|
|
#else
|
2019-03-08 14:19:05 -07:00
|
|
|
z_arc_v2_aux_reg_write(_ARC_V2_AUX_IRQ_CTRL, ctx.irq_ctrl);
|
2019-08-01 12:39:35 +08:00
|
|
|
#endif
|
2019-03-08 14:19:05 -07:00
|
|
|
z_arc_v2_aux_reg_write(_ARC_V2_IRQ_VECT_BASE, ctx.irq_vect_base);
|
2016-11-03 10:45:33 +00:00
|
|
|
|
2021-05-07 14:18:57 -07:00
|
|
|
_arc_v2_irq_unit_device_power_state = PM_DEVICE_STATE_ACTIVE;
|
2016-11-03 10:45:33 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-04-06 11:56:08 +08:00
|
|
|
/*
|
|
|
|
* @brief Get the power state of interrupt unit
|
|
|
|
*
|
|
|
|
* @return the power state of interrupt unit
|
|
|
|
*/
|
2020-04-30 20:33:38 +02:00
|
|
|
static int arc_v2_irq_unit_get_state(const struct device *dev)
|
2016-11-03 10:45:33 +00:00
|
|
|
{
|
|
|
|
ARG_UNUSED(dev);
|
|
|
|
|
|
|
|
return _arc_v2_irq_unit_device_power_state;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2020-04-06 11:56:08 +08:00
|
|
|
* @brief Implement the driver control of interrupt unit
|
|
|
|
*
|
|
|
|
* The operation on interrupt unit requires interrupt lock.
|
|
|
|
* The *context may include IN data or/and OUT data
|
|
|
|
*
|
|
|
|
* @return operation result
|
2016-11-03 10:45:33 +00:00
|
|
|
*/
|
2021-03-22 10:28:25 -04:00
|
|
|
static int arc_v2_irq_unit_device_ctrl(const struct device *dev,
|
2021-03-31 23:06:33 -07:00
|
|
|
uint32_t ctrl_command, uint32_t *context,
|
2021-05-03 18:01:40 +02:00
|
|
|
pm_device_cb cb, void *arg)
|
2016-11-03 10:45:33 +00:00
|
|
|
{
|
2019-02-14 09:35:42 +05:30
|
|
|
int ret = 0;
|
2020-04-06 11:56:08 +08:00
|
|
|
unsigned int key = arch_irq_lock();
|
2019-02-14 09:35:42 +05:30
|
|
|
|
2021-05-03 18:32:53 +02:00
|
|
|
if (ctrl_command == PM_DEVICE_STATE_SET) {
|
2021-05-07 14:18:57 -07:00
|
|
|
if (*((uint32_t *)context) == PM_DEVICE_STATE_SUSPEND) {
|
2021-03-22 10:28:25 -04:00
|
|
|
ret = arc_v2_irq_unit_suspend(dev);
|
2021-05-07 14:18:57 -07:00
|
|
|
} else if (*((uint32_t *)context) == PM_DEVICE_STATE_ACTIVE) {
|
2021-03-22 10:28:25 -04:00
|
|
|
ret = arc_v2_irq_unit_resume(dev);
|
2016-11-03 10:45:33 +00:00
|
|
|
}
|
2021-05-03 18:32:53 +02:00
|
|
|
} else if (ctrl_command == PM_DEVICE_STATE_GET) {
|
2021-03-22 10:28:25 -04:00
|
|
|
*((uint32_t *)context) = arc_v2_irq_unit_get_state(dev);
|
2016-11-03 10:45:33 +00:00
|
|
|
}
|
2019-02-14 09:35:42 +05:30
|
|
|
|
2020-04-06 11:56:08 +08:00
|
|
|
arch_irq_unlock(key);
|
|
|
|
|
2019-02-14 09:35:42 +05:30
|
|
|
if (cb) {
|
2021-03-22 10:28:25 -04:00
|
|
|
cb(dev, ret, context, arg);
|
2019-02-14 09:35:42 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
2016-11-03 10:45:33 +00:00
|
|
|
}
|
|
|
|
|
2019-03-12 15:15:42 -06:00
|
|
|
SYS_DEVICE_DEFINE("arc_v2_irq_unit", arc_v2_irq_unit_init,
|
|
|
|
arc_v2_irq_unit_device_ctrl, PRE_KERNEL_1,
|
|
|
|
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);
|
2016-11-03 10:45:33 +00:00
|
|
|
#else
|
2019-03-12 15:15:42 -06:00
|
|
|
SYS_INIT(arc_v2_irq_unit_init, PRE_KERNEL_1,
|
2016-11-15 15:19:00 +00:00
|
|
|
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);
|
2020-09-01 18:31:40 -04:00
|
|
|
#endif /* CONFIG_PM_DEVICE */
|