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.

dmaengine: dw-edma: Add virtual IRQ for interrupt-emulation doorbells

Interrupt emulation can assert the dw-edma IRQ line without updating the
DONE/ABORT bits. With the shared read/write/common IRQ handlers, the
driver cannot reliably distinguish such an emulated interrupt from a
real one and leaving a level IRQ asserted may wedge the line.

Allocate a dedicated, requestable Linux virtual IRQ (db_irq) for
interrupt emulation and attach an irq_chip whose .irq_ack runs the
core-specific deassert sequence (.ack_emulated_irq()). The physical
dw-edma interrupt handlers raise this virtual IRQ via
generic_handle_irq(), ensuring emulated IRQs are always deasserted.

Export the virtual IRQ number (db_irq) and the doorbell register offset
(db_offset) via struct dw_edma_chip so platform users can expose
interrupt emulation as a doorbell.

Without this, a single interrupt-emulation write can leave the level IRQ
line asserted and cause the generic IRQ layer to disable it.

Signed-off-by: Koichiro Den <den@valinux.co.jp>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Link: https://patch.msgid.link/20260215152216.3393561-3-den@valinux.co.jp
Signed-off-by: Vinod Koul <vkoul@kernel.org>

authored by

Koichiro Den and committed by
Vinod Koul
d9d5e1bd 2e872687

+128 -5
+122 -5
drivers/dma/dw-edma/dw-edma-core.c
··· 663 663 chan->status = EDMA_ST_IDLE; 664 664 } 665 665 666 - static inline irqreturn_t dw_edma_interrupt_write(int irq, void *data) 666 + static void dw_edma_emul_irq_ack(struct irq_data *d) 667 + { 668 + struct dw_edma *dw = irq_data_get_irq_chip_data(d); 669 + 670 + dw_edma_core_ack_emulated_irq(dw); 671 + } 672 + 673 + /* 674 + * irq_chip implementation for interrupt-emulation doorbells. 675 + * 676 + * The emulated source has no mask/unmask mechanism. With handle_level_irq(), 677 + * the flow is therefore: 678 + * 1) .irq_ack() deasserts the source 679 + * 2) registered handlers (if any) are dispatched 680 + * Since deassertion is already done in .irq_ack(), handlers do not need to take 681 + * care of it, hence IRQCHIP_ONESHOT_SAFE. 682 + */ 683 + static struct irq_chip dw_edma_emul_irqchip = { 684 + .name = "dw-edma-emul", 685 + .irq_ack = dw_edma_emul_irq_ack, 686 + .flags = IRQCHIP_ONESHOT_SAFE | IRQCHIP_SKIP_SET_WAKE, 687 + }; 688 + 689 + static int dw_edma_emul_irq_alloc(struct dw_edma *dw) 690 + { 691 + struct dw_edma_chip *chip = dw->chip; 692 + int virq; 693 + 694 + chip->db_irq = 0; 695 + chip->db_offset = ~0; 696 + 697 + /* 698 + * Only meaningful when the core provides the deassert sequence 699 + * for interrupt emulation. 700 + */ 701 + if (!dw->core->ack_emulated_irq) 702 + return 0; 703 + 704 + /* 705 + * Allocate a single, requestable Linux virtual IRQ number. 706 + * Use >= 1 so that 0 can remain a "not available" sentinel. 707 + */ 708 + virq = irq_alloc_desc(NUMA_NO_NODE); 709 + if (virq < 0) 710 + return virq; 711 + 712 + irq_set_chip_and_handler(virq, &dw_edma_emul_irqchip, handle_level_irq); 713 + irq_set_chip_data(virq, dw); 714 + irq_set_noprobe(virq); 715 + 716 + chip->db_irq = virq; 717 + chip->db_offset = dw_edma_core_db_offset(dw); 718 + 719 + return 0; 720 + } 721 + 722 + static void dw_edma_emul_irq_free(struct dw_edma *dw) 723 + { 724 + struct dw_edma_chip *chip = dw->chip; 725 + 726 + if (!chip) 727 + return; 728 + if (chip->db_irq <= 0) 729 + return; 730 + 731 + irq_free_descs(chip->db_irq, 1); 732 + chip->db_irq = 0; 733 + chip->db_offset = ~0; 734 + } 735 + 736 + static inline irqreturn_t dw_edma_interrupt_emulated(void *data) 737 + { 738 + struct dw_edma_irq *dw_irq = data; 739 + struct dw_edma *dw = dw_irq->dw; 740 + int db_irq = dw->chip->db_irq; 741 + 742 + if (db_irq > 0) { 743 + /* 744 + * Interrupt emulation may assert the IRQ line without updating the 745 + * normal DONE/ABORT status bits. With a shared IRQ handler we 746 + * cannot reliably detect such events by status registers alone, so 747 + * always perform the core-specific deassert sequence. 748 + */ 749 + generic_handle_irq(db_irq); 750 + return IRQ_HANDLED; 751 + } 752 + return IRQ_NONE; 753 + } 754 + 755 + static inline irqreturn_t dw_edma_interrupt_write_inner(int irq, void *data) 667 756 { 668 757 struct dw_edma_irq *dw_irq = data; 669 758 ··· 761 672 dw_edma_abort_interrupt); 762 673 } 763 674 764 - static inline irqreturn_t dw_edma_interrupt_read(int irq, void *data) 675 + static inline irqreturn_t dw_edma_interrupt_read_inner(int irq, void *data) 765 676 { 766 677 struct dw_edma_irq *dw_irq = data; 767 678 ··· 770 681 dw_edma_abort_interrupt); 771 682 } 772 683 773 - static irqreturn_t dw_edma_interrupt_common(int irq, void *data) 684 + static inline irqreturn_t dw_edma_interrupt_write(int irq, void *data) 774 685 { 775 686 irqreturn_t ret = IRQ_NONE; 776 687 777 - ret |= dw_edma_interrupt_write(irq, data); 778 - ret |= dw_edma_interrupt_read(irq, data); 688 + ret |= dw_edma_interrupt_write_inner(irq, data); 689 + ret |= dw_edma_interrupt_emulated(data); 690 + 691 + return ret; 692 + } 693 + 694 + static inline irqreturn_t dw_edma_interrupt_read(int irq, void *data) 695 + { 696 + irqreturn_t ret = IRQ_NONE; 697 + 698 + ret |= dw_edma_interrupt_read_inner(irq, data); 699 + ret |= dw_edma_interrupt_emulated(data); 700 + 701 + return ret; 702 + } 703 + 704 + static inline irqreturn_t dw_edma_interrupt_common(int irq, void *data) 705 + { 706 + irqreturn_t ret = IRQ_NONE; 707 + 708 + ret |= dw_edma_interrupt_write_inner(irq, data); 709 + ret |= dw_edma_interrupt_read_inner(irq, data); 710 + ret |= dw_edma_interrupt_emulated(data); 779 711 780 712 return ret; 781 713 } ··· 1083 973 if (err) 1084 974 return err; 1085 975 976 + /* Allocate a dedicated virtual IRQ for interrupt-emulation doorbells */ 977 + err = dw_edma_emul_irq_alloc(dw); 978 + if (err) 979 + dev_warn(dev, "Failed to allocate emulation IRQ: %d\n", err); 980 + 1086 981 /* Setup write/read channels */ 1087 982 err = dw_edma_channel_setup(dw, wr_alloc, rd_alloc); 1088 983 if (err) ··· 1103 988 err_irq_free: 1104 989 for (i = (dw->nr_irqs - 1); i >= 0; i--) 1105 990 free_irq(chip->ops->irq_vector(dev, i), &dw->irq[i]); 991 + dw_edma_emul_irq_free(dw); 1106 992 1107 993 return err; 1108 994 } ··· 1126 1010 /* Free irqs */ 1127 1011 for (i = (dw->nr_irqs - 1); i >= 0; i--) 1128 1012 free_irq(chip->ops->irq_vector(dev, i), &dw->irq[i]); 1013 + dw_edma_emul_irq_free(dw); 1129 1014 1130 1015 /* Deregister eDMA device */ 1131 1016 dma_async_device_unregister(&dw->dma);
+6
include/linux/dma/edma.h
··· 73 73 * @ll_region_rd: DMA descriptor link list memory for read channel 74 74 * @dt_region_wr: DMA data memory for write channel 75 75 * @dt_region_rd: DMA data memory for read channel 76 + * @db_irq: Virtual IRQ dedicated to interrupt emulation 77 + * @db_offset: Offset from DMA register base 76 78 * @mf: DMA register map format 77 79 * @dw: struct dw_edma that is filled by dw_edma_probe() 78 80 */ ··· 95 93 /* data region */ 96 94 struct dw_edma_region dt_region_wr[EDMA_MAX_WR_CH]; 97 95 struct dw_edma_region dt_region_rd[EDMA_MAX_RD_CH]; 96 + 97 + /* interrupt emulation */ 98 + int db_irq; 99 + resource_size_t db_offset; 98 100 99 101 enum dw_edma_map_format mf; 100 102