blob: adaaaaa47f2acdae4ee121c9c6544edd88a018de [file] [log] [blame]
/* 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;
}