Commit ef363122 authored by Marek Vasut's avatar Marek Vasut Committed by David S. Miller

net: ks8851: Register MDIO bus and the internal PHY

The KS8851 has a reduced internal PHY, which is accessible through its
registers at offset 0xe4. The PHY is compatible with KS886x PHY present
in Micrel switches, except the PHY ID Low/High registers are swapped.
Register MDIO bus so this PHY can be detected and probed by phylib.
Reviewed-by: default avatarAndrew Lunn <andrew@lunn.ch>
Signed-off-by: default avatarMarek Vasut <marex@denx.de>
Cc: Andrew Lunn <andrew@lunn.ch>
Cc: Heiner Kallweit <hkallweit1@gmail.com>
Cc: Lukas Wunner <lukas@wunner.de>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent ab36a3a2
...@@ -358,6 +358,7 @@ union ks8851_tx_hdr { ...@@ -358,6 +358,7 @@ union ks8851_tx_hdr {
* @vdd_reg: Optional regulator supplying the chip * @vdd_reg: Optional regulator supplying the chip
* @vdd_io: Optional digital power supply for IO * @vdd_io: Optional digital power supply for IO
* @gpio: Optional reset_n gpio * @gpio: Optional reset_n gpio
* @mii_bus: Pointer to MII bus structure
* @lock: Bus access lock callback * @lock: Bus access lock callback
* @unlock: Bus access unlock callback * @unlock: Bus access unlock callback
* @rdreg16: 16bit register read callback * @rdreg16: 16bit register read callback
...@@ -403,6 +404,7 @@ struct ks8851_net { ...@@ -403,6 +404,7 @@ struct ks8851_net {
struct regulator *vdd_reg; struct regulator *vdd_reg;
struct regulator *vdd_io; struct regulator *vdd_io;
int gpio; int gpio;
struct mii_bus *mii_bus;
void (*lock)(struct ks8851_net *ks, void (*lock)(struct ks8851_net *ks,
unsigned long *flags); unsigned long *flags);
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#include <linux/gpio.h> #include <linux/gpio.h>
#include <linux/of_gpio.h> #include <linux/of_gpio.h>
#include <linux/of_mdio.h>
#include <linux/of_net.h> #include <linux/of_net.h>
#include "ks8851.h" #include "ks8851.h"
...@@ -932,7 +933,25 @@ static int ks8851_phy_reg(int reg) ...@@ -932,7 +933,25 @@ static int ks8851_phy_reg(int reg)
return KS_P1ANLPR; return KS_P1ANLPR;
} }
return 0x0; return -EOPNOTSUPP;
}
static int ks8851_phy_read_common(struct net_device *dev, int phy_addr, int reg)
{
struct ks8851_net *ks = netdev_priv(dev);
unsigned long flags;
int result;
int ksreg;
ksreg = ks8851_phy_reg(reg);
if (ksreg < 0)
return ksreg;
ks8851_lock(ks, &flags);
result = ks8851_rdreg16(ks, ksreg);
ks8851_unlock(ks, &flags);
return result;
} }
/** /**
...@@ -952,20 +971,13 @@ static int ks8851_phy_reg(int reg) ...@@ -952,20 +971,13 @@ static int ks8851_phy_reg(int reg)
*/ */
static int ks8851_phy_read(struct net_device *dev, int phy_addr, int reg) static int ks8851_phy_read(struct net_device *dev, int phy_addr, int reg)
{ {
struct ks8851_net *ks = netdev_priv(dev); int ret;
unsigned long flags;
int ksreg;
int result;
ksreg = ks8851_phy_reg(reg); ret = ks8851_phy_read_common(dev, phy_addr, reg);
if (!ksreg) if (ret < 0)
return 0x0; /* no error return allowed, so use zero */ return 0x0; /* no error return allowed, so use zero */
ks8851_lock(ks, &flags); return ret;
result = ks8851_rdreg16(ks, ksreg);
ks8851_unlock(ks, &flags);
return result;
} }
static void ks8851_phy_write(struct net_device *dev, static void ks8851_phy_write(struct net_device *dev,
...@@ -976,13 +988,37 @@ static void ks8851_phy_write(struct net_device *dev, ...@@ -976,13 +988,37 @@ static void ks8851_phy_write(struct net_device *dev,
int ksreg; int ksreg;
ksreg = ks8851_phy_reg(reg); ksreg = ks8851_phy_reg(reg);
if (ksreg) { if (ksreg >= 0) {
ks8851_lock(ks, &flags); ks8851_lock(ks, &flags);
ks8851_wrreg16(ks, ksreg, value); ks8851_wrreg16(ks, ksreg, value);
ks8851_unlock(ks, &flags); ks8851_unlock(ks, &flags);
} }
} }
static int ks8851_mdio_read(struct mii_bus *bus, int phy_id, int reg)
{
struct ks8851_net *ks = bus->priv;
if (phy_id != 0)
return -EOPNOTSUPP;
/* KS8851 PHY ID registers are swapped in HW, swap them back. */
if (reg == MII_PHYSID1)
reg = MII_PHYSID2;
else if (reg == MII_PHYSID2)
reg = MII_PHYSID1;
return ks8851_phy_read_common(ks->netdev, phy_id, reg);
}
static int ks8851_mdio_write(struct mii_bus *bus, int phy_id, int reg, u16 val)
{
struct ks8851_net *ks = bus->priv;
ks8851_phy_write(ks->netdev, phy_id, reg, val);
return 0;
}
/** /**
* ks8851_read_selftest - read the selftest memory info. * ks8851_read_selftest - read the selftest memory info.
* @ks: The device state * @ks: The device state
...@@ -1046,6 +1082,42 @@ int ks8851_resume(struct device *dev) ...@@ -1046,6 +1082,42 @@ int ks8851_resume(struct device *dev)
} }
#endif #endif
static int ks8851_register_mdiobus(struct ks8851_net *ks, struct device *dev)
{
struct mii_bus *mii_bus;
int ret;
mii_bus = mdiobus_alloc();
if (!mii_bus)
return -ENOMEM;
mii_bus->name = "ks8851_eth_mii";
mii_bus->read = ks8851_mdio_read;
mii_bus->write = ks8851_mdio_write;
mii_bus->priv = ks;
mii_bus->parent = dev;
mii_bus->phy_mask = ~((u32)BIT(0));
snprintf(mii_bus->id, MII_BUS_ID_SIZE, "%s", dev_name(dev));
ret = mdiobus_register(mii_bus);
if (ret)
goto err_mdiobus_register;
ks->mii_bus = mii_bus;
return 0;
err_mdiobus_register:
mdiobus_free(mii_bus);
return ret;
}
static void ks8851_unregister_mdiobus(struct ks8851_net *ks)
{
mdiobus_unregister(ks->mii_bus);
mdiobus_free(ks->mii_bus);
}
int ks8851_probe_common(struct net_device *netdev, struct device *dev, int ks8851_probe_common(struct net_device *netdev, struct device *dev,
int msg_en) int msg_en)
{ {
...@@ -1104,6 +1176,8 @@ int ks8851_probe_common(struct net_device *netdev, struct device *dev, ...@@ -1104,6 +1176,8 @@ int ks8851_probe_common(struct net_device *netdev, struct device *dev,
INIT_WORK(&ks->rxctrl_work, ks8851_rxctrl_work); INIT_WORK(&ks->rxctrl_work, ks8851_rxctrl_work);
SET_NETDEV_DEV(netdev, dev);
/* setup EEPROM state */ /* setup EEPROM state */
ks->eeprom.data = ks; ks->eeprom.data = ks;
ks->eeprom.width = PCI_EEPROM_WIDTH_93C46; ks->eeprom.width = PCI_EEPROM_WIDTH_93C46;
...@@ -1120,6 +1194,10 @@ int ks8851_probe_common(struct net_device *netdev, struct device *dev, ...@@ -1120,6 +1194,10 @@ int ks8851_probe_common(struct net_device *netdev, struct device *dev,
dev_info(dev, "message enable is %d\n", msg_en); dev_info(dev, "message enable is %d\n", msg_en);
ret = ks8851_register_mdiobus(ks, dev);
if (ret)
goto err_mdio;
/* set the default message enable */ /* set the default message enable */
ks->msg_enable = netif_msg_init(msg_en, NETIF_MSG_DRV | ks->msg_enable = netif_msg_init(msg_en, NETIF_MSG_DRV |
NETIF_MSG_PROBE | NETIF_MSG_PROBE |
...@@ -1128,7 +1206,6 @@ int ks8851_probe_common(struct net_device *netdev, struct device *dev, ...@@ -1128,7 +1206,6 @@ int ks8851_probe_common(struct net_device *netdev, struct device *dev,
skb_queue_head_init(&ks->txq); skb_queue_head_init(&ks->txq);
netdev->ethtool_ops = &ks8851_ethtool_ops; netdev->ethtool_ops = &ks8851_ethtool_ops;
SET_NETDEV_DEV(netdev, dev);
dev_set_drvdata(dev, ks); dev_set_drvdata(dev, ks);
...@@ -1156,7 +1233,7 @@ int ks8851_probe_common(struct net_device *netdev, struct device *dev, ...@@ -1156,7 +1233,7 @@ int ks8851_probe_common(struct net_device *netdev, struct device *dev,
ret = register_netdev(netdev); ret = register_netdev(netdev);
if (ret) { if (ret) {
dev_err(dev, "failed to register network device\n"); dev_err(dev, "failed to register network device\n");
goto err_netdev; goto err_id;
} }
netdev_info(netdev, "revision %d, MAC %pM, IRQ %d, %s EEPROM\n", netdev_info(netdev, "revision %d, MAC %pM, IRQ %d, %s EEPROM\n",
...@@ -1165,8 +1242,9 @@ int ks8851_probe_common(struct net_device *netdev, struct device *dev, ...@@ -1165,8 +1242,9 @@ int ks8851_probe_common(struct net_device *netdev, struct device *dev,
return 0; return 0;
err_netdev:
err_id: err_id:
ks8851_unregister_mdiobus(ks);
err_mdio:
if (gpio_is_valid(gpio)) if (gpio_is_valid(gpio))
gpio_set_value(gpio, 0); gpio_set_value(gpio, 0);
regulator_disable(ks->vdd_reg); regulator_disable(ks->vdd_reg);
...@@ -1180,6 +1258,8 @@ int ks8851_remove_common(struct device *dev) ...@@ -1180,6 +1258,8 @@ int ks8851_remove_common(struct device *dev)
{ {
struct ks8851_net *priv = dev_get_drvdata(dev); struct ks8851_net *priv = dev_get_drvdata(dev);
ks8851_unregister_mdiobus(priv);
if (netif_msg_drv(priv)) if (netif_msg_drv(priv))
dev_info(dev, "remove\n"); dev_info(dev, "remove\n");
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment