/** * Copyright (c) 2012 Jean-Christophe Dubois. * All rights reserved. * * 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, 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * @file pl190.c * @author Jean-Christophe Dubois (jcd@tribudubois.net) * @brief PL190 Vectored Interrupt Controller source */ #include #include #define VERSATILE_VIC_MAX_NR 1 struct pl190_chip_data { u32 irq_offset; virtual_addr_t cpu_base; }; static struct pl190_chip_data pl190_data[VERSATILE_VIC_MAX_NR]; static inline void pl190_write(u32 val, virtual_addr_t addr) { arm_writel(val, (void *)(addr)); } static inline u32 pl190_read(virtual_addr_t addr) { return arm_readl((void *)(addr)); } int pl190_active_irq(u32 pl190_nr) { u32 int_status; if (VERSATILE_VIC_MAX_NR <= pl190_nr) { return -1; } int_status = pl190_read(pl190_data[pl190_nr].cpu_base + PL190_IRQ_STATUS); if (int_status) { int ret; for (ret = 0; ret < 32; ret++) { if ((int_status >> ret) & 1) { break; } } ret += pl190_data[pl190_nr].irq_offset; return ret; } else { return -1; } } int pl190_eoi_irq(u32 pl190_nr, u32 irq) { u32 mask; if (VERSATILE_VIC_MAX_NR <= pl190_nr) { return -1; } if (irq < pl190_data[pl190_nr].irq_offset) { return -1; } mask = 1 << (irq - pl190_data[pl190_nr].irq_offset); pl190_write(mask, pl190_data[pl190_nr].cpu_base + PL190_INT_ENABLE_CLEAR); pl190_write(mask, pl190_data[pl190_nr].cpu_base + PL190_INT_SOFT_CLEAR); pl190_write(mask, pl190_data[pl190_nr].cpu_base + PL190_INT_ENABLE); return 0; } int pl190_mask(u32 pl190_nr, u32 irq) { u32 mask; if (VERSATILE_VIC_MAX_NR <= pl190_nr) { return -1; } if (irq < pl190_data[pl190_nr].irq_offset) { return -1; } mask = 1 << (irq - pl190_data[pl190_nr].irq_offset); pl190_write(mask, pl190_data[pl190_nr].cpu_base + PL190_INT_ENABLE_CLEAR); return 0; } int pl190_unmask(u32 pl190_nr, u32 irq) { u32 mask; if (VERSATILE_VIC_MAX_NR <= pl190_nr) { return -1; } if (irq < pl190_data[pl190_nr].irq_offset) { return -1; } mask = 1 << (irq - pl190_data[pl190_nr].irq_offset); pl190_write(mask, pl190_data[pl190_nr].cpu_base + PL190_INT_ENABLE); return 0; } int pl190_cpu_init(u32 pl190_nr, virtual_addr_t base) { //unsigned int i; if (pl190_nr >= VERSATILE_VIC_MAX_NR) { return -1; } pl190_data[pl190_nr].cpu_base = base; /* We have 32 interrupts per PL190 */ pl190_data[pl190_nr].irq_offset = 32 * pl190_nr; pl190_write(0, pl190_data[pl190_nr].cpu_base + PL190_INT_SELECT); pl190_write(0, pl190_data[pl190_nr].cpu_base + PL190_INT_ENABLE); pl190_write(~0, pl190_data[pl190_nr].cpu_base + PL190_INT_ENABLE_CLEAR); pl190_write(0, pl190_data[pl190_nr].cpu_base + PL190_IRQ_STATUS); pl190_write(0, pl190_data[pl190_nr].cpu_base + PL190_ITCR); pl190_write(~0, pl190_data[pl190_nr].cpu_base + PL190_INT_SOFT_CLEAR); #if 0 pl190_write(0, pl190_data[pl190_nr].cpu_base + PL190_VECT_ADDR); for (i = 0; i < 19; i++) { unsigned int value = pl190_read(pl190_data[pl190_nr].cpu_base + PL190_VECT_ADDR); pl190_write(value, pl190_data[pl190_nr].cpu_base + PL190_VECT_ADDR); } for (i = 0; i < 16; i++) { pl190_write(VIC_VECT_CNTL_ENABLE | i, pl190_data[pl190_nr].cpu_base + PL190_VECT_CNTL0 + (i * 4)); } pl190_write(32, pl190_data[pl190_nr].cpu_base + PL190_DEF_VECT_ADDR); #endif return 0; }