143 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/* Copyright (c) Tehuti Networks Ltd. */3 4#include <linux/netdevice.h>5#include <linux/pci.h>6#include <linux/phylink.h>7 8#include "tn40.h"9 10#define TN40_MDIO_DEVAD_MASK GENMASK(4, 0)11#define TN40_MDIO_PRTAD_MASK GENMASK(9, 5)12#define TN40_MDIO_CMD_VAL(device, port) \13 (FIELD_PREP(TN40_MDIO_DEVAD_MASK, (device)) | \14 (FIELD_PREP(TN40_MDIO_PRTAD_MASK, (port))))15#define TN40_MDIO_CMD_READ BIT(15)16 17static void tn40_mdio_set_speed(struct tn40_priv *priv, u32 speed)18{19 void __iomem *regs = priv->regs;20 int mdio_cfg;21 22 if (speed == TN40_MDIO_SPEED_1MHZ)23 mdio_cfg = (0x7d << 7) | 0x08; /* 1MHz */24 else25 mdio_cfg = 0xA08; /* 6MHz */26 mdio_cfg |= (1 << 6);27 writel(mdio_cfg, regs + TN40_REG_MDIO_CMD_STAT);28 msleep(100);29}30 31static u32 tn40_mdio_stat(struct tn40_priv *priv)32{33 void __iomem *regs = priv->regs;34 35 return readl(regs + TN40_REG_MDIO_CMD_STAT);36}37 38static int tn40_mdio_wait_nobusy(struct tn40_priv *priv, u32 *val)39{40 u32 stat;41 int ret;42 43 ret = readx_poll_timeout_atomic(tn40_mdio_stat, priv, stat,44 TN40_GET_MDIO_BUSY(stat) == 0, 10,45 10000);46 if (val)47 *val = stat;48 return ret;49}50 51static int tn40_mdio_read(struct tn40_priv *priv, int port, int device,52 u16 regnum)53{54 void __iomem *regs = priv->regs;55 u32 i;56 57 /* wait until MDIO is not busy */58 if (tn40_mdio_wait_nobusy(priv, NULL))59 return -EIO;60 61 i = TN40_MDIO_CMD_VAL(device, port);62 writel(i, regs + TN40_REG_MDIO_CMD);63 writel((u32)regnum, regs + TN40_REG_MDIO_ADDR);64 if (tn40_mdio_wait_nobusy(priv, NULL))65 return -EIO;66 67 writel(TN40_MDIO_CMD_READ | i, regs + TN40_REG_MDIO_CMD);68 /* read CMD_STAT until not busy */69 if (tn40_mdio_wait_nobusy(priv, NULL))70 return -EIO;71 72 return lower_16_bits(readl(regs + TN40_REG_MDIO_DATA));73}74 75static int tn40_mdio_write(struct tn40_priv *priv, int port, int device,76 u16 regnum, u16 data)77{78 void __iomem *regs = priv->regs;79 u32 tmp_reg = 0;80 int ret;81 82 /* wait until MDIO is not busy */83 if (tn40_mdio_wait_nobusy(priv, NULL))84 return -EIO;85 writel(TN40_MDIO_CMD_VAL(device, port), regs + TN40_REG_MDIO_CMD);86 writel((u32)regnum, regs + TN40_REG_MDIO_ADDR);87 if (tn40_mdio_wait_nobusy(priv, NULL))88 return -EIO;89 writel((u32)data, regs + TN40_REG_MDIO_DATA);90 /* read CMD_STAT until not busy */91 ret = tn40_mdio_wait_nobusy(priv, &tmp_reg);92 if (ret)93 return -EIO;94 95 if (TN40_GET_MDIO_RD_ERR(tmp_reg)) {96 dev_err(&priv->pdev->dev, "MDIO error after write command\n");97 return -EIO;98 }99 return 0;100}101 102static int tn40_mdio_read_c45(struct mii_bus *mii_bus, int addr, int devnum,103 int regnum)104{105 return tn40_mdio_read(mii_bus->priv, addr, devnum, regnum);106}107 108static int tn40_mdio_write_c45(struct mii_bus *mii_bus, int addr, int devnum,109 int regnum, u16 val)110{111 return tn40_mdio_write(mii_bus->priv, addr, devnum, regnum, val);112}113 114int tn40_mdiobus_init(struct tn40_priv *priv)115{116 struct pci_dev *pdev = priv->pdev;117 struct mii_bus *bus;118 int ret;119 120 bus = devm_mdiobus_alloc(&pdev->dev);121 if (!bus)122 return -ENOMEM;123 124 bus->name = TN40_DRV_NAME;125 bus->parent = &pdev->dev;126 snprintf(bus->id, MII_BUS_ID_SIZE, "tn40xx-%x-%x",127 pci_domain_nr(pdev->bus), pci_dev_id(pdev));128 bus->priv = priv;129 130 bus->read_c45 = tn40_mdio_read_c45;131 bus->write_c45 = tn40_mdio_write_c45;132 133 ret = devm_mdiobus_register(&pdev->dev, bus);134 if (ret) {135 dev_err(&pdev->dev, "failed to register mdiobus %d %u %u\n",136 ret, bus->state, MDIOBUS_UNREGISTERED);137 return ret;138 }139 tn40_mdio_set_speed(priv, TN40_MDIO_SPEED_6MHZ);140 priv->mdio = bus;141 return 0;142}143