/* Copyright (c) 2018, The Linux Foundation. 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 version 2 and * only version 2 as published by the Free Software Foundation. * * 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 "gsi_emulation.h" /* * ***************************************************************************** * The following used to set up the EMULATION interrupt controller... * ***************************************************************************** */ int setup_emulator_cntrlr( void __iomem *intcntrlr_base, u32 intcntrlr_mem_size) { uint32_t val, ver, intrCnt, rangeCnt, range; val = gsi_emu_readl(intcntrlr_base + GE_INT_CTL_VER_CNT); intrCnt = val & 0xFFFF; ver = (val >> 16) & 0xFFFF; rangeCnt = intrCnt / 32; GSIDBG( "CTL_VER_CNT reg val(0x%x) intr cnt(%u) cntrlr ver(0x%x) rangeCnt(%u)\n", val, intrCnt, ver, rangeCnt); /* * Verify the interrupt controller version */ if (ver == 0 || ver == 0xFFFF || ver < DEO_IC_INT_CTL_VER_MIN) { GSIERR( "Error: invalid interrupt controller version 0x%x\n", ver); return -GSI_STATUS_INVALID_PARAMS; } /* * Verify the interrupt count * * NOTE: intrCnt must be at least one block and multiple of 32 */ if ((intrCnt % 32) != 0) { GSIERR( "Invalid interrupt count read from HW 0x%04x\n", intrCnt); return -GSI_STATUS_ERROR; } /* * Calculate number of ranges used, each range handles 32 int lines */ if (rangeCnt > DEO_IC_MAX_RANGE_CNT) { GSIERR( "SW interrupt limit(%u) passed, increase DEO_IC_MAX_RANGE_CNT(%u)\n", rangeCnt, DEO_IC_MAX_RANGE_CNT); return -GSI_STATUS_ERROR; } /* * Let's take the last register offset minus the first * register offset (ie. range) and compare it to the interrupt * controller's dtsi defined memory size. The range better * fit within the size. */ val = GE_SOFT_INT_n(rangeCnt-1) - GE_INT_CTL_VER_CNT; if (val > intcntrlr_mem_size) { GSIERR( "Interrupt controller register range (%u) exceeds dtsi provisioned size (%u)\n", val, intcntrlr_mem_size); return -GSI_STATUS_ERROR; } /* * The following will disable the emulators interrupt controller, * so that we can config it... */ GSIDBG("Writing GE_INT_MASTER_ENABLE\n"); gsi_emu_writel( 0x0, intcntrlr_base + GE_INT_MASTER_ENABLE); /* * Init register maps of all ranges */ for (range = 0; range < rangeCnt; range++) { /* * Disable all int sources by setting all enable clear bits */ GSIDBG("Writing GE_INT_ENABLE_CLEAR_n(%u)\n", range); gsi_emu_writel( 0xFFFFFFFF, intcntrlr_base + GE_INT_ENABLE_CLEAR_n(range)); /* * Clear all raw statuses */ GSIDBG("Writing GE_INT_CLEAR_n(%u)\n", range); gsi_emu_writel( 0xFFFFFFFF, intcntrlr_base + GE_INT_CLEAR_n(range)); /* * Init all int types */ GSIDBG("Writing GE_INT_TYPE_n(%u)\n", range); gsi_emu_writel( 0x0, intcntrlr_base + GE_INT_TYPE_n(range)); } /* * The following tells the interrupt controller to interrupt us * when it sees interupts from ipa and/or gsi. * * Interrupts: * =================================================================== * DUT0 [ 63 : 16 ] * ipa_irq [ 3 : 0 ] <---HERE * ipa_gsi_bam_irq [ 7 : 4 ] <---HERE * ipa_bam_apu_sec_error_irq [ 8 ] * ipa_bam_apu_non_sec_error_irq [ 9 ] * ipa_bam_xpu2_msa_intr [ 10 ] * ipa_vmidmt_nsgcfgirpt [ 11 ] * ipa_vmidmt_nsgirpt [ 12 ] * ipa_vmidmt_gcfgirpt [ 13 ] * ipa_vmidmt_girpt [ 14 ] * bam_xpu3_qad_non_secure_intr_sp [ 15 ] */ GSIDBG("Writing GE_INT_ENABLE_n(0)\n"); gsi_emu_writel( 0x00FF, /* See <---HERE above */ intcntrlr_base + GE_INT_ENABLE_n(0)); /* * The following will enable the IC post config... */ GSIDBG("Writing GE_INT_MASTER_ENABLE\n"); gsi_emu_writel( 0x1, intcntrlr_base + GE_INT_MASTER_ENABLE); return 0; } /* * ***************************************************************************** * The following for EMULATION hard irq... * ***************************************************************************** */ irqreturn_t emulator_hard_irq_isr( int irq, void *ctxt) { struct gsi_ctx *gsi_ctx_ptr = (struct gsi_ctx *) ctxt; uint32_t val; val = gsi_emu_readl(gsi_ctx_ptr->intcntrlr_base + GE_INT_MASTER_STATUS); /* * If bit zero is set, interrupt is for us, hence return IRQ_NONE * when it's not set... */ if (!(val & 0x00000001)) return IRQ_NONE; /* * The following will mask (ie. turn off) future interrupts from * the emulator's interrupt controller. It wil stay this way until * we turn back on...which will be done in the bottom half * (ie. emulator_soft_irq_isr)... */ gsi_emu_writel( 0x0, gsi_ctx_ptr->intcntrlr_base + GE_INT_OUT_ENABLE); return IRQ_WAKE_THREAD; } /* * ***************************************************************************** * The following for EMULATION soft irq... * ***************************************************************************** */ irqreturn_t emulator_soft_irq_isr( int irq, void *ctxt) { struct gsi_ctx *gsi_ctx_ptr = (struct gsi_ctx *) ctxt; irqreturn_t retVal = IRQ_HANDLED; uint32_t val; val = gsi_emu_readl(gsi_ctx_ptr->intcntrlr_base + GE_IRQ_STATUS_n(0)); GSIDBG("Got irq(%d) with status(0x%08X)\n", irq, val); if (val & 0xF0 && gsi_ctx_ptr->intcntrlr_gsi_isr) { GSIDBG("Got gsi interrupt\n"); retVal = gsi_ctx_ptr->intcntrlr_gsi_isr(irq, ctxt); } if (val & 0x0F && gsi_ctx_ptr->intcntrlr_client_isr) { GSIDBG("Got ipa interrupt\n"); retVal = gsi_ctx_ptr->intcntrlr_client_isr(irq, 0); } /* * The following will clear the interrupts... */ gsi_emu_writel( 0xFFFFFFFF, gsi_ctx_ptr->intcntrlr_base + GE_INT_CLEAR_n(0)); /* * The following will unmask (ie. turn on) future interrupts from * the emulator's interrupt controller... */ gsi_emu_writel( 0x1, gsi_ctx_ptr->intcntrlr_base + GE_INT_OUT_ENABLE); return retVal; }