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 | iowrite32(value, lp->regs + offset); | 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 | /* Function prototypes visible in xilinx_axienet_mdio.c for other files */ | 601 | /* Function prototypes visible in xilinx_axienet_mdio.c for other files */ |
| 551 | int axienet_mdio_enable(struct axienet_local *lp); | 602 | int axienet_mdio_enable(struct axienet_local *lp); |
| 552 | void axienet_mdio_disable(struct axienet_local *lp); | 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 | return ioread32(lp->dma_regs + reg); | 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 | static void desc_set_phys_addr(struct axienet_local *lp, dma_addr_t addr, | 136 | static void desc_set_phys_addr(struct axienet_local *lp, dma_addr_t addr, |
| 161 | struct axidma_bd *desc) | 137 | struct axidma_bd *desc) |
| 162 | { | 138 | { |
| @@ -2061,6 +2037,10 @@ static int axienet_probe(struct platform_device *pdev) · 1898 unmodified lines | |||
| 2061 | iowrite32(0x0, desc); | 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 | ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(addr_width)); | 2045 | ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(addr_width)); |
| 2066 | if (ret) { | 2046 | if (ret) { |