linux/drivers/net/mdio/mdio-cavium.c
Andrew Lunn a9770eac51 net: mdio: Move MDIO drivers into a new subdirectory
Move all the MDIO drivers and multiplexers into drivers/net/mdio.  The
mdio core is however left in the phy directory, due to mutual
dependencies between the MDIO core and the PHY core.

Take this opportunity to sort the Kconfig based on the menuconfig
strings, and move the multiplexers to the end with a separating
comment.

v2:
Fix typo in commit message

Acked-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: Andrew Lunn <andrew@lunn.ch>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-08-27 06:55:50 -07:00

151 lines
3.4 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2009-2016 Cavium, Inc.
*/
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/phy.h>
#include <linux/io.h>
#include "mdio-cavium.h"
static void cavium_mdiobus_set_mode(struct cavium_mdiobus *p,
enum cavium_mdiobus_mode m)
{
union cvmx_smix_clk smi_clk;
if (m == p->mode)
return;
smi_clk.u64 = oct_mdio_readq(p->register_base + SMI_CLK);
smi_clk.s.mode = (m == C45) ? 1 : 0;
smi_clk.s.preamble = 1;
oct_mdio_writeq(smi_clk.u64, p->register_base + SMI_CLK);
p->mode = m;
}
static int cavium_mdiobus_c45_addr(struct cavium_mdiobus *p,
int phy_id, int regnum)
{
union cvmx_smix_cmd smi_cmd;
union cvmx_smix_wr_dat smi_wr;
int timeout = 1000;
cavium_mdiobus_set_mode(p, C45);
smi_wr.u64 = 0;
smi_wr.s.dat = regnum & 0xffff;
oct_mdio_writeq(smi_wr.u64, p->register_base + SMI_WR_DAT);
regnum = (regnum >> 16) & 0x1f;
smi_cmd.u64 = 0;
smi_cmd.s.phy_op = 0; /* MDIO_CLAUSE_45_ADDRESS */
smi_cmd.s.phy_adr = phy_id;
smi_cmd.s.reg_adr = regnum;
oct_mdio_writeq(smi_cmd.u64, p->register_base + SMI_CMD);
do {
/* Wait 1000 clocks so we don't saturate the RSL bus
* doing reads.
*/
__delay(1000);
smi_wr.u64 = oct_mdio_readq(p->register_base + SMI_WR_DAT);
} while (smi_wr.s.pending && --timeout);
if (timeout <= 0)
return -EIO;
return 0;
}
int cavium_mdiobus_read(struct mii_bus *bus, int phy_id, int regnum)
{
struct cavium_mdiobus *p = bus->priv;
union cvmx_smix_cmd smi_cmd;
union cvmx_smix_rd_dat smi_rd;
unsigned int op = 1; /* MDIO_CLAUSE_22_READ */
int timeout = 1000;
if (regnum & MII_ADDR_C45) {
int r = cavium_mdiobus_c45_addr(p, phy_id, regnum);
if (r < 0)
return r;
regnum = (regnum >> 16) & 0x1f;
op = 3; /* MDIO_CLAUSE_45_READ */
} else {
cavium_mdiobus_set_mode(p, C22);
}
smi_cmd.u64 = 0;
smi_cmd.s.phy_op = op;
smi_cmd.s.phy_adr = phy_id;
smi_cmd.s.reg_adr = regnum;
oct_mdio_writeq(smi_cmd.u64, p->register_base + SMI_CMD);
do {
/* Wait 1000 clocks so we don't saturate the RSL bus
* doing reads.
*/
__delay(1000);
smi_rd.u64 = oct_mdio_readq(p->register_base + SMI_RD_DAT);
} while (smi_rd.s.pending && --timeout);
if (smi_rd.s.val)
return smi_rd.s.dat;
else
return -EIO;
}
EXPORT_SYMBOL(cavium_mdiobus_read);
int cavium_mdiobus_write(struct mii_bus *bus, int phy_id, int regnum, u16 val)
{
struct cavium_mdiobus *p = bus->priv;
union cvmx_smix_cmd smi_cmd;
union cvmx_smix_wr_dat smi_wr;
unsigned int op = 0; /* MDIO_CLAUSE_22_WRITE */
int timeout = 1000;
if (regnum & MII_ADDR_C45) {
int r = cavium_mdiobus_c45_addr(p, phy_id, regnum);
if (r < 0)
return r;
regnum = (regnum >> 16) & 0x1f;
op = 1; /* MDIO_CLAUSE_45_WRITE */
} else {
cavium_mdiobus_set_mode(p, C22);
}
smi_wr.u64 = 0;
smi_wr.s.dat = val;
oct_mdio_writeq(smi_wr.u64, p->register_base + SMI_WR_DAT);
smi_cmd.u64 = 0;
smi_cmd.s.phy_op = op;
smi_cmd.s.phy_adr = phy_id;
smi_cmd.s.reg_adr = regnum;
oct_mdio_writeq(smi_cmd.u64, p->register_base + SMI_CMD);
do {
/* Wait 1000 clocks so we don't saturate the RSL bus
* doing reads.
*/
__delay(1000);
smi_wr.u64 = oct_mdio_readq(p->register_base + SMI_WR_DAT);
} while (smi_wr.s.pending && --timeout);
if (timeout <= 0)
return -EIO;
return 0;
}
EXPORT_SYMBOL(cavium_mdiobus_write);
MODULE_DESCRIPTION("Common code for OCTEON and Thunder MDIO bus drivers");
MODULE_AUTHOR("David Daney");
MODULE_LICENSE("GPL v2");