Boot Linux faster!

Check our new training course

Boot Linux faster!

Check our new training course
and Creative Commons CC-BY-SA
lecture and lab materials

Bootlin logo

Elixir Cross Referencer

/*
 * Copyright (c) 2014 Wind River Systems, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * @file
 * @brief ARCv2 interrupt management
 *
 *
 * Interrupt management:
 *
 * - enabling/disabling
 * - dynamic ISR connecting/replacing
 *
 * SW_ISR_TABLE_DYNAMIC has to be enabled for connecting ISRs at runtime.
 *
 * An IRQ number passed to the @a irq parameters found in this file is a
 * number from 16 to last IRQ number on the platform.
 */

#include <nanokernel.h>
#include <arch/cpu.h>
#include <misc/__assert.h>
#include <toolchain.h>
#include <sections.h>
#include <sw_isr_table.h>
#include <irq.h>

/*
 * @brief Enable an interrupt line
 *
 * Clear possible pending interrupts on the line, and enable the interrupt
 * line. After this call, the CPU will receive interrupts for the specified
 * @a irq.
 *
 * @return N/A
 */

void _arch_irq_enable(unsigned int irq)
{
	int key = irq_lock();

	_arc_v2_irq_unit_int_enable(irq);
	irq_unlock(key);
}

/*
 * @brief Disable an interrupt line
 *
 * Disable an interrupt line. After this call, the CPU will stop receiving
 * interrupts for the specified @a irq.
 *
 * @return N/A
 */

void _arch_irq_disable(unsigned int irq)
{
	int key = irq_lock();

	_arc_v2_irq_unit_int_disable(irq);
	irq_unlock(key);
}

/*
 * @internal
 *
 * @brief Set an interrupt's priority
 *
 * Valid values are from 0 to 13. Lower values take priority over higher
 * values. Special case priorities are expressed via mutually exclusive
 * flags.

 * The priority is verified if ASSERT_ON is enabled.
 *
 * @return N/A
 */

void _irq_priority_set(unsigned int irq, unsigned int prio, uint32_t flags)
{
	int key = irq_lock();

	/* HW priorties 0 and 1 are special case selected by flags.
	 * 2 and above are 'regular' IRQs.
	 * Since the IRQ_CONNECT() API specifies that all priority values
	 * have the same semantics and 'special' priorities are set via
	 * flags, add 2 to the supplied value if flags are not in play.
	 */
	if (flags & IRQ_ZERO_LATENCY) {
		prio = 0;
	} else if (flags & IRQ_NON_MASKABLE) {
		prio = 1;
	} else {
		prio += 2;
	}

	__ASSERT(prio >= 0 && prio < CONFIG_NUM_IRQ_PRIO_LEVELS,
			 "invalid priority!");
	_arc_v2_irq_unit_prio_set(irq, prio);
	irq_unlock(key);
}

/*
 * @brief Spurious interrupt handler
 *
 * Installed in all dynamic interrupt slots at boot time. Throws an error if
 * called.
 *
 * @return N/A
 */

#include <misc/printk.h>
void _irq_spurious(void *unused)
{
	ARG_UNUSED(unused);
	printk("_irq_spurious(). Spinning...\n");
	for (;;)
		;
}

#if CONFIG_SW_ISR_TABLE_DYNAMIC
/*
 * @internal
 *
 * @brief Replace an interrupt handler by another
 *
 * An interrupt's ISR can be replaced at runtime.
 *
 * @return N/A
 */

void _irq_handler_set(
	unsigned int irq,
	void (*new)(void *arg),
	void *arg
)
{
	int key = irq_lock();
	int index = irq - 16;

	__ASSERT(irq < CONFIG_NUM_IRQS, "IRQ number too high");
	_sw_isr_table[index].isr = new;
	_sw_isr_table[index].arg = arg;

	irq_unlock(key);
}

/*
 * @brief Connect an ISR to an interrupt line
 *
 * @a isr is connected to interrupt line @a irq, a number greater than or equal
 * 16. No prior ISR can have been connected on @a irq interrupt line since the
 * system booted.
 *
 * This routine will hang if another ISR was connected for interrupt line @a irq
 * and ASSERT_ON is enabled; if ASSERT_ON is disabled, it will fail silently.
 *
 * @return the interrupt line number
 */

int _arch_irq_connect_dynamic(
	unsigned int irq,
	unsigned int prio,
	void (*isr)(void *arg),
	void *arg,
	uint32_t flags
)
{
	ARG_UNUSED(flags);
	_irq_handler_set(irq, isr, arg);
	_irq_priority_set(irq, prio);
	return irq;
}

/*
 * @internal
 *
 * @brief Disconnect an ISR from an interrupt line
 *
 * Interrupt line @a irq is disconnected from its ISR and the latter is
 * replaced by _irq_spurious(). irq_disable() should have been called before
 * invoking this routine.
 *
 * @return N/A
 */

void _irq_disconnect(unsigned int irq)
{
	_irq_handler_set(irq, _irq_spurious, NULL);
}

#endif /* CONFIG_SW_ISR_TABLE_DYNAMIC */