Andy Chiu says: ==================== net: axienet: fix DMA Tx error We ran into multiple DMA TX errors while writing files over a network block device running on top of a DMA-connected AXI Ethernet device on 64-bit RISC-V machines. The errors indicated that the DMA had fetched a null descriptor and we found that the reason for this is that AXI DMA had unexpectedly processed a partially updated tail descriptor pointer. To fix it, we suggest that the driver should use one 64-bit write instead of two 32-bit writes to perform such update if possible. For those archectures where double-word load/stores are unavailable, e.g. 32-bit archectures, force a driver probe failure if the driver finds 64-bit capability on DMA. ====================
Unified · Split
| @@ -547,6 +547,57 @@ static inline void axienet_iow(struct axienet_local *lp, off_t offset, · 546 unmodified lines | ||
| 547 | 547 | iowrite32(value, lp->regs + offset); |
| 548 | 548 | } |
| 549 | 549 | |
| 550 | +/** | |
| 551 | + * axienet_dma_out32 - Memory mapped Axi DMA register write. | |
| 552 | + * @lp: Pointer to axienet local structure | |
| 553 | + * @reg: Address offset from the base address of the Axi DMA core | |
| 554 | + * @value: Value to be written into the Axi DMA register | |
| 555 | + * | |
| 556 | + * This function writes the desired value into the corresponding Axi DMA | |
| 557 | + * register. | |
| 558 | + */ | |
| 559 | + | |
| 560 | +static inline void axienet_dma_out32(struct axienet_local *lp, | |
| 561 | + off_t reg, u32 value) | |
| 562 | +{ | |
| 563 | + iowrite32(value, lp->dma_regs + reg); | |
| 564 | +} | |
| 565 | + | |
| 566 | +#ifdef CONFIG_64BIT | |
| 567 | +/** | |
| 568 | + * axienet_dma_out64 - Memory mapped Axi DMA register write. | |
| 569 | + * @lp: Pointer to axienet local structure | |
| 570 | + * @reg: Address offset from the base address of the Axi DMA core | |
| 571 | + * @value: Value to be written into the Axi DMA register | |
| 572 | + * | |
| 573 | + * This function writes the desired value into the corresponding Axi DMA | |
| 574 | + * register. | |
| 575 | + */ | |
| 576 | +static inline void axienet_dma_out64(struct axienet_local *lp, | |
| 577 | + off_t reg, u64 value) | |
| 578 | +{ | |
| 579 | + iowrite64(value, lp->dma_regs + reg); | |
| 580 | +} | |
| 581 | + | |
| 582 | +static void axienet_dma_out_addr(struct axienet_local *lp, off_t reg, | |
| 583 | + dma_addr_t addr) | |
| 584 | +{ | |
| 585 | + if (lp->features & XAE_FEATURE_DMA_64BIT) | |
| 586 | + axienet_dma_out64(lp, reg, addr); | |
| 587 | + else | |
| 588 | + axienet_dma_out32(lp, reg, lower_32_bits(addr)); | |
| 589 | +} | |
| 590 | + | |
| 591 | +#else /* CONFIG_64BIT */ | |
| 592 | + | |
| 593 | +static void axienet_dma_out_addr(struct axienet_local *lp, off_t reg, | |
| 594 | + dma_addr_t addr) | |
| 595 | +{ | |
| 596 | + axienet_dma_out32(lp, reg, lower_32_bits(addr)); | |
| 597 | +} | |
| 598 | + | |
| 599 | +#endif /* CONFIG_64BIT */ | |
| 600 | + | |
| 550 | 601 | /* Function prototypes visible in xilinx_axienet_mdio.c for other files */ |
| 551 | 602 | int axienet_mdio_enable(struct axienet_local *lp); |
| 552 | 603 | void axienet_mdio_disable(struct axienet_local *lp); |
| @@ -133,30 +133,6 @@ static inline u32 axienet_dma_in32(struct axienet_local *lp, off_t reg) · 132 unmodified lines | ||
| 133 | 133 | return ioread32(lp->dma_regs + reg); |
| 134 | 134 | } |
| 135 | 135 | |
| 136 | −/** | |
| 137 | − * axienet_dma_out32 - Memory mapped Axi DMA register write. | |
| 138 | − * @lp: Pointer to axienet local structure | |
| 139 | − * @reg: Address offset from the base address of the Axi DMA core | |
| 140 | − * @value: Value to be written into the Axi DMA register | |
| 141 | − * | |
| 142 | − * This function writes the desired value into the corresponding Axi DMA | |
| 143 | − * register. | |
| 144 | − */ | |
| 145 | −static inline void axienet_dma_out32(struct axienet_local *lp, | |
| 146 | − off_t reg, u32 value) | |
| 147 | −{ | |
| 148 | − iowrite32(value, lp->dma_regs + reg); | |
| 149 | −} | |
| 150 | − | |
| 151 | −static void axienet_dma_out_addr(struct axienet_local *lp, off_t reg, | |
| 152 | − dma_addr_t addr) | |
| 153 | −{ | |
| 154 | − axienet_dma_out32(lp, reg, lower_32_bits(addr)); | |
| 155 | − | |
| 156 | − if (lp->features & XAE_FEATURE_DMA_64BIT) | |
| 157 | − axienet_dma_out32(lp, reg + 4, upper_32_bits(addr)); | |
| 158 | −} | |
| 159 | − | |
| 160 | 136 | static void desc_set_phys_addr(struct axienet_local *lp, dma_addr_t addr, |
| 161 | 137 | struct axidma_bd *desc) |
| 162 | 138 | { |
| @@ -2061,6 +2037,10 @@ static int axienet_probe(struct platform_device *pdev) · 1898 unmodified lines | ||
| 2061 | 2037 | iowrite32(0x0, desc); |
| 2062 | 2038 | } |
| 2063 | 2039 | } |
| 2040 | + if (!IS_ENABLED(CONFIG_64BIT) && lp->features & XAE_FEATURE_DMA_64BIT) { | |
| 2041 | + dev_err(&pdev->dev, "64-bit addressable DMA is not compatible with 32-bit archecture\n"); | |
| 2042 | + goto cleanup_clk; | |
| 2043 | + } | |
| 2064 | 2044 | |
| 2065 | 2045 | ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(addr_width)); |
| 2066 | 2046 | if (ret) { |