On Wed, Mar 06, 2024 at 09:07:17AM +0100, Herve Codina wrote:
> The QMC HDLC driver provides support for HDLC using the QMC (QUICC
> Multichannel Controller) to transfer the HDLC data.
> 
> Signed-off-by: Herve Codina <herve.cod...@bootlin.com>
> Reviewed-by: Christophe Leroy <christophe.le...@csgroup.eu>
> Acked-by: Jakub Kicinski <k...@kernel.org>
> Reviewed-by: Andy Shevchenko <andriy.shevche...@linux.intel.com>
> ---
>  drivers/net/wan/Kconfig        |  12 +
>  drivers/net/wan/Makefile       |   1 +
>  drivers/net/wan/fsl_qmc_hdlc.c | 413 +++++++++++++++++++++++++++++++++
>  3 files changed, 426 insertions(+)
>  create mode 100644 drivers/net/wan/fsl_qmc_hdlc.c
> 
> diff --git a/drivers/net/wan/Kconfig b/drivers/net/wan/Kconfig
> index 7dda87756d3f..31ab2136cdf1 100644
> --- a/drivers/net/wan/Kconfig
> +++ b/drivers/net/wan/Kconfig
> @@ -197,6 +197,18 @@ config FARSYNC
>         To compile this driver as a module, choose M here: the
>         module will be called farsync.
>  
> +config FSL_QMC_HDLC
> +     tristate "Freescale QMC HDLC support"
> +     depends on HDLC
> +     depends on CPM_QMC
> +     help
> +       HDLC support using the Freescale QUICC Multichannel Controller (QMC).
> +
> +       To compile this driver as a module, choose M here: the
> +       module will be called fsl_qmc_hdlc.
> +
> +       If unsure, say N.
> +
>  config FSL_UCC_HDLC
>       tristate "Freescale QUICC Engine HDLC support"
>       depends on HDLC
> diff --git a/drivers/net/wan/Makefile b/drivers/net/wan/Makefile
> index 8119b49d1da9..00e9b7ee1e01 100644
> --- a/drivers/net/wan/Makefile
> +++ b/drivers/net/wan/Makefile
> @@ -25,6 +25,7 @@ obj-$(CONFIG_WANXL)         += wanxl.o
>  obj-$(CONFIG_PCI200SYN)              += pci200syn.o
>  obj-$(CONFIG_PC300TOO)               += pc300too.o
>  obj-$(CONFIG_IXP4XX_HSS)     += ixp4xx_hss.o
> +obj-$(CONFIG_FSL_QMC_HDLC)   += fsl_qmc_hdlc.o
>  obj-$(CONFIG_FSL_UCC_HDLC)   += fsl_ucc_hdlc.o
>  obj-$(CONFIG_SLIC_DS26522)   += slic_ds26522.o
>  
> diff --git a/drivers/net/wan/fsl_qmc_hdlc.c b/drivers/net/wan/fsl_qmc_hdlc.c
> new file mode 100644
> index 000000000000..90063a92209e
> --- /dev/null
> +++ b/drivers/net/wan/fsl_qmc_hdlc.c
> @@ -0,0 +1,413 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Freescale QMC HDLC Device Driver
> + *
> + * Copyright 2023 CS GROUP France
> + *
> + * Author: Herve Codina <herve.cod...@bootlin.com>
> + */
> +
> +#include <linux/array_size.h>
> +#include <linux/bug.h>
> +#include <linux/cleanup.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/device.h>
> +#include <linux/err.h>
> +#include <linux/hdlc.h>
> +#include <linux/mod_devicetable.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +#include <linux/spinlock.h>
> +#include <linux/types.h>
> +
> +#include <soc/fsl/qe/qmc.h>
> +
> +struct qmc_hdlc_desc {
> +     struct net_device *netdev;
> +     struct sk_buff *skb; /* NULL if the descriptor is not in use */
> +     dma_addr_t dma_addr;
> +     size_t dma_size;
> +};
> +
> +struct qmc_hdlc {
> +     struct device *dev;
> +     struct qmc_chan *qmc_chan;
> +     struct net_device *netdev;
> +     bool is_crc32;
> +     spinlock_t tx_lock; /* Protect tx descriptors */
> +     struct qmc_hdlc_desc tx_descs[8];
> +     unsigned int tx_out;
> +     struct qmc_hdlc_desc rx_descs[4];
> +};
> +
> +static struct qmc_hdlc *netdev_to_qmc_hdlc(struct net_device *netdev)
> +{
> +     return dev_to_hdlc(netdev)->priv;
> +}
> +
> +static int qmc_hdlc_recv_queue(struct qmc_hdlc *qmc_hdlc, struct 
> qmc_hdlc_desc *desc, size_t size);
> +
> +#define QMC_HDLC_RX_ERROR_FLAGS                              \
> +     (QMC_RX_FLAG_HDLC_OVF | QMC_RX_FLAG_HDLC_UNA |  \
> +      QMC_RX_FLAG_HDLC_CRC | QMC_RX_FLAG_HDLC_ABORT)
> +
> +static void qmc_hcld_recv_complete(void *context, size_t length, unsigned 
> int flags)
> +{
> +     struct qmc_hdlc_desc *desc = context;
> +     struct net_device *netdev = desc->netdev;
> +     struct qmc_hdlc *qmc_hdlc = netdev_to_qmc_hdlc(netdev);
> +     int ret;
> +
> +     dma_unmap_single(qmc_hdlc->dev, desc->dma_addr, desc->dma_size, 
> DMA_FROM_DEVICE);
> +
> +     if (flags & QMC_HDLC_RX_ERROR_FLAGS) {
> +             netdev->stats.rx_errors++;
> +             if (flags & QMC_RX_FLAG_HDLC_OVF) /* Data overflow */
> +                     netdev->stats.rx_over_errors++;
> +             if (flags & QMC_RX_FLAG_HDLC_UNA) /* bits received not multiple 
> of 8 */
> +                     netdev->stats.rx_frame_errors++;
> +             if (flags & QMC_RX_FLAG_HDLC_ABORT) /* Received an abort 
> sequence */
> +                     netdev->stats.rx_frame_errors++;
> +             if (flags & QMC_RX_FLAG_HDLC_CRC) /* CRC error */
> +                     netdev->stats.rx_crc_errors++;

It's minor, but you can avoid conditionals doing something like:

                netdev->stats.rx_over_errors += !!(flags & 
QMC_RX_FLAG_HDLC_OVF);

Thanks,
Yury

> +             kfree_skb(desc->skb);
> +     } else {
> +             netdev->stats.rx_packets++;
> +             netdev->stats.rx_bytes += length;
> +
> +             skb_put(desc->skb, length);
> +             desc->skb->protocol = hdlc_type_trans(desc->skb, netdev);
> +             netif_rx(desc->skb);
> +     }
> +
> +     /* Re-queue a transfer using the same descriptor */
> +     ret = qmc_hdlc_recv_queue(qmc_hdlc, desc, desc->dma_size);
> +     if (ret) {
> +             dev_err(qmc_hdlc->dev, "queue recv desc failed (%d)\n", ret);
> +             netdev->stats.rx_errors++;
> +     }
> +}

Reply via email to