/* i915_irq.c -- IRQ support for the I915 -*- linux-c -*-
*/ /* * Copyright 2003 Tungsten Graphics, Inc., Cedar Park, Texas. * 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, sub license, 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 NON-INFRINGEMENT. * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS 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. *
*/
/** * DOC: interrupt handling * * These functions provide the basic support for enabling and disabling the * interrupt handling support. There's a lot more functionality in i915_irq.c * and related files, but that will be described in separate chapters.
*/
/* * Interrupt statistic for PMU. Increments the counter only if the * interrupt originated from the GPU so interrupts from a device which * shares the interrupt line are not accounted.
*/ staticinlinevoid pmu_irq_stats(struct drm_i915_private *i915,
irqreturn_t res)
{ if (unlikely(res != IRQ_HANDLED)) return;
/* * A clever compiler translates that into INC. A not so clever one * should at least prevent store tearing.
*/
WRITE_ONCE(i915->pmu.irq_count, i915->pmu.irq_count + 1);
}
/* IIR can theoretically queue up two events. Be paranoid. */
intel_uncore_write(uncore, regs.iir, 0xffffffff);
intel_uncore_posting_read(uncore, regs.iir);
intel_uncore_write(uncore, regs.iir, 0xffffffff);
intel_uncore_posting_read(uncore, regs.iir);
}
/* * We should clear IMR at preinstall/uninstall, and just check at postinstall.
*/ void gen2_assert_iir_is_zero(struct intel_uncore *uncore, i915_reg_t reg)
{
u32 val = intel_uncore_read(uncore, reg);
/** * ivb_parity_work - Workqueue called when a parity error interrupt * occurred. * @work: workqueue struct * * Doesn't actually do anything except notify userspace. As a consequence of * this event, userspace should try to remap the bad rows since statistically * it is likely the same row is more likely to go bad again.
*/ staticvoid ivb_parity_work(struct work_struct *work)
{ struct drm_i915_private *dev_priv =
container_of(work, typeof(*dev_priv), l3_parity.error_work); struct intel_gt *gt = to_gt(dev_priv);
u32 error_status, row, bank, subbank; char *parity_event[6];
u32 misccpctl;
u8 slice = 0;
/* We must turn off DOP level clock gating to access the L3 registers. * In order to prevent a get/put style interface, acquire struct mutex * any time we access those registers.
*/
mutex_lock(&dev_priv->drm.struct_mutex);
/* If we've screwed up tracking, just let the interrupt fire again */ if (drm_WARN_ON(&dev_priv->drm, !dev_priv->l3_parity.which_slice)) goto out;
/* * Theory on interrupt generation, based on empirical evidence: * * x = ((VLV_IIR & VLV_IER) || * (((GT_IIR & GT_IER) || (GEN6_PMIIR & GEN6_PMIER)) && * (VLV_MASTER_IER & MASTER_INTERRUPT_ENABLE))); * * A CPU interrupt will only be raised when 'x' has a 0->1 edge. * Hence we clear MASTER_INTERRUPT_ENABLE and VLV_IER to * guarantee the CPU interrupt will be raised again even if we * don't end up clearing all the VLV_IIR, GT_IIR, GEN6_PMIIR * bits this time around.
*/
intel_uncore_write(&dev_priv->uncore, VLV_MASTER_IER, 0);
ier = intel_uncore_rmw(&dev_priv->uncore, VLV_IER, ~0, 0);
if (gt_iir)
intel_uncore_write(&dev_priv->uncore, GTIIR, gt_iir); if (pm_iir)
intel_uncore_write(&dev_priv->uncore, GEN6_PMIIR, pm_iir);
if (iir & I915_DISPLAY_PORT_INTERRUPT)
hotplug_status = i9xx_hpd_irq_ack(display);
if (iir & I915_MASTER_ERROR_INTERRUPT)
vlv_display_error_irq_ack(display, &eir, &dpinvgtt);
/* Call regardless, as some status bits might not be
* signalled in IIR */
i9xx_pipestat_irq_ack(display, iir, pipe_stats);
if (iir & (I915_LPE_PIPE_A_INTERRUPT |
I915_LPE_PIPE_B_INTERRUPT))
intel_lpe_audio_irq_handler(display);
/* * VLV_IIR is single buffered, and reflects the level * from PIPESTAT/PORT_HOTPLUG_STAT, hence clear it last.
*/ if (iir)
intel_uncore_write(&dev_priv->uncore, VLV_IIR, iir);
/* * Theory on interrupt generation, based on empirical evidence: * * x = ((VLV_IIR & VLV_IER) || * ((GEN8_MASTER_IRQ & ~GEN8_MASTER_IRQ_CONTROL) && * (GEN8_MASTER_IRQ & GEN8_MASTER_IRQ_CONTROL))); * * A CPU interrupt will only be raised when 'x' has a 0->1 edge. * Hence we clear GEN8_MASTER_IRQ_CONTROL and VLV_IER to * guarantee the CPU interrupt will be raised again even if we * don't end up clearing all the VLV_IIR and GEN8_MASTER_IRQ_CONTROL * bits this time around.
*/
intel_uncore_write(&dev_priv->uncore, GEN8_MASTER_IRQ, 0);
ier = intel_uncore_rmw(&dev_priv->uncore, VLV_IER, ~0, 0);
gen8_gt_irq_handler(to_gt(dev_priv), master_ctl);
if (iir & I915_DISPLAY_PORT_INTERRUPT)
hotplug_status = i9xx_hpd_irq_ack(display);
if (iir & I915_MASTER_ERROR_INTERRUPT)
vlv_display_error_irq_ack(display, &eir, &dpinvgtt);
/* Call regardless, as some status bits might not be
* signalled in IIR */
i9xx_pipestat_irq_ack(display, iir, pipe_stats);
if (iir & (I915_LPE_PIPE_A_INTERRUPT |
I915_LPE_PIPE_B_INTERRUPT |
I915_LPE_PIPE_C_INTERRUPT))
intel_lpe_audio_irq_handler(display);
/* * VLV_IIR is single buffered, and reflects the level * from PIPESTAT/PORT_HOTPLUG_STAT, hence clear it last.
*/ if (iir)
intel_uncore_write(&dev_priv->uncore, VLV_IIR, iir);
/* Disable south interrupts. We'll only write to SDEIIR once, so further * interrupts will will be stored on its back queue, and then we'll be * able to process them after we restore SDEIER (as soon as we restore * it, we'll get an interrupt if SDEIIR still has something to process
* due to its back queue). */ if (!HAS_PCH_NOP(i915)) {
sde_ier = raw_reg_read(regs, SDEIER);
raw_reg_write(regs, SDEIER, 0);
}
/* Find, clear, then process each source of interrupt */
gt_iir = raw_reg_read(regs, GTIIR); if (gt_iir) {
raw_reg_write(regs, GTIIR, gt_iir); if (GRAPHICS_VER(i915) >= 6)
gen6_gt_irq_handler(to_gt(i915), gt_iir); else
gen5_gt_irq_handler(to_gt(i915), gt_iir);
ret = IRQ_HANDLED;
}
de_iir = raw_reg_read(regs, DEIIR); if (de_iir) {
raw_reg_write(regs, DEIIR, de_iir); if (DISPLAY_VER(i915) >= 7)
ivb_display_irq_handler(display, de_iir); else
ilk_display_irq_handler(display, de_iir);
ret = IRQ_HANDLED;
}
if (GRAPHICS_VER(i915) >= 6) {
u32 pm_iir = raw_reg_read(regs, GEN6_PMIIR); if (pm_iir) {
raw_reg_write(regs, GEN6_PMIIR, pm_iir);
gen6_rps_irq_handler(&to_gt(i915)->rps, pm_iir);
ret = IRQ_HANDLED;
}
}
raw_reg_write(regs, DEIER, de_ier); if (sde_ier)
raw_reg_write(regs, SDEIER, sde_ier);
pmu_irq_stats(i915, ret);
/* IRQs are synced during runtime_suspend, we don't require a wakeref */
enable_rpm_wakeref_asserts(&i915->runtime_pm);
/* * Now with master disabled, get a sample of level indications * for this interrupt. Indications will be cleared on related acks. * New indications can and will light up during processing, * and will generate new interrupt after enabling master.
*/ return raw_reg_read(regs, GEN8_MASTER_IRQ);
}
if (!intel_irqs_enabled(dev_priv)) return IRQ_NONE;
master_ctl = gen8_master_intr_disable(regs); if (!master_ctl) {
gen8_master_intr_enable(regs); return IRQ_NONE;
}
/* Find, queue (onto bottom-halves), then clear each source */
gen8_gt_irq_handler(to_gt(dev_priv), master_ctl);
/* IRQs are synced during runtime_suspend, we don't require a wakeref */ if (master_ctl & ~GEN8_GT_IRQS) {
disable_rpm_wakeref_asserts(&dev_priv->runtime_pm);
gen8_de_irq_handler(display, master_ctl);
enable_rpm_wakeref_asserts(&dev_priv->runtime_pm);
}
/* * Now with master disabled, get a sample of level indications * for this interrupt. Indications will be cleared on related acks. * New indications can and will light up during processing, * and will generate new interrupt after enabling master.
*/ return raw_reg_read(regs, GEN11_GFX_MSTR_IRQ);
}
static u32 i9xx_error_mask(struct drm_i915_private *i915)
{ /* * On gen2/3 FBC generates (seemingly spurious) * display INVALID_GTT/INVALID_GTT_PTE table errors. * * Also gen3 bspec has this to say: * "DISPA_INVALID_GTT_PTE " [DevNapa] : Reserved. This bit does not reflect the page " table error for the display plane A." * * Unfortunately we can't mask off individual PGTBL_ER bits, * so we just have to mask off all page table errors via EMR.
*/ if (HAS_FBC(i915)) return I915_ERROR_MEMORY_REFRESH; else return I915_ERROR_PAGE_TABLE |
I915_ERROR_MEMORY_REFRESH;
}
*eir_stuck = intel_uncore_read(&dev_priv->uncore, EIR); if (*eir_stuck == 0) return;
/* * Toggle all EMR bits to make sure we get an edge * in the ISR master error bit if we don't clear * all the EIR bits. Otherwise the edge triggered * IIR on i965/g4x wouldn't notice that an interrupt * is still pending. Also some EIR bits can't be * cleared except by handling the underlying error * (or by a GPU reset) so we mask any bit that * remains set.
*/
emr = intel_uncore_read(&dev_priv->uncore, EMR);
intel_uncore_write(&dev_priv->uncore, EMR, 0xffffffff);
intel_uncore_write(&dev_priv->uncore, EMR, emr | *eir_stuck);
}
static u32 i965_error_mask(struct drm_i915_private *i915)
{ /* * Enable some error detection, note the instruction error mask * bit is reserved, so we leave it masked. * * i965 FBC no longer generates spurious GTT errors, * so we can always enable the page table errors.
*/ if (IS_G4X(i915)) return GM45_ERROR_PAGE_TABLE |
GM45_ERROR_MEM_PRIV |
GM45_ERROR_CP_PRIV |
I915_ERROR_MEMORY_REFRESH; else return I915_ERROR_PAGE_TABLE |
I915_ERROR_MEMORY_REFRESH;
}
/** * intel_irq_init - initializes irq support * @dev_priv: i915 device instance * * This function initializes all the irq support including work items, timers * and all the vtables. It does not setup the interrupt itself though.
*/ void intel_irq_init(struct drm_i915_private *dev_priv)
{ int i;
INIT_WORK(&dev_priv->l3_parity.error_work, ivb_parity_work); for (i = 0; i < MAX_L3_SLICES; ++i)
dev_priv->l3_parity.remap_info[i] = NULL;
/* pre-gen11 the guc irqs bits are in the upper 16 bits of the pm reg */ if (HAS_GT_UC(dev_priv) && GRAPHICS_VER(dev_priv) < 11)
to_gt(dev_priv)->pm_guc_events = GUC_INTR_GUC2HOST << 16;
}
/** * intel_irq_fini - deinitializes IRQ support * @i915: i915 device instance * * This function deinitializes all the IRQ support.
*/ void intel_irq_fini(struct drm_i915_private *i915)
{ int i;
for (i = 0; i < MAX_L3_SLICES; ++i)
kfree(i915->l3_parity.remap_info[i]);
}
/** * intel_irq_install - enables the hardware interrupt * @dev_priv: i915 device instance * * This function enables the hardware interrupt handling, but leaves the hotplug * handling still disabled. It is called after intel_irq_init(). * * In the driver load and resume code we need working interrupts in a few places * but don't want to deal with the hassle of concurrent probe and hotplug * workers. Hence the split into this two-stage approach.
*/ int intel_irq_install(struct drm_i915_private *dev_priv)
{ int irq = to_pci_dev(dev_priv->drm.dev)->irq; int ret;
/* * We enable some interrupt sources in our postinstall hooks, so mark * interrupts as enabled _before_ actually enabling them to avoid * special cases in our ordering checks.
*/
dev_priv->irqs_enabled = true;
intel_irq_reset(dev_priv);
ret = request_irq(irq, intel_irq_handler(dev_priv),
IRQF_SHARED, DRIVER_NAME, dev_priv); if (ret < 0) {
dev_priv->irqs_enabled = false; return ret;
}
intel_irq_postinstall(dev_priv);
return ret;
}
/** * intel_irq_uninstall - finalizes all irq handling * @dev_priv: i915 device instance * * This stops interrupt and hotplug handling and unregisters and frees all * resources acquired in the init functions.
*/ void intel_irq_uninstall(struct drm_i915_private *dev_priv)
{ struct intel_display *display = dev_priv->display; int irq = to_pci_dev(dev_priv->drm.dev)->irq;
if (drm_WARN_ON(&dev_priv->drm, !dev_priv->irqs_enabled)) return;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.