/* * Copyright (c) 2023 ITE Corporation. All Rights Reserved * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #include #include #include "intc_ite_it8xxx2.h" LOG_MODULE_REGISTER(intc_it8xxx2_v2, LOG_LEVEL_DBG); #define IT8XXX2_INTC_BASE DT_REG_ADDR(DT_NODELABEL(intc)) #define IT8XXX2_INTC_BASE_SHIFT(g) (IT8XXX2_INTC_BASE + ((g) << 2)) /* Interrupt status register */ #define IT8XXX2_INTC_ISR(g) ECREG(IT8XXX2_INTC_BASE_SHIFT(g) + \ ((g) < 4 ? 0x0 : 0x4)) /* Interrupt enable register */ #define IT8XXX2_INTC_IER(g) ECREG(IT8XXX2_INTC_BASE_SHIFT(g) + \ ((g) < 4 ? 0x1 : 0x5)) /* Interrupt edge/level triggered mode register */ #define IT8XXX2_INTC_IELMR(g) ECREG(IT8XXX2_INTC_BASE_SHIFT(g) + \ ((g) < 4 ? 0x2 : 0x6)) /* Interrupt polarity register */ #define IT8XXX2_INTC_IPOLR(g) ECREG(IT8XXX2_INTC_BASE_SHIFT(g) + \ ((g) < 4 ? 0x3 : 0x7)) #define IT8XXX2_INTC_GROUP_CNT 24 #define MAX_REGISR_IRQ_NUM 8 #define IVECT_OFFSET_WITH_IRQ 0x10 /* Interrupt number of INTC module */ static uint8_t intc_irq; static uint8_t ier_setting[IT8XXX2_INTC_GROUP_CNT]; void ite_intc_save_and_disable_interrupts(void) { /* Disable global interrupt for critical section */ unsigned int key = irq_lock(); /* Save and disable interrupts */ for (int i = 0; i < IT8XXX2_INTC_GROUP_CNT; i++) { ier_setting[i] = IT8XXX2_INTC_IER(i); IT8XXX2_INTC_IER(i) = 0; } /* * This load operation will guarantee the above modification of * SOC's register can be seen by any following instructions. * Note: Barrier instruction can not synchronize chip register, * so we introduce workaround here. */ IT8XXX2_INTC_IER(IT8XXX2_INTC_GROUP_CNT - 1); irq_unlock(key); } void ite_intc_restore_interrupts(void) { /* * Ensure the highest priority interrupt will be the first fired * interrupt when soc is ready to go. */ unsigned int key = irq_lock(); /* Restore interrupt state */ for (int i = 0; i < IT8XXX2_INTC_GROUP_CNT; i++) { IT8XXX2_INTC_IER(i) = ier_setting[i]; } irq_unlock(key); } void ite_intc_isr_clear(unsigned int irq) { uint32_t group, index; if (irq > CONFIG_NUM_IRQS) { return; } group = irq / MAX_REGISR_IRQ_NUM; index = irq % MAX_REGISR_IRQ_NUM; IT8XXX2_INTC_ISR(group) = BIT(index); } void __soc_ram_code ite_intc_irq_enable(unsigned int irq) { uint32_t group, index; if (irq > CONFIG_NUM_IRQS) { return; } group = irq / MAX_REGISR_IRQ_NUM; index = irq % MAX_REGISR_IRQ_NUM; /* Critical section due to run a bit-wise OR operation */ unsigned int key = irq_lock(); IT8XXX2_INTC_IER(group) |= BIT(index); irq_unlock(key); } void __soc_ram_code ite_intc_irq_disable(unsigned int irq) { uint32_t group, index; if (irq > CONFIG_NUM_IRQS) { return; } group = irq / MAX_REGISR_IRQ_NUM; index = irq % MAX_REGISR_IRQ_NUM; /* Critical section due to run a bit-wise NAND operation */ unsigned int key = irq_lock(); IT8XXX2_INTC_IER(group) &= ~BIT(index); /* * This load operation will guarantee the above modification of * SOC's register can be seen by any following instructions. */ IT8XXX2_INTC_IER(group); irq_unlock(key); } void ite_intc_irq_polarity_set(unsigned int irq, unsigned int flags) { uint32_t group, index; if (irq > CONFIG_NUM_IRQS) { return; } if ((flags & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH) { return; } group = irq / MAX_REGISR_IRQ_NUM; index = irq % MAX_REGISR_IRQ_NUM; if ((flags & IRQ_TYPE_LEVEL_HIGH) || (flags & IRQ_TYPE_EDGE_RISING)) { IT8XXX2_INTC_IPOLR(group) &= ~BIT(index); } else { IT8XXX2_INTC_IPOLR(group) |= BIT(index); } if ((flags & IRQ_TYPE_LEVEL_LOW) || (flags & IRQ_TYPE_LEVEL_HIGH)) { IT8XXX2_INTC_IELMR(group) &= ~BIT(index); } else { IT8XXX2_INTC_IELMR(group) |= BIT(index); } } int __soc_ram_code ite_intc_irq_is_enable(unsigned int irq) { uint32_t group, index; if (irq > CONFIG_NUM_IRQS) { return 0; } group = irq / MAX_REGISR_IRQ_NUM; index = irq % MAX_REGISR_IRQ_NUM; return IS_MASK_SET(IT8XXX2_INTC_IER(group), BIT(index)); } uint8_t __soc_ram_code ite_intc_get_irq_num(void) { return intc_irq; } bool __soc_ram_code ite_intc_no_irq(void) { return (IVECT == IVECT_OFFSET_WITH_IRQ); } uint8_t __soc_ram_code get_irq(void *arg) { ARG_UNUSED(arg); /* Wait until two equal interrupt values are read */ do { /* Read interrupt number from interrupt vector register */ intc_irq = IVECT; /* * WORKAROUND: when the interrupt vector register (IVECT) * isn't latched in a load operation, we read it again to make * sure the value we got is the correct value. */ } while (intc_irq != IVECT); /* Determine interrupt number */ intc_irq -= IVECT_OFFSET_WITH_IRQ; /* * Look for pending interrupt if there's interrupt number 0 from * the AIVECT register. */ if (intc_irq == 0) { uint8_t int_pending; for (int i = (IT8XXX2_INTC_GROUP_CNT - 1); i >= 0; i--) { int_pending = (IT8XXX2_INTC_ISR(i) & IT8XXX2_INTC_IER(i)); if (int_pending != 0) { intc_irq = (MAX_REGISR_IRQ_NUM * i) + find_msb_set(int_pending) - 1; LOG_DBG("Pending interrupt found: %d", intc_irq); LOG_DBG("CPU mepc: 0x%lx", csr_read(mepc)); break; } } } /* Clear interrupt status */ ite_intc_isr_clear(intc_irq); /* Return interrupt number */ return intc_irq; } void soc_interrupt_init(void) { /* Ensure interrupts of soc are disabled at default */ for (int i = 0; i < IT8XXX2_INTC_GROUP_CNT; i++) { IT8XXX2_INTC_IER(i) = 0; } /* Enable M-mode external interrupt */ csr_set(mie, MIP_MEIP); }