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

Commit 495b6090 authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

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

parents bc40c40e df159af6
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