Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions drivers/net/ethernet/freescale/fec.h
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,16 @@ struct bufdesc_ex {
*/
#define FEC_QUIRK_HAS_MDIO_C45 BIT(24)

/* i.MX8MM FEC hardware RX checksum offload incorrectly validates certain IPv4
* UDP packets, causing them to be silently dropped. This quirk drives two
* workarounds:
* - fec_restart() clears FEC_RACC_PRODIS so the hardware stops discarding
* these frames before the driver receives them.
* - the RX path forces software re-validation for IPv4/UDP by downgrading
* CHECKSUM_UNNECESSARY to CHECKSUM_NONE.
*/
#define FEC_QUIRK_ERR_UDP_CSUM BIT(25)

struct bufdesc_prop {
int qid;
/* Address of Rx and Tx buffers */
Expand Down
50 changes: 46 additions & 4 deletions drivers/net/ethernet/freescale/fec_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 |
Expand Down Expand Up @@ -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,
Expand All @@ -232,6 +246,7 @@ enum imx_fec_type {
MVF600_FEC,
IMX6SX_FEC,
IMX6UL_FEC,
IMX8MM_FEC,
IMX8MQ_FEC,
IMX8QM_FEC,
S32V234_FEC,
Expand All @@ -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], },
Expand Down Expand Up @@ -1093,11 +1109,25 @@ fec_restart(struct net_device *ndev)

/* align IP header */
val |= FEC_RACC_SHIFT16;
if (fep->csum_flags & FLAG_RX_CSUM_ENABLED)
if (fep->csum_flags & FLAG_RX_CSUM_ENABLED) {
/* set RX checksum */
val |= FEC_RACC_OPTIONS;
else
u32 racc_opts = FEC_RACC_OPTIONS;

/* i.MX8MM FEC incorrectly discards valid UDP frames when
* PRODIS is active due to a hardware checksum errata.
* Disable protocol discard for this variant only.
*/
if (fep->quirks & FEC_QUIRK_ERR_UDP_CSUM)
racc_opts &= ~FEC_RACC_PRODIS;
/* clear all option bits first so any masked-out bits
* (e.g. PRODIS) already set in the register are cleared
* rather than left active by the OR below
*/
val &= ~FEC_RACC_OPTIONS;
val |= racc_opts;
Comment thread
Overdr0ne marked this conversation as resolved.
} else {
val &= ~FEC_RACC_OPTIONS;
}
writel(val, fep->hwp + FEC_RACC);
writel(PKT_MAXBUF_SIZE, fep->hwp + FEC_FTRL);
}
Expand Down Expand Up @@ -1786,8 +1816,20 @@ 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 &&
skb->protocol == htons(ETH_P_IP)) {
struct iphdr _iph;
const struct iphdr *iph;

iph = skb_header_pointer(skb, 0, sizeof(_iph), &_iph);
if (iph && iph->protocol == IPPROTO_UDP)
skb->ip_summed = CHECKSUM_NONE;
}
Comment thread
Overdr0ne marked this conversation as resolved.
} else {
skb_checksum_none_assert(skb);
}
Expand Down