Loading Documentation/devicetree/bindings/platform/msm/ipa.txt +4 −0 Original line number Diff line number Diff line Loading @@ -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 Loading drivers/platform/msm/gsi/Makefile +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 drivers/platform/msm/gsi/gsi.c +174 −19 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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) Loading Loading @@ -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); Loading Loading @@ -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) Loading @@ -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); Loading @@ -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; Loading @@ -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; Loading @@ -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); Loading Loading @@ -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; Loading Loading @@ -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. Loading Loading @@ -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"); Loading drivers/platform/msm/gsi/gsi.h +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 Loading @@ -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 Loading Loading @@ -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 drivers/platform/msm/gsi/gsi_emulation.c 0 → 100644 +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
Documentation/devicetree/bindings/platform/msm/ipa.txt +4 −0 Original line number Diff line number Diff line Loading @@ -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 Loading
drivers/platform/msm/gsi/Makefile +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
drivers/platform/msm/gsi/gsi.c +174 −19 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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) Loading Loading @@ -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); Loading Loading @@ -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) Loading @@ -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); Loading @@ -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; Loading @@ -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; Loading @@ -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); Loading Loading @@ -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; Loading Loading @@ -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. Loading Loading @@ -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"); Loading
drivers/platform/msm/gsi/gsi.h +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 Loading @@ -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 Loading Loading @@ -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
drivers/platform/msm/gsi/gsi_emulation.c 0 → 100644 +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; }