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

Commit 6a918627 authored by Jennifer L. Zenner's avatar Jennifer L. Zenner Committed by Jie Luo
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>
Signed-off-by: default avatarJie Luo <jluo@codeaurora.org>
parent a7816506
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -122,6 +122,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

+6 −0
Original line number Diff line number Diff line
@@ -166,4 +166,10 @@ config SEEMP_CORE
	  a log and rates the actions according to whether a typical user would
	  use the tools.

config IPA_EMULATION
	bool "IPA on X86 Linux (IPA emulation support)"
	depends on X86 && IPA3
	help
	  This option is used only when building the X86 version of
	  the IPA/GSI driver. Never set this when building for ARM.
endmenu
+2 −0
Original line number Diff line number Diff line
gsidbg-$(CONFIG_DEBUG_FS) += gsi_dbg.o
obj-$(CONFIG_GSI) += gsi.o gsidbg.o

obj-$(CONFIG_IPA_EMULATION) += gsi_emulation.o
+150 −9
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
@@ -42,6 +43,13 @@ static const struct of_device_id msm_gsi_match[] = {
	{ },
};


#if defined(CONFIG_IPA_EMULATION)
static bool running_emulation = true;
#else
static bool running_emulation;
#endif

struct gsi_ctx *gsi_ctx;

static void __gsi_config_type_irq(int ee, uint32_t mask, uint32_t val)
@@ -624,7 +632,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);
@@ -859,17 +867,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, which
		 * 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,
				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)
@@ -889,6 +937,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);
@@ -896,11 +979,21 @@ int gsi_register_device(struct gsi_per_props *props, unsigned long *dev_hdl)
	atomic_set(&gsi_ctx->num_evt_ring, 0);
	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;
	}
	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;
	}
@@ -913,7 +1006,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);
@@ -954,6 +1049,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;
@@ -3103,7 +3214,8 @@ static void gsi_configure_ieps(void *base, enum gsi_ver ver)

static void gsi_configure_bck_prs_matrix(void *base)
{
	void __iomem *gsi_base = 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.
@@ -3372,16 +3484,45 @@ static struct platform_driver msm_gsi_driver = {
	},
};

static struct platform_device *pdev;

/**
 * Module Init.
 */
static int __init gsi_init(void)
{
	pr_debug("%s\n", __func__);
	return platform_driver_register(&msm_gsi_driver);
	int ret;

	pr_debug("gsi_init\n");

	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;
}
arch_initcall(gsi_init);

/*
 * 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);

MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Generic Software Interface (GSI)");
+15 −0
Original line number Diff line number Diff line
@@ -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 defined(CONFIG_IPA_EMULATION)
# include "gsi_emulation_stubs.h"
#endif

#define GSI_CHAN_MAX      31
#define GSI_EVT_RING_MAX  24
#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 {
Loading