| /* |
| * Copyright (c) 2020 ITE Corporation. All Rights Reserved |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include <zephyr/kernel.h> |
| #include <zephyr/arch/cpu.h> |
| #include <zephyr/init.h> |
| #include <zephyr/sys/printk.h> |
| #include <zephyr/sw_isr_table.h> |
| #include "intc_ite_it8xxx2.h" |
| |
| #define MAX_REGISR_IRQ_NUM 8 |
| #define IVECT_OFFSET_WITH_IRQ 0x10 |
| |
| /* Interrupt number of INTC module */ |
| static uint8_t intc_irq; |
| |
| static volatile uint8_t *const reg_status[] = { |
| &ISR0, &ISR1, &ISR2, &ISR3, |
| &ISR4, &ISR5, &ISR6, &ISR7, |
| &ISR8, &ISR9, &ISR10, &ISR11, |
| &ISR12, &ISR13, &ISR14, &ISR15, |
| &ISR16, &ISR17, &ISR18, &ISR19, |
| &ISR20, &ISR21, &ISR22, &ISR23 |
| }; |
| |
| static volatile uint8_t *const reg_enable[] = { |
| &IER0, &IER1, &IER2, &IER3, |
| &IER4, &IER5, &IER6, &IER7, |
| &IER8, &IER9, &IER10, &IER11, |
| &IER12, &IER13, &IER14, &IER15, |
| &IER16, &IER17, &IER18, &IER19, |
| &IER20, &IER21, &IER22, &IER23 |
| }; |
| |
| /* edge/level trigger register */ |
| static volatile uint8_t *const reg_ielmr[] = { |
| &IELMR0, &IELMR1, &IELMR2, &IELMR3, |
| &IELMR4, &IELMR5, &IELMR6, &IELMR7, |
| &IELMR8, &IELMR9, &IELMR10, &IELMR11, |
| &IELMR12, &IELMR13, &IELMR14, &IELMR15, |
| &IELMR16, &IELMR17, &IELMR18, &IELMR19, |
| &IELMR20, &IELMR21, &IELMR22, &IELMR23, |
| }; |
| |
| /* high/low trigger register */ |
| static volatile uint8_t *const reg_ipolr[] = { |
| &IPOLR0, &IPOLR1, &IPOLR2, &IPOLR3, |
| &IPOLR4, &IPOLR5, &IPOLR6, &IPOLR7, |
| &IPOLR8, &IPOLR9, &IPOLR10, &IPOLR11, |
| &IPOLR12, &IPOLR13, &IPOLR14, &IPOLR15, |
| &IPOLR16, &IPOLR17, &IPOLR18, &IPOLR19, |
| &IPOLR20, &IPOLR21, &IPOLR22, &IPOLR23 |
| }; |
| |
| #define IT8XXX2_IER_COUNT ARRAY_SIZE(reg_enable) |
| static uint8_t ier_setting[IT8XXX2_IER_COUNT]; |
| |
| 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_IER_COUNT; i++) { |
| ier_setting[i] = *reg_enable[i]; |
| *reg_enable[i] = 0; |
| } |
| 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_IER_COUNT; i++) { |
| *reg_enable[i] = ier_setting[i]; |
| } |
| irq_unlock(key); |
| } |
| |
| void ite_intc_isr_clear(unsigned int irq) |
| { |
| uint32_t g, i; |
| volatile uint8_t *isr; |
| |
| if (irq > CONFIG_NUM_IRQS) { |
| return; |
| } |
| g = irq / MAX_REGISR_IRQ_NUM; |
| i = irq % MAX_REGISR_IRQ_NUM; |
| isr = reg_status[g]; |
| *isr = BIT(i); |
| } |
| |
| void ite_intc_irq_enable(unsigned int irq) |
| { |
| uint32_t g, i; |
| volatile uint8_t *en; |
| |
| if (irq > CONFIG_NUM_IRQS) { |
| return; |
| } |
| g = irq / MAX_REGISR_IRQ_NUM; |
| i = irq % MAX_REGISR_IRQ_NUM; |
| en = reg_enable[g]; |
| |
| /* critical section due to run a bit-wise OR operation */ |
| unsigned int key = irq_lock(); |
| SET_MASK(*en, BIT(i)); |
| irq_unlock(key); |
| } |
| |
| void ite_intc_irq_disable(unsigned int irq) |
| { |
| uint32_t g, i; |
| volatile uint8_t *en; |
| volatile uint8_t _ier __unused; |
| |
| if (irq > CONFIG_NUM_IRQS) { |
| return; |
| } |
| g = irq / MAX_REGISR_IRQ_NUM; |
| i = irq % MAX_REGISR_IRQ_NUM; |
| en = reg_enable[g]; |
| |
| /* critical section due to run a bit-wise OR operation */ |
| unsigned int key = irq_lock(); |
| CLEAR_MASK(*en, BIT(i)); |
| /* |
| * This load operation will guarantee the above modification of |
| * SOC's register can be seen by any following instructions. |
| */ |
| _ier = *en; |
| irq_unlock(key); |
| } |
| |
| void ite_intc_irq_polarity_set(unsigned int irq, unsigned int flags) |
| { |
| uint32_t g, i; |
| volatile uint8_t *tri; |
| |
| if ((irq > CONFIG_NUM_IRQS) || ((flags&IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH)) { |
| return; |
| } |
| g = irq / MAX_REGISR_IRQ_NUM; |
| i = irq % MAX_REGISR_IRQ_NUM; |
| tri = reg_ipolr[g]; |
| if ((flags&IRQ_TYPE_LEVEL_HIGH) || (flags&IRQ_TYPE_EDGE_RISING)) { |
| CLEAR_MASK(*tri, BIT(i)); |
| } else { |
| SET_MASK(*tri, BIT(i)); |
| } |
| tri = reg_ielmr[g]; |
| if ((flags&IRQ_TYPE_LEVEL_LOW) || (flags&IRQ_TYPE_LEVEL_HIGH)) { |
| CLEAR_MASK(*tri, BIT(i)); |
| } else { |
| SET_MASK(*tri, BIT(i)); |
| } |
| } |
| |
| int ite_intc_irq_is_enable(unsigned int irq) |
| { |
| uint32_t g, i; |
| volatile uint8_t *en; |
| |
| if (irq > CONFIG_NUM_IRQS) { |
| return 0; |
| } |
| g = irq / MAX_REGISR_IRQ_NUM; |
| i = irq % MAX_REGISR_IRQ_NUM; |
| en = reg_enable[g]; |
| return IS_MASK_SET(*en, BIT(i)); |
| } |
| |
| uint8_t ite_intc_get_irq_num(void) |
| { |
| return intc_irq; |
| } |
| |
| bool ite_intc_no_irq(void) |
| { |
| return (IVECT == IVECT_OFFSET_WITH_IRQ); |
| } |
| |
| uint8_t 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; |
| /* clear interrupt status */ |
| ite_intc_isr_clear(intc_irq); |
| /* return interrupt number */ |
| return intc_irq; |
| } |
| |
| void ite_intc_init(void) |
| { |
| /* Ensure interrupts of soc are disabled at default */ |
| for (int i = 0; i < ARRAY_SIZE(reg_enable); i++) |
| *reg_enable[i] = 0; |
| |
| /* Enable M-mode external interrupt */ |
| csr_set(mie, MIP_MEIP); |
| } |