From abf35df21513c51d7761c41fa6d3b819cdf4103e Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Tue, 9 Mar 2010 09:17:42 +0000 Subject: NET: Support clause 45 MDIO commands at the MDIO bus level IEEE 802.3ae clause 45 specifies a somewhat modified MDIO protocol for use by 10GIGE phys. The main change is a 21 bit address split into a 5 bit device ID and a 16 bit register offset. The definition is designed so that normal and extended devices can run on the same MDIO bus. Extend mdio-bitbang to do the new protocol. At the MDIO bus level the protocol is requested by or'ing MII_ADDR_C45 into the register offset. Make phy_read/phy_write/etc pass a full 32 bit register offset. This does not attempt to make the phy layer support C45 style PHYs, just to provide the MDIO bus support. Tested against a Broadcom 10GE phy with ID 0x206034, and several Broadcom 10/100/1000 Phys in normal mode. Signed-off-by: Jason Gunthorpe Signed-off-by: David S. Miller --- drivers/net/phy/mdio-bitbang.c | 60 +++++++++++++++++++++++++++++++++++------- drivers/net/phy/mdio_bus.c | 4 +-- 2 files changed, 53 insertions(+), 11 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/phy/mdio-bitbang.c b/drivers/net/phy/mdio-bitbang.c index 2576055..0ff0661 100644 --- a/drivers/net/phy/mdio-bitbang.c +++ b/drivers/net/phy/mdio-bitbang.c @@ -23,8 +23,13 @@ #include #include -#define MDIO_READ 1 -#define MDIO_WRITE 0 +#define MDIO_READ 2 +#define MDIO_WRITE 1 + +#define MDIO_C45 (1<<15) +#define MDIO_C45_ADDR (MDIO_C45 | 0) +#define MDIO_C45_READ (MDIO_C45 | 3) +#define MDIO_C45_WRITE (MDIO_C45 | 1) #define MDIO_SETUP_TIME 10 #define MDIO_HOLD_TIME 10 @@ -90,7 +95,7 @@ static u16 mdiobb_get_num(struct mdiobb_ctrl *ctrl, int bits) /* Utility to send the preamble, address, and * register (common to read and write). */ -static void mdiobb_cmd(struct mdiobb_ctrl *ctrl, int read, u8 phy, u8 reg) +static void mdiobb_cmd(struct mdiobb_ctrl *ctrl, int op, u8 phy, u8 reg) { const struct mdiobb_ops *ops = ctrl->ops; int i; @@ -109,23 +114,56 @@ static void mdiobb_cmd(struct mdiobb_ctrl *ctrl, int read, u8 phy, u8 reg) for (i = 0; i < 32; i++) mdiobb_send_bit(ctrl, 1); - /* send the start bit (01) and the read opcode (10) or write (10) */ + /* send the start bit (01) and the read opcode (10) or write (10). + Clause 45 operation uses 00 for the start and 11, 10 for + read/write */ mdiobb_send_bit(ctrl, 0); - mdiobb_send_bit(ctrl, 1); - mdiobb_send_bit(ctrl, read); - mdiobb_send_bit(ctrl, !read); + if (op & MDIO_C45) + mdiobb_send_bit(ctrl, 0); + else + mdiobb_send_bit(ctrl, 1); + mdiobb_send_bit(ctrl, (op >> 1) & 1); + mdiobb_send_bit(ctrl, (op >> 0) & 1); mdiobb_send_num(ctrl, phy, 5); mdiobb_send_num(ctrl, reg, 5); } +/* 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 + MDIO_WRITE except for a different code. To enable clause 45 mode or + MII_ADDR_C45 into the address. Theoretically clause 45 and normal devices + can exist on the same bus. Normal devices should ignore the MDIO_ADDR + phase. */ +static int mdiobb_cmd_addr(struct mdiobb_ctrl *ctrl, int phy, u32 addr) +{ + unsigned int dev_addr = (addr >> 16) & 0x1F; + unsigned int reg = addr & 0xFFFF; + mdiobb_cmd(ctrl, MDIO_C45_ADDR, phy, dev_addr); + + /* send the turnaround (10) */ + mdiobb_send_bit(ctrl, 1); + mdiobb_send_bit(ctrl, 0); + + mdiobb_send_num(ctrl, reg, 16); + + ctrl->ops->set_mdio_dir(ctrl, 0); + mdiobb_get_bit(ctrl); + + return dev_addr; +} static int mdiobb_read(struct mii_bus *bus, int phy, int reg) { struct mdiobb_ctrl *ctrl = bus->priv; int ret, i; - mdiobb_cmd(ctrl, MDIO_READ, phy, reg); + if (reg & MII_ADDR_C45) { + reg = mdiobb_cmd_addr(ctrl, phy, reg); + mdiobb_cmd(ctrl, MDIO_C45_READ, phy, reg); + } else + mdiobb_cmd(ctrl, MDIO_READ, phy, reg); + ctrl->ops->set_mdio_dir(ctrl, 0); /* check the turnaround bit: the PHY should be driving it to zero */ @@ -148,7 +186,11 @@ static int mdiobb_write(struct mii_bus *bus, int phy, int reg, u16 val) { struct mdiobb_ctrl *ctrl = bus->priv; - mdiobb_cmd(ctrl, MDIO_WRITE, phy, reg); + if (reg & MII_ADDR_C45) { + reg = mdiobb_cmd_addr(ctrl, phy, reg); + mdiobb_cmd(ctrl, MDIO_C45_WRITE, phy, reg); + } else + mdiobb_cmd(ctrl, MDIO_WRITE, phy, reg); /* send the turnaround (10) */ mdiobb_send_bit(ctrl, 1); diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index e17b702..6a6b819 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -208,7 +208,7 @@ EXPORT_SYMBOL(mdiobus_scan); * because the bus read/write functions may wait for an interrupt * to conclude the operation. */ -int mdiobus_read(struct mii_bus *bus, int addr, u16 regnum) +int mdiobus_read(struct mii_bus *bus, int addr, u32 regnum) { int retval; @@ -233,7 +233,7 @@ EXPORT_SYMBOL(mdiobus_read); * because the bus read/write functions may wait for an interrupt * to conclude the operation. */ -int mdiobus_write(struct mii_bus *bus, int addr, u16 regnum, u16 val) +int mdiobus_write(struct mii_bus *bus, int addr, u32 regnum, u16 val) { int err; -- cgit v1.1