Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

net: fec: set GPR bit on suspend by DT configuration.

On some SoCs, such as the i.MX6, it is necessary to set a bit
in the SoC level GPR register before suspending for wake on lan
to work.

The fec platform callback sleep_mode_enable was intended to allow this
but the platform implementation was NAK'd back in 2015 [1]

This means that, currently, wake on lan is broken on mainline for
the i.MX6 at least.

So implement the required bit setting in the fec driver by itself
by adding a new optional DT property indicating the GPR register
and adding the offset and bit information to the driver.

[1] https://www.spinics.net/lists/netdev/msg310922.html

Signed-off-by: Martin Fuzzey <martin.fuzzey@flowbird.group>
Signed-off-by: Fugang Duan <fugang.duan@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Martin Fuzzey and committed by
David S. Miller
da722186 045065f0

+127 -29
+7
drivers/net/ethernet/freescale/fec.h
··· 488 488 struct sk_buff *rx_skbuff[RX_RING_SIZE]; 489 489 }; 490 490 491 + struct fec_stop_mode_gpr { 492 + struct regmap *gpr; 493 + u8 reg; 494 + u8 bit; 495 + }; 496 + 491 497 /* The FEC buffer descriptors track the ring buffers. The rx_bd_base and 492 498 * tx_bd_base always point to the base of the buffer descriptors. The 493 499 * cur_rx and cur_tx point to the currently available buffer. ··· 568 562 int hwts_tx_en; 569 563 struct delayed_work time_keep; 570 564 struct regulator *reg_phy; 565 + struct fec_stop_mode_gpr stop_gpr; 571 566 572 567 unsigned int tx_align; 573 568 unsigned int rx_align;
+120 -29
drivers/net/ethernet/freescale/fec_main.c
··· 62 62 #include <linux/if_vlan.h> 63 63 #include <linux/pinctrl/consumer.h> 64 64 #include <linux/prefetch.h> 65 + #include <linux/mfd/syscon.h> 66 + #include <linux/regmap.h> 65 67 #include <soc/imx/cpuidle.h> 66 68 67 69 #include <asm/cacheflush.h> ··· 86 84 #define FEC_ENET_OPD_V 0xFFF0 87 85 #define FEC_MDIO_PM_TIMEOUT 100 /* ms */ 88 86 87 + struct fec_devinfo { 88 + u32 quirks; 89 + u8 stop_gpr_reg; 90 + u8 stop_gpr_bit; 91 + }; 92 + 93 + static const struct fec_devinfo fec_imx25_info = { 94 + .quirks = FEC_QUIRK_USE_GASKET | FEC_QUIRK_MIB_CLEAR | 95 + FEC_QUIRK_HAS_FRREG, 96 + }; 97 + 98 + static const struct fec_devinfo fec_imx27_info = { 99 + .quirks = FEC_QUIRK_MIB_CLEAR | FEC_QUIRK_HAS_FRREG, 100 + }; 101 + 102 + static const struct fec_devinfo fec_imx28_info = { 103 + .quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_SWAP_FRAME | 104 + FEC_QUIRK_SINGLE_MDIO | FEC_QUIRK_HAS_RACC | 105 + FEC_QUIRK_HAS_FRREG, 106 + }; 107 + 108 + static const struct fec_devinfo fec_imx6q_info = { 109 + .quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT | 110 + FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM | 111 + FEC_QUIRK_HAS_VLAN | FEC_QUIRK_ERR006358 | 112 + FEC_QUIRK_HAS_RACC, 113 + .stop_gpr_reg = 0x34, 114 + .stop_gpr_bit = 27, 115 + }; 116 + 117 + static const struct fec_devinfo fec_mvf600_info = { 118 + .quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_RACC, 119 + }; 120 + 121 + static const struct fec_devinfo fec_imx6x_info = { 122 + .quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT | 123 + FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM | 124 + FEC_QUIRK_HAS_VLAN | FEC_QUIRK_HAS_AVB | 125 + FEC_QUIRK_ERR007885 | FEC_QUIRK_BUG_CAPTURE | 126 + FEC_QUIRK_HAS_RACC | FEC_QUIRK_HAS_COALESCE, 127 + }; 128 + 129 + static const struct fec_devinfo fec_imx6ul_info = { 130 + .quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT | 131 + FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM | 132 + FEC_QUIRK_HAS_VLAN | FEC_QUIRK_ERR007885 | 133 + FEC_QUIRK_BUG_CAPTURE | FEC_QUIRK_HAS_RACC | 134 + FEC_QUIRK_HAS_COALESCE, 135 + }; 136 + 89 137 static struct platform_device_id fec_devtype[] = { 90 138 { 91 139 /* keep it for coldfire */ ··· 143 91 .driver_data = 0, 144 92 }, { 145 93 .name = "imx25-fec", 146 - .driver_data = FEC_QUIRK_USE_GASKET | FEC_QUIRK_MIB_CLEAR | 147 - FEC_QUIRK_HAS_FRREG, 94 + .driver_data = (kernel_ulong_t)&fec_imx25_info, 148 95 }, { 149 96 .name = "imx27-fec", 150 - .driver_data = FEC_QUIRK_MIB_CLEAR | FEC_QUIRK_HAS_FRREG, 97 + .driver_data = (kernel_ulong_t)&fec_imx27_info, 151 98 }, { 152 99 .name = "imx28-fec", 153 - .driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_SWAP_FRAME | 154 - FEC_QUIRK_SINGLE_MDIO | FEC_QUIRK_HAS_RACC | 155 - FEC_QUIRK_HAS_FRREG, 100 + .driver_data = (kernel_ulong_t)&fec_imx28_info, 156 101 }, { 157 102 .name = "imx6q-fec", 158 - .driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT | 159 - FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM | 160 - FEC_QUIRK_HAS_VLAN | FEC_QUIRK_ERR006358 | 161 - FEC_QUIRK_HAS_RACC, 103 + .driver_data = (kernel_ulong_t)&fec_imx6q_info, 162 104 }, { 163 105 .name = "mvf600-fec", 164 - .driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_RACC, 106 + .driver_data = (kernel_ulong_t)&fec_mvf600_info, 165 107 }, { 166 108 .name = "imx6sx-fec", 167 - .driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT | 168 - FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM | 169 - FEC_QUIRK_HAS_VLAN | FEC_QUIRK_HAS_AVB | 170 - FEC_QUIRK_ERR007885 | FEC_QUIRK_BUG_CAPTURE | 171 - FEC_QUIRK_HAS_RACC | FEC_QUIRK_HAS_COALESCE, 109 + .driver_data = (kernel_ulong_t)&fec_imx6x_info, 172 110 }, { 173 111 .name = "imx6ul-fec", 174 - .driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT | 175 - FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM | 176 - FEC_QUIRK_HAS_VLAN | FEC_QUIRK_ERR007885 | 177 - FEC_QUIRK_BUG_CAPTURE | FEC_QUIRK_HAS_RACC | 178 - FEC_QUIRK_HAS_COALESCE, 112 + .driver_data = (kernel_ulong_t)&fec_imx6ul_info, 179 113 }, { 180 114 /* sentinel */ 181 115 } ··· 1130 1092 1131 1093 } 1132 1094 1095 + static void fec_enet_stop_mode(struct fec_enet_private *fep, bool enabled) 1096 + { 1097 + struct fec_platform_data *pdata = fep->pdev->dev.platform_data; 1098 + struct fec_stop_mode_gpr *stop_gpr = &fep->stop_gpr; 1099 + 1100 + if (stop_gpr->gpr) { 1101 + if (enabled) 1102 + regmap_update_bits(stop_gpr->gpr, stop_gpr->reg, 1103 + BIT(stop_gpr->bit), 1104 + BIT(stop_gpr->bit)); 1105 + else 1106 + regmap_update_bits(stop_gpr->gpr, stop_gpr->reg, 1107 + BIT(stop_gpr->bit), 0); 1108 + } else if (pdata && pdata->sleep_mode_enable) { 1109 + pdata->sleep_mode_enable(enabled); 1110 + } 1111 + } 1112 + 1133 1113 static void 1134 1114 fec_stop(struct net_device *ndev) 1135 1115 { 1136 1116 struct fec_enet_private *fep = netdev_priv(ndev); 1137 - struct fec_platform_data *pdata = fep->pdev->dev.platform_data; 1138 1117 u32 rmii_mode = readl(fep->hwp + FEC_R_CNTRL) & (1 << 8); 1139 1118 u32 val; 1140 1119 ··· 1180 1125 val = readl(fep->hwp + FEC_ECNTRL); 1181 1126 val |= (FEC_ECR_MAGICEN | FEC_ECR_SLEEP); 1182 1127 writel(val, fep->hwp + FEC_ECNTRL); 1183 - 1184 - if (pdata && pdata->sleep_mode_enable) 1185 - pdata->sleep_mode_enable(true); 1128 + fec_enet_stop_mode(fep, true); 1186 1129 } 1187 1130 writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED); 1188 1131 ··· 3451 3398 return irq_cnt; 3452 3399 } 3453 3400 3401 + static int fec_enet_init_stop_mode(struct fec_enet_private *fep, 3402 + struct fec_devinfo *dev_info, 3403 + struct device_node *np) 3404 + { 3405 + struct device_node *gpr_np; 3406 + int ret = 0; 3407 + 3408 + if (!dev_info) 3409 + return 0; 3410 + 3411 + gpr_np = of_parse_phandle(np, "gpr", 0); 3412 + if (!gpr_np) 3413 + return 0; 3414 + 3415 + fep->stop_gpr.gpr = syscon_node_to_regmap(gpr_np); 3416 + if (IS_ERR(fep->stop_gpr.gpr)) { 3417 + dev_err(&fep->pdev->dev, "could not find gpr regmap\n"); 3418 + ret = PTR_ERR(fep->stop_gpr.gpr); 3419 + fep->stop_gpr.gpr = NULL; 3420 + goto out; 3421 + } 3422 + 3423 + fep->stop_gpr.reg = dev_info->stop_gpr_reg; 3424 + fep->stop_gpr.bit = dev_info->stop_gpr_bit; 3425 + 3426 + out: 3427 + of_node_put(gpr_np); 3428 + 3429 + return ret; 3430 + } 3431 + 3454 3432 static int 3455 3433 fec_probe(struct platform_device *pdev) 3456 3434 { ··· 3497 3413 int num_rx_qs; 3498 3414 char irq_name[8]; 3499 3415 int irq_cnt; 3416 + struct fec_devinfo *dev_info; 3500 3417 3501 3418 fec_enet_get_queue_num(pdev, &num_tx_qs, &num_rx_qs); 3502 3419 ··· 3515 3430 of_id = of_match_device(fec_dt_ids, &pdev->dev); 3516 3431 if (of_id) 3517 3432 pdev->id_entry = of_id->data; 3518 - fep->quirks = pdev->id_entry->driver_data; 3433 + dev_info = (struct fec_devinfo *)pdev->id_entry->driver_data; 3434 + if (dev_info) 3435 + fep->quirks = dev_info->quirks; 3519 3436 3520 3437 fep->netdev = ndev; 3521 3438 fep->num_rx_queues = num_rx_qs; ··· 3550 3463 3551 3464 if (of_get_property(np, "fsl,magic-packet", NULL)) 3552 3465 fep->wol_flag |= FEC_WOL_HAS_MAGIC_PACKET; 3466 + 3467 + ret = fec_enet_init_stop_mode(fep, dev_info, np); 3468 + if (ret) 3469 + goto failed_stop_mode; 3553 3470 3554 3471 phy_node = of_parse_phandle(np, "phy-handle", 0); 3555 3472 if (!phy_node && of_phy_is_fixed_link(np)) { ··· 3723 3632 if (of_phy_is_fixed_link(np)) 3724 3633 of_phy_deregister_fixed_link(np); 3725 3634 of_node_put(phy_node); 3635 + failed_stop_mode: 3726 3636 failed_phy: 3727 3637 dev_id--; 3728 3638 failed_ioremap: ··· 3801 3709 { 3802 3710 struct net_device *ndev = dev_get_drvdata(dev); 3803 3711 struct fec_enet_private *fep = netdev_priv(ndev); 3804 - struct fec_platform_data *pdata = fep->pdev->dev.platform_data; 3805 3712 int ret; 3806 3713 int val; 3807 3714 ··· 3818 3727 goto failed_clk; 3819 3728 } 3820 3729 if (fep->wol_flag & FEC_WOL_FLAG_ENABLE) { 3821 - if (pdata && pdata->sleep_mode_enable) 3822 - pdata->sleep_mode_enable(false); 3730 + fec_enet_stop_mode(fep, false); 3731 + 3823 3732 val = readl(fep->hwp + FEC_ECNTRL); 3824 3733 val &= ~(FEC_ECR_MAGICEN | FEC_ECR_SLEEP); 3825 3734 writel(val, fep->hwp + FEC_ECNTRL);