/* * (C) Copyright 2007-2013 * Allwinner Technology Co., Ltd. * Jerry Wang * * See file CREDITS for list of people who contributed to this * project. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * */ #include #include #include #include DECLARE_GLOBAL_DATA_PTR; struct _irq_handler { void *m_data; void (*m_func)(void *data); }; struct _irq_handler sunxi_int_handlers[PLIC_IRQ_NUM]; extern int interrupts_is_open(void); static void default_isr(void *data) { printf("default_isr(): called from IRQ 0x%lx\n", (phys_addr_t)data); while (1); } int irq_enable(int irq_no) { uint reg_val; uint offset; /* static int old_irq_no; */ if (irq_no >= PLIC_IRQ_NUM) { printf("irq NO.(%d) > PLIC_IRQ_NUM(%d) !!\n", irq_no, PLIC_IRQ_NUM); return -1; } offset = irq_no >> 5; reg_val = readl(PLIC_M_S_IE_REG(offset)); reg_val |= (1 << (irq_no & 0x1f)); writel(reg_val, PLIC_M_S_IE_REG(offset)); writel(1, PLIC_PRIO_REG(irq_no)); /* if (old_irq_no != 0) { * writel(old_irq_no, PLIC_SCLAIM_REG); * } * old_irq_no = irq_no; */ return 0; } int irq_disable(int irq_no) { uint reg_val; uint offset; if (irq_no >= PLIC_IRQ_NUM) { printf("irq NO.(%d) > PLIC_IRQ_NUM(%d) !!\n", irq_no, PLIC_IRQ_NUM); return -1; } offset = irq_no >> 5; reg_val = readl(PLIC_M_S_IE_REG(offset)); reg_val &= ~(1 << (irq_no & 0x1f)); writel(reg_val, PLIC_M_S_IE_REG(offset)); return 0; } static void plic_spi_handler(uint irq_no) { if (sunxi_int_handlers[irq_no].m_func != default_isr) { sunxi_int_handlers[irq_no].m_func(sunxi_int_handlers[irq_no].m_data); } } void irq_install_handler (int irq, interrupt_handler_t handle_irq, void *data) { /* int flag = interrupts_is_open(); * //when irq_handler call this function , irq enable bit has already disabled in irq_mode,so don't need to enable I bit * if (flag) { * disable_interrupts(); * } */ if (irq >= PLIC_IRQ_NUM || !handle_irq) { return; } sunxi_int_handlers[irq].m_data = data; sunxi_int_handlers[irq].m_func = handle_irq; /* if (flag) { * enable_interrupts(); * } */ } void irq_free_handler(int irq) { disable_interrupts(); if (irq >= PLIC_IRQ_NUM) { enable_interrupts(); return; } sunxi_int_handlers[irq].m_data = NULL; sunxi_int_handlers[irq].m_func = default_isr; enable_interrupts(); } void external_interrupt(struct pt_regs *regs) { u32 idnum = 0; unsigned long riscv_mode = get_cur_riscv_mode(); riscv_mode == PRV_M ? csr_clear(mie, SR_MIE) : csr_clear(sie, SR_SIE); do { idnum = readl(PLIC_M_S_CLAIM_REG); if (idnum != 0) { plic_spi_handler(idnum); irq_enable(idnum); writel(idnum, PLIC_M_S_CLAIM_REG); } } while (idnum != 0); riscv_mode == PRV_M ? csr_set(mie, SR_MIE) : csr_set(sie, SR_SIE); return; }