This patch adds the ability for the fec to use the mdc/mdio bitbang protocol to communicate with its phy. It adds two new optional parameters in the devicetree definition for the two needed GPIO (mdc and mdio). It uses the mdio-bitbang generic implementation. Signed-off-by: Adrien Grassein <adrien.grassein@xxxxxxxxx> --- drivers/net/ethernet/freescale/fec.h | 5 ++ drivers/net/ethernet/freescale/fec_main.c | 101 +++++++++++++++++++--- 2 files changed, 95 insertions(+), 11 deletions(-) diff --git a/drivers/net/ethernet/freescale/fec.h b/drivers/net/ethernet/freescale/fec.h index c527f4ee1d3a..84bf9be4c6f5 100644 --- a/drivers/net/ethernet/freescale/fec.h +++ b/drivers/net/ethernet/freescale/fec.h @@ -15,6 +15,7 @@ /****************************************************************************/ #include <linux/clocksource.h> +#include <linux/mdio-bitbang.h> #include <linux/net_tstamp.h> #include <linux/ptp_clock_kernel.h> #include <linux/timecounter.h> @@ -590,6 +591,10 @@ struct fec_enet_private { int pps_enable; unsigned int next_counter; + struct mdiobb_ctrl fec_main_bb_ctrl; + struct gpio_desc *gd_mdc; + struct gpio_desc *gd_mdio; + u64 ethtool_stats[]; }; diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index 04f24c66cf36..4f22c8e3fe7e 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -2050,6 +2050,48 @@ static int fec_enet_mii_probe(struct net_device *ndev) return 0; } +static void fec_main_bb_ctrl_set_mdc(struct mdiobb_ctrl *ctrl, int level) +{ + struct fec_enet_private *fep = container_of(ctrl, + struct fec_enet_private, fec_main_bb_ctrl); + if (level) + gpiod_direction_input(fep->gd_mdc); + else + gpiod_direction_output(fep->gd_mdc, 0); +} + +static void fec_main_bb_ctrl_set_mdio_dir(struct mdiobb_ctrl *ctrl, int output) +{ + struct fec_enet_private *fep = container_of(ctrl, + struct fec_enet_private, fec_main_bb_ctrl); + if (output) + gpiod_direction_output(fep->gd_mdio, 0); + else + gpiod_direction_input(fep->gd_mdio); +} + +static void fec_main_bb_ctrl_set_mdio_data(struct mdiobb_ctrl *ctrl, int value) +{ + struct fec_enet_private *fep = container_of(ctrl, + struct fec_enet_private, fec_main_bb_ctrl); + gpiod_direction_output(fep->gd_mdio, value); +} + +static int fec_main_bb_ctrl_get_mdio_data(struct mdiobb_ctrl *ctrl) +{ + struct fec_enet_private *fep = container_of(ctrl, + struct fec_enet_private, fec_main_bb_ctrl); + return gpiod_get_value(fep->gd_mdio); +} + +static const struct mdiobb_ops fec_main_bb_ops = { + .owner = THIS_MODULE, + .set_mdc = fec_main_bb_ctrl_set_mdc, + .set_mdio_dir = fec_main_bb_ctrl_set_mdio_dir, + .set_mdio_data = fec_main_bb_ctrl_set_mdio_data, + .get_mdio_data = fec_main_bb_ctrl_get_mdio_data, +}; + static int fec_enet_mii_init(struct platform_device *pdev) { static struct mii_bus *fec0_mii_bus; @@ -2150,18 +2192,27 @@ static int fec_enet_mii_init(struct platform_device *pdev) /* Clear any pending transaction complete indication */ writel(FEC_ENET_MII, fep->hwp + FEC_IEVENT); - fep->mii_bus = mdiobus_alloc(); - if (fep->mii_bus == NULL) { - err = -ENOMEM; - goto err_out; - } + if (fep->gd_mdc && fep->gd_mdio) { + fep->fec_main_bb_ctrl.ops = &fec_main_bb_ops; + fep->mii_bus = alloc_mdio_bitbang(&fep->fec_main_bb_ctrl); + if (!fep->mii_bus) { + err = -ENOMEM; + goto err_out; + } + } else { + fep->mii_bus = mdiobus_alloc(); + if (!fep->mii_bus) { + err = -ENOMEM; + goto err_out; + } + fep->mii_bus->read = fec_enet_mdio_read; + fep->mii_bus->write = fec_enet_mdio_write; - fep->mii_bus->name = "fec_enet_mii_bus"; - fep->mii_bus->read = fec_enet_mdio_read; - fep->mii_bus->write = fec_enet_mdio_write; + fep->mii_bus->priv = fep; + } snprintf(fep->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x", pdev->name, fep->dev_id + 1); - fep->mii_bus->priv = fep; + fep->mii_bus->name = "fec_enet_mii_bus"; fep->mii_bus->parent = &pdev->dev; err = of_mdiobus_register(fep->mii_bus, node); @@ -2178,7 +2229,10 @@ static int fec_enet_mii_init(struct platform_device *pdev) return 0; err_out_free_mdiobus: - mdiobus_free(fep->mii_bus); + if (fep->gd_mdc && fep->gd_mdio) + free_mdio_bitbang(fep->mii_bus); + else + mdiobus_free(fep->mii_bus); err_out: return err; } @@ -2187,7 +2241,10 @@ static void fec_enet_mii_remove(struct fec_enet_private *fep) { if (--mii_cnt == 0) { mdiobus_unregister(fep->mii_bus); - mdiobus_free(fep->mii_bus); + if (fep->gd_mdc && fep->gd_mdio) + free_mdio_bitbang(fep->mii_bus); + else + mdiobus_free(fep->mii_bus); } } @@ -3525,6 +3582,8 @@ fec_probe(struct platform_device *pdev) char irq_name[8]; int irq_cnt; struct fec_devinfo *dev_info; + struct gpio_desc *gd; + fec_enet_get_queue_num(pdev, &num_tx_qs, &num_rx_qs); @@ -3575,6 +3634,26 @@ fec_probe(struct platform_device *pdev) !of_property_read_bool(np, "fsl,err006687-workaround-present")) fep->quirks |= FEC_QUIRK_ERR006687; + gd = devm_gpiod_get_optional(&pdev->dev, "mdc", GPIOD_IN); + if (IS_ERR(gd)) { + if (PTR_ERR(gd) != -EPROBE_DEFER) + dev_err(&pdev->dev, "Failed to get mdc gpio: %ld\n", + PTR_ERR(gd)); + ret = PTR_ERR(gd); + goto failed_ioremap; + } + fep->gd_mdc = gd; + + gd = devm_gpiod_get_optional(&pdev->dev, "mdio", GPIOD_IN); + if (IS_ERR(gd)) { + if (PTR_ERR(gd) != -EPROBE_DEFER) + dev_err(&pdev->dev, "Failed to get mdio gpio: %ld\n", + PTR_ERR(gd)); + ret = PTR_ERR(gd); + goto failed_ioremap; + } + fep->gd_mdio = gd; + if (of_get_property(np, "fsl,magic-packet", NULL)) fep->wol_flag |= FEC_WOL_HAS_MAGIC_PACKET; -- 2.20.1