Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

OMAPDSS: fix shared irq handlers

DSS uses shared irq handlers for DISPC and DSI, because on OMAP3, the
DISPC and DSI share the same irq line.

However, the irq handlers presume that the hardware is enabled, which,
in theory, may not be the case with shared irq handlers. So if an
interrupt happens while the DISPC/DSI is off, the kernel will halt as
the irq handler tries to access the DISPC/DSI registers.

In practice that should never happen, as both DSI and DISPC are in the
same power domain. So if there's an IRQ for one of them, the other is
also enabled. However, if CONFIG_DEBUG_SHIRQ is enabled, the kernel will
generate a spurious IRQ, which then causes the problem.

This patch adds an is_enabled field for both DISPC and DSI, which is
used to track if the HW is enabled. For DISPC the code is slightly more
complex, as the users of DISPC can register the interrupt handler, and
we want to hide the is_enabled handling from the users of DISPC.

Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>

+68 -7
+48 -7
drivers/video/omap2/dss/dispc.c
··· 101 101 void __iomem *base; 102 102 103 103 int irq; 104 + irq_handler_t user_handler; 105 + void *user_data; 104 106 105 107 unsigned long core_clk_rate; 106 108 unsigned long tv_pclk_rate; ··· 115 113 u32 ctx[DISPC_SZ_REGS / sizeof(u32)]; 116 114 117 115 const struct dispc_features *feat; 116 + 117 + bool is_enabled; 118 118 } dispc; 119 119 120 120 enum omap_color_component { ··· 3673 3669 return 0; 3674 3670 } 3675 3671 3672 + static irqreturn_t dispc_irq_handler(int irq, void *arg) 3673 + { 3674 + if (!dispc.is_enabled) 3675 + return IRQ_NONE; 3676 + 3677 + return dispc.user_handler(irq, dispc.user_data); 3678 + } 3679 + 3676 3680 int dispc_request_irq(irq_handler_t handler, void *dev_id) 3677 3681 { 3678 - return devm_request_irq(&dispc.pdev->dev, dispc.irq, handler, 3679 - IRQF_SHARED, "OMAP DISPC", dev_id); 3682 + int r; 3683 + 3684 + if (dispc.user_handler != NULL) 3685 + return -EBUSY; 3686 + 3687 + dispc.user_handler = handler; 3688 + dispc.user_data = dev_id; 3689 + 3690 + /* ensure the dispc_irq_handler sees the values above */ 3691 + smp_wmb(); 3692 + 3693 + r = devm_request_irq(&dispc.pdev->dev, dispc.irq, dispc_irq_handler, 3694 + IRQF_SHARED, "OMAP DISPC", &dispc); 3695 + if (r) { 3696 + dispc.user_handler = NULL; 3697 + dispc.user_data = NULL; 3698 + } 3699 + 3700 + return r; 3680 3701 } 3681 3702 EXPORT_SYMBOL(dispc_request_irq); 3682 3703 3683 3704 void dispc_free_irq(void *dev_id) 3684 3705 { 3685 - devm_free_irq(&dispc.pdev->dev, dispc.irq, dev_id); 3706 + devm_free_irq(&dispc.pdev->dev, dispc.irq, &dispc); 3707 + 3708 + dispc.user_handler = NULL; 3709 + dispc.user_data = NULL; 3686 3710 } 3687 3711 EXPORT_SYMBOL(dispc_free_irq); 3688 3712 ··· 3782 3750 3783 3751 static int dispc_runtime_suspend(struct device *dev) 3784 3752 { 3753 + dispc.is_enabled = false; 3754 + /* ensure the dispc_irq_handler sees the is_enabled value */ 3755 + smp_wmb(); 3756 + /* wait for current handler to finish before turning the DISPC off */ 3757 + synchronize_irq(dispc.irq); 3758 + 3785 3759 dispc_save_context(); 3786 3760 3787 3761 return 0; ··· 3801 3763 * _omap_dispc_initial_config(). We can thus use it to detect if 3802 3764 * we have lost register context. 3803 3765 */ 3804 - if (REG_GET(DISPC_CONFIG, 2, 1) == OMAP_DSS_LOAD_FRAME_ONLY) 3805 - return 0; 3766 + if (REG_GET(DISPC_CONFIG, 2, 1) != OMAP_DSS_LOAD_FRAME_ONLY) { 3767 + _omap_dispc_initial_config(); 3806 3768 3807 - _omap_dispc_initial_config(); 3769 + dispc_restore_context(); 3770 + } 3808 3771 3809 - dispc_restore_context(); 3772 + dispc.is_enabled = true; 3773 + /* ensure the dispc_irq_handler sees the is_enabled value */ 3774 + smp_wmb(); 3810 3775 3811 3776 return 0; 3812 3777 }
+20
drivers/video/omap2/dss/dsi.c
··· 297 297 298 298 int irq; 299 299 300 + bool is_enabled; 301 + 300 302 struct clk *dss_clk; 301 303 struct clk *sys_clk; 302 304 ··· 796 794 797 795 dsidev = (struct platform_device *) arg; 798 796 dsi = dsi_get_dsidrv_data(dsidev); 797 + 798 + if (!dsi->is_enabled) 799 + return IRQ_NONE; 799 800 800 801 spin_lock(&dsi->irq_lock); 801 802 ··· 5676 5671 5677 5672 static int dsi_runtime_suspend(struct device *dev) 5678 5673 { 5674 + struct platform_device *pdev = to_platform_device(dev); 5675 + struct dsi_data *dsi = dsi_get_dsidrv_data(pdev); 5676 + 5677 + dsi->is_enabled = false; 5678 + /* ensure the irq handler sees the is_enabled value */ 5679 + smp_wmb(); 5680 + /* wait for current handler to finish before turning the DSI off */ 5681 + synchronize_irq(dsi->irq); 5682 + 5679 5683 dispc_runtime_put(); 5680 5684 5681 5685 return 0; ··· 5692 5678 5693 5679 static int dsi_runtime_resume(struct device *dev) 5694 5680 { 5681 + struct platform_device *pdev = to_platform_device(dev); 5682 + struct dsi_data *dsi = dsi_get_dsidrv_data(pdev); 5695 5683 int r; 5696 5684 5697 5685 r = dispc_runtime_get(); 5698 5686 if (r) 5699 5687 return r; 5688 + 5689 + dsi->is_enabled = true; 5690 + /* ensure the irq handler sees the is_enabled value */ 5691 + smp_wmb(); 5700 5692 5701 5693 return 0; 5702 5694 }