Commit ef1757ef authored by Jakub Kicinski's avatar Jakub Kicinski

Merge branch 'net-mdio-start-separating-c22-and-c45'

Michael Walle says:

====================
net: mdio: Start separating C22 and C45

This patch set starts the separation of C22 and C45 MDIO bus
transactions at the API level to the MDIO Bus drivers. C45 read and
write ops are added to the MDIO bus driver structure, and the MDIO
core will try to use these ops if requested to perform a C45
transfer. If not available a fallback to the older API is made, to
allow backwards compatibility until all drivers are converted.

A few drivers are then converted to this new API.

The core DSA patch was dropped for now as there is still an ongoing
discussion. It will be picked up in a later series again.

v2: https://lore.kernel.org/r/20221227-v6-2-rc1-c45-seperation-v2-0-ddb37710e5a7@walle.cc
v1: https://lore.kernel.org/r/20220508153049.427227-1-andrew@lunn.ch
====================

Link: https://lore.kernel.org/r/20221227-v6-2-rc1-c45-seperation-v3-0-ade1deb438da@walle.ccSigned-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents 96b7a9d1 743a19e3
This diff is collapsed.
...@@ -454,6 +454,13 @@ struct mv88e6xxx_ops { ...@@ -454,6 +454,13 @@ struct mv88e6xxx_ops {
struct mii_bus *bus, struct mii_bus *bus,
int addr, int reg, u16 val); int addr, int reg, u16 val);
int (*phy_read_c45)(struct mv88e6xxx_chip *chip,
struct mii_bus *bus,
int addr, int devad, int reg, u16 *val);
int (*phy_write_c45)(struct mv88e6xxx_chip *chip,
struct mii_bus *bus,
int addr, int devad, int reg, u16 val);
/* Priority Override Table operations */ /* Priority Override Table operations */
int (*pot_clear)(struct mv88e6xxx_chip *chip); int (*pot_clear)(struct mv88e6xxx_chip *chip);
......
...@@ -739,20 +739,18 @@ static int mv88e6xxx_g2_smi_phy_read_data_c45(struct mv88e6xxx_chip *chip, ...@@ -739,20 +739,18 @@ static int mv88e6xxx_g2_smi_phy_read_data_c45(struct mv88e6xxx_chip *chip,
return mv88e6xxx_g2_read(chip, MV88E6XXX_G2_SMI_PHY_DATA, data); return mv88e6xxx_g2_read(chip, MV88E6XXX_G2_SMI_PHY_DATA, data);
} }
static int mv88e6xxx_g2_smi_phy_read_c45(struct mv88e6xxx_chip *chip, static int _mv88e6xxx_g2_smi_phy_read_c45(struct mv88e6xxx_chip *chip,
bool external, int port, int reg, bool external, int port, int devad,
u16 *data) int reg, u16 *data)
{ {
int dev = (reg >> 16) & 0x1f;
int addr = reg & 0xffff;
int err; int err;
err = mv88e6xxx_g2_smi_phy_write_addr_c45(chip, external, port, dev, err = mv88e6xxx_g2_smi_phy_write_addr_c45(chip, external, port, devad,
addr); reg);
if (err) if (err)
return err; return err;
return mv88e6xxx_g2_smi_phy_read_data_c45(chip, external, port, dev, return mv88e6xxx_g2_smi_phy_read_data_c45(chip, external, port, devad,
data); data);
} }
...@@ -771,51 +769,65 @@ static int mv88e6xxx_g2_smi_phy_write_data_c45(struct mv88e6xxx_chip *chip, ...@@ -771,51 +769,65 @@ static int mv88e6xxx_g2_smi_phy_write_data_c45(struct mv88e6xxx_chip *chip,
return mv88e6xxx_g2_smi_phy_access_c45(chip, external, op, port, dev); return mv88e6xxx_g2_smi_phy_access_c45(chip, external, op, port, dev);
} }
static int mv88e6xxx_g2_smi_phy_write_c45(struct mv88e6xxx_chip *chip, static int _mv88e6xxx_g2_smi_phy_write_c45(struct mv88e6xxx_chip *chip,
bool external, int port, int reg, bool external, int port, int devad,
u16 data) int reg, u16 data)
{ {
int dev = (reg >> 16) & 0x1f;
int addr = reg & 0xffff;
int err; int err;
err = mv88e6xxx_g2_smi_phy_write_addr_c45(chip, external, port, dev, err = mv88e6xxx_g2_smi_phy_write_addr_c45(chip, external, port, devad,
addr); reg);
if (err) if (err)
return err; return err;
return mv88e6xxx_g2_smi_phy_write_data_c45(chip, external, port, dev, return mv88e6xxx_g2_smi_phy_write_data_c45(chip, external, port, devad,
data); data);
} }
int mv88e6xxx_g2_smi_phy_read(struct mv88e6xxx_chip *chip, struct mii_bus *bus, int mv88e6xxx_g2_smi_phy_read_c22(struct mv88e6xxx_chip *chip,
struct mii_bus *bus,
int addr, int reg, u16 *val) int addr, int reg, u16 *val)
{ {
struct mv88e6xxx_mdio_bus *mdio_bus = bus->priv; struct mv88e6xxx_mdio_bus *mdio_bus = bus->priv;
bool external = mdio_bus->external; bool external = mdio_bus->external;
if (reg & MII_ADDR_C45)
return mv88e6xxx_g2_smi_phy_read_c45(chip, external, addr, reg,
val);
return mv88e6xxx_g2_smi_phy_read_data_c22(chip, external, addr, reg, return mv88e6xxx_g2_smi_phy_read_data_c22(chip, external, addr, reg,
val); val);
} }
int mv88e6xxx_g2_smi_phy_write(struct mv88e6xxx_chip *chip, struct mii_bus *bus, int mv88e6xxx_g2_smi_phy_read_c45(struct mv88e6xxx_chip *chip,
int addr, int reg, u16 val) struct mii_bus *bus, int addr, int devad,
int reg, u16 *val)
{ {
struct mv88e6xxx_mdio_bus *mdio_bus = bus->priv; struct mv88e6xxx_mdio_bus *mdio_bus = bus->priv;
bool external = mdio_bus->external; bool external = mdio_bus->external;
if (reg & MII_ADDR_C45) return _mv88e6xxx_g2_smi_phy_read_c45(chip, external, addr, devad, reg,
return mv88e6xxx_g2_smi_phy_write_c45(chip, external, addr, reg,
val); val);
}
int mv88e6xxx_g2_smi_phy_write_c22(struct mv88e6xxx_chip *chip,
struct mii_bus *bus, int addr, int reg,
u16 val)
{
struct mv88e6xxx_mdio_bus *mdio_bus = bus->priv;
bool external = mdio_bus->external;
return mv88e6xxx_g2_smi_phy_write_data_c22(chip, external, addr, reg, return mv88e6xxx_g2_smi_phy_write_data_c22(chip, external, addr, reg,
val); val);
} }
int mv88e6xxx_g2_smi_phy_write_c45(struct mv88e6xxx_chip *chip,
struct mii_bus *bus, int addr, int devad,
int reg, u16 val)
{
struct mv88e6xxx_mdio_bus *mdio_bus = bus->priv;
bool external = mdio_bus->external;
return _mv88e6xxx_g2_smi_phy_write_c45(chip, external, addr, devad, reg,
val);
}
/* Offset 0x1B: Watchdog Control */ /* Offset 0x1B: Watchdog Control */
static int mv88e6097_watchdog_action(struct mv88e6xxx_chip *chip, int irq) static int mv88e6097_watchdog_action(struct mv88e6xxx_chip *chip, int irq)
{ {
......
...@@ -314,12 +314,18 @@ int mv88e6xxx_g2_wait_bit(struct mv88e6xxx_chip *chip, int reg, ...@@ -314,12 +314,18 @@ int mv88e6xxx_g2_wait_bit(struct mv88e6xxx_chip *chip, int reg,
int mv88e6352_g2_irl_init_all(struct mv88e6xxx_chip *chip, int port); int mv88e6352_g2_irl_init_all(struct mv88e6xxx_chip *chip, int port);
int mv88e6390_g2_irl_init_all(struct mv88e6xxx_chip *chip, int port); int mv88e6390_g2_irl_init_all(struct mv88e6xxx_chip *chip, int port);
int mv88e6xxx_g2_smi_phy_read(struct mv88e6xxx_chip *chip, int mv88e6xxx_g2_smi_phy_read_c22(struct mv88e6xxx_chip *chip,
struct mii_bus *bus, struct mii_bus *bus,
int addr, int reg, u16 *val); int addr, int reg, u16 *val);
int mv88e6xxx_g2_smi_phy_write(struct mv88e6xxx_chip *chip, int mv88e6xxx_g2_smi_phy_write_c22(struct mv88e6xxx_chip *chip,
struct mii_bus *bus, struct mii_bus *bus,
int addr, int reg, u16 val); int addr, int reg, u16 val);
int mv88e6xxx_g2_smi_phy_read_c45(struct mv88e6xxx_chip *chip,
struct mii_bus *bus,
int addr, int devad, int reg, u16 *val);
int mv88e6xxx_g2_smi_phy_write_c45(struct mv88e6xxx_chip *chip,
struct mii_bus *bus,
int addr, int devad, int reg, u16 val);
int mv88e6xxx_g2_set_switch_mac(struct mv88e6xxx_chip *chip, u8 *addr); int mv88e6xxx_g2_set_switch_mac(struct mv88e6xxx_chip *chip, u8 *addr);
int mv88e6xxx_g2_get_eeprom8(struct mv88e6xxx_chip *chip, int mv88e6xxx_g2_get_eeprom8(struct mv88e6xxx_chip *chip,
......
...@@ -55,6 +55,38 @@ int mv88e6xxx_phy_write(struct mv88e6xxx_chip *chip, int phy, int reg, u16 val) ...@@ -55,6 +55,38 @@ int mv88e6xxx_phy_write(struct mv88e6xxx_chip *chip, int phy, int reg, u16 val)
return chip->info->ops->phy_write(chip, bus, addr, reg, val); return chip->info->ops->phy_write(chip, bus, addr, reg, val);
} }
int mv88e6xxx_phy_read_c45(struct mv88e6xxx_chip *chip, int phy, int devad,
int reg, u16 *val)
{
int addr = phy; /* PHY devices addresses start at 0x0 */
struct mii_bus *bus;
bus = mv88e6xxx_default_mdio_bus(chip);
if (!bus)
return -EOPNOTSUPP;
if (!chip->info->ops->phy_read_c45)
return -EOPNOTSUPP;
return chip->info->ops->phy_read_c45(chip, bus, addr, devad, reg, val);
}
int mv88e6xxx_phy_write_c45(struct mv88e6xxx_chip *chip, int phy, int devad,
int reg, u16 val)
{
int addr = phy; /* PHY devices addresses start at 0x0 */
struct mii_bus *bus;
bus = mv88e6xxx_default_mdio_bus(chip);
if (!bus)
return -EOPNOTSUPP;
if (!chip->info->ops->phy_write_c45)
return -EOPNOTSUPP;
return chip->info->ops->phy_write_c45(chip, bus, addr, devad, reg, val);
}
static int mv88e6xxx_phy_page_get(struct mv88e6xxx_chip *chip, int phy, u8 page) static int mv88e6xxx_phy_page_get(struct mv88e6xxx_chip *chip, int phy, u8 page)
{ {
return mv88e6xxx_phy_write(chip, phy, MV88E6XXX_PHY_PAGE, page); return mv88e6xxx_phy_write(chip, phy, MV88E6XXX_PHY_PAGE, page);
......
...@@ -28,6 +28,10 @@ int mv88e6xxx_phy_read(struct mv88e6xxx_chip *chip, int phy, ...@@ -28,6 +28,10 @@ int mv88e6xxx_phy_read(struct mv88e6xxx_chip *chip, int phy,
int reg, u16 *val); int reg, u16 *val);
int mv88e6xxx_phy_write(struct mv88e6xxx_chip *chip, int phy, int mv88e6xxx_phy_write(struct mv88e6xxx_chip *chip, int phy,
int reg, u16 val); int reg, u16 val);
int mv88e6xxx_phy_read_c45(struct mv88e6xxx_chip *chip, int phy, int devad,
int reg, u16 *val);
int mv88e6xxx_phy_write_c45(struct mv88e6xxx_chip *chip, int phy, int devad,
int reg, u16 val);
int mv88e6xxx_phy_page_read(struct mv88e6xxx_chip *chip, int phy, int mv88e6xxx_phy_page_read(struct mv88e6xxx_chip *chip, int phy,
u8 page, int reg, u16 *val); u8 page, int reg, u16 *val);
int mv88e6xxx_phy_page_write(struct mv88e6xxx_chip *chip, int phy, int mv88e6xxx_phy_page_write(struct mv88e6xxx_chip *chip, int phy,
......
...@@ -36,17 +36,13 @@ static int mv88e6352_serdes_write(struct mv88e6xxx_chip *chip, int reg, ...@@ -36,17 +36,13 @@ static int mv88e6352_serdes_write(struct mv88e6xxx_chip *chip, int reg,
static int mv88e6390_serdes_read(struct mv88e6xxx_chip *chip, static int mv88e6390_serdes_read(struct mv88e6xxx_chip *chip,
int lane, int device, int reg, u16 *val) int lane, int device, int reg, u16 *val)
{ {
int reg_c45 = MII_ADDR_C45 | device << 16 | reg; return mv88e6xxx_phy_read_c45(chip, lane, device, reg, val);
return mv88e6xxx_phy_read(chip, lane, reg_c45, val);
} }
static int mv88e6390_serdes_write(struct mv88e6xxx_chip *chip, static int mv88e6390_serdes_write(struct mv88e6xxx_chip *chip,
int lane, int device, int reg, u16 val) int lane, int device, int reg, u16 val)
{ {
int reg_c45 = MII_ADDR_C45 | device << 16 | reg; return mv88e6xxx_phy_write_c45(chip, lane, device, reg, val);
return mv88e6xxx_phy_write(chip, lane, reg_c45, val);
} }
static int mv88e6xxx_serdes_pcs_get_state(struct mv88e6xxx_chip *chip, static int mv88e6xxx_serdes_pcs_get_state(struct mv88e6xxx_chip *chip,
......
...@@ -1987,24 +1987,58 @@ static int fec_enet_mdio_wait(struct fec_enet_private *fep) ...@@ -1987,24 +1987,58 @@ static int fec_enet_mdio_wait(struct fec_enet_private *fep)
return ret; return ret;
} }
static int fec_enet_mdio_read(struct mii_bus *bus, int mii_id, int regnum) static int fec_enet_mdio_read_c22(struct mii_bus *bus, int mii_id, int regnum)
{ {
struct fec_enet_private *fep = bus->priv; struct fec_enet_private *fep = bus->priv;
struct device *dev = &fep->pdev->dev; struct device *dev = &fep->pdev->dev;
int ret = 0, frame_start, frame_addr, frame_op; int ret = 0, frame_start, frame_addr, frame_op;
bool is_c45 = !!(regnum & MII_ADDR_C45);
ret = pm_runtime_resume_and_get(dev); ret = pm_runtime_resume_and_get(dev);
if (ret < 0) if (ret < 0)
return ret; return ret;
if (is_c45) { /* C22 read */
frame_op = FEC_MMFR_OP_READ;
frame_start = FEC_MMFR_ST;
frame_addr = regnum;
/* start a read op */
writel(frame_start | frame_op |
FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(frame_addr) |
FEC_MMFR_TA, fep->hwp + FEC_MII_DATA);
/* wait for end of transfer */
ret = fec_enet_mdio_wait(fep);
if (ret) {
netdev_err(fep->netdev, "MDIO read timeout\n");
goto out;
}
ret = FEC_MMFR_DATA(readl(fep->hwp + FEC_MII_DATA));
out:
pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev);
return ret;
}
static int fec_enet_mdio_read_c45(struct mii_bus *bus, int mii_id,
int devad, int regnum)
{
struct fec_enet_private *fep = bus->priv;
struct device *dev = &fep->pdev->dev;
int ret = 0, frame_start, frame_op;
ret = pm_runtime_resume_and_get(dev);
if (ret < 0)
return ret;
frame_start = FEC_MMFR_ST_C45; frame_start = FEC_MMFR_ST_C45;
/* write address */ /* write address */
frame_addr = (regnum >> 16);
writel(frame_start | FEC_MMFR_OP_ADDR_WRITE | writel(frame_start | FEC_MMFR_OP_ADDR_WRITE |
FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(frame_addr) | FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(devad) |
FEC_MMFR_TA | (regnum & 0xFFFF), FEC_MMFR_TA | (regnum & 0xFFFF),
fep->hwp + FEC_MII_DATA); fep->hwp + FEC_MII_DATA);
...@@ -2017,16 +2051,9 @@ static int fec_enet_mdio_read(struct mii_bus *bus, int mii_id, int regnum) ...@@ -2017,16 +2051,9 @@ static int fec_enet_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
frame_op = FEC_MMFR_OP_READ_C45; frame_op = FEC_MMFR_OP_READ_C45;
} else {
/* C22 read */
frame_op = FEC_MMFR_OP_READ;
frame_start = FEC_MMFR_ST;
frame_addr = regnum;
}
/* start a read op */ /* start a read op */
writel(frame_start | frame_op | writel(frame_start | frame_op |
FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(frame_addr) | FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(devad) |
FEC_MMFR_TA, fep->hwp + FEC_MII_DATA); FEC_MMFR_TA, fep->hwp + FEC_MII_DATA);
/* wait for end of transfer */ /* wait for end of transfer */
...@@ -2045,25 +2072,54 @@ static int fec_enet_mdio_read(struct mii_bus *bus, int mii_id, int regnum) ...@@ -2045,25 +2072,54 @@ static int fec_enet_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
return ret; return ret;
} }
static int fec_enet_mdio_write(struct mii_bus *bus, int mii_id, int regnum, static int fec_enet_mdio_write_c22(struct mii_bus *bus, int mii_id, int regnum,
u16 value) u16 value)
{ {
struct fec_enet_private *fep = bus->priv; struct fec_enet_private *fep = bus->priv;
struct device *dev = &fep->pdev->dev; struct device *dev = &fep->pdev->dev;
int ret, frame_start, frame_addr; int ret, frame_start, frame_addr;
bool is_c45 = !!(regnum & MII_ADDR_C45);
ret = pm_runtime_resume_and_get(dev); ret = pm_runtime_resume_and_get(dev);
if (ret < 0) if (ret < 0)
return ret; return ret;
if (is_c45) { /* C22 write */
frame_start = FEC_MMFR_ST;
frame_addr = regnum;
/* start a write op */
writel(frame_start | FEC_MMFR_OP_WRITE |
FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(frame_addr) |
FEC_MMFR_TA | FEC_MMFR_DATA(value),
fep->hwp + FEC_MII_DATA);
/* wait for end of transfer */
ret = fec_enet_mdio_wait(fep);
if (ret)
netdev_err(fep->netdev, "MDIO write timeout\n");
pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev);
return ret;
}
static int fec_enet_mdio_write_c45(struct mii_bus *bus, int mii_id,
int devad, int regnum, u16 value)
{
struct fec_enet_private *fep = bus->priv;
struct device *dev = &fep->pdev->dev;
int ret, frame_start;
ret = pm_runtime_resume_and_get(dev);
if (ret < 0)
return ret;
frame_start = FEC_MMFR_ST_C45; frame_start = FEC_MMFR_ST_C45;
/* write address */ /* write address */
frame_addr = (regnum >> 16);
writel(frame_start | FEC_MMFR_OP_ADDR_WRITE | writel(frame_start | FEC_MMFR_OP_ADDR_WRITE |
FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(frame_addr) | FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(devad) |
FEC_MMFR_TA | (regnum & 0xFFFF), FEC_MMFR_TA | (regnum & 0xFFFF),
fep->hwp + FEC_MII_DATA); fep->hwp + FEC_MII_DATA);
...@@ -2073,15 +2129,10 @@ static int fec_enet_mdio_write(struct mii_bus *bus, int mii_id, int regnum, ...@@ -2073,15 +2129,10 @@ static int fec_enet_mdio_write(struct mii_bus *bus, int mii_id, int regnum,
netdev_err(fep->netdev, "MDIO address write timeout\n"); netdev_err(fep->netdev, "MDIO address write timeout\n");
goto out; goto out;
} }
} else {
/* C22 write */
frame_start = FEC_MMFR_ST;
frame_addr = regnum;
}
/* start a write op */ /* start a write op */
writel(frame_start | FEC_MMFR_OP_WRITE | writel(frame_start | FEC_MMFR_OP_WRITE |
FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(frame_addr) | FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(devad) |
FEC_MMFR_TA | FEC_MMFR_DATA(value), FEC_MMFR_TA | FEC_MMFR_DATA(value),
fep->hwp + FEC_MII_DATA); fep->hwp + FEC_MII_DATA);
...@@ -2381,8 +2432,10 @@ static int fec_enet_mii_init(struct platform_device *pdev) ...@@ -2381,8 +2432,10 @@ static int fec_enet_mii_init(struct platform_device *pdev)
} }
fep->mii_bus->name = "fec_enet_mii_bus"; fep->mii_bus->name = "fec_enet_mii_bus";
fep->mii_bus->read = fec_enet_mdio_read; fep->mii_bus->read = fec_enet_mdio_read_c22;
fep->mii_bus->write = fec_enet_mdio_write; fep->mii_bus->write = fec_enet_mdio_write_c22;
fep->mii_bus->read_c45 = fec_enet_mdio_read_c45;
fep->mii_bus->write_c45 = fec_enet_mdio_write_c45;
snprintf(fep->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x", snprintf(fep->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x",
pdev->name, fep->dev_id + 1); pdev->name, fep->dev_id + 1);
fep->mii_bus->priv = fep; fep->mii_bus->priv = fep;
......
...@@ -128,30 +128,49 @@ static int xgmac_wait_until_done(struct device *dev, ...@@ -128,30 +128,49 @@ static int xgmac_wait_until_done(struct device *dev,
return 0; return 0;
} }
/* static int xgmac_mdio_write_c22(struct mii_bus *bus, int phy_id, int regnum,
* Write value to the PHY for this device to the register at regnum,waiting u16 value)
* until the write is done before it returns. All PHY configuration has to be
* done through the TSEC1 MIIM regs.
*/
static int xgmac_mdio_write(struct mii_bus *bus, int phy_id, int regnum, u16 value)
{ {
struct mdio_fsl_priv *priv = (struct mdio_fsl_priv *)bus->priv; struct mdio_fsl_priv *priv = (struct mdio_fsl_priv *)bus->priv;
struct tgec_mdio_controller __iomem *regs = priv->mdio_base; struct tgec_mdio_controller __iomem *regs = priv->mdio_base;
uint16_t dev_addr; bool endian = priv->is_little_endian;
u16 dev_addr = regnum & 0x1f;
u32 mdio_ctl, mdio_stat; u32 mdio_ctl, mdio_stat;
int ret; int ret;
mdio_stat = xgmac_read32(&regs->mdio_stat, endian);
mdio_stat &= ~MDIO_STAT_ENC;
xgmac_write32(mdio_stat, &regs->mdio_stat, endian);
ret = xgmac_wait_until_free(&bus->dev, regs, endian);
if (ret)
return ret;
/* Set the port and dev addr */
mdio_ctl = MDIO_CTL_PORT_ADDR(phy_id) | MDIO_CTL_DEV_ADDR(dev_addr);
xgmac_write32(mdio_ctl, &regs->mdio_ctl, endian);
/* Write the value to the register */
xgmac_write32(MDIO_DATA(value), &regs->mdio_data, endian);
ret = xgmac_wait_until_done(&bus->dev, regs, endian);
if (ret)
return ret;
return 0;
}
static int xgmac_mdio_write_c45(struct mii_bus *bus, int phy_id, int dev_addr,
int regnum, u16 value)
{
struct mdio_fsl_priv *priv = (struct mdio_fsl_priv *)bus->priv;
struct tgec_mdio_controller __iomem *regs = priv->mdio_base;
bool endian = priv->is_little_endian; bool endian = priv->is_little_endian;
u32 mdio_ctl, mdio_stat;
int ret;
mdio_stat = xgmac_read32(&regs->mdio_stat, endian); mdio_stat = xgmac_read32(&regs->mdio_stat, endian);
if (regnum & MII_ADDR_C45) {
/* Clause 45 (ie 10G) */
dev_addr = (regnum >> 16) & 0x1f;
mdio_stat |= MDIO_STAT_ENC; mdio_stat |= MDIO_STAT_ENC;
} else {
/* Clause 22 (ie 1G) */
dev_addr = regnum & 0x1f;
mdio_stat &= ~MDIO_STAT_ENC;
}
xgmac_write32(mdio_stat, &regs->mdio_stat, endian); xgmac_write32(mdio_stat, &regs->mdio_stat, endian);
...@@ -164,13 +183,11 @@ static int xgmac_mdio_write(struct mii_bus *bus, int phy_id, int regnum, u16 val ...@@ -164,13 +183,11 @@ static int xgmac_mdio_write(struct mii_bus *bus, int phy_id, int regnum, u16 val
xgmac_write32(mdio_ctl, &regs->mdio_ctl, endian); xgmac_write32(mdio_ctl, &regs->mdio_ctl, endian);
/* Set the register address */ /* Set the register address */
if (regnum & MII_ADDR_C45) {
xgmac_write32(regnum & 0xffff, &regs->mdio_addr, endian); xgmac_write32(regnum & 0xffff, &regs->mdio_addr, endian);
ret = xgmac_wait_until_free(&bus->dev, regs, endian); ret = xgmac_wait_until_free(&bus->dev, regs, endian);
if (ret) if (ret)
return ret; return ret;
}
/* Write the value to the register */ /* Write the value to the register */
xgmac_write32(MDIO_DATA(value), &regs->mdio_data, endian); xgmac_write32(MDIO_DATA(value), &regs->mdio_data, endian);
...@@ -182,31 +199,82 @@ static int xgmac_mdio_write(struct mii_bus *bus, int phy_id, int regnum, u16 val ...@@ -182,31 +199,82 @@ static int xgmac_mdio_write(struct mii_bus *bus, int phy_id, int regnum, u16 val
return 0; return 0;
} }
/* /* Reads from register regnum in the PHY for device dev, returning the value.
* Reads from register regnum in the PHY for device dev, returning the value.
* Clears miimcom first. All PHY configuration has to be done through the * Clears miimcom first. All PHY configuration has to be done through the
* TSEC1 MIIM regs. * TSEC1 MIIM regs.
*/ */
static int xgmac_mdio_read(struct mii_bus *bus, int phy_id, int regnum) static int xgmac_mdio_read_c22(struct mii_bus *bus, int phy_id, int regnum)
{ {
struct mdio_fsl_priv *priv = (struct mdio_fsl_priv *)bus->priv; struct mdio_fsl_priv *priv = (struct mdio_fsl_priv *)bus->priv;
struct tgec_mdio_controller __iomem *regs = priv->mdio_base; struct tgec_mdio_controller __iomem *regs = priv->mdio_base;
bool endian = priv->is_little_endian;
u16 dev_addr = regnum & 0x1f;
unsigned long flags; unsigned long flags;
uint16_t dev_addr;
uint32_t mdio_stat; uint32_t mdio_stat;
uint32_t mdio_ctl; uint32_t mdio_ctl;
int ret; int ret;
bool endian = priv->is_little_endian;
mdio_stat = xgmac_read32(&regs->mdio_stat, endian); mdio_stat = xgmac_read32(&regs->mdio_stat, endian);
if (regnum & MII_ADDR_C45) {
dev_addr = (regnum >> 16) & 0x1f;
mdio_stat |= MDIO_STAT_ENC;
} else {
dev_addr = regnum & 0x1f;
mdio_stat &= ~MDIO_STAT_ENC; mdio_stat &= ~MDIO_STAT_ENC;
xgmac_write32(mdio_stat, &regs->mdio_stat, endian);
ret = xgmac_wait_until_free(&bus->dev, regs, endian);
if (ret)
return ret;
/* Set the Port and Device Addrs */
mdio_ctl = MDIO_CTL_PORT_ADDR(phy_id) | MDIO_CTL_DEV_ADDR(dev_addr);
xgmac_write32(mdio_ctl, &regs->mdio_ctl, endian);
if (priv->has_a009885)
/* Once the operation completes, i.e. MDIO_STAT_BSY clears, we
* must read back the data register within 16 MDC cycles.
*/
local_irq_save(flags);
/* Initiate the read */
xgmac_write32(mdio_ctl | MDIO_CTL_READ, &regs->mdio_ctl, endian);
ret = xgmac_wait_until_done(&bus->dev, regs, endian);
if (ret)
goto irq_restore;
/* Return all Fs if nothing was there */
if ((xgmac_read32(&regs->mdio_stat, endian) & MDIO_STAT_RD_ER) &&
!priv->has_a011043) {
dev_dbg(&bus->dev,
"Error while reading PHY%d reg at %d.%d\n",
phy_id, dev_addr, regnum);
ret = 0xffff;
} else {
ret = xgmac_read32(&regs->mdio_data, endian) & 0xffff;
dev_dbg(&bus->dev, "read %04x\n", ret);
} }
irq_restore:
if (priv->has_a009885)
local_irq_restore(flags);
return ret;
}
/* Reads from register regnum in the PHY for device dev, returning the value.
* Clears miimcom first. All PHY configuration has to be done through the
* TSEC1 MIIM regs.
*/
static int xgmac_mdio_read_c45(struct mii_bus *bus, int phy_id, int dev_addr,
int regnum)
{
struct mdio_fsl_priv *priv = (struct mdio_fsl_priv *)bus->priv;
struct tgec_mdio_controller __iomem *regs = priv->mdio_base;
bool endian = priv->is_little_endian;
u32 mdio_stat, mdio_ctl;
unsigned long flags;
int ret;
mdio_stat = xgmac_read32(&regs->mdio_stat, endian);
mdio_stat |= MDIO_STAT_ENC;
xgmac_write32(mdio_stat, &regs->mdio_stat, endian); xgmac_write32(mdio_stat, &regs->mdio_stat, endian);
ret = xgmac_wait_until_free(&bus->dev, regs, endian); ret = xgmac_wait_until_free(&bus->dev, regs, endian);
...@@ -218,13 +286,11 @@ static int xgmac_mdio_read(struct mii_bus *bus, int phy_id, int regnum) ...@@ -218,13 +286,11 @@ static int xgmac_mdio_read(struct mii_bus *bus, int phy_id, int regnum)
xgmac_write32(mdio_ctl, &regs->mdio_ctl, endian); xgmac_write32(mdio_ctl, &regs->mdio_ctl, endian);
/* Set the register address */ /* Set the register address */
if (regnum & MII_ADDR_C45) {
xgmac_write32(regnum & 0xffff, &regs->mdio_addr, endian); xgmac_write32(regnum & 0xffff, &regs->mdio_addr, endian);
ret = xgmac_wait_until_free(&bus->dev, regs, endian); ret = xgmac_wait_until_free(&bus->dev, regs, endian);
if (ret) if (ret)
return ret; return ret;
}
if (priv->has_a009885) if (priv->has_a009885)
/* Once the operation completes, i.e. MDIO_STAT_BSY clears, we /* Once the operation completes, i.e. MDIO_STAT_BSY clears, we
...@@ -326,8 +392,10 @@ static int xgmac_mdio_probe(struct platform_device *pdev) ...@@ -326,8 +392,10 @@ static int xgmac_mdio_probe(struct platform_device *pdev)
return -ENOMEM; return -ENOMEM;
bus->name = "Freescale XGMAC MDIO Bus"; bus->name = "Freescale XGMAC MDIO Bus";
bus->read = xgmac_mdio_read; bus->read = xgmac_mdio_read_c22;
bus->write = xgmac_mdio_write; bus->write = xgmac_mdio_write_c22;
bus->read_c45 = xgmac_mdio_read_c45;
bus->write_c45 = xgmac_mdio_write_c45;
bus->parent = &pdev->dev; bus->parent = &pdev->dev;
bus->probe_capabilities = MDIOBUS_C22_C45; bus->probe_capabilities = MDIOBUS_C22_C45;
snprintf(bus->id, MII_BUS_ID_SIZE, "%pa", &res->start); snprintf(bus->id, MII_BUS_ID_SIZE, "%pa", &res->start);
......
...@@ -204,21 +204,17 @@ static const struct orion_mdio_ops orion_mdio_xsmi_ops = { ...@@ -204,21 +204,17 @@ static const struct orion_mdio_ops orion_mdio_xsmi_ops = {
.poll_interval_max = MVMDIO_XSMI_POLL_INTERVAL_MAX, .poll_interval_max = MVMDIO_XSMI_POLL_INTERVAL_MAX,
}; };
static int orion_mdio_xsmi_read(struct mii_bus *bus, int mii_id, static int orion_mdio_xsmi_read_c45(struct mii_bus *bus, int mii_id,
int regnum) int dev_addr, int regnum)
{ {
struct orion_mdio_dev *dev = bus->priv; struct orion_mdio_dev *dev = bus->priv;
u16 dev_addr = (regnum >> 16) & GENMASK(4, 0);
int ret; int ret;
if (!(regnum & MII_ADDR_C45))
return -EOPNOTSUPP;
ret = orion_mdio_wait_ready(&orion_mdio_xsmi_ops, bus); ret = orion_mdio_wait_ready(&orion_mdio_xsmi_ops, bus);
if (ret < 0) if (ret < 0)
return ret; return ret;
writel(regnum & GENMASK(15, 0), dev->regs + MVMDIO_XSMI_ADDR_REG); writel(regnum, dev->regs + MVMDIO_XSMI_ADDR_REG);
writel((mii_id << MVMDIO_XSMI_PHYADDR_SHIFT) | writel((mii_id << MVMDIO_XSMI_PHYADDR_SHIFT) |
(dev_addr << MVMDIO_XSMI_DEVADDR_SHIFT) | (dev_addr << MVMDIO_XSMI_DEVADDR_SHIFT) |
MVMDIO_XSMI_READ_OPERATION, MVMDIO_XSMI_READ_OPERATION,
...@@ -237,21 +233,17 @@ static int orion_mdio_xsmi_read(struct mii_bus *bus, int mii_id, ...@@ -237,21 +233,17 @@ static int orion_mdio_xsmi_read(struct mii_bus *bus, int mii_id,
return readl(dev->regs + MVMDIO_XSMI_MGNT_REG) & GENMASK(15, 0); return readl(dev->regs + MVMDIO_XSMI_MGNT_REG) & GENMASK(15, 0);
} }
static int orion_mdio_xsmi_write(struct mii_bus *bus, int mii_id, static int orion_mdio_xsmi_write_c45(struct mii_bus *bus, int mii_id,
int regnum, u16 value) int dev_addr, int regnum, u16 value)
{ {
struct orion_mdio_dev *dev = bus->priv; struct orion_mdio_dev *dev = bus->priv;
u16 dev_addr = (regnum >> 16) & GENMASK(4, 0);
int ret; int ret;
if (!(regnum & MII_ADDR_C45))
return -EOPNOTSUPP;
ret = orion_mdio_wait_ready(&orion_mdio_xsmi_ops, bus); ret = orion_mdio_wait_ready(&orion_mdio_xsmi_ops, bus);
if (ret < 0) if (ret < 0)
return ret; return ret;
writel(regnum & GENMASK(15, 0), dev->regs + MVMDIO_XSMI_ADDR_REG); writel(regnum, dev->regs + MVMDIO_XSMI_ADDR_REG);
writel((mii_id << MVMDIO_XSMI_PHYADDR_SHIFT) | writel((mii_id << MVMDIO_XSMI_PHYADDR_SHIFT) |
(dev_addr << MVMDIO_XSMI_DEVADDR_SHIFT) | (dev_addr << MVMDIO_XSMI_DEVADDR_SHIFT) |
MVMDIO_XSMI_WRITE_OPERATION | value, MVMDIO_XSMI_WRITE_OPERATION | value,
...@@ -302,8 +294,8 @@ static int orion_mdio_probe(struct platform_device *pdev) ...@@ -302,8 +294,8 @@ static int orion_mdio_probe(struct platform_device *pdev)
bus->write = orion_mdio_smi_write; bus->write = orion_mdio_smi_write;
break; break;
case BUS_TYPE_XSMI: case BUS_TYPE_XSMI:
bus->read = orion_mdio_xsmi_read; bus->read_c45 = orion_mdio_xsmi_read_c45;
bus->write = orion_mdio_xsmi_write; bus->write_c45 = orion_mdio_xsmi_write_c45;
break; break;
} }
......
...@@ -3044,23 +3044,46 @@ static int sh_mdio_release(struct sh_eth_private *mdp) ...@@ -3044,23 +3044,46 @@ static int sh_mdio_release(struct sh_eth_private *mdp)
return 0; return 0;
} }
static int sh_mdiobb_read(struct mii_bus *bus, int phy, int reg) static int sh_mdiobb_read_c22(struct mii_bus *bus, int phy, int reg)
{ {
int res; int res;
pm_runtime_get_sync(bus->parent); pm_runtime_get_sync(bus->parent);
res = mdiobb_read(bus, phy, reg); res = mdiobb_read_c22(bus, phy, reg);
pm_runtime_put(bus->parent); pm_runtime_put(bus->parent);
return res; return res;
} }
static int sh_mdiobb_write(struct mii_bus *bus, int phy, int reg, u16 val) static int sh_mdiobb_write_c22(struct mii_bus *bus, int phy, int reg, u16 val)
{ {
int res; int res;
pm_runtime_get_sync(bus->parent); pm_runtime_get_sync(bus->parent);
res = mdiobb_write(bus, phy, reg, val); res = mdiobb_write_c22(bus, phy, reg, val);
pm_runtime_put(bus->parent);
return res;
}
static int sh_mdiobb_read_c45(struct mii_bus *bus, int phy, int devad, int reg)
{
int res;
pm_runtime_get_sync(bus->parent);
res = mdiobb_read_c45(bus, phy, devad, reg);
pm_runtime_put(bus->parent);
return res;
}
static int sh_mdiobb_write_c45(struct mii_bus *bus, int phy, int devad,
int reg, u16 val)
{
int res;
pm_runtime_get_sync(bus->parent);
res = mdiobb_write_c45(bus, phy, devad, reg, val);
pm_runtime_put(bus->parent); pm_runtime_put(bus->parent);
return res; return res;
...@@ -3091,8 +3114,10 @@ static int sh_mdio_init(struct sh_eth_private *mdp, ...@@ -3091,8 +3114,10 @@ static int sh_mdio_init(struct sh_eth_private *mdp,
return -ENOMEM; return -ENOMEM;
/* Wrap accessors with Runtime PM-aware ops */ /* Wrap accessors with Runtime PM-aware ops */
mdp->mii_bus->read = sh_mdiobb_read; mdp->mii_bus->read = sh_mdiobb_read_c22;
mdp->mii_bus->write = sh_mdiobb_write; mdp->mii_bus->write = sh_mdiobb_write_c22;
mdp->mii_bus->read_c45 = sh_mdiobb_read_c45;
mdp->mii_bus->write_c45 = sh_mdiobb_write_c45;
/* Hook up MII support for ethtool */ /* Hook up MII support for ethtool */
mdp->mii_bus->name = "sh_mii"; mdp->mii_bus->name = "sh_mii";
......
...@@ -225,7 +225,7 @@ static int davinci_get_mdio_data(struct mdiobb_ctrl *ctrl) ...@@ -225,7 +225,7 @@ static int davinci_get_mdio_data(struct mdiobb_ctrl *ctrl)
return test_bit(MDIO_PIN, &reg); return test_bit(MDIO_PIN, &reg);
} }
static int davinci_mdiobb_read(struct mii_bus *bus, int phy, int reg) static int davinci_mdiobb_read_c22(struct mii_bus *bus, int phy, int reg)
{ {
int ret; int ret;
...@@ -233,7 +233,7 @@ static int davinci_mdiobb_read(struct mii_bus *bus, int phy, int reg) ...@@ -233,7 +233,7 @@ static int davinci_mdiobb_read(struct mii_bus *bus, int phy, int reg)
if (ret < 0) if (ret < 0)
return ret; return ret;
ret = mdiobb_read(bus, phy, reg); ret = mdiobb_read_c22(bus, phy, reg);
pm_runtime_mark_last_busy(bus->parent); pm_runtime_mark_last_busy(bus->parent);
pm_runtime_put_autosuspend(bus->parent); pm_runtime_put_autosuspend(bus->parent);
...@@ -241,7 +241,7 @@ static int davinci_mdiobb_read(struct mii_bus *bus, int phy, int reg) ...@@ -241,7 +241,7 @@ static int davinci_mdiobb_read(struct mii_bus *bus, int phy, int reg)
return ret; return ret;
} }
static int davinci_mdiobb_write(struct mii_bus *bus, int phy, int reg, static int davinci_mdiobb_write_c22(struct mii_bus *bus, int phy, int reg,
u16 val) u16 val)
{ {
int ret; int ret;
...@@ -250,7 +250,41 @@ static int davinci_mdiobb_write(struct mii_bus *bus, int phy, int reg, ...@@ -250,7 +250,41 @@ static int davinci_mdiobb_write(struct mii_bus *bus, int phy, int reg,
if (ret < 0) if (ret < 0)
return ret; return ret;
ret = mdiobb_write(bus, phy, reg, val); ret = mdiobb_write_c22(bus, phy, reg, val);
pm_runtime_mark_last_busy(bus->parent);
pm_runtime_put_autosuspend(bus->parent);
return ret;
}
static int davinci_mdiobb_read_c45(struct mii_bus *bus, int phy, int devad,
int reg)
{
int ret;
ret = pm_runtime_resume_and_get(bus->parent);
if (ret < 0)
return ret;
ret = mdiobb_read_c45(bus, phy, devad, reg);
pm_runtime_mark_last_busy(bus->parent);
pm_runtime_put_autosuspend(bus->parent);
return ret;
}
static int davinci_mdiobb_write_c45(struct mii_bus *bus, int phy, int devad,
int reg, u16 val)
{
int ret;
ret = pm_runtime_resume_and_get(bus->parent);
if (ret < 0)
return ret;
ret = mdiobb_write_c45(bus, phy, devad, reg, val);
pm_runtime_mark_last_busy(bus->parent); pm_runtime_mark_last_busy(bus->parent);
pm_runtime_put_autosuspend(bus->parent); pm_runtime_put_autosuspend(bus->parent);
...@@ -573,8 +607,10 @@ static int davinci_mdio_probe(struct platform_device *pdev) ...@@ -573,8 +607,10 @@ static int davinci_mdio_probe(struct platform_device *pdev)
data->bus->name = dev_name(dev); data->bus->name = dev_name(dev);
if (data->manual_mode) { if (data->manual_mode) {
data->bus->read = davinci_mdiobb_read; data->bus->read = davinci_mdiobb_read_c22;
data->bus->write = davinci_mdiobb_write; data->bus->write = davinci_mdiobb_write_c22;
data->bus->read_c45 = davinci_mdiobb_read_c45;
data->bus->write_c45 = davinci_mdiobb_write_c45;
data->bus->reset = davinci_mdiobb_reset; data->bus->reset = davinci_mdiobb_reset;
dev_info(dev, "Configuring MDIO in manual mode\n"); dev_info(dev, "Configuring MDIO in manual mode\n");
......
...@@ -127,14 +127,12 @@ static void mdiobb_cmd(struct mdiobb_ctrl *ctrl, int op, u8 phy, u8 reg) ...@@ -127,14 +127,12 @@ static void mdiobb_cmd(struct mdiobb_ctrl *ctrl, int op, u8 phy, u8 reg)
/* In clause 45 mode all commands are prefixed by MDIO_ADDR to specify the /* In clause 45 mode all commands are prefixed by MDIO_ADDR to specify the
lower 16 bits of the 21 bit address. This transfer is done identically to a lower 16 bits of the 21 bit address. This transfer is done identically to a
MDIO_WRITE except for a different code. To enable clause 45 mode or MDIO_WRITE except for a different code. Theoretically clause 45 and normal
MII_ADDR_C45 into the address. Theoretically clause 45 and normal devices devices can exist on the same bus. Normal devices should ignore the MDIO_ADDR
can exist on the same bus. Normal devices should ignore the MDIO_ADDR
phase. */ phase. */
static int mdiobb_cmd_addr(struct mdiobb_ctrl *ctrl, int phy, u32 addr) static void mdiobb_cmd_addr(struct mdiobb_ctrl *ctrl, int phy, int dev_addr,
int reg)
{ {
unsigned int dev_addr = (addr >> 16) & 0x1F;
unsigned int reg = addr & 0xFFFF;
mdiobb_cmd(ctrl, MDIO_C45_ADDR, phy, dev_addr); mdiobb_cmd(ctrl, MDIO_C45_ADDR, phy, dev_addr);
/* send the turnaround (10) */ /* send the turnaround (10) */
...@@ -145,21 +143,13 @@ static int mdiobb_cmd_addr(struct mdiobb_ctrl *ctrl, int phy, u32 addr) ...@@ -145,21 +143,13 @@ static int mdiobb_cmd_addr(struct mdiobb_ctrl *ctrl, int phy, u32 addr)
ctrl->ops->set_mdio_dir(ctrl, 0); ctrl->ops->set_mdio_dir(ctrl, 0);
mdiobb_get_bit(ctrl); mdiobb_get_bit(ctrl);
return dev_addr;
} }
int mdiobb_read(struct mii_bus *bus, int phy, int reg) static int mdiobb_read_common(struct mii_bus *bus, int phy)
{ {
struct mdiobb_ctrl *ctrl = bus->priv; struct mdiobb_ctrl *ctrl = bus->priv;
int ret, i; int ret, i;
if (reg & MII_ADDR_C45) {
reg = mdiobb_cmd_addr(ctrl, phy, reg);
mdiobb_cmd(ctrl, MDIO_C45_READ, phy, reg);
} else
mdiobb_cmd(ctrl, ctrl->op_c22_read, phy, reg);
ctrl->ops->set_mdio_dir(ctrl, 0); ctrl->ops->set_mdio_dir(ctrl, 0);
/* check the turnaround bit: the PHY should be driving it to zero, if this /* check the turnaround bit: the PHY should be driving it to zero, if this
...@@ -180,17 +170,31 @@ int mdiobb_read(struct mii_bus *bus, int phy, int reg) ...@@ -180,17 +170,31 @@ int mdiobb_read(struct mii_bus *bus, int phy, int reg)
mdiobb_get_bit(ctrl); mdiobb_get_bit(ctrl);
return ret; return ret;
} }
EXPORT_SYMBOL(mdiobb_read);
int mdiobb_write(struct mii_bus *bus, int phy, int reg, u16 val) int mdiobb_read_c22(struct mii_bus *bus, int phy, int reg)
{ {
struct mdiobb_ctrl *ctrl = bus->priv; struct mdiobb_ctrl *ctrl = bus->priv;
if (reg & MII_ADDR_C45) { mdiobb_cmd(ctrl, ctrl->op_c22_read, phy, reg);
reg = mdiobb_cmd_addr(ctrl, phy, reg);
mdiobb_cmd(ctrl, MDIO_C45_WRITE, phy, reg); return mdiobb_read_common(bus, phy);
} else }
mdiobb_cmd(ctrl, ctrl->op_c22_write, phy, reg); EXPORT_SYMBOL(mdiobb_read_c22);
int mdiobb_read_c45(struct mii_bus *bus, int phy, int devad, int reg)
{
struct mdiobb_ctrl *ctrl = bus->priv;
mdiobb_cmd_addr(ctrl, phy, devad, reg);
mdiobb_cmd(ctrl, MDIO_C45_READ, phy, reg);
return mdiobb_read_common(bus, phy);
}
EXPORT_SYMBOL(mdiobb_read_c45);
static int mdiobb_write_common(struct mii_bus *bus, u16 val)
{
struct mdiobb_ctrl *ctrl = bus->priv;
/* send the turnaround (10) */ /* send the turnaround (10) */
mdiobb_send_bit(ctrl, 1); mdiobb_send_bit(ctrl, 1);
...@@ -202,7 +206,27 @@ int mdiobb_write(struct mii_bus *bus, int phy, int reg, u16 val) ...@@ -202,7 +206,27 @@ int mdiobb_write(struct mii_bus *bus, int phy, int reg, u16 val)
mdiobb_get_bit(ctrl); mdiobb_get_bit(ctrl);
return 0; return 0;
} }
EXPORT_SYMBOL(mdiobb_write);
int mdiobb_write_c22(struct mii_bus *bus, int phy, int reg, u16 val)
{
struct mdiobb_ctrl *ctrl = bus->priv;
mdiobb_cmd(ctrl, ctrl->op_c22_write, phy, reg);
return mdiobb_write_common(bus, val);
}
EXPORT_SYMBOL(mdiobb_write_c22);
int mdiobb_write_c45(struct mii_bus *bus, int phy, int devad, int reg, u16 val)
{
struct mdiobb_ctrl *ctrl = bus->priv;
mdiobb_cmd_addr(ctrl, phy, devad, reg);
mdiobb_cmd(ctrl, MDIO_C45_WRITE, phy, reg);
return mdiobb_write_common(bus, val);
}
EXPORT_SYMBOL(mdiobb_write_c45);
struct mii_bus *alloc_mdio_bitbang(struct mdiobb_ctrl *ctrl) struct mii_bus *alloc_mdio_bitbang(struct mdiobb_ctrl *ctrl)
{ {
...@@ -214,8 +238,11 @@ struct mii_bus *alloc_mdio_bitbang(struct mdiobb_ctrl *ctrl) ...@@ -214,8 +238,11 @@ struct mii_bus *alloc_mdio_bitbang(struct mdiobb_ctrl *ctrl)
__module_get(ctrl->ops->owner); __module_get(ctrl->ops->owner);
bus->read = mdiobb_read; bus->read = mdiobb_read_c22;
bus->write = mdiobb_write; bus->write = mdiobb_write_c22;
bus->read_c45 = mdiobb_read_c45;
bus->write_c45 = mdiobb_write_c45;
bus->priv = ctrl; bus->priv = ctrl;
if (!ctrl->override_op_c22) { if (!ctrl->override_op_c22) {
ctrl->op_c22_read = MDIO_READ; ctrl->op_c22_read = MDIO_READ;
......
...@@ -199,9 +199,7 @@ int xpcs_write(struct dw_xpcs *xpcs, int dev, u32 reg, u16 val) ...@@ -199,9 +199,7 @@ int xpcs_write(struct dw_xpcs *xpcs, int dev, u32 reg, u16 val)
static int xpcs_modify_changed(struct dw_xpcs *xpcs, int dev, u32 reg, static int xpcs_modify_changed(struct dw_xpcs *xpcs, int dev, u32 reg,
u16 mask, u16 set) u16 mask, u16 set)
{ {
u32 reg_addr = mdiobus_c45_addr(dev, reg); return mdiodev_c45_modify_changed(xpcs->mdiodev, dev, reg, mask, set);
return mdiodev_modify_changed(xpcs->mdiodev, reg_addr, mask, set);
} }
static int xpcs_read_vendor(struct dw_xpcs *xpcs, int dev, u32 reg) static int xpcs_read_vendor(struct dw_xpcs *xpcs, int dev, u32 reg)
......
...@@ -526,8 +526,15 @@ int __mdiobus_register(struct mii_bus *bus, struct module *owner) ...@@ -526,8 +526,15 @@ int __mdiobus_register(struct mii_bus *bus, struct module *owner)
int i, err; int i, err;
struct gpio_desc *gpiod; struct gpio_desc *gpiod;
if (NULL == bus || NULL == bus->name || if (!bus || !bus->name)
NULL == bus->read || NULL == bus->write) return -EINVAL;
/* An access method always needs both read and write operations */
if (!!bus->read != !!bus->write || !!bus->read_c45 != !!bus->write_c45)
return -EINVAL;
/* At least one method is mandatory */
if (!bus->read && !bus->read_c45)
return -EINVAL; return -EINVAL;
if (bus->parent && bus->parent->of_node) if (bus->parent && bus->parent->of_node)
...@@ -759,7 +766,10 @@ int __mdiobus_read(struct mii_bus *bus, int addr, u32 regnum) ...@@ -759,7 +766,10 @@ int __mdiobus_read(struct mii_bus *bus, int addr, u32 regnum)
lockdep_assert_held_once(&bus->mdio_lock); lockdep_assert_held_once(&bus->mdio_lock);
if (bus->read)
retval = bus->read(bus, addr, regnum); retval = bus->read(bus, addr, regnum);
else
retval = -EOPNOTSUPP;
trace_mdio_access(bus, 1, addr, regnum, retval, retval); trace_mdio_access(bus, 1, addr, regnum, retval, retval);
mdiobus_stats_acct(&bus->stats[addr], true, retval); mdiobus_stats_acct(&bus->stats[addr], true, retval);
...@@ -785,7 +795,10 @@ int __mdiobus_write(struct mii_bus *bus, int addr, u32 regnum, u16 val) ...@@ -785,7 +795,10 @@ int __mdiobus_write(struct mii_bus *bus, int addr, u32 regnum, u16 val)
lockdep_assert_held_once(&bus->mdio_lock); lockdep_assert_held_once(&bus->mdio_lock);
if (bus->write)
err = bus->write(bus, addr, regnum, val); err = bus->write(bus, addr, regnum, val);
else
err = -EOPNOTSUPP;
trace_mdio_access(bus, 0, addr, regnum, val, err); trace_mdio_access(bus, 0, addr, regnum, val, err);
mdiobus_stats_acct(&bus->stats[addr], false, err); mdiobus_stats_acct(&bus->stats[addr], false, err);
...@@ -826,6 +839,105 @@ int __mdiobus_modify_changed(struct mii_bus *bus, int addr, u32 regnum, ...@@ -826,6 +839,105 @@ int __mdiobus_modify_changed(struct mii_bus *bus, int addr, u32 regnum,
} }
EXPORT_SYMBOL_GPL(__mdiobus_modify_changed); EXPORT_SYMBOL_GPL(__mdiobus_modify_changed);
static u32 mdiobus_c45_addr(int devad, u16 regnum)
{
return MII_ADDR_C45 | devad << MII_DEVADDR_C45_SHIFT | regnum;
}
/**
* __mdiobus_c45_read - Unlocked version of the mdiobus_c45_read function
* @bus: the mii_bus struct
* @addr: the phy address
* @devad: device address to read
* @regnum: register number to read
*
* Read a MDIO bus register. Caller must hold the mdio bus lock.
*
* NOTE: MUST NOT be called from interrupt context.
*/
int __mdiobus_c45_read(struct mii_bus *bus, int addr, int devad, u32 regnum)
{
int retval;
lockdep_assert_held_once(&bus->mdio_lock);
if (bus->read_c45)
retval = bus->read_c45(bus, addr, devad, regnum);
else
retval = bus->read(bus, addr, mdiobus_c45_addr(devad, regnum));
trace_mdio_access(bus, 1, addr, regnum, retval, retval);
mdiobus_stats_acct(&bus->stats[addr], true, retval);
return retval;
}
EXPORT_SYMBOL(__mdiobus_c45_read);
/**
* __mdiobus_c45_write - Unlocked version of the mdiobus_write function
* @bus: the mii_bus struct
* @addr: the phy address
* @devad: device address to read
* @regnum: register number to write
* @val: value to write to @regnum
*
* Write a MDIO bus register. Caller must hold the mdio bus lock.
*
* NOTE: MUST NOT be called from interrupt context.
*/
int __mdiobus_c45_write(struct mii_bus *bus, int addr, int devad, u32 regnum,
u16 val)
{
int err;
lockdep_assert_held_once(&bus->mdio_lock);
if (bus->write_c45)
err = bus->write_c45(bus, addr, devad, regnum, val);
else
err = bus->write(bus, addr, mdiobus_c45_addr(devad, regnum),
val);
trace_mdio_access(bus, 0, addr, regnum, val, err);
mdiobus_stats_acct(&bus->stats[addr], false, err);
return err;
}
EXPORT_SYMBOL(__mdiobus_c45_write);
/**
* __mdiobus_c45_modify_changed - Unlocked version of the mdiobus_modify function
* @bus: the mii_bus struct
* @addr: the phy address
* @devad: device address to read
* @regnum: register number to modify
* @mask: bit mask of bits to clear
* @set: bit mask of bits to set
*
* Read, modify, and if any change, write the register value back to the
* device. Any error returns a negative number.
*
* NOTE: MUST NOT be called from interrupt context.
*/
static int __mdiobus_c45_modify_changed(struct mii_bus *bus, int addr,
int devad, u32 regnum, u16 mask,
u16 set)
{
int new, ret;
ret = __mdiobus_c45_read(bus, addr, devad, regnum);
if (ret < 0)
return ret;
new = (ret & ~mask) | set;
if (new == ret)
return 0;
ret = __mdiobus_c45_write(bus, addr, devad, regnum, new);
return ret < 0 ? ret : 1;
}
/** /**
* mdiobus_read_nested - Nested version of the mdiobus_read function * mdiobus_read_nested - Nested version of the mdiobus_read function
* @bus: the mii_bus struct * @bus: the mii_bus struct
...@@ -873,6 +985,56 @@ int mdiobus_read(struct mii_bus *bus, int addr, u32 regnum) ...@@ -873,6 +985,56 @@ int mdiobus_read(struct mii_bus *bus, int addr, u32 regnum)
} }
EXPORT_SYMBOL(mdiobus_read); EXPORT_SYMBOL(mdiobus_read);
/**
* mdiobus_c45_read - Convenience function for reading a given MII mgmt register
* @bus: the mii_bus struct
* @addr: the phy address
* @devad: device address to read
* @regnum: register number to read
*
* NOTE: MUST NOT be called from interrupt context,
* because the bus read/write functions may wait for an interrupt
* to conclude the operation.
*/
int mdiobus_c45_read(struct mii_bus *bus, int addr, int devad, u32 regnum)
{
int retval;
mutex_lock(&bus->mdio_lock);
retval = __mdiobus_c45_read(bus, addr, devad, regnum);
mutex_unlock(&bus->mdio_lock);
return retval;
}
EXPORT_SYMBOL(mdiobus_c45_read);
/**
* mdiobus_c45_read_nested - Nested version of the mdiobus_c45_read function
* @bus: the mii_bus struct
* @addr: the phy address
* @devad: device address to read
* @regnum: register number to read
*
* In case of nested MDIO bus access avoid lockdep false positives by
* using mutex_lock_nested().
*
* NOTE: MUST NOT be called from interrupt context,
* because the bus read/write functions may wait for an interrupt
* to conclude the operation.
*/
int mdiobus_c45_read_nested(struct mii_bus *bus, int addr, int devad,
u32 regnum)
{
int retval;
mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
retval = __mdiobus_c45_read(bus, addr, devad, regnum);
mutex_unlock(&bus->mdio_lock);
return retval;
}
EXPORT_SYMBOL(mdiobus_c45_read_nested);
/** /**
* mdiobus_write_nested - Nested version of the mdiobus_write function * mdiobus_write_nested - Nested version of the mdiobus_write function
* @bus: the mii_bus struct * @bus: the mii_bus struct
...@@ -922,6 +1084,59 @@ int mdiobus_write(struct mii_bus *bus, int addr, u32 regnum, u16 val) ...@@ -922,6 +1084,59 @@ int mdiobus_write(struct mii_bus *bus, int addr, u32 regnum, u16 val)
} }
EXPORT_SYMBOL(mdiobus_write); EXPORT_SYMBOL(mdiobus_write);
/**
* mdiobus_c45_write - Convenience function for writing a given MII mgmt register
* @bus: the mii_bus struct
* @addr: the phy address
* @devad: device address to read
* @regnum: register number to write
* @val: value to write to @regnum
*
* NOTE: MUST NOT be called from interrupt context,
* because the bus read/write functions may wait for an interrupt
* to conclude the operation.
*/
int mdiobus_c45_write(struct mii_bus *bus, int addr, int devad, u32 regnum,
u16 val)
{
int err;
mutex_lock(&bus->mdio_lock);
err = __mdiobus_c45_write(bus, addr, devad, regnum, val);
mutex_unlock(&bus->mdio_lock);
return err;
}
EXPORT_SYMBOL(mdiobus_c45_write);
/**
* mdiobus_c45_write_nested - Nested version of the mdiobus_c45_write function
* @bus: the mii_bus struct
* @addr: the phy address
* @devad: device address to read
* @regnum: register number to write
* @val: value to write to @regnum
*
* In case of nested MDIO bus access avoid lockdep false positives by
* using mutex_lock_nested().
*
* NOTE: MUST NOT be called from interrupt context,
* because the bus read/write functions may wait for an interrupt
* to conclude the operation.
*/
int mdiobus_c45_write_nested(struct mii_bus *bus, int addr, int devad,
u32 regnum, u16 val)
{
int err;
mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
err = __mdiobus_c45_write(bus, addr, devad, regnum, val);
mutex_unlock(&bus->mdio_lock);
return err;
}
EXPORT_SYMBOL(mdiobus_c45_write_nested);
/** /**
* mdiobus_modify - Convenience function for modifying a given mdio device * mdiobus_modify - Convenience function for modifying a given mdio device
* register * register
...@@ -943,6 +1158,30 @@ int mdiobus_modify(struct mii_bus *bus, int addr, u32 regnum, u16 mask, u16 set) ...@@ -943,6 +1158,30 @@ int mdiobus_modify(struct mii_bus *bus, int addr, u32 regnum, u16 mask, u16 set)
} }
EXPORT_SYMBOL_GPL(mdiobus_modify); EXPORT_SYMBOL_GPL(mdiobus_modify);
/**
* mdiobus_c45_modify - Convenience function for modifying a given mdio device
* register
* @bus: the mii_bus struct
* @addr: the phy address
* @devad: device address to read
* @regnum: register number to write
* @mask: bit mask of bits to clear
* @set: bit mask of bits to set
*/
int mdiobus_c45_modify(struct mii_bus *bus, int addr, int devad, u32 regnum,
u16 mask, u16 set)
{
int err;
mutex_lock(&bus->mdio_lock);
err = __mdiobus_c45_modify_changed(bus, addr, devad, regnum,
mask, set);
mutex_unlock(&bus->mdio_lock);
return err < 0 ? err : 0;
}
EXPORT_SYMBOL_GPL(mdiobus_c45_modify);
/** /**
* mdiobus_modify_changed - Convenience function for modifying a given mdio * mdiobus_modify_changed - Convenience function for modifying a given mdio
* device register and returning if it changed * device register and returning if it changed
...@@ -965,6 +1204,29 @@ int mdiobus_modify_changed(struct mii_bus *bus, int addr, u32 regnum, ...@@ -965,6 +1204,29 @@ int mdiobus_modify_changed(struct mii_bus *bus, int addr, u32 regnum,
} }
EXPORT_SYMBOL_GPL(mdiobus_modify_changed); EXPORT_SYMBOL_GPL(mdiobus_modify_changed);
/**
* mdiobus_c45_modify_changed - Convenience function for modifying a given mdio
* device register and returning if it changed
* @bus: the mii_bus struct
* @addr: the phy address
* @devad: device address to read
* @regnum: register number to write
* @mask: bit mask of bits to clear
* @set: bit mask of bits to set
*/
int mdiobus_c45_modify_changed(struct mii_bus *bus, int devad, int addr,
u32 regnum, u16 mask, u16 set)
{
int err;
mutex_lock(&bus->mdio_lock);
err = __mdiobus_c45_modify_changed(bus, addr, devad, regnum, mask, set);
mutex_unlock(&bus->mdio_lock);
return err;
}
EXPORT_SYMBOL_GPL(mdiobus_c45_modify_changed);
/** /**
* mdio_bus_match - determine if given MDIO driver supports the given * mdio_bus_match - determine if given MDIO driver supports the given
* MDIO device * MDIO device
......
...@@ -38,8 +38,10 @@ struct mdiobb_ctrl { ...@@ -38,8 +38,10 @@ struct mdiobb_ctrl {
u8 op_c22_write; u8 op_c22_write;
}; };
int mdiobb_read(struct mii_bus *bus, int phy, int reg); int mdiobb_read_c22(struct mii_bus *bus, int phy, int reg);
int mdiobb_write(struct mii_bus *bus, int phy, int reg, u16 val); int mdiobb_write_c22(struct mii_bus *bus, int phy, int reg, u16 val);
int mdiobb_read_c45(struct mii_bus *bus, int devad, int phy, int reg);
int mdiobb_write_c45(struct mii_bus *bus, int devad, int phy, int reg, u16 val);
/* The returned bus is not yet registered with the phy layer. */ /* The returned bus is not yet registered with the phy layer. */
struct mii_bus *alloc_mdio_bitbang(struct mdiobb_ctrl *ctrl); struct mii_bus *alloc_mdio_bitbang(struct mdiobb_ctrl *ctrl);
......
...@@ -423,6 +423,21 @@ int mdiobus_modify(struct mii_bus *bus, int addr, u32 regnum, u16 mask, ...@@ -423,6 +423,21 @@ int mdiobus_modify(struct mii_bus *bus, int addr, u32 regnum, u16 mask,
u16 set); u16 set);
int mdiobus_modify_changed(struct mii_bus *bus, int addr, u32 regnum, int mdiobus_modify_changed(struct mii_bus *bus, int addr, u32 regnum,
u16 mask, u16 set); u16 mask, u16 set);
int __mdiobus_c45_read(struct mii_bus *bus, int addr, int devad, u32 regnum);
int mdiobus_c45_read(struct mii_bus *bus, int addr, int devad, u32 regnum);
int mdiobus_c45_read_nested(struct mii_bus *bus, int addr, int devad,
u32 regnum);
int __mdiobus_c45_write(struct mii_bus *bus, int addr, int devad, u32 regnum,
u16 val);
int mdiobus_c45_write(struct mii_bus *bus, int addr, int devad, u32 regnum,
u16 val);
int mdiobus_c45_write_nested(struct mii_bus *bus, int addr, int devad,
u32 regnum, u16 val);
int mdiobus_c45_modify(struct mii_bus *bus, int addr, int devad, u32 regnum,
u16 mask, u16 set);
int mdiobus_c45_modify_changed(struct mii_bus *bus, int addr, int devad,
u32 regnum, u16 mask, u16 set);
static inline int mdiodev_read(struct mdio_device *mdiodev, u32 regnum) static inline int mdiodev_read(struct mdio_device *mdiodev, u32 regnum)
{ {
...@@ -448,11 +463,6 @@ static inline int mdiodev_modify_changed(struct mdio_device *mdiodev, ...@@ -448,11 +463,6 @@ static inline int mdiodev_modify_changed(struct mdio_device *mdiodev,
mask, set); mask, set);
} }
static inline u32 mdiobus_c45_addr(int devad, u16 regnum)
{
return MII_ADDR_C45 | devad << MII_DEVADDR_C45_SHIFT | regnum;
}
static inline u16 mdiobus_c45_regad(u32 regnum) static inline u16 mdiobus_c45_regad(u32 regnum)
{ {
return FIELD_GET(MII_REGADDR_C45_MASK, regnum); return FIELD_GET(MII_REGADDR_C45_MASK, regnum);
...@@ -463,29 +473,19 @@ static inline u16 mdiobus_c45_devad(u32 regnum) ...@@ -463,29 +473,19 @@ static inline u16 mdiobus_c45_devad(u32 regnum)
return FIELD_GET(MII_DEVADDR_C45_MASK, regnum); return FIELD_GET(MII_DEVADDR_C45_MASK, regnum);
} }
static inline int __mdiobus_c45_read(struct mii_bus *bus, int prtad, int devad, static inline int mdiodev_c45_modify(struct mdio_device *mdiodev, int devad,
u16 regnum) u32 regnum, u16 mask, u16 set)
{
return __mdiobus_read(bus, prtad, mdiobus_c45_addr(devad, regnum));
}
static inline int __mdiobus_c45_write(struct mii_bus *bus, int prtad, int devad,
u16 regnum, u16 val)
{
return __mdiobus_write(bus, prtad, mdiobus_c45_addr(devad, regnum),
val);
}
static inline int mdiobus_c45_read(struct mii_bus *bus, int prtad, int devad,
u16 regnum)
{ {
return mdiobus_read(bus, prtad, mdiobus_c45_addr(devad, regnum)); return mdiobus_c45_modify(mdiodev->bus, mdiodev->addr, devad, regnum,
mask, set);
} }
static inline int mdiobus_c45_write(struct mii_bus *bus, int prtad, int devad, static inline int mdiodev_c45_modify_changed(struct mdio_device *mdiodev,
u16 regnum, u16 val) int devad, u32 regnum, u16 mask,
u16 set)
{ {
return mdiobus_write(bus, prtad, mdiobus_c45_addr(devad, regnum), val); return mdiobus_c45_modify_changed(mdiodev->bus, mdiodev->addr, devad,
regnum, mask, set);
} }
static inline int mdiodev_c45_read(struct mdio_device *mdiodev, int devad, static inline int mdiodev_c45_read(struct mdio_device *mdiodev, int devad,
......
...@@ -364,6 +364,11 @@ struct mii_bus { ...@@ -364,6 +364,11 @@ struct mii_bus {
int (*read)(struct mii_bus *bus, int addr, int regnum); int (*read)(struct mii_bus *bus, int addr, int regnum);
/** @write: Perform a write transfer on the bus */ /** @write: Perform a write transfer on the bus */
int (*write)(struct mii_bus *bus, int addr, int regnum, u16 val); int (*write)(struct mii_bus *bus, int addr, int regnum, u16 val);
/** @read_c45: Perform a C45 read transfer on the bus */
int (*read_c45)(struct mii_bus *bus, int addr, int devnum, int regnum);
/** @write_c45: Perform a C45 write transfer on the bus */
int (*write_c45)(struct mii_bus *bus, int addr, int devnum,
int regnum, u16 val);
/** @reset: Perform a reset of the bus */ /** @reset: Perform a reset of the bus */
int (*reset)(struct mii_bus *bus); int (*reset)(struct mii_bus *bus);
......
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