diff options
Diffstat (limited to 'drivers/net/phy/xilinx_gmii2rgmii.c')
| -rw-r--r-- | drivers/net/phy/xilinx_gmii2rgmii.c | 85 |
1 files changed, 61 insertions, 24 deletions
diff --git a/drivers/net/phy/xilinx_gmii2rgmii.c b/drivers/net/phy/xilinx_gmii2rgmii.c index d15dd3938ba8..2024d8ef36d9 100644 --- a/drivers/net/phy/xilinx_gmii2rgmii.c +++ b/drivers/net/phy/xilinx_gmii2rgmii.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* Xilinx GMII2RGMII Converter driver * * Copyright (C) 2016 Xilinx, Inc. @@ -8,22 +9,13 @@ * * Description: * This driver is developed for Xilinx GMII2RGMII Converter - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include <linux/module.h> #include <linux/kernel.h> #include <linux/mii.h> #include <linux/mdio.h> #include <linux/phy.h> +#include <linux/clk.h> #include <linux/of_mdio.h> #define XILINX_GMII2RGMII_REG 0x10 @@ -31,29 +23,61 @@ struct gmii2rgmii { struct phy_device *phy_dev; - struct phy_driver *phy_drv; + const struct phy_driver *phy_drv; struct phy_driver conv_phy_drv; - int addr; + struct mdio_device *mdio; }; -static int xgmiitorgmii_read_status(struct phy_device *phydev) +static void xgmiitorgmii_configure(struct gmii2rgmii *priv, int speed) { - struct gmii2rgmii *priv = phydev->priv; - u16 val = 0; + struct mii_bus *bus = priv->mdio->bus; + int addr = priv->mdio->addr; + u16 val; - priv->phy_drv->read_status(phydev); + val = mdiobus_read(bus, addr, XILINX_GMII2RGMII_REG); + val &= ~XILINX_GMII2RGMII_SPEED_MASK; - val = mdiobus_read(phydev->mdio.bus, priv->addr, XILINX_GMII2RGMII_REG); - val &= XILINX_GMII2RGMII_SPEED_MASK; - - if (phydev->speed == SPEED_1000) + if (speed == SPEED_1000) val |= BMCR_SPEED1000; - else if (phydev->speed == SPEED_100) + else if (speed == SPEED_100) val |= BMCR_SPEED100; else val |= BMCR_SPEED10; - mdiobus_write(phydev->mdio.bus, priv->addr, XILINX_GMII2RGMII_REG, val); + mdiobus_write(bus, addr, XILINX_GMII2RGMII_REG, val); +} + +static int xgmiitorgmii_read_status(struct phy_device *phydev) +{ + struct gmii2rgmii *priv = mdiodev_get_drvdata(&phydev->mdio); + int err; + + if (priv->phy_drv->read_status) + err = priv->phy_drv->read_status(phydev); + else + err = genphy_read_status(phydev); + if (err < 0) + return err; + + xgmiitorgmii_configure(priv, phydev->speed); + + return 0; +} + +static int xgmiitorgmii_set_loopback(struct phy_device *phydev, bool enable, + int speed) +{ + struct gmii2rgmii *priv = mdiodev_get_drvdata(&phydev->mdio); + int err; + + if (priv->phy_drv->set_loopback) + err = priv->phy_drv->set_loopback(phydev, enable, speed); + else + err = genphy_loopback(phydev, enable, speed); + if (err < 0) + return err; + + xgmiitorgmii_configure(priv, phydev->speed); return 0; } @@ -63,11 +87,17 @@ static int xgmiitorgmii_probe(struct mdio_device *mdiodev) struct device *dev = &mdiodev->dev; struct device_node *np = dev->of_node, *phy_node; struct gmii2rgmii *priv; + struct clk *clkin; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; + clkin = devm_clk_get_optional_enabled(dev, NULL); + if (IS_ERR(clkin)) + return dev_err_probe(dev, PTR_ERR(clkin), + "Failed to get and enable clock from Device Tree\n"); + phy_node = of_parse_phandle(np, "phy-handle", 0); if (!phy_node) { dev_err(dev, "Couldn't parse phy-handle\n"); @@ -81,12 +111,19 @@ static int xgmiitorgmii_probe(struct mdio_device *mdiodev) return -EPROBE_DEFER; } - priv->addr = mdiodev->addr; + if (!priv->phy_dev->drv) { + dev_info(dev, "Attached phy not ready\n"); + put_device(&priv->phy_dev->mdio.dev); + return -EPROBE_DEFER; + } + + priv->mdio = mdiodev; priv->phy_drv = priv->phy_dev->drv; memcpy(&priv->conv_phy_drv, priv->phy_dev->drv, sizeof(struct phy_driver)); priv->conv_phy_drv.read_status = xgmiitorgmii_read_status; - priv->phy_dev->priv = priv; + priv->conv_phy_drv.set_loopback = xgmiitorgmii_set_loopback; + mdiodev_set_drvdata(&priv->phy_dev->mdio, priv); priv->phy_dev->drv = &priv->conv_phy_drv; return 0; |
