From patchwork Thu Jan 28 10:21:36 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Wang, Zhi A" X-Patchwork-Id: 8148561 Return-Path: X-Original-To: patchwork-intel-gfx@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork1.web.kernel.org (Postfix) with ESMTP id CE24B9F440 for ; Thu, 28 Jan 2016 10:24:49 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 4883220218 for ; Thu, 28 Jan 2016 10:24:47 +0000 (UTC) Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) by mail.kernel.org (Postfix) with ESMTP id 5BFE62034F for ; Thu, 28 Jan 2016 10:24:44 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 978536E7EC; Thu, 28 Jan 2016 02:24:43 -0800 (PST) X-Original-To: intel-gfx@lists.freedesktop.org Delivered-To: intel-gfx@lists.freedesktop.org Received: from mga01.intel.com (mga01.intel.com [192.55.52.88]) by gabe.freedesktop.org (Postfix) with ESMTP id 7FB0D6E7EC for ; Thu, 28 Jan 2016 02:24:42 -0800 (PST) Received: from orsmga001.jf.intel.com ([10.7.209.18]) by fmsmga101.fm.intel.com with ESMTP; 28 Jan 2016 02:24:22 -0800 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.22,358,1449561600"; d="scan'208";a="870588247" Received: from dev-inno.bj.intel.com ([10.238.135.69]) by orsmga001.jf.intel.com with ESMTP; 28 Jan 2016 02:24:18 -0800 From: Zhi Wang To: intel-gfx@lists.freedesktop.org, igvt-g@lists.01.org Date: Thu, 28 Jan 2016 18:21:36 +0800 Message-Id: <1453976511-27322-15-git-send-email-zhi.a.wang@intel.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1453976511-27322-1-git-send-email-zhi.a.wang@intel.com> References: <1453976511-27322-1-git-send-email-zhi.a.wang@intel.com> Cc: daniel.vetter@ffwll.ch, david.j.cowperthwaite@intel.com Subject: [Intel-gfx] [RFC 14/29] drm/i915: gvt: vGPU interrupt emulation framework X-BeenThere: intel-gfx@lists.freedesktop.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: Intel graphics driver community testing & development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: intel-gfx-bounces@lists.freedesktop.org Sender: "Intel-gfx" X-Spam-Status: No, score=-4.2 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_MED, RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP This patch introduces vGPU interrupt emulation framework. The vGPU intrerrupt emulation framework is an event-based interrupt emulation framework. It's responsible for emulating GEN hardware interrupts demanded by other GVT-g emulation core component. It consists serveral components: - Descriptions of interrupt register bit definition - Upper level <-> lower level interrupt mapping - GEN HW IER/IMR/IIR register emulation routines - Event-based interrupt propagation interface The specific GVT-g component wants to emulate an interrupt to a VM just need to specify an event: e.g RCS_MI_USER_INTERRUPT to the framework. The framework will: - Generate related virtual IIR bit according to guest virtual IER and IMRs, - Generate related upper level interrupt virtual IIR bit accodring to the cascade interrupt mapping - Inject a MSI to guest Then guest GEN driver will see a virtual GEN interrupt is asserted. Signed-off-by: Zhi Wang --- drivers/gpu/drm/i915/gvt/Makefile | 2 +- drivers/gpu/drm/i915/gvt/gvt.c | 4 + drivers/gpu/drm/i915/gvt/gvt.h | 6 + drivers/gpu/drm/i915/gvt/interrupt.c | 648 +++++++++++++++++++++++++++++++++++ drivers/gpu/drm/i915/gvt/interrupt.h | 232 +++++++++++++ drivers/gpu/drm/i915/gvt/perf.h | 39 +++ drivers/gpu/drm/i915/gvt/reg.h | 31 ++ 7 files changed, 961 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/drm/i915/gvt/interrupt.c create mode 100644 drivers/gpu/drm/i915/gvt/interrupt.h create mode 100644 drivers/gpu/drm/i915/gvt/perf.h diff --git a/drivers/gpu/drm/i915/gvt/Makefile b/drivers/gpu/drm/i915/gvt/Makefile index 285eb84..78deefc 100644 --- a/drivers/gpu/drm/i915/gvt/Makefile +++ b/drivers/gpu/drm/i915/gvt/Makefile @@ -1,5 +1,5 @@ GVT_SOURCE := gvt.o params.o aperture_gm.o mmio.o handlers.o instance.o \ - trace_points.o fb_decoder.o + trace_points.o interrupt.o fb_decoder.o ccflags-y += -I$(src) -I$(src)/.. -Wall -Werror -Wno-unused-function i915_gvt-y := $(GVT_SOURCE) diff --git a/drivers/gpu/drm/i915/gvt/gvt.c b/drivers/gpu/drm/i915/gvt/gvt.c index f5066bc..a0a9667 100644 --- a/drivers/gpu/drm/i915/gvt/gvt.c +++ b/drivers/gpu/drm/i915/gvt/gvt.c @@ -238,6 +238,7 @@ static bool init_service_thread(struct pgt_device *pdev) static void clean_pgt_device(struct pgt_device *pdev) { clean_service_thread(pdev); + gvt_irq_exit(pdev); gvt_clean_mmio_emulation_state(pdev); clean_initial_mmio_state(pdev); gvt_clean_resource_allocator(pdev); @@ -258,6 +259,9 @@ static bool init_pgt_device(struct pgt_device *pdev, struct drm_i915_private *de if (!gvt_setup_mmio_emulation_state(pdev)) goto err; + if (!gvt_irq_init(pdev)) + goto err; + if (!init_service_thread(pdev)) goto err; diff --git a/drivers/gpu/drm/i915/gvt/gvt.h b/drivers/gpu/drm/i915/gvt/gvt.h index c58305f..fad56b1 100644 --- a/drivers/gpu/drm/i915/gvt/gvt.h +++ b/drivers/gpu/drm/i915/gvt/gvt.h @@ -34,6 +34,8 @@ #include "mpt.h" #include "fb_decoder.h" #include "mmio.h" +#include "interrupt.h" +#include "perf.h" #define GVT_MAX_VGPU 8 @@ -111,6 +113,7 @@ struct vgt_device { bool warn_untrack; atomic_t active; struct gvt_virtual_device_state state; + struct gvt_statistics stat; }; struct gvt_gm_allocator { @@ -160,6 +163,9 @@ struct pgt_device { u32 aux_table_index; DECLARE_HASHTABLE(mmio_table, GVT_HASH_BITS); + + struct gvt_irq_state irq_state; + struct pgt_statistics stat; }; /* definitions for physical aperture/GM space */ diff --git a/drivers/gpu/drm/i915/gvt/interrupt.c b/drivers/gpu/drm/i915/gvt/interrupt.c new file mode 100644 index 0000000..23d40ce --- /dev/null +++ b/drivers/gpu/drm/i915/gvt/interrupt.c @@ -0,0 +1,648 @@ +/* + * Copyright(c) 2011-2016 Intel Corporation. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "gvt.h" +#include "interrupt.h" + +static void update_upstream_irq(struct vgt_device *vgt, + struct gvt_irq_info *info); + +static int gvt_irq_warn_once[GVT_MAX_VGPU+1][GVT_EVENT_MAX]; + +char *gvt_irq_name[GVT_EVENT_MAX] = { + // GT + [RCS_MI_USER_INTERRUPT] = "Render Command Streamer MI USER INTERRUPT", + [RCS_DEBUG] = "Render EU debug from SVG", + [RCS_MMIO_SYNC_FLUSH] = "Render MMIO sync flush status", + [RCS_CMD_STREAMER_ERR] = "Render Command Streamer error interrupt", + [RCS_PIPE_CONTROL] = "Render PIPE CONTROL notify", + [RCS_WATCHDOG_EXCEEDED] = "Render Command Streamer Watchdog counter exceeded", + [RCS_PAGE_DIRECTORY_FAULT] = "Render page directory faults", + [RCS_AS_CONTEXT_SWITCH] = "Render AS Context Switch Interrupt", + + [VCS_MI_USER_INTERRUPT] = "Video Command Streamer MI USER INTERRUPT", + [VCS_MMIO_SYNC_FLUSH] = "Video MMIO sync flush status", + [VCS_CMD_STREAMER_ERR] = "Video Command Streamer error interrupt", + [VCS_MI_FLUSH_DW] = "Video MI FLUSH DW notify", + [VCS_WATCHDOG_EXCEEDED] = "Video Command Streamer Watchdog counter exceeded", + [VCS_PAGE_DIRECTORY_FAULT] = "Video page directory faults", + [VCS_AS_CONTEXT_SWITCH] = "Video AS Context Switch Interrupt", + [VCS2_MI_USER_INTERRUPT] = "VCS2 Video Command Streamer MI USER INTERRUPT", + [VCS2_MI_FLUSH_DW] = "VCS2 Video MI FLUSH DW notify", + [VCS2_AS_CONTEXT_SWITCH] = "VCS2 Context Switch Interrupt", + + [BCS_MI_USER_INTERRUPT] = "Blitter Command Streamer MI USER INTERRUPT", + [BCS_MMIO_SYNC_FLUSH] = "Billter MMIO sync flush status", + [BCS_CMD_STREAMER_ERR] = "Blitter Command Streamer error interrupt", + [BCS_MI_FLUSH_DW] = "Blitter MI FLUSH DW notify", + [BCS_PAGE_DIRECTORY_FAULT] = "Blitter page directory faults", + [BCS_AS_CONTEXT_SWITCH] = "Blitter AS Context Switch Interrupt", + + [VECS_MI_FLUSH_DW] = "Video Enhanced Streamer MI FLUSH DW notify", + [VECS_AS_CONTEXT_SWITCH] = "VECS Context Switch Interrupt", + + // DISPLAY + [PIPE_A_FIFO_UNDERRUN] = "Pipe A FIFO underrun", + [PIPE_A_CRC_ERR] = "Pipe A CRC error", + [PIPE_A_CRC_DONE] = "Pipe A CRC done", + [PIPE_A_VSYNC] = "Pipe A vsync", + [PIPE_A_LINE_COMPARE] = "Pipe A line compare", + [PIPE_A_ODD_FIELD] = "Pipe A odd field", + [PIPE_A_EVEN_FIELD] = "Pipe A even field", + [PIPE_A_VBLANK] = "Pipe A vblank", + [PIPE_B_FIFO_UNDERRUN] = "Pipe B FIFO underrun", + [PIPE_B_CRC_ERR] = "Pipe B CRC error", + [PIPE_B_CRC_DONE] = "Pipe B CRC done", + [PIPE_B_VSYNC] = "Pipe B vsync", + [PIPE_B_LINE_COMPARE] = "Pipe B line compare", + [PIPE_B_ODD_FIELD] = "Pipe B odd field", + [PIPE_B_EVEN_FIELD] = "Pipe B even field", + [PIPE_B_VBLANK] = "Pipe B vblank", + [PIPE_C_VBLANK] = "Pipe C vblank", + [DPST_PHASE_IN] = "DPST phase in event", + [DPST_HISTOGRAM] = "DPST histogram event", + [GSE] = "GSE", + [DP_A_HOTPLUG] = "DP A Hotplug", + [AUX_CHANNEL_A] = "AUX Channel A", + [PERF_COUNTER] = "Performance counter", + [POISON] = "Poison", + [GTT_FAULT] = "GTT fault", + [PRIMARY_A_FLIP_DONE] = "Primary Plane A flip done", + [PRIMARY_B_FLIP_DONE] = "Primary Plane B flip done", + [PRIMARY_C_FLIP_DONE] = "Primary Plane C flip done", + [SPRITE_A_FLIP_DONE] = "Sprite Plane A flip done", + [SPRITE_B_FLIP_DONE] = "Sprite Plane B flip done", + [SPRITE_C_FLIP_DONE] = "Sprite Plane C flip done", + + [PCU_THERMAL] = "PCU Thermal Event", + [PCU_PCODE2DRIVER_MAILBOX] = "PCU pcode2driver mailbox event", + + // PCH + [FDI_RX_INTERRUPTS_TRANSCODER_A] = "FDI RX Interrupts Combined A", + [AUDIO_CP_CHANGE_TRANSCODER_A] = "Audio CP Change Transcoder A", + [AUDIO_CP_REQUEST_TRANSCODER_A] = "Audio CP Request Transcoder A", + [FDI_RX_INTERRUPTS_TRANSCODER_B] = "FDI RX Interrupts Combined B", + [AUDIO_CP_CHANGE_TRANSCODER_B] = "Audio CP Change Transcoder B", + [AUDIO_CP_REQUEST_TRANSCODER_B] = "Audio CP Request Transcoder B", + [FDI_RX_INTERRUPTS_TRANSCODER_C] = "FDI RX Interrupts Combined C", + [AUDIO_CP_CHANGE_TRANSCODER_C] = "Audio CP Change Transcoder C", + [AUDIO_CP_REQUEST_TRANSCODER_C] = "Audio CP Request Transcoder C", + [ERR_AND_DBG] = "South Error and Debug Interupts Combined", + [GMBUS] = "Gmbus", + [SDVO_B_HOTPLUG] = "SDVO B hotplug", + [CRT_HOTPLUG] = "CRT Hotplug", + [DP_B_HOTPLUG] = "DisplayPort/HDMI/DVI B Hotplug", + [DP_C_HOTPLUG] = "DisplayPort/HDMI/DVI C Hotplug", + [DP_D_HOTPLUG] = "DisplayPort/HDMI/DVI D Hotplug", + [AUX_CHENNEL_B] = "AUX Channel B", + [AUX_CHENNEL_C] = "AUX Channel C", + [AUX_CHENNEL_D] = "AUX Channel D", + [AUDIO_POWER_STATE_CHANGE_B] = "Audio Power State change Port B", + [AUDIO_POWER_STATE_CHANGE_C] = "Audio Power State change Port C", + [AUDIO_POWER_STATE_CHANGE_D] = "Audio Power State change Port D", + + [GVT_EVENT_RESERVED] = "RESERVED EVENTS!!!", +}; + +static inline struct gvt_irq_info *regbase_to_irq_info(struct pgt_device *pdev, + unsigned int reg) +{ + struct gvt_irq_state *state = &pdev->irq_state; + int i; + + for_each_set_bit(i, state->irq_info_bitmap, GVT_IRQ_INFO_MAX) { + if (state->info[i]->reg_base == reg) + return state->info[i]; + } + + return NULL; +} + +bool gvt_reg_imr_handler(struct vgt_device *vgt, + unsigned int reg, void *p_data, unsigned int bytes) +{ + uint32_t changed, masked, unmasked; + uint32_t imr = *(u32 *)p_data; + struct pgt_device *pdev = vgt->pdev; + struct gvt_irq_ops *ops = gvt_get_irq_ops(pdev); + + gvt_dbg_irq("IRQ: capture IMR write on reg (%x) with val (%x)", + reg, imr); + + gvt_dbg_irq("IRQ: old vIMR(%x), pIMR(%x)", + __vreg(vgt, reg), gvt_mmio_read(pdev, reg)); + + /* figure out newly masked/unmasked bits */ + changed = __vreg(vgt, reg) ^ imr; + masked = (__vreg(vgt, reg) & changed) ^ changed; + unmasked = masked ^ changed; + + gvt_dbg_irq("IRQ: changed (%x), masked(%x), unmasked (%x)", + changed, masked, unmasked); + + __vreg(vgt, reg) = imr; + + ops->check_pending_irq(vgt); + gvt_dbg_irq("IRQ: new vIMR(%x), pIMR(%x)", + __vreg(vgt, reg), gvt_mmio_read(pdev, reg)); + return true; +} + +bool gvt_reg_master_irq_handler(struct vgt_device *vgt, + unsigned int reg, void *p_data, unsigned int bytes) +{ + uint32_t changed, enabled, disabled; + uint32_t ier = *(u32 *)p_data; + uint32_t virtual_ier = __vreg(vgt, reg); + struct pgt_device *pdev = vgt->pdev; + struct gvt_irq_ops *ops = gvt_get_irq_ops(pdev); + + gvt_dbg_irq("IRQ: capture master irq write on reg (%x) with val (%x)", + reg, ier); + + gvt_dbg_irq("IRQ: old vreg(%x), preg(%x)", + __vreg(vgt, reg), gvt_mmio_read(pdev, reg)); + + /* + * GEN8_MASTER_IRQ is a special irq register, + * only bit 31 is allowed to be modified + * and treated as an IER bit. + */ + ier &= GEN8_MASTER_IRQ_CONTROL; + virtual_ier &= GEN8_MASTER_IRQ_CONTROL; + __vreg(vgt, reg) &= ~GEN8_MASTER_IRQ_CONTROL; + __vreg(vgt, reg) |= ier; + + /* figure out newly enabled/disable bits */ + changed = virtual_ier ^ ier; + enabled = (virtual_ier & changed) ^ changed; + disabled = enabled ^ changed; + + gvt_dbg_irq("vGT_IRQ: changed (%x), enabled(%x), disabled(%x)", + changed, enabled, disabled); + + ops->check_pending_irq(vgt); + gvt_dbg_irq("IRQ: new vreg(%x), preg(%x)", + __vreg(vgt, reg), gvt_mmio_read(pdev, reg)); + return true; +} + +bool gvt_reg_ier_handler(struct vgt_device *vgt, + unsigned int reg, void *p_data, unsigned int bytes) +{ + uint32_t changed, enabled, disabled; + uint32_t ier = *(u32 *)p_data; + struct pgt_device *pdev = vgt->pdev; + struct gvt_irq_ops *ops = gvt_get_irq_ops(pdev); + struct gvt_irq_info *info; + + gvt_dbg_irq("IRQ: capture IER write on reg (%x) with val (%x)", + reg, ier); + + gvt_dbg_irq("IRQ: old vIER(%x), pIER(%x)", + __vreg(vgt, reg), gvt_mmio_read(pdev, reg)); + + /* figure out newly enabled/disable bits */ + changed = __vreg(vgt, reg) ^ ier; + enabled = (__vreg(vgt, reg) & changed) ^ changed; + disabled = enabled ^ changed; + + gvt_dbg_irq("vGT_IRQ: changed (%x), enabled(%x), disabled(%x)", + changed, enabled, disabled); + __vreg(vgt, reg) = ier; + + info = regbase_to_irq_info(pdev, ier_to_regbase(reg)); + if (!info) + return false; + + if (info->has_upstream_irq) + update_upstream_irq(vgt, info); + + ops->check_pending_irq(vgt); + gvt_dbg_irq("IRQ: new vIER(%x), pIER(%x)", + __vreg(vgt, reg), gvt_mmio_read(pdev, reg)); + return true; +} + +bool gvt_reg_iir_handler(struct vgt_device *vgt, unsigned int reg, + void *p_data, unsigned int bytes) +{ + struct gvt_irq_info *info = regbase_to_irq_info(vgt->pdev, iir_to_regbase(reg)); + u32 iir = *(u32 *)p_data; + + gvt_dbg_irq("IRQ: capture IIR write on reg (%x) with val (%x)", + reg, iir); + + if (!info) + return false; + + /* TODO: need use an atomic operation. Now it's safe due to big lock */ + __vreg(vgt, reg) &= ~iir; + + if (info->has_upstream_irq) + update_upstream_irq(vgt, info); + + return true; +} + +bool gvt_reg_isr_write(struct vgt_device *vgt, unsigned int reg, + void *p_data, unsigned int bytes) +{ + gvt_dbg_irq("IRQ: capture ISR write on reg (%x) with val (%x)." \ + " Will be ignored!", reg, *(u32 *)p_data); + + return true; +} + +struct gvt_irq_map gen8_irq_map[] = { + { GVT_IRQ_INFO_MASTER, 0, GVT_IRQ_INFO_GT0, 0xffff }, + { GVT_IRQ_INFO_MASTER, 1, GVT_IRQ_INFO_GT0, 0xffff0000 }, + { GVT_IRQ_INFO_MASTER, 2, GVT_IRQ_INFO_GT1, 0xffff }, + { GVT_IRQ_INFO_MASTER, 3, GVT_IRQ_INFO_GT1, 0xffff0000 }, + { GVT_IRQ_INFO_MASTER, 4, GVT_IRQ_INFO_GT2, 0xffff }, + { GVT_IRQ_INFO_MASTER, 6, GVT_IRQ_INFO_GT3, 0xffff }, + { GVT_IRQ_INFO_MASTER, 16, GVT_IRQ_INFO_DE_PIPE_A, ~0 }, + { GVT_IRQ_INFO_MASTER, 17, GVT_IRQ_INFO_DE_PIPE_B, ~0 }, + { GVT_IRQ_INFO_MASTER, 18, GVT_IRQ_INFO_DE_PIPE_C, ~0 }, + { GVT_IRQ_INFO_MASTER, 20, GVT_IRQ_INFO_DE_PORT, ~0 }, + { GVT_IRQ_INFO_MASTER, 22, GVT_IRQ_INFO_DE_MISC, ~0 }, + { GVT_IRQ_INFO_MASTER, 23, GVT_IRQ_INFO_PCH, ~0 }, + { GVT_IRQ_INFO_MASTER, 30, GVT_IRQ_INFO_PCU, ~0 }, + { -1, -1, ~0 }, +}; + +static void update_upstream_irq(struct vgt_device *vgt, + struct gvt_irq_info *info) +{ + struct gvt_irq_state *state = &vgt->pdev->irq_state; + struct gvt_irq_map *map = state->irq_map; + struct gvt_irq_info *up_irq_info = NULL; + u32 set_bits = 0; + u32 clear_bits = 0; + int bit; + u32 val = __vreg(vgt, regbase_to_iir(info->reg_base)) + & __vreg(vgt, regbase_to_ier(info->reg_base)); + + if (!info->has_upstream_irq) + return; + + for (map = state->irq_map; map->up_irq_bit != -1; map++) { + if (info->group != map->down_irq_group) + continue; + + if (!up_irq_info) + up_irq_info = state->info[map->up_irq_group]; + else + ASSERT(up_irq_info == state->info[map->up_irq_group]); + + bit = map->up_irq_bit; + + if (val & map->down_irq_bitmask) + set_bits |= (1 << bit); + else + clear_bits |= (1 << bit); + } + + ASSERT(up_irq_info); + + if (up_irq_info->group == GVT_IRQ_INFO_MASTER) { + u32 isr = up_irq_info->reg_base; + __vreg(vgt, isr) &= ~clear_bits; + __vreg(vgt, isr) |= set_bits; + } else { + u32 iir = regbase_to_iir(up_irq_info->reg_base); + u32 imr = regbase_to_imr(up_irq_info->reg_base); + __vreg(vgt, iir) |= (set_bits & ~__vreg(vgt, imr)); + } + + if (up_irq_info->has_upstream_irq) + update_upstream_irq(vgt, up_irq_info); +} + +static void gvt_irq_map_init(struct gvt_irq_state *state) +{ + struct gvt_irq_map *map; + struct gvt_irq_info *up_info, *down_info; + int up_bit; + + for (map = state->irq_map; map->up_irq_bit != -1; map++) { + up_info = state->info[map->up_irq_group]; + up_bit = map->up_irq_bit; + down_info = state->info[map->down_irq_group]; + + set_bit(up_bit, up_info->downstream_irq_bitmap); + down_info->has_upstream_irq = true; + + gvt_dbg_irq("irq map [upstream] group: %d, bit: %d -> [downstream] group: %d, bitmask: 0x%x", + up_info->group, up_bit, down_info->group, map->down_irq_bitmask); + } +} + +/* =======================vEvent injection===================== */ +static int gvt_inject_virtual_interrupt(struct vgt_device *vgt) +{ + hypervisor_inject_msi(vgt); + + vgt->stat.irq_num++; + vgt->stat.last_injection = get_cycles(); + return 0; +} + +static void propagate_event(struct gvt_irq_state *state, + enum gvt_event_type event, struct vgt_device *vgt) +{ + int bit; + struct gvt_irq_info *info; + unsigned int reg_base; + + info = gvt_get_irq_info(state, event); + if (!info) { + gvt_err("IRQ(%d): virt-inject: no irq reg info!!!", + vgt->vm_id); + return; + } + + reg_base = info->reg_base; + bit = state->events[event].bit; + + if (!test_bit(bit, (void*)&__vreg(vgt, regbase_to_imr(reg_base)))) { + gvt_dbg_irq("IRQ: set bit (%d) for (%s) for VM (%d)", + bit, gvt_irq_name[event], vgt->vm_id); + set_bit(bit, (void*)&__vreg(vgt, regbase_to_iir(reg_base))); + } +} + +/* =======================vEvent Handlers===================== */ +static void handle_default_event_virt(struct gvt_irq_state *state, + enum gvt_event_type event, struct vgt_device *vgt) +{ + if (!gvt_irq_warn_once[vgt->id][event]) { + gvt_info("IRQ: VM(%d) receive event %d (%s)", + vgt->vm_id, event, gvt_irq_name[event]); + gvt_irq_warn_once[vgt->id][event] = 1; + } + propagate_event(state, event, vgt); + vgt->stat.events[event]++; +} + +/* =====================GEN specific logic======================= */ +/* GEN8 interrupt routines. */ + +#define DEFINE_GVT_GEN8_GVT_IRQ_INFO(regname, regbase) \ + static struct gvt_irq_info gen8_##regname##_info = { \ + .name = #regname"-IRQ", \ + .reg_base = regbase, \ + .bit_to_event = {[0 ... GVT_IRQ_BITWIDTH-1] = GVT_EVENT_RESERVED}, \ + }; + +DEFINE_GVT_GEN8_GVT_IRQ_INFO(gt0, _GEN8_GT_ISR(0)); +DEFINE_GVT_GEN8_GVT_IRQ_INFO(gt1, _GEN8_GT_ISR(1)); +DEFINE_GVT_GEN8_GVT_IRQ_INFO(gt2, _GEN8_GT_ISR(2)); +DEFINE_GVT_GEN8_GVT_IRQ_INFO(gt3, _GEN8_GT_ISR(3)); +DEFINE_GVT_GEN8_GVT_IRQ_INFO(de_pipe_a, _GEN8_DE_PIPE_ISR(PIPE_A)); +DEFINE_GVT_GEN8_GVT_IRQ_INFO(de_pipe_b, _GEN8_DE_PIPE_ISR(PIPE_B)); +DEFINE_GVT_GEN8_GVT_IRQ_INFO(de_pipe_c, _GEN8_DE_PIPE_ISR(PIPE_C)); +DEFINE_GVT_GEN8_GVT_IRQ_INFO(de_port, _GEN8_DE_PORT_ISR); +DEFINE_GVT_GEN8_GVT_IRQ_INFO(de_misc, _GEN8_DE_MISC_ISR); +DEFINE_GVT_GEN8_GVT_IRQ_INFO(pcu, _GEN8_PCU_ISR); +DEFINE_GVT_GEN8_GVT_IRQ_INFO(master, _GEN8_MASTER_IRQ); + +static struct gvt_irq_info gvt_base_pch_info = { + .name = "PCH-IRQ", + .reg_base = _SDEISR, + .bit_to_event = {[0 ... GVT_IRQ_BITWIDTH-1] = GVT_EVENT_RESERVED}, +}; + +static void gen8_check_pending_irq(struct vgt_device *vgt) +{ + struct gvt_irq_state *state = &vgt->pdev->irq_state; + int i; + + if (!(__vreg(vgt, _GEN8_MASTER_IRQ) & + GEN8_MASTER_IRQ_CONTROL)) + return; + + for_each_set_bit(i, state->irq_info_bitmap, GVT_IRQ_INFO_MAX) { + struct gvt_irq_info *info = state->info[i]; + + if (!info->has_upstream_irq) + continue; + + if ((__vreg(vgt, regbase_to_iir(info->reg_base)) + & __vreg(vgt, regbase_to_ier(info->reg_base)))) + update_upstream_irq(vgt, info); + } + + if (__vreg(vgt, _GEN8_MASTER_IRQ) & ~GEN8_MASTER_IRQ_CONTROL) + gvt_inject_virtual_interrupt(vgt); +} + +static void gen8_init_irq( + struct gvt_irq_state *state) +{ + struct pgt_device *pdev = gvt_irq_state_to_pdev(state); + +#define SET_BIT_INFO(s, b, e, i) \ + do { \ + s->events[e].bit = b; \ + s->events[e].info = s->info[i]; \ + s->info[i]->bit_to_event[b] = e;\ + } while (0); + +#define SET_IRQ_GROUP(s, g, i) \ + do { \ + s->info[g] = i; \ + (i)->group = g; \ + set_bit(g, s->irq_info_bitmap); \ + } while (0); + + SET_IRQ_GROUP(state, GVT_IRQ_INFO_MASTER, &gen8_master_info); + SET_IRQ_GROUP(state, GVT_IRQ_INFO_GT0, &gen8_gt0_info); + SET_IRQ_GROUP(state, GVT_IRQ_INFO_GT1, &gen8_gt1_info); + SET_IRQ_GROUP(state, GVT_IRQ_INFO_GT2, &gen8_gt2_info); + SET_IRQ_GROUP(state, GVT_IRQ_INFO_GT3, &gen8_gt3_info); + SET_IRQ_GROUP(state, GVT_IRQ_INFO_DE_PIPE_A, &gen8_de_pipe_a_info); + SET_IRQ_GROUP(state, GVT_IRQ_INFO_DE_PIPE_B, &gen8_de_pipe_b_info); + SET_IRQ_GROUP(state, GVT_IRQ_INFO_DE_PIPE_C, &gen8_de_pipe_c_info); + SET_IRQ_GROUP(state, GVT_IRQ_INFO_DE_PORT, &gen8_de_port_info); + SET_IRQ_GROUP(state, GVT_IRQ_INFO_DE_MISC, &gen8_de_misc_info); + SET_IRQ_GROUP(state, GVT_IRQ_INFO_PCU, &gen8_pcu_info); + SET_IRQ_GROUP(state, GVT_IRQ_INFO_PCH, &gvt_base_pch_info); + + /* GEN8 level 2 interrupts. */ + + /* GEN8 interrupt GT0 events */ + SET_BIT_INFO(state, 0, RCS_MI_USER_INTERRUPT, GVT_IRQ_INFO_GT0); + SET_BIT_INFO(state, 4, RCS_PIPE_CONTROL, GVT_IRQ_INFO_GT0); + SET_BIT_INFO(state, 8, RCS_AS_CONTEXT_SWITCH, GVT_IRQ_INFO_GT0); + + SET_BIT_INFO(state, 16, BCS_MI_USER_INTERRUPT, GVT_IRQ_INFO_GT0); + SET_BIT_INFO(state, 20, BCS_MI_FLUSH_DW, GVT_IRQ_INFO_GT0); + SET_BIT_INFO(state, 24, BCS_AS_CONTEXT_SWITCH, GVT_IRQ_INFO_GT0); + + /* GEN8 interrupt GT1 events */ + SET_BIT_INFO(state, 0, VCS_MI_USER_INTERRUPT, GVT_IRQ_INFO_GT1); + SET_BIT_INFO(state, 4, VCS_MI_FLUSH_DW, GVT_IRQ_INFO_GT1); + SET_BIT_INFO(state, 8, VCS_AS_CONTEXT_SWITCH, GVT_IRQ_INFO_GT1); + + if (IS_BDW_GT3(pdev->dev_priv)) { + SET_BIT_INFO(state, 16, VCS2_MI_USER_INTERRUPT, GVT_IRQ_INFO_GT1); + SET_BIT_INFO(state, 20, VCS2_MI_FLUSH_DW, GVT_IRQ_INFO_GT1); + SET_BIT_INFO(state, 24, VCS2_AS_CONTEXT_SWITCH, GVT_IRQ_INFO_GT1); + } + + /* GEN8 interrupt GT3 events */ + SET_BIT_INFO(state, 0, VECS_MI_USER_INTERRUPT, GVT_IRQ_INFO_GT3); + SET_BIT_INFO(state, 4, VECS_MI_FLUSH_DW, GVT_IRQ_INFO_GT3); + SET_BIT_INFO(state, 8, VECS_AS_CONTEXT_SWITCH, GVT_IRQ_INFO_GT3); + + SET_BIT_INFO(state, 0, PIPE_A_VBLANK, GVT_IRQ_INFO_DE_PIPE_A); + SET_BIT_INFO(state, 4, PRIMARY_A_FLIP_DONE, GVT_IRQ_INFO_DE_PIPE_A); + SET_BIT_INFO(state, 5, SPRITE_A_FLIP_DONE, GVT_IRQ_INFO_DE_PIPE_A); + + SET_BIT_INFO(state, 0, PIPE_B_VBLANK, GVT_IRQ_INFO_DE_PIPE_B); + SET_BIT_INFO(state, 4, PRIMARY_B_FLIP_DONE, GVT_IRQ_INFO_DE_PIPE_B); + SET_BIT_INFO(state, 5, SPRITE_B_FLIP_DONE, GVT_IRQ_INFO_DE_PIPE_B); + + SET_BIT_INFO(state, 0, PIPE_C_VBLANK, GVT_IRQ_INFO_DE_PIPE_C); + SET_BIT_INFO(state, 4, PRIMARY_C_FLIP_DONE, GVT_IRQ_INFO_DE_PIPE_C); + SET_BIT_INFO(state, 5, SPRITE_C_FLIP_DONE, GVT_IRQ_INFO_DE_PIPE_C); + + /* GEN8 interrupt DE PORT events */ + SET_BIT_INFO(state, 0, AUX_CHANNEL_A, GVT_IRQ_INFO_DE_PORT); + SET_BIT_INFO(state, 3, DP_A_HOTPLUG, GVT_IRQ_INFO_DE_PORT); + + /* GEN8 interrupt DE MISC events */ + SET_BIT_INFO(state, 0, GSE, GVT_IRQ_INFO_DE_MISC); + + /* PCH events */ + SET_BIT_INFO(state, 17, GMBUS, GVT_IRQ_INFO_PCH); + SET_BIT_INFO(state, 19, CRT_HOTPLUG, GVT_IRQ_INFO_PCH); + SET_BIT_INFO(state, 21, DP_B_HOTPLUG, GVT_IRQ_INFO_PCH); + SET_BIT_INFO(state, 22, DP_C_HOTPLUG, GVT_IRQ_INFO_PCH); + SET_BIT_INFO(state, 23, DP_D_HOTPLUG, GVT_IRQ_INFO_PCH); + SET_BIT_INFO(state, 25, AUX_CHENNEL_B, GVT_IRQ_INFO_PCH); + SET_BIT_INFO(state, 26, AUX_CHENNEL_C, GVT_IRQ_INFO_PCH); + SET_BIT_INFO(state, 27, AUX_CHENNEL_D, GVT_IRQ_INFO_PCH); + + /* GEN8 interrupt PCU events */ + SET_BIT_INFO(state, 24, PCU_THERMAL, GVT_IRQ_INFO_PCU); + SET_BIT_INFO(state, 25, PCU_PCODE2DRIVER_MAILBOX, GVT_IRQ_INFO_PCU); +} + +struct gvt_irq_ops gen8_irq_ops = { + .init_irq = gen8_init_irq, + .check_pending_irq = gen8_check_pending_irq, +}; + +/* ======================common event logic====================== */ + +/* + * Trigger a virtual event which comes from other requests like hotplug agent + * instead of from pirq. + */ +void gvt_trigger_virtual_event(struct vgt_device *vgt, + enum gvt_event_type event) +{ + struct pgt_device *pdev = vgt->pdev; + struct gvt_irq_state *state = &pdev->irq_state; + gvt_event_virt_handler_t handler; + struct gvt_irq_ops *ops = gvt_get_irq_ops(pdev); + + handler = gvt_get_event_virt_handler(state, event); + ASSERT(handler); + + handler(state, event, vgt); + + ops->check_pending_irq(vgt); +} + +/* default handler will be invoked, if not explicitly specified here */ +static void gvt_init_events( + struct gvt_irq_state *state) +{ + int i; + +#define SET_POLICY_ALL(h, e) \ + ((h)->events[e].policy = GVT_EVENT_FW_ALL) +#define SET_POLICY_NONE(h, e) \ + ((h)->events[e].policy = GVT_EVENT_FW_NONE) +#define SET_V_HANDLER(s, e, h) \ + ((s)->events[e].v_handler = h) + + for (i = 0; i < GVT_EVENT_MAX; i++) { + state->events[i].info = NULL; + /* Default forwarding to all VMs (render and most display events) */ + SET_POLICY_ALL(state, i); + state->events[i].v_handler = handle_default_event_virt;; + } +} + +/* + * Do interrupt initialization for vGT driver + */ +bool gvt_irq_init(struct pgt_device *pdev) +{ + struct gvt_irq_state *state = &pdev->irq_state; + + gvt_dbg_core("init irq framework"); + + if (IS_BROADWELL(pdev->dev_priv)) { + state->ops = &gen8_irq_ops; + state->irq_map = gen8_irq_map; + } else { + gvt_err("Unsupported device"); + return false; + } + + /* common event initialization */ + gvt_init_events(state); + + /* gen specific initialization */ + state->ops->init_irq(state); + + gvt_irq_map_init(state); + + return true; +} + +void gvt_irq_exit(struct pgt_device *pdev) +{ + return; +} + +void gvt_inject_flip_done(struct vgt_device *vgt, int pipe) +{ + enum gvt_event_type event = GVT_EVENT_MAX; + + if (pipe == PIPE_A) { + event = PRIMARY_A_FLIP_DONE; + } else if (pipe == PIPE_B) { + event = PRIMARY_B_FLIP_DONE; + } else if (pipe == PIPE_C) { + event = PRIMARY_C_FLIP_DONE; + } + + if (event != GVT_EVENT_MAX) + gvt_trigger_virtual_event(vgt, event); +} diff --git a/drivers/gpu/drm/i915/gvt/interrupt.h b/drivers/gpu/drm/i915/gvt/interrupt.h new file mode 100644 index 0000000..cee85b6 --- /dev/null +++ b/drivers/gpu/drm/i915/gvt/interrupt.h @@ -0,0 +1,232 @@ +/* + * Copyright(c) 2011-2016 Intel Corporation. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _GVT_INTERRUPT_H_ +#define _GVT_INTERRUPT_H_ + +enum gvt_event_type { + // GT + RCS_MI_USER_INTERRUPT = 0, + RCS_DEBUG, + RCS_MMIO_SYNC_FLUSH, + RCS_CMD_STREAMER_ERR, + RCS_PIPE_CONTROL, + RCS_L3_PARITY_ERR, /* IVB */ + RCS_WATCHDOG_EXCEEDED, + RCS_PAGE_DIRECTORY_FAULT, + RCS_AS_CONTEXT_SWITCH, + RCS_MONITOR_BUFF_HALF_FULL, /* IVB */ + + VCS_MI_USER_INTERRUPT, + VCS_MMIO_SYNC_FLUSH, + VCS_CMD_STREAMER_ERR, + VCS_MI_FLUSH_DW, + VCS_WATCHDOG_EXCEEDED, + VCS_PAGE_DIRECTORY_FAULT, + VCS_AS_CONTEXT_SWITCH, + + VCS2_MI_USER_INTERRUPT, + VCS2_MI_FLUSH_DW, + VCS2_AS_CONTEXT_SWITCH, + + BCS_MI_USER_INTERRUPT, + BCS_MMIO_SYNC_FLUSH, + BCS_CMD_STREAMER_ERR, + BCS_MI_FLUSH_DW, + BCS_PAGE_DIRECTORY_FAULT, + BCS_AS_CONTEXT_SWITCH, + + VECS_MI_USER_INTERRUPT, + VECS_MI_FLUSH_DW, + VECS_AS_CONTEXT_SWITCH, + + // DISPLAY + PIPE_A_FIFO_UNDERRUN, /* This is an active high level for the duration of the Pipe A FIFO underrun */ + PIPE_B_FIFO_UNDERRUN, /* This is an active high level for the duration of the Pipe B FIFO underrun */ + PIPE_A_CRC_ERR, /* This is an active high pulse on the Pipe A CRC error */ + PIPE_B_CRC_ERR, /* This is an active high pulse on the Pipe B CRC error */ + PIPE_A_CRC_DONE, /* This is an active high pulse on the Pipe A CRC done */ + PIPE_B_CRC_DONE, /* This is an active high pulse on the Pipe B CRC done */ + PIPE_A_ODD_FIELD, /* This is an active high level for the duration of the Pipe A interlaced odd field */ + PIPE_B_ODD_FIELD, /* This is an active high level for the duration of the Pipe B interlaced odd field */ + PIPE_A_EVEN_FIELD, /* This is an active high level for the duration of the Pipe A interlaced even field */ + PIPE_B_EVEN_FIELD, /* This is an active high level for the duration of the Pipe B interlaced even field */ + PIPE_A_LINE_COMPARE, /* This is an active high level for the duration of the selected Pipe A scan lines */ + PIPE_B_LINE_COMPARE, /* This is an active high level for the duration of the selected Pipe B scan lines */ + PIPE_C_LINE_COMPARE, /* This is an active high level for the duration of the selected Pipe C scan lines */ + PIPE_A_VBLANK, /* This is an active high level for the duration of the Pipe A vertical blank */ + PIPE_B_VBLANK, /* This is an active high level for the duration of the Pipe B vertical blank */ + PIPE_C_VBLANK, /* This is an active high level for the duration of the Pipe C vertical blank */ + PIPE_A_VSYNC, /* This is an active high level for the duration of the Pipe A vertical sync */ + PIPE_B_VSYNC, /* This is an active high level for the duration of the Pipe B vertical sync */ + PIPE_C_VSYNC, /* This is an active high level for the duration of the Pipe C vertical sync */ + PRIMARY_A_FLIP_DONE, /* This is an active high pulse when a primary plane A flip is done */ + PRIMARY_B_FLIP_DONE, /* This is an active high pulse when a primary plane B flip is done */ + PRIMARY_C_FLIP_DONE, /* This is an active high pulse when a primary plane C flip is done */ + SPRITE_A_FLIP_DONE, /* This is an active high pulse when a sprite plane A flip is done */ + SPRITE_B_FLIP_DONE, /* This is an active high pulse when a sprite plane B flip is done */ + SPRITE_C_FLIP_DONE, /* This is an active high pulse when a sprite plane C flip is done */ + + PCU_THERMAL, + PCU_PCODE2DRIVER_MAILBOX, + + DPST_PHASE_IN, // This is an active high pulse on the DPST phase in event + DPST_HISTOGRAM, // This is an active high pulse on the AUX A done event. + GSE, + DP_A_HOTPLUG, + AUX_CHANNEL_A, // This is an active high pulse on the AUX A done event. + PERF_COUNTER, // This is an active high pulse when the performance counter reaches the threshold value programmed in the Performance Counter Source register + POISON, // This is an active high pulse on receiving the poison message + GTT_FAULT, // This is an active high level while either of the GTT Fault Status register bits are set + ERROR_INTERRUPT_COMBINED, + + // PCH + FDI_RX_INTERRUPTS_TRANSCODER_A, // This is an active high level while any of the FDI_RX_ISR bits are set for transcoder A + AUDIO_CP_CHANGE_TRANSCODER_A, // This is an active high level while any of the FDI_RX_ISR bits are set for transcoder A + AUDIO_CP_REQUEST_TRANSCODER_A, // This is an active high level indicating content protection is requested by audio azalia verb programming for transcoder A + FDI_RX_INTERRUPTS_TRANSCODER_B, + AUDIO_CP_CHANGE_TRANSCODER_B, + AUDIO_CP_REQUEST_TRANSCODER_B, + FDI_RX_INTERRUPTS_TRANSCODER_C, + AUDIO_CP_CHANGE_TRANSCODER_C, + AUDIO_CP_REQUEST_TRANSCODER_C, + ERR_AND_DBG, + GMBUS, + SDVO_B_HOTPLUG, + CRT_HOTPLUG, + DP_B_HOTPLUG, + DP_C_HOTPLUG, + DP_D_HOTPLUG, + AUX_CHENNEL_B, + AUX_CHENNEL_C, + AUX_CHENNEL_D, + AUDIO_POWER_STATE_CHANGE_B, + AUDIO_POWER_STATE_CHANGE_C, + AUDIO_POWER_STATE_CHANGE_D, + + GVT_EVENT_RESERVED, + GVT_EVENT_MAX, +}; + +struct gvt_irq_state; +struct pgt_device; + +typedef void (*gvt_event_phys_handler_t)(struct gvt_irq_state *hstate, + enum gvt_event_type event); +typedef void (*gvt_event_virt_handler_t)(struct gvt_irq_state *hstate, + enum gvt_event_type event, struct vgt_device *vgt); + +struct gvt_irq_ops { + void (*init_irq) (struct gvt_irq_state *hstate); + void (*check_pending_irq) (struct vgt_device *vgt); +}; + +/* the list of physical interrupt control register groups */ +enum gvt_irq_type { + GVT_IRQ_INFO_GT, + GVT_IRQ_INFO_DPY, + GVT_IRQ_INFO_PCH, + GVT_IRQ_INFO_PM, + + GVT_IRQ_INFO_MASTER, + GVT_IRQ_INFO_GT0, + GVT_IRQ_INFO_GT1, + GVT_IRQ_INFO_GT2, + GVT_IRQ_INFO_GT3, + GVT_IRQ_INFO_DE_PIPE_A, + GVT_IRQ_INFO_DE_PIPE_B, + GVT_IRQ_INFO_DE_PIPE_C, + GVT_IRQ_INFO_DE_PORT, + GVT_IRQ_INFO_DE_MISC, + GVT_IRQ_INFO_AUD, + GVT_IRQ_INFO_PCU, + + GVT_IRQ_INFO_MAX, +}; + +#define GVT_IRQ_BITWIDTH 32 + +/* device specific interrupt bit definitions */ +struct gvt_irq_info { + char *name; + int reg_base; + enum gvt_event_type bit_to_event[GVT_IRQ_BITWIDTH]; + unsigned long warned; + int group; + DECLARE_BITMAP(downstream_irq_bitmap, GVT_IRQ_BITWIDTH); + bool has_upstream_irq; +}; + +#define GVT_EVENT_FW_ALL 0 /* event forwarded to all instances */ +#define GVT_EVENT_FW_NONE 1 /* no forward */ + +/* per-event information */ +struct gvt_event_info { + /* device specific info */ + int bit; /* map to register bit */ + struct gvt_irq_info *info; /* register info */ + + /* device neutral info */ + int policy; /* forwarding policy */ + gvt_event_phys_handler_t p_handler; /* for p_event */ + gvt_event_virt_handler_t v_handler; /* for v_event */ +}; + +struct gvt_irq_map { + int up_irq_group; + int up_irq_bit; + int down_irq_group; + u32 down_irq_bitmask; +}; + +/* structure containing device specific IRQ state */ +struct gvt_irq_state { + struct gvt_irq_ops *ops; + struct gvt_irq_info *info[GVT_IRQ_INFO_MAX]; + DECLARE_BITMAP(irq_info_bitmap, GVT_IRQ_INFO_MAX); + struct gvt_event_info events[GVT_EVENT_MAX]; + DECLARE_BITMAP(pending_events, GVT_EVENT_MAX); + struct gvt_irq_map *irq_map; +}; + +#define gvt_get_event_virt_handler(h, e) (h->events[e].v_handler) +#define gvt_get_event_policy(h, e) (h->events[e].policy) +#define gvt_get_irq_info(h, e) (h->events[e].info) +#define gvt_get_irq_ops(p) (p->irq_state.ops) + +/* common offset among interrupt control registers */ +#define regbase_to_isr(base) (base) +#define regbase_to_imr(base) (base + 0x4) +#define regbase_to_iir(base) (base + 0x8) +#define regbase_to_ier(base) (base + 0xC) + +#define iir_to_regbase(iir) (iir - 0x8) +#define ier_to_regbase(ier) (ier - 0xC) + +#define gvt_irq_state_to_pdev(state) \ + container_of(state, struct pgt_device, irq_state) + +bool gvt_irq_init(struct pgt_device *pdev); +void gvt_irq_exit(struct pgt_device *pdev); + +#endif /* _GVT_INTERRUPT_H_ */ diff --git a/drivers/gpu/drm/i915/gvt/perf.h b/drivers/gpu/drm/i915/gvt/perf.h new file mode 100644 index 0000000..8f0cd15 --- /dev/null +++ b/drivers/gpu/drm/i915/gvt/perf.h @@ -0,0 +1,39 @@ +/* + * Copyright(c) 2011-2016 Intel Corporation. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _GVT_PERF_H_ +#define _GVT_PERF_H_ + +struct gvt_statistics { + u64 irq_num; + u64 events[GVT_EVENT_MAX]; + u64 last_injection; +}; + +struct pgt_statistics { + u64 irq_num; + u64 irq_delay_cycles; + u64 events[GVT_EVENT_MAX]; +}; + +#endif diff --git a/drivers/gpu/drm/i915/gvt/reg.h b/drivers/gpu/drm/i915/gvt/reg.h index 2edaf7c..0e28a71 100644 --- a/drivers/gpu/drm/i915/gvt/reg.h +++ b/drivers/gpu/drm/i915/gvt/reg.h @@ -58,4 +58,35 @@ #define _GEN6_GT_THREAD_STATUS_REG 0x13805c #define _GEN6_GT_CORE_STATUS 0x138060 +#define _GEN8_DE_PORT_IMR (0x44444) +#define _GEN8_DE_PORT_IER (0x4444c) +#define _GEN8_DE_PORT_IIR (0x44448) +#define _GEN8_DE_PORT_ISR (0x44440) + +#define _GEN8_DE_MISC_IMR (0x44464) +#define _GEN8_DE_MISC_IER (0x4446c) +#define _GEN8_DE_MISC_IIR (0x44468) +#define _GEN8_DE_MISC_ISR (0x44460) + +#define _GEN8_PCU_IMR (0x444e4) +#define _GEN8_PCU_IER (0x444ec) +#define _GEN8_PCU_IIR (0x444e8) +#define _GEN8_PCU_ISR (0x444e0) +#define _GEN8_MASTER_IRQ (0x44200) + +#define _SDEIMR (0xc4004) +#define _SDEIER (0xc400c) +#define _SDEIIR (0xc4008) +#define _SDEISR (0xc4000) + +#define _GEN8_GT_ISR(which) (0x44300 + (0x10 * (which))) +#define _GEN8_GT_IMR(which) (0x44304 + (0x10 * (which))) +#define _GEN8_GT_IIR(which) (0x44308 + (0x10 * (which))) +#define _GEN8_GT_IER(which) (0x4430c + (0x10 * (which))) + +#define _GEN8_DE_PIPE_ISR(pipe) (0x44400 + (0x10 * (pipe))) +#define _GEN8_DE_PIPE_IMR(pipe) (0x44404 + (0x10 * (pipe))) +#define _GEN8_DE_PIPE_IIR(pipe) (0x44408 + (0x10 * (pipe))) +#define _GEN8_DE_PIPE_IER(pipe) (0x4440c + (0x10 * (pipe))) + #endif