[PATCH 1/2] net: dwc_eth_qos: restore support of not cache-aligned descriptor
Ramon Fried
rfried.dev at gmail.com
Sun Jul 3 21:22:45 CEST 2022
On Thu, Jun 30, 2022 at 12:10 PM Patrick Delaunay
<patrick.delaunay at 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 at 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 at gmail.com>
More information about the U-Boot
mailing list