Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit df159af6 authored by Jennifer L. Zenner's avatar Jennifer L. Zenner
Browse files

msm: ipa3: Add support for IPA/GSI emulation system



Changes made to the IPA/GSI drivers in support of an emulation
system. The emulation system is an FPGA based system that allows for
the prototyping of new IPA features before committing them to silicon.

Change-Id: I6ad6474c6574e0efb869c1b07e611d6bbd89fae8
CRs-fixed: 2204018
Acked-by: default avatarPerry Randise <prandise@qti.qualcomm.com>
Signed-off-by: default avatarJennifer L. Zenner <jzenner@codeaurora.org>
parent 8f3515e7
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -118,6 +118,10 @@ Optional properties:
    controller phandle and "clk_ipa_clk" as macro for "iface_clk"
- clock-names: This property shall contain the clock input names used
    by driver in same order as the clocks property.This should be "iface_clk"
- emulator-bar0-offset: Specifies the offset, within PCIe BAR0, where
    IPA/GSI programmable registers reside.  This property is used only
    with the IPA/GSI emulation system, which is connected to and
    communicated with via PCIe.

IPA SMMU sub nodes

+7 −0
Original line number Diff line number Diff line
obj-$(CONFIG_GSI) += gsi.o gsi_dbg.o

ifdef CONFIG_X86
ccflags-y += -DIPA_EMULATION_COMPILE=1
obj-$(CONFIG_GSI) += gsi_emulation.o
else
ccflags-y += -DIPA_EMULATION_COMPILE=0
endif
+174 −19
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@
#include <linux/delay.h>
#include "gsi.h"
#include "gsi_reg.h"
#include "gsi_emulation.h"

#define GSI_CMD_TIMEOUT (5*HZ)
#define GSI_STOP_CMD_TIMEOUT_MS 20
@@ -33,6 +34,8 @@ static const struct of_device_id msm_gsi_match[] = {
	{ },
};

static bool running_emulation = IPA_EMULATION_COMPILE;

struct gsi_ctx *gsi_ctx;

static void __gsi_config_type_irq(int ee, uint32_t mask, uint32_t val)
@@ -577,7 +580,7 @@ static void gsi_handle_irq(void)
		if (!type)
			break;

		GSIDBG_LOW("type %x\n", type);
		GSIDBG_LOW("type 0x%x\n", type);

		if (type & GSI_EE_n_CNTXT_TYPE_IRQ_CH_CTRL_BMSK)
			gsi_handle_ch_ctrl(ee);
@@ -777,17 +780,57 @@ int gsi_register_device(struct gsi_per_props *props, unsigned long *dev_hdl)
			GSIERR("bad irq specified %u\n", props->irq);
			return -GSI_STATUS_INVALID_PARAMS;
		}

		/*
		 * On a real UE, there are two separate interrupt
		 * vectors that get directed toward the GSI/IPA
		 * drivers.  They are handled by gsi_isr() and
		 * (ipa_isr() or ipa3_isr()) respectively.  In the
		 * emulation environment, this is not the case;
		 * instead, interrupt vectors are routed to the
		 * emualation hardware's interrupt controller, who in
		 * turn, forwards a single interrupt to the GSI/IPA
		 * driver.  When the new interrupt vector is received,
		 * the driver needs to probe the interrupt
		 * controller's registers so see if one, the other, or
		 * both interrupts have occurred.  Given the above, we
		 * now need to handle both situations, namely: the
		 * emulator's and the real UE.
		 */
		if (running_emulation) {
			/*
			 * New scheme involving the emulator's
			 * interrupt controller.
			 */
			res = devm_request_threaded_irq(
				gsi_ctx->dev,
				props->irq,
				/* top half handler to follow */
				emulator_hard_irq_isr,
				/* threaded bottom half handler to follow */
				emulator_soft_irq_isr,
				IRQF_SHARED,
				"emulator_intcntrlr",
				gsi_ctx);
		} else {
			/*
			 * Traditional scheme used on the real UE.
			 */
			res = devm_request_irq(gsi_ctx->dev, props->irq,
				(irq_handler_t) gsi_isr,
				props->req_clk_cb ? IRQF_TRIGGER_RISING :
					IRQF_TRIGGER_HIGH,
				"gsi",
				gsi_ctx);
		}
		if (res) {
			GSIERR("failed to register isr for %u\n", props->irq);
			GSIERR(
			 "failed to register isr for %u\n",
			 props->irq);
			return -GSI_STATUS_ERROR;
		}
		GSIDBG(
			"succeeded to register isr for %u\n",
			props->irq);

		res = enable_irq_wake(props->irq);
		if (res)
@@ -808,6 +851,41 @@ int gsi_register_device(struct gsi_per_props *props, unsigned long *dev_hdl)
		return -GSI_STATUS_RES_ALLOC_FAILURE;
	}

	GSIDBG("GSI base(%pa) mapped to (%pK) with len (0x%lx)\n",
	       &(props->phys_addr),
	       gsi_ctx->base,
	       props->size);

	if (running_emulation) {
		GSIDBG("GSI SW ver register value 0x%x\n",
		       gsi_readl(gsi_ctx->base +
		       GSI_EE_n_GSI_SW_VERSION_OFFS(0)));
		gsi_ctx->intcntrlr_mem_size =
		    props->emulator_intcntrlr_size;
		gsi_ctx->intcntrlr_base =
		    devm_ioremap_nocache(
			gsi_ctx->dev,
			props->emulator_intcntrlr_addr,
			props->emulator_intcntrlr_size);
		if (!gsi_ctx->intcntrlr_base) {
			GSIERR(
			  "failed to remap emulator's interrupt controller HW\n");
			devm_iounmap(gsi_ctx->dev, gsi_ctx->base);
			devm_free_irq(gsi_ctx->dev, props->irq, gsi_ctx);
			return -GSI_STATUS_RES_ALLOC_FAILURE;
		}

		GSIDBG(
		    "Emulator's interrupt controller base(%pa) mapped to (%pK) with len (0x%lx)\n",
		    &(props->emulator_intcntrlr_addr),
		    gsi_ctx->intcntrlr_base,
		    props->emulator_intcntrlr_size);

		gsi_ctx->intcntrlr_gsi_isr = gsi_isr;
		gsi_ctx->intcntrlr_client_isr =
		    props->emulator_intcntrlr_client_isr;
	}

	gsi_ctx->per = *props;
	gsi_ctx->per_registered = true;
	mutex_init(&gsi_ctx->mlock);
@@ -816,6 +894,9 @@ int gsi_register_device(struct gsi_per_props *props, unsigned long *dev_hdl)
	gsi_ctx->max_ch = gsi_get_max_channels(gsi_ctx->per.ver);
	if (gsi_ctx->max_ch == 0) {
		devm_iounmap(gsi_ctx->dev, gsi_ctx->base);
		if (running_emulation)
			devm_iounmap(gsi_ctx->dev, gsi_ctx->intcntrlr_base);
		gsi_ctx->base = gsi_ctx->intcntrlr_base = NULL;
		devm_free_irq(gsi_ctx->dev, props->irq, gsi_ctx);
		GSIERR("failed to get max channels\n");
		return -GSI_STATUS_ERROR;
@@ -823,6 +904,9 @@ int gsi_register_device(struct gsi_per_props *props, unsigned long *dev_hdl)
	gsi_ctx->max_ev = gsi_get_max_event_rings(gsi_ctx->per.ver);
	if (gsi_ctx->max_ev == 0) {
		devm_iounmap(gsi_ctx->dev, gsi_ctx->base);
		if (running_emulation)
			devm_iounmap(gsi_ctx->dev, gsi_ctx->intcntrlr_base);
		gsi_ctx->base = gsi_ctx->intcntrlr_base = NULL;
		devm_free_irq(gsi_ctx->dev, props->irq, gsi_ctx);
		GSIERR("failed to get max event rings\n");
		return -GSI_STATUS_ERROR;
@@ -831,7 +915,9 @@ int gsi_register_device(struct gsi_per_props *props, unsigned long *dev_hdl)
	if (props->mhi_er_id_limits_valid &&
	    props->mhi_er_id_limits[0] > (gsi_ctx->max_ev - 1)) {
		devm_iounmap(gsi_ctx->dev, gsi_ctx->base);
		gsi_ctx->base = NULL;
		if (running_emulation)
			devm_iounmap(gsi_ctx->dev, gsi_ctx->intcntrlr_base);
		gsi_ctx->base = gsi_ctx->intcntrlr_base = NULL;
		devm_free_irq(gsi_ctx->dev, props->irq, gsi_ctx);
		GSIERR("MHI event ring start id %u is beyond max %u\n",
			props->mhi_er_id_limits[0], gsi_ctx->max_ev);
@@ -872,6 +958,22 @@ int gsi_register_device(struct gsi_per_props *props, unsigned long *dev_hdl)
		gsi_writel(0, gsi_ctx->base +
			GSI_EE_n_ERROR_LOG_OFFS(gsi_ctx->per.ee));

	if (running_emulation) {
		/*
		 * Set up the emulator's interrupt controller...
		 */
		res = setup_emulator_cntrlr(
		    gsi_ctx->intcntrlr_base, gsi_ctx->intcntrlr_mem_size);
		if (res != 0) {
			devm_iounmap(gsi_ctx->dev, gsi_ctx->base);
			devm_iounmap(gsi_ctx->dev, gsi_ctx->intcntrlr_base);
			gsi_ctx->base = gsi_ctx->intcntrlr_base = NULL;
			devm_free_irq(gsi_ctx->dev, props->irq, gsi_ctx);
			GSIERR("setup_emulator_cntrlr() failed\n");
			return res;
		}
	}

	*dev_hdl = (uintptr_t)gsi_ctx;

	return GSI_STATUS_SUCCESS;
@@ -2730,24 +2832,47 @@ static void gsi_configure_ieps(void *base)
{
	void __iomem *gsi_base = (void __iomem *)base;

	gsi_writel(1, gsi_base + GSI_GSI_IRAM_PTR_CH_CMD_OFFS);
	gsi_writel(2, gsi_base + GSI_GSI_IRAM_PTR_CH_DB_OFFS);
	gsi_writel(3, gsi_base + GSI_GSI_IRAM_PTR_CH_DIS_COMP_OFFS);
	gsi_writel(4, gsi_base + GSI_GSI_IRAM_PTR_CH_EMPTY_OFFS);
	gsi_writel(5, gsi_base + GSI_GSI_IRAM_PTR_EE_GENERIC_CMD_OFFS);
	gsi_writel(6, gsi_base + GSI_GSI_IRAM_PTR_EVENT_GEN_COMP_OFFS);
	gsi_writel(7, gsi_base + GSI_GSI_IRAM_PTR_INT_MOD_STOPED_OFFS);
	gsi_writel(8, gsi_base + GSI_GSI_IRAM_PTR_PERIPH_IF_TLV_IN_0_OFFS);
	gsi_writel(9, gsi_base + GSI_GSI_IRAM_PTR_PERIPH_IF_TLV_IN_2_OFFS);
	gsi_writel(10, gsi_base + GSI_GSI_IRAM_PTR_PERIPH_IF_TLV_IN_1_OFFS);
	gsi_writel(11, gsi_base + GSI_GSI_IRAM_PTR_NEW_RE_OFFS);
	gsi_writel(12, gsi_base + GSI_GSI_IRAM_PTR_READ_ENG_COMP_OFFS);
	gsi_writel(13, gsi_base + GSI_GSI_IRAM_PTR_TIMER_EXPIRED_OFFS);
	gsi_writel(1,
		   gsi_base + GSI_GSI_IRAM_PTR_CH_CMD_OFFS);
	gsi_writel(2,
		   gsi_base + GSI_GSI_IRAM_PTR_CH_DB_OFFS);
	gsi_writel(3,
		   gsi_base + GSI_GSI_IRAM_PTR_CH_DIS_COMP_OFFS);
	gsi_writel(4,
		   gsi_base + GSI_GSI_IRAM_PTR_CH_EMPTY_OFFS);
	gsi_writel(5,
		   gsi_base + GSI_GSI_IRAM_PTR_EE_GENERIC_CMD_OFFS);
	gsi_writel(6,
		   gsi_base + GSI_GSI_IRAM_PTR_EVENT_GEN_COMP_OFFS);
	gsi_writel(7,
		   gsi_base + GSI_GSI_IRAM_PTR_INT_MOD_STOPED_OFFS);
	gsi_writel(8,
		   gsi_base + GSI_GSI_IRAM_PTR_PERIPH_IF_TLV_IN_0_OFFS);
	gsi_writel(9,
		   gsi_base + GSI_GSI_IRAM_PTR_PERIPH_IF_TLV_IN_2_OFFS);
	gsi_writel(10,
		   gsi_base + GSI_GSI_IRAM_PTR_PERIPH_IF_TLV_IN_1_OFFS);
	gsi_writel(11,
		   gsi_base + GSI_GSI_IRAM_PTR_NEW_RE_OFFS);
	gsi_writel(12,
		   gsi_base + GSI_GSI_IRAM_PTR_READ_ENG_COMP_OFFS);
	gsi_writel(13,
		   gsi_base + GSI_GSI_IRAM_PTR_TIMER_EXPIRED_OFFS);

	if (running_emulation) {
		gsi_writel(14,
			   gsi_base + GSI_GSI_IRAM_PTR_EV_DB_OFFS);
		gsi_writel(15,
			   gsi_base + GSI_GSI_IRAM_PTR_UC_GP_INT_OFFS);
		gsi_writel(16,
			   gsi_base + GSI_GSI_IRAM_PTR_WRITE_ENG_COMP_OFFS);
	}
}

static void gsi_configure_bck_prs_matrix(void *base)
{
	void __iomem *gsi_base = (void __iomem *)base;

	/*
	 * For now, these are default values. In the future, GSI FW image will
	 * produce optimized back-pressure values based on the FW image.
@@ -2970,15 +3095,45 @@ static struct platform_driver msm_gsi_driver = {
	},
};

static struct platform_device *pdev;

/**
 * Module Init.
 */
static int __init gsi_init(void)
{
	int ret;

	pr_debug("gsi_init\n");
	return platform_driver_register(&msm_gsi_driver);

	ret = platform_driver_register(&msm_gsi_driver);
	if (ret < 0)
		goto out;

	if (running_emulation) {
		pdev = platform_device_register_simple("gsi", -1, NULL, 0);
		if (IS_ERR(pdev)) {
			ret = PTR_ERR(pdev);
			platform_driver_unregister(&msm_gsi_driver);
			goto out;
		}
	}

out:
	return ret;
}

/*
 * Module exit.
 */
static void __exit gsi_exit(void)
{
	if (running_emulation && pdev)
		platform_device_unregister(pdev);
	platform_driver_unregister(&msm_gsi_driver);
}

module_exit(gsi_exit);
arch_initcall(gsi_init);

MODULE_LICENSE("GPL v2");
+16 −1
Original line number Diff line number Diff line
/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
/* Copyright (c) 2015-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
@@ -18,8 +18,16 @@
#include <linux/mutex.h>
#include <linux/spinlock.h>
#include <linux/msm_gsi.h>
#include <linux/errno.h>
#include <linux/ipc_logging.h>

/*
 * The following for adding code (ie. for EMULATION) not found on x86.
 */
#if IPA_EMULATION_COMPILE == 1
# include "gsi_emulation_stubs.h"
#endif

#define GSI_CHAN_MAX      31
#define GSI_EVT_RING_MAX  23
#define GSI_NO_EVT_ERINDEX 31
@@ -204,6 +212,13 @@ struct gsi_ctx {
	struct completion gen_ee_cmd_compl;
	void *ipc_logbuf;
	void *ipc_logbuf_low;
	/*
	 * The following used only on emulation systems.
	 */
	void __iomem *intcntrlr_base;
	u32 intcntrlr_mem_size;
	irq_handler_t intcntrlr_gsi_isr;
	irq_handler_t intcntrlr_client_isr;
};

enum gsi_re_type {
+233 −0
Original line number Diff line number Diff line
/* 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;
}
Loading