[PATCH 1/2] net: dwc_eth_qos: restore support of not cache-aligned descriptor

Solve an issue with AXI_WIDTH_32 on a the 64 bytes cache line platform; in this case the requested descriptor padding length should be 12 but the associated parameter EQOS_DMA_CH0_CONTROL.DSL is limited to 3bits = 7.
As the DMA descriptor can't be correctly aligned with the cache line, the maintenance of each descriptor can't be guaranteed by a simple cache line operation: flush or invalid.
To avoid all the maintenance issues, these descriptors need to be allocated in a NOT CACHEABLE memory by noncached_alloc() when CONFIG_SYS_NONCACHED_MEMORY is enable.
This patch doesn't change the current behavior when the descriptors can be cache-aligned with the field "Descriptor Skip Length" of the DMA channel control register, when eqos->desc_pad = true.
Signed-off-by: Patrick Delaunay patrick.delaunay@foss.st.com --- This patch is required for ETH support on STM32MP13x family with AXI_WIDTH_32 value.
This patch doesn't change the behavior for other parameters, tested on STM32MP15x boards.
drivers/net/dwc_eth_qos.c | 79 ++++++++++++++++++++++++++++----------- 1 file changed, 57 insertions(+), 22 deletions(-)
diff --git a/drivers/net/dwc_eth_qos.c b/drivers/net/dwc_eth_qos.c index 9d255cf95ff..0c2ba206056 100644 --- a/drivers/net/dwc_eth_qos.c +++ b/drivers/net/dwc_eth_qos.c @@ -51,6 +51,7 @@ #include <asm/arch/clock.h> #include <asm/mach-imx/sys_proto.h> #endif +#include <dm/device_compat.h> #include <linux/bitops.h> #include <linux/delay.h>
@@ -210,6 +211,7 @@ struct eqos_dma_regs { #define EQOS_DMA_SYSBUS_MODE_BLEN4 BIT(1)
#define EQOS_DMA_CH0_CONTROL_DSL_SHIFT 18 +#define EQOS_DMA_CH0_CONTROL_DSL_MAX 7 #define EQOS_DMA_CH0_CONTROL_PBLX8 BIT(16)
#define EQOS_DMA_CH0_TX_CONTROL_TXPBL_SHIFT 16 @@ -274,9 +276,11 @@ struct eqos_config { struct eqos_ops *ops; };
+struct eqos_priv; + struct eqos_ops { - void (*eqos_inval_desc)(void *desc); - void (*eqos_flush_desc)(void *desc); + void (*eqos_inval_desc)(struct eqos_priv *eqos, void *desc); + void (*eqos_flush_desc)(struct eqos_priv *eqos, void *desc); void (*eqos_inval_buffer)(void *buf, size_t size); void (*eqos_flush_buffer)(void *buf, size_t size); int (*eqos_probe_resources)(struct udevice *dev); @@ -319,6 +323,7 @@ struct eqos_priv { bool started; bool reg_access_ok; bool clk_ck_enabled; + bool use_cached_mem; };
/* @@ -341,15 +346,38 @@ struct eqos_priv { */ static void *eqos_alloc_descs(struct eqos_priv *eqos, unsigned int num) { + void *descs = NULL; + ulong desc_pad; + + /* + * if descriptors can to be cache-line aligned with the DSL = + * "Descriptor Skip Length" field of the DMA channel control register + */ eqos->desc_size = ALIGN(sizeof(struct eqos_desc), (unsigned int)ARCH_DMA_MINALIGN); + desc_pad = (eqos->desc_size - sizeof(struct eqos_desc)) / + eqos->config->axi_bus_width; + if (desc_pad <= EQOS_DMA_CH0_CONTROL_DSL_MAX) { + eqos->use_cached_mem = true; + descs = memalign(eqos->desc_size, num * eqos->desc_size); + } else { + eqos->use_cached_mem = false; + eqos->desc_size = sizeof(struct eqos_desc); +#ifdef CONFIG_SYS_NONCACHED_MEMORY + descs = (void *)noncached_alloc(num * eqos->desc_size, ARCH_DMA_MINALIGN); +#else + log_err("DMA descriptors with cached memory."); +#endif + }
- return memalign(eqos->desc_size, num * eqos->desc_size); + return descs; }
-static void eqos_free_descs(void *descs) +static void eqos_free_descs(struct eqos_priv *eqos) { - free(descs); + if (eqos->use_cached_mem) + free(eqos->descs); + /* memory allocated by noncached_alloc() can't be freed */ }
static struct eqos_desc *eqos_get_desc(struct eqos_priv *eqos, @@ -359,22 +387,24 @@ static struct eqos_desc *eqos_get_desc(struct eqos_priv *eqos, ((rx ? EQOS_DESCRIPTORS_TX : 0) + num) * eqos->desc_size; }
-static void eqos_inval_desc_generic(void *desc) +static void eqos_inval_desc_generic(struct eqos_priv *eqos, void *desc) { unsigned long start = (unsigned long)desc; unsigned long end = ALIGN(start + sizeof(struct eqos_desc), ARCH_DMA_MINALIGN);
- invalidate_dcache_range(start, end); + if (eqos->use_cached_mem) + invalidate_dcache_range(start, end); }
-static void eqos_flush_desc_generic(void *desc) +static void eqos_flush_desc_generic(struct eqos_priv *eqos, void *desc) { unsigned long start = (unsigned long)desc; unsigned long end = ALIGN(start + sizeof(struct eqos_desc), ARCH_DMA_MINALIGN);
- flush_dcache_range(start, end); + if (eqos->use_cached_mem) + flush_dcache_range(start, end); }
static void eqos_inval_buffer_tegra186(void *buf, size_t size) @@ -1262,12 +1292,17 @@ static int eqos_start(struct udevice *dev) EQOS_MAX_PACKET_SIZE << EQOS_DMA_CH0_RX_CONTROL_RBSZ_SHIFT);
- desc_pad = (eqos->desc_size - sizeof(struct eqos_desc)) / - eqos->config->axi_bus_width; + setbits_le32(&eqos->dma_regs->ch0_control, EQOS_DMA_CH0_CONTROL_PBLX8);
- setbits_le32(&eqos->dma_regs->ch0_control, - EQOS_DMA_CH0_CONTROL_PBLX8 | - (desc_pad << EQOS_DMA_CH0_CONTROL_DSL_SHIFT)); + /* "Descriptor Skip Length" field of the DMA channel control register */ + if (eqos->use_cached_mem) { + desc_pad = (eqos->desc_size - sizeof(struct eqos_desc)) / + eqos->config->axi_bus_width; + setbits_le32(&eqos->dma_regs->ch0_control, + desc_pad << EQOS_DMA_CH0_CONTROL_DSL_SHIFT); + if (desc_pad > EQOS_DMA_CH0_CONTROL_DSL_MAX) + dev_dbg(dev, "DMA_CH0_CONTROL.DSL overflow"); + }
/* * Burst length must be < 1/2 FIFO size. @@ -1300,7 +1335,7 @@ static int eqos_start(struct udevice *dev)
for (i = 0; i < EQOS_DESCRIPTORS_TX; i++) { struct eqos_desc *tx_desc = eqos_get_desc(eqos, i, false); - eqos->config->ops->eqos_flush_desc(tx_desc); + eqos->config->ops->eqos_flush_desc(eqos, tx_desc); }
for (i = 0; i < EQOS_DESCRIPTORS_RX; i++) { @@ -1309,7 +1344,7 @@ static int eqos_start(struct udevice *dev) (i * EQOS_MAX_PACKET_SIZE)); rx_desc->des3 = EQOS_DESC3_OWN | EQOS_DESC3_BUF1V; mb(); - eqos->config->ops->eqos_flush_desc(rx_desc); + eqos->config->ops->eqos_flush_desc(eqos, rx_desc); eqos->config->ops->eqos_inval_buffer(eqos->rx_dma_buf + (i * EQOS_MAX_PACKET_SIZE), EQOS_MAX_PACKET_SIZE); @@ -1437,13 +1472,13 @@ static int eqos_send(struct udevice *dev, void *packet, int length) */ mb(); tx_desc->des3 = EQOS_DESC3_OWN | EQOS_DESC3_FD | EQOS_DESC3_LD | length; - eqos->config->ops->eqos_flush_desc(tx_desc); + eqos->config->ops->eqos_flush_desc(eqos, tx_desc);
writel((ulong)eqos_get_desc(eqos, eqos->tx_desc_idx, false), &eqos->dma_regs->ch0_txdesc_tail_pointer);
for (i = 0; i < 1000000; i++) { - eqos->config->ops->eqos_inval_desc(tx_desc); + eqos->config->ops->eqos_inval_desc(eqos, tx_desc); if (!(readl(&tx_desc->des3) & EQOS_DESC3_OWN)) return 0; udelay(1); @@ -1463,7 +1498,7 @@ static int eqos_recv(struct udevice *dev, int flags, uchar **packetp) debug("%s(dev=%p, flags=%x):\n", __func__, dev, flags);
rx_desc = eqos_get_desc(eqos, eqos->rx_desc_idx, true); - eqos->config->ops->eqos_inval_desc(rx_desc); + eqos->config->ops->eqos_inval_desc(eqos, rx_desc); if (rx_desc->des3 & EQOS_DESC3_OWN) { debug("%s: RX packet not available\n", __func__); return -EAGAIN; @@ -1501,7 +1536,7 @@ static int eqos_free_pkt(struct udevice *dev, uchar *packet, int length)
rx_desc->des0 = 0; mb(); - eqos->config->ops->eqos_flush_desc(rx_desc); + eqos->config->ops->eqos_flush_desc(eqos, rx_desc); eqos->config->ops->eqos_inval_buffer(packet, length); rx_desc->des0 = (u32)(ulong)packet; rx_desc->des1 = 0; @@ -1512,7 +1547,7 @@ static int eqos_free_pkt(struct udevice *dev, uchar *packet, int length) */ mb(); rx_desc->des3 = EQOS_DESC3_OWN | EQOS_DESC3_BUF1V; - eqos->config->ops->eqos_flush_desc(rx_desc); + eqos->config->ops->eqos_flush_desc(eqos, rx_desc);
writel((ulong)rx_desc, &eqos->dma_regs->ch0_rxdesc_tail_pointer);
@@ -1587,7 +1622,7 @@ static int eqos_remove_resources_core(struct udevice *dev) free(eqos->rx_pkt); free(eqos->rx_dma_buf); free(eqos->tx_dma_buf); - eqos_free_descs(eqos->descs); + eqos_free_descs(eqos);
debug("%s: OK\n", __func__); return 0;

Reorder include files in the U-Boot expected order.
Signed-off-by: Patrick Delaunay patrick.delaunay@foss.st.com ---
drivers/net/dwc_eth_qos.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/net/dwc_eth_qos.c b/drivers/net/dwc_eth_qos.c index 0c2ba206056..97279839baf 100644 --- a/drivers/net/dwc_eth_qos.c +++ b/drivers/net/dwc_eth_qos.c @@ -34,6 +34,7 @@ #include <cpu_func.h> #include <dm.h> #include <errno.h> +#include <eth_phy.h> #include <log.h> #include <malloc.h> #include <memalign.h> @@ -46,7 +47,6 @@ #include <asm/cache.h> #include <asm/gpio.h> #include <asm/io.h> -#include <eth_phy.h> #ifdef CONFIG_ARCH_IMX8M #include <asm/arch/clock.h> #include <asm/mach-imx/sys_proto.h>

On Thu, Jun 30, 2022 at 12:10 PM Patrick Delaunay patrick.delaunay@foss.st.com wrote:
Reorder include files in the U-Boot expected order.
Signed-off-by: Patrick Delaunay patrick.delaunay@foss.st.com
drivers/net/dwc_eth_qos.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/net/dwc_eth_qos.c b/drivers/net/dwc_eth_qos.c index 0c2ba206056..97279839baf 100644 --- a/drivers/net/dwc_eth_qos.c +++ b/drivers/net/dwc_eth_qos.c @@ -34,6 +34,7 @@ #include <cpu_func.h> #include <dm.h> #include <errno.h> +#include <eth_phy.h> #include <log.h> #include <malloc.h> #include <memalign.h> @@ -46,7 +47,6 @@ #include <asm/cache.h> #include <asm/gpio.h> #include <asm/io.h> -#include <eth_phy.h> #ifdef CONFIG_ARCH_IMX8M #include <asm/arch/clock.h>
#include <asm/mach-imx/sys_proto.h>
2.25.1
Reviewed-by: Ramon Fried rfried.dev@gmail.com

On Thu, Jun 30, 2022 at 11:09:41AM +0200, Patrick Delaunay wrote:
Reorder include files in the U-Boot expected order.
Signed-off-by: Patrick Delaunay patrick.delaunay@foss.st.com Reviewed-by: Ramon Fried rfried.dev@gmail.com
Applied to u-boot/master, thanks!

On Thu, Jun 30, 2022 at 12:10 PM Patrick Delaunay patrick.delaunay@foss.st.com wrote:
Solve an issue with AXI_WIDTH_32 on a the 64 bytes cache line platform; in this case the requested descriptor padding length should be 12 but the associated parameter EQOS_DMA_CH0_CONTROL.DSL is limited to 3bits = 7.
As the DMA descriptor can't be correctly aligned with the cache line, the maintenance of each descriptor can't be guaranteed by a simple cache line operation: flush or invalid.
To avoid all the maintenance issues, these descriptors need to be allocated in a NOT CACHEABLE memory by noncached_alloc() when CONFIG_SYS_NONCACHED_MEMORY is enable.
This patch doesn't change the current behavior when the descriptors can be cache-aligned with the field "Descriptor Skip Length" of the DMA channel control register, when eqos->desc_pad = true.
Signed-off-by: Patrick Delaunay patrick.delaunay@foss.st.com
This patch is required for ETH support on STM32MP13x family with AXI_WIDTH_32 value.
This patch doesn't change the behavior for other parameters, tested on STM32MP15x boards.
drivers/net/dwc_eth_qos.c | 79 ++++++++++++++++++++++++++++----------- 1 file changed, 57 insertions(+), 22 deletions(-)
diff --git a/drivers/net/dwc_eth_qos.c b/drivers/net/dwc_eth_qos.c index 9d255cf95ff..0c2ba206056 100644 --- a/drivers/net/dwc_eth_qos.c +++ b/drivers/net/dwc_eth_qos.c @@ -51,6 +51,7 @@ #include <asm/arch/clock.h> #include <asm/mach-imx/sys_proto.h> #endif +#include <dm/device_compat.h> #include <linux/bitops.h> #include <linux/delay.h>
@@ -210,6 +211,7 @@ struct eqos_dma_regs { #define EQOS_DMA_SYSBUS_MODE_BLEN4 BIT(1)
#define EQOS_DMA_CH0_CONTROL_DSL_SHIFT 18 +#define EQOS_DMA_CH0_CONTROL_DSL_MAX 7 #define EQOS_DMA_CH0_CONTROL_PBLX8 BIT(16)
#define EQOS_DMA_CH0_TX_CONTROL_TXPBL_SHIFT 16 @@ -274,9 +276,11 @@ struct eqos_config { struct eqos_ops *ops; };
+struct eqos_priv;
struct eqos_ops {
void (*eqos_inval_desc)(void *desc);
void (*eqos_flush_desc)(void *desc);
void (*eqos_inval_desc)(struct eqos_priv *eqos, void *desc);
void (*eqos_flush_desc)(struct eqos_priv *eqos, void *desc); void (*eqos_inval_buffer)(void *buf, size_t size); void (*eqos_flush_buffer)(void *buf, size_t size); int (*eqos_probe_resources)(struct udevice *dev);
@@ -319,6 +323,7 @@ struct eqos_priv { bool started; bool reg_access_ok; bool clk_ck_enabled;
bool use_cached_mem;
};
/* @@ -341,15 +346,38 @@ struct eqos_priv { */ static void *eqos_alloc_descs(struct eqos_priv *eqos, unsigned int num) {
void *descs = NULL;
ulong desc_pad;
/*
* if descriptors can to be cache-line aligned with the DSL =
* "Descriptor Skip Length" field of the DMA channel control register
*/ eqos->desc_size = ALIGN(sizeof(struct eqos_desc), (unsigned int)ARCH_DMA_MINALIGN);
desc_pad = (eqos->desc_size - sizeof(struct eqos_desc)) /
eqos->config->axi_bus_width;
if (desc_pad <= EQOS_DMA_CH0_CONTROL_DSL_MAX) {
eqos->use_cached_mem = true;
descs = memalign(eqos->desc_size, num * eqos->desc_size);
} else {
eqos->use_cached_mem = false;
eqos->desc_size = sizeof(struct eqos_desc);
+#ifdef CONFIG_SYS_NONCACHED_MEMORY
descs = (void *)noncached_alloc(num * eqos->desc_size, ARCH_DMA_MINALIGN);
+#else
log_err("DMA descriptors with cached memory.");
+#endif
}
return memalign(eqos->desc_size, num * eqos->desc_size);
return descs;
}
-static void eqos_free_descs(void *descs) +static void eqos_free_descs(struct eqos_priv *eqos) {
free(descs);
if (eqos->use_cached_mem)
free(eqos->descs);
/* memory allocated by noncached_alloc() can't be freed */
}
static struct eqos_desc *eqos_get_desc(struct eqos_priv *eqos, @@ -359,22 +387,24 @@ static struct eqos_desc *eqos_get_desc(struct eqos_priv *eqos, ((rx ? EQOS_DESCRIPTORS_TX : 0) + num) * eqos->desc_size; }
-static void eqos_inval_desc_generic(void *desc) +static void eqos_inval_desc_generic(struct eqos_priv *eqos, void *desc) { unsigned long start = (unsigned long)desc; unsigned long end = ALIGN(start + sizeof(struct eqos_desc), ARCH_DMA_MINALIGN);
invalidate_dcache_range(start, end);
if (eqos->use_cached_mem)
invalidate_dcache_range(start, end);
}
-static void eqos_flush_desc_generic(void *desc) +static void eqos_flush_desc_generic(struct eqos_priv *eqos, void *desc) { unsigned long start = (unsigned long)desc; unsigned long end = ALIGN(start + sizeof(struct eqos_desc), ARCH_DMA_MINALIGN);
flush_dcache_range(start, end);
if (eqos->use_cached_mem)
flush_dcache_range(start, end);
}
static void eqos_inval_buffer_tegra186(void *buf, size_t size) @@ -1262,12 +1292,17 @@ static int eqos_start(struct udevice *dev) EQOS_MAX_PACKET_SIZE << EQOS_DMA_CH0_RX_CONTROL_RBSZ_SHIFT);
desc_pad = (eqos->desc_size - sizeof(struct eqos_desc)) /
eqos->config->axi_bus_width;
setbits_le32(&eqos->dma_regs->ch0_control, EQOS_DMA_CH0_CONTROL_PBLX8);
setbits_le32(&eqos->dma_regs->ch0_control,
EQOS_DMA_CH0_CONTROL_PBLX8 |
(desc_pad << EQOS_DMA_CH0_CONTROL_DSL_SHIFT));
/* "Descriptor Skip Length" field of the DMA channel control register */
if (eqos->use_cached_mem) {
desc_pad = (eqos->desc_size - sizeof(struct eqos_desc)) /
eqos->config->axi_bus_width;
setbits_le32(&eqos->dma_regs->ch0_control,
desc_pad << EQOS_DMA_CH0_CONTROL_DSL_SHIFT);
if (desc_pad > EQOS_DMA_CH0_CONTROL_DSL_MAX)
dev_dbg(dev, "DMA_CH0_CONTROL.DSL overflow");
} /* * Burst length must be < 1/2 FIFO size.
@@ -1300,7 +1335,7 @@ static int eqos_start(struct udevice *dev)
for (i = 0; i < EQOS_DESCRIPTORS_TX; i++) { struct eqos_desc *tx_desc = eqos_get_desc(eqos, i, false);
eqos->config->ops->eqos_flush_desc(tx_desc);
eqos->config->ops->eqos_flush_desc(eqos, tx_desc); } for (i = 0; i < EQOS_DESCRIPTORS_RX; i++) {
@@ -1309,7 +1344,7 @@ static int eqos_start(struct udevice *dev) (i * EQOS_MAX_PACKET_SIZE)); rx_desc->des3 = EQOS_DESC3_OWN | EQOS_DESC3_BUF1V; mb();
eqos->config->ops->eqos_flush_desc(rx_desc);
eqos->config->ops->eqos_flush_desc(eqos, rx_desc); eqos->config->ops->eqos_inval_buffer(eqos->rx_dma_buf + (i * EQOS_MAX_PACKET_SIZE), EQOS_MAX_PACKET_SIZE);
@@ -1437,13 +1472,13 @@ static int eqos_send(struct udevice *dev, void *packet, int length) */ mb(); tx_desc->des3 = EQOS_DESC3_OWN | EQOS_DESC3_FD | EQOS_DESC3_LD | length;
eqos->config->ops->eqos_flush_desc(tx_desc);
eqos->config->ops->eqos_flush_desc(eqos, tx_desc); writel((ulong)eqos_get_desc(eqos, eqos->tx_desc_idx, false), &eqos->dma_regs->ch0_txdesc_tail_pointer); for (i = 0; i < 1000000; i++) {
eqos->config->ops->eqos_inval_desc(tx_desc);
eqos->config->ops->eqos_inval_desc(eqos, tx_desc); if (!(readl(&tx_desc->des3) & EQOS_DESC3_OWN)) return 0; udelay(1);
@@ -1463,7 +1498,7 @@ static int eqos_recv(struct udevice *dev, int flags, uchar **packetp) debug("%s(dev=%p, flags=%x):\n", __func__, dev, flags);
rx_desc = eqos_get_desc(eqos, eqos->rx_desc_idx, true);
eqos->config->ops->eqos_inval_desc(rx_desc);
eqos->config->ops->eqos_inval_desc(eqos, rx_desc); if (rx_desc->des3 & EQOS_DESC3_OWN) { debug("%s: RX packet not available\n", __func__); return -EAGAIN;
@@ -1501,7 +1536,7 @@ static int eqos_free_pkt(struct udevice *dev, uchar *packet, int length)
rx_desc->des0 = 0; mb();
eqos->config->ops->eqos_flush_desc(rx_desc);
eqos->config->ops->eqos_flush_desc(eqos, rx_desc); eqos->config->ops->eqos_inval_buffer(packet, length); rx_desc->des0 = (u32)(ulong)packet; rx_desc->des1 = 0;
@@ -1512,7 +1547,7 @@ static int eqos_free_pkt(struct udevice *dev, uchar *packet, int length) */ mb(); rx_desc->des3 = EQOS_DESC3_OWN | EQOS_DESC3_BUF1V;
eqos->config->ops->eqos_flush_desc(rx_desc);
eqos->config->ops->eqos_flush_desc(eqos, rx_desc); writel((ulong)rx_desc, &eqos->dma_regs->ch0_rxdesc_tail_pointer);
@@ -1587,7 +1622,7 @@ static int eqos_remove_resources_core(struct udevice *dev) free(eqos->rx_pkt); free(eqos->rx_dma_buf); free(eqos->tx_dma_buf);
eqos_free_descs(eqos->descs);
eqos_free_descs(eqos); debug("%s: OK\n", __func__); return 0;
-- 2.25.1
Reviewed-by: Ramon Fried rfried.dev@gmail.com

On Thu, Jun 30, 2022 at 11:09:40AM +0200, Patrick Delaunay wrote:
Solve an issue with AXI_WIDTH_32 on a the 64 bytes cache line platform; in this case the requested descriptor padding length should be 12 but the associated parameter EQOS_DMA_CH0_CONTROL.DSL is limited to 3bits = 7.
As the DMA descriptor can't be correctly aligned with the cache line, the maintenance of each descriptor can't be guaranteed by a simple cache line operation: flush or invalid.
To avoid all the maintenance issues, these descriptors need to be allocated in a NOT CACHEABLE memory by noncached_alloc() when CONFIG_SYS_NONCACHED_MEMORY is enable.
This patch doesn't change the current behavior when the descriptors can be cache-aligned with the field "Descriptor Skip Length" of the DMA channel control register, when eqos->desc_pad = true.
Signed-off-by: Patrick Delaunay patrick.delaunay@foss.st.com Reviewed-by: Ramon Fried rfried.dev@gmail.com
Please rebase this on top of current master, thanks.
participants (3)
-
Patrick Delaunay
-
Ramon Fried
-
Tom Rini