diff --git a/drivers/net/ethernet/freescale/fec.h b/drivers/net/ethernet/freescale/fec.h index 423838c8a27ae..df33dc711ebd0 100644 --- a/drivers/net/ethernet/freescale/fec.h +++ b/drivers/net/ethernet/freescale/fec.h @@ -514,6 +514,12 @@ struct bufdesc_ex { */ #define FEC_QUIRK_HAS_MDIO_C45 BIT(24) +/* i.MX8MM FEC hardware RX checksum offload incorrectly validates certain UDP + * packets, causing them to be silently dropped by the kernel UDP stack. + * Force software re-validation for UDP by downgrading CHECKSUM_UNNECESSARY. + */ +#define FEC_QUIRK_ERR_UDP_CSUM BIT(25) + struct bufdesc_prop { int qid; /* Address of Rx and Tx buffers */ diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index b445ae835849d..2bee5ccc0cb75 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -154,6 +154,17 @@ static const struct fec_devinfo fec_imx6ul_info = { FEC_QUIRK_HAS_MDIO_C45, }; +static const struct fec_devinfo fec_imx8mm_info = { + .quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT | + FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM | + FEC_QUIRK_HAS_VLAN | FEC_QUIRK_HAS_AVB | + FEC_QUIRK_ERR007885 | FEC_QUIRK_BUG_CAPTURE | + FEC_QUIRK_HAS_RACC | FEC_QUIRK_HAS_COALESCE | + FEC_QUIRK_CLEAR_SETUP_MII | FEC_QUIRK_HAS_MULTI_QUEUES | + FEC_QUIRK_HAS_EEE | FEC_QUIRK_WAKEUP_FROM_INT2 | + FEC_QUIRK_HAS_MDIO_C45 | FEC_QUIRK_ERR_UDP_CSUM, +}; + static const struct fec_devinfo fec_imx8mq_info = { .quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT | FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM | @@ -209,6 +220,9 @@ static struct platform_device_id fec_devtype[] = { }, { .name = "imx6ul-fec", .driver_data = (kernel_ulong_t)&fec_imx6ul_info, + }, { + .name = "imx8mm-fec", + .driver_data = (kernel_ulong_t)&fec_imx8mm_info, }, { .name = "imx8mq-fec", .driver_data = (kernel_ulong_t)&fec_imx8mq_info, @@ -232,6 +246,7 @@ enum imx_fec_type { MVF600_FEC, IMX6SX_FEC, IMX6UL_FEC, + IMX8MM_FEC, IMX8MQ_FEC, IMX8QM_FEC, S32V234_FEC, @@ -245,6 +260,7 @@ static const struct of_device_id fec_dt_ids[] = { { .compatible = "fsl,mvf600-fec", .data = &fec_devtype[MVF600_FEC], }, { .compatible = "fsl,imx6sx-fec", .data = &fec_devtype[IMX6SX_FEC], }, { .compatible = "fsl,imx6ul-fec", .data = &fec_devtype[IMX6UL_FEC], }, + { .compatible = "fsl,imx8mm-fec", .data = &fec_devtype[IMX8MM_FEC], }, { .compatible = "fsl,imx8mq-fec", .data = &fec_devtype[IMX8MQ_FEC], }, { .compatible = "fsl,imx8qm-fec", .data = &fec_devtype[IMX8QM_FEC], }, { .compatible = "fsl,s32v234-fec", .data = &fec_devtype[S32V234_FEC], }, @@ -1786,8 +1802,17 @@ fec_enet_rx_queue(struct net_device *ndev, int budget, u16 queue_id) if (fep->bufdesc_ex && (fep->csum_flags & FLAG_RX_CSUM_ENABLED)) { if (!(ebdp->cbd_esc & cpu_to_fec32(FLAG_RX_CSUM_ERROR))) { - /* don't check it */ skb->ip_summed = CHECKSUM_UNNECESSARY; + /* i.MX8MM FEC HW checksum offload incorrectly + * validates certain UDP packets (FEC_QUIRK_ERR_UDP_CSUM). + * Force software re-validation for UDP only. + */ + if (fep->quirks & FEC_QUIRK_ERR_UDP_CSUM) { + struct iphdr *iph = (struct iphdr *)skb_network_header(skb); + + if (iph && iph->protocol == IPPROTO_UDP) + skb->ip_summed = CHECKSUM_NONE; + } } else { skb_checksum_none_assert(skb); }