
Some phys have additional registers that are not covered by standard. Access to this registers can be done via specific sequence according to the phy datasheet. The driver for Micrel phy contains some additional function, that the board maintainer can call to tune the phy. However, these registers cannot be accessed with "mmdio" command.
Add calback to the phy API to allow to call the function for reading / writing extended registers with the mmdio command.
Stefano Babic (5): phy: add missing constants for Micrel KSZ9031 net: fix mask for phy Micrel KSZ9031 net: add extended function to phy API net: add function to read/write extended registers in Micrel Phy net: add support for extended registers to mdio command
common/cmd_mdio.c | 75 ++++++++++++++++++++++++++++++++++++---------- drivers/net/phy/micrel.c | 34 ++++++++++++++++++++- include/micrel.h | 5 ++++ include/phy.h | 3 ++ 4 files changed, 100 insertions(+), 17 deletions(-)

Signed-off-by: Stefano Babic sbabic@denx.de
--- include/micrel.h | 5 +++++ 1 file changed, 5 insertions(+)
diff --git a/include/micrel.h b/include/micrel.h index e1c62d8..04c9ecf 100644 --- a/include/micrel.h +++ b/include/micrel.h @@ -15,6 +15,11 @@ #define MII_KSZ9031_MOD_DATA_POST_INC_RW 0x8000 #define MII_KSZ9031_MOD_DATA_POST_INC_W 0xC000
+#define MII_KSZ9031_EXT_RGMII_CTRL_SIG_SKEW 0x4 +#define MII_KSZ9031_EXT_RGMII_RX_DATA_SKEW 0x5 +#define MII_KSZ9031_EXT_RGMII_TX_DATA_SKEW 0x6 +#define MII_KSZ9031_EXT_RGMII_CLOCK_SKEW 0x8 + struct phy_device; int ksz9021_phy_extended_write(struct phy_device *phydev, int regnum, u16 val); int ksz9021_phy_extended_read(struct phy_device *phydev, int regnum);

Signed-off-by: Stefano Babic sbabic@denx.de --- drivers/net/phy/micrel.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c index a7450f8..f3e3054 100644 --- a/drivers/net/phy/micrel.c +++ b/drivers/net/phy/micrel.c @@ -174,7 +174,7 @@ int ksz9031_phy_extended_read(struct phy_device *phydev, int devaddr, static struct phy_driver ksz9031_driver = { .name = "Micrel ksz9031", .uid = 0x221620, - .mask = 0xfffffe, + .mask = 0xfffff0, .features = PHY_GBIT_FEATURES, .config = &genphy_config, .startup = &ksz90xx_startup,

Some phys (Micrel) has extended registers that must be accessed in a special way. Add pointers to the phy driver structure to allow to use these functions with mdio command.
Signed-off-by: Stefano Babic sbabic@denx.de --- include/phy.h | 3 +++ 1 file changed, 3 insertions(+)
diff --git a/include/phy.h b/include/phy.h index f0f522a..d3001f1 100644 --- a/include/phy.h +++ b/include/phy.h @@ -125,6 +125,9 @@ struct phy_driver { /* Called when bringing down the controller */ int (*shutdown)(struct phy_device *phydev);
+ int (*readext)(struct phy_device *phydev, int addr, int devad, int reg); + int (*writeext)(struct phy_device *phydev, int addr, int devad, int reg, + u16 val); struct list_head list; };

Signed-off-by: Stefano Babic sbabic@denx.de --- drivers/net/phy/micrel.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+)
diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c index f3e3054..5d7e3be 100644 --- a/drivers/net/phy/micrel.c +++ b/drivers/net/phy/micrel.c @@ -100,6 +100,19 @@ int ksz9021_phy_extended_read(struct phy_device *phydev, int regnum) return phy_read(phydev, MDIO_DEVAD_NONE, MII_KSZ9021_EXTENDED_DATAR); }
+ +static int ksz9021_phy_extread(struct phy_device *phydev, int addr, int devaddr, + int regnum) +{ + return ksz9021_phy_extended_read(phydev, regnum); +} + +static int ksz9021_phy_extwrite(struct phy_device *phydev, int addr, + int devaddr, int regnum, u16 val) +{ + return ksz9021_phy_extended_write(phydev, regnum, val); +} + /* Micrel ksz9021 */ static int ksz9021_config(struct phy_device *phydev) { @@ -131,6 +144,8 @@ static struct phy_driver ksz9021_driver = { .config = &ksz9021_config, .startup = &ksz90xx_startup, .shutdown = &genphy_shutdown, + .writeext = &ksz9021_phy_extwrite, + .readext = &ksz9021_phy_extread, }; #endif
@@ -171,6 +186,21 @@ int ksz9031_phy_extended_read(struct phy_device *phydev, int devaddr, return phy_read(phydev, MDIO_DEVAD_NONE, MII_KSZ9031_MMD_REG_DATA); }
+static int ksz9031_phy_extread(struct phy_device *phydev, int addr, int devaddr, + int regnum) +{ + return ksz9031_phy_extended_read(phydev, devaddr, regnum, + MII_KSZ9031_MOD_DATA_NO_POST_INC); +}; + +static int ksz9031_phy_extwrite(struct phy_device *phydev, int addr, + int devaddr, int regnum, u16 val) +{ + return ksz9031_phy_extended_write(phydev, devaddr, regnum, + MII_KSZ9031_MOD_DATA_POST_INC_RW, val); +}; + + static struct phy_driver ksz9031_driver = { .name = "Micrel ksz9031", .uid = 0x221620, @@ -179,6 +209,8 @@ static struct phy_driver ksz9031_driver = { .config = &genphy_config, .startup = &ksz90xx_startup, .shutdown = &genphy_shutdown, + .writeext = &ksz9031_phy_extwrite, + .readext = &ksz9031_phy_extread, };
int phy_micrel_init(void)

Some phys (Micrel) have additional registers that can be accessed using a special sequence. This patch allows to use standard "mdio" command to accesss these registers.
Signed-off-by: Stefano Babic sbabic@denx.de --- common/cmd_mdio.c | 75 +++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 59 insertions(+), 16 deletions(-)
diff --git a/common/cmd_mdio.c b/common/cmd_mdio.c index 65a1f10..fb13d05 100644 --- a/common/cmd_mdio.c +++ b/common/cmd_mdio.c @@ -41,9 +41,11 @@ static int extract_range(char *input, int *plo, int *phi) return 0; }
-static int mdio_write_ranges(struct mii_dev *bus, int addrlo, +static int mdio_write_ranges(struct phy_device *phydev, struct mii_dev *bus, + int addrlo, int addrhi, int devadlo, int devadhi, - int reglo, int reghi, unsigned short data) + int reglo, int reghi, unsigned short data, + int extended) { int addr, devad, reg; int err = 0; @@ -51,7 +53,12 @@ static int mdio_write_ranges(struct mii_dev *bus, int addrlo, for (addr = addrlo; addr <= addrhi; addr++) { for (devad = devadlo; devad <= devadhi; devad++) { for (reg = reglo; reg <= reghi; reg++) { - err = bus->write(bus, addr, devad, reg, data); + if (!extended) + err = bus->write(bus, addr, devad, + reg, data); + else + err = phydev->drv->writeext(phydev, + addr, devad, reg, data);
if (err) goto err_out; @@ -63,9 +70,10 @@ err_out: return err; }
-static int mdio_read_ranges(struct mii_dev *bus, int addrlo, +static int mdio_read_ranges(struct phy_device *phydev, struct mii_dev *bus, + int addrlo, int addrhi, int devadlo, int devadhi, - int reglo, int reghi) + int reglo, int reghi, int extended) { int addr, devad, reg;
@@ -77,7 +85,12 @@ static int mdio_read_ranges(struct mii_dev *bus, int addrlo, for (reg = reglo; reg <= reghi; reg++) { int val;
- val = bus->read(bus, addr, devad, reg); + if (!extended) + val = bus->read(bus, addr, devad, reg); + else + val = phydev->drv->readext(phydev, addr, + devad, reg); + if (val < 0) { printf("Error\n");
@@ -126,9 +139,10 @@ static int extract_reg_range(char *input, int *devadlo, int *devadhi, }
static int extract_phy_range(char *const argv[], int argc, struct mii_dev **bus, + struct phy_device **phydev, int *addrlo, int *addrhi) { - struct phy_device *phydev; + struct phy_device *dev = *phydev;
if ((argc < 1) || (argc > 2)) return -1; @@ -154,11 +168,11 @@ static int extract_phy_range(char *const argv[], int argc, struct mii_dev **bus, * device by the given name. If none are found, we call * extract_range() on the string, and see if it's an address range. */ - phydev = mdio_phydev_for_ethname(argv[0]); + dev = mdio_phydev_for_ethname(argv[0]);
- if (phydev) { - *addrlo = *addrhi = phydev->addr; - *bus = phydev->bus; + if (dev) { + *addrlo = *addrhi = dev->addr; + *bus = dev->bus;
return 0; } @@ -175,6 +189,8 @@ static int do_mdio(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) unsigned short data; int pos = argc - 1; struct mii_dev *bus; + struct phy_device *phydev = NULL; + int extended = 0;
if (argc < 2) return CMD_RET_USAGE; @@ -197,6 +213,29 @@ static int do_mdio(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) if (flag & CMD_FLAG_REPEAT) op[0] = last_op[0];
+ if (strlen(argv[1]) > 1) { + op[1] = argv[1][1]; + if (op[1] == 'x') { + phydev = mdio_phydev_for_ethname(argv[2]); + + if (phydev) { + addrlo = phydev->addr; + addrhi = addrlo; + bus = phydev->bus; + extended = 1; + } else { + return -1; + } + + if (!phydev->drv || + (!phydev->drv->writeext && (op[0] == 'w')) || + (!phydev->drv->readext && (op[0] == 'r'))) { + puts("PHY does not have extended functions\n"); + return -1; + } + } + } + switch (op[0]) { case 'w': if (pos > 1) @@ -210,7 +249,7 @@ static int do_mdio(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) default: if (pos > 1) if (extract_phy_range(&(argv[2]), pos - 1, &bus, - &addrlo, &addrhi)) + &phydev, &addrlo, &addrhi)) return -1;
break; @@ -227,13 +266,13 @@ static int do_mdio(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
switch (op[0]) { case 'w': - mdio_write_ranges(bus, addrlo, addrhi, devadlo, devadhi, - reglo, reghi, data); + mdio_write_ranges(phydev, bus, addrlo, addrhi, devadlo, devadhi, + reglo, reghi, data, extended); break;
case 'r': - mdio_read_ranges(bus, addrlo, addrhi, devadlo, devadhi, - reglo, reghi); + mdio_read_ranges(phydev, bus, addrlo, addrhi, devadlo, devadhi, + reglo, reghi, extended); break; }
@@ -262,6 +301,10 @@ U_BOOT_CMD( "read PHY's register at <devad>.<reg>\n" "mdio write <phydev> [<devad>.]<reg> <data> - " "write PHY's register at <devad>.<reg>\n" + "mdio rx <phydev> [<devad>.]<reg> - " + "read PHY's extended register at <devad>.<reg>\n" + "mdio wx <phydev> [<devad>.]<reg> <data> - " + "write PHY's extended register at <devad>.<reg>\n" "<phydev> may be:\n" " <busname> <addr>\n" " <addr>\n"

Hello Stefano,
Stefano Babic wrote on 2013-09-02:
Some phys have additional registers that are not covered by standard. Access to this registers can be done via specific sequence according to the phy datasheet. The driver for Micrel phy contains some additional function, that the board maintainer can call to tune the phy. However, these registers cannot be accessed with "mmdio" command.
Add calback to the phy API to allow to call the function for reading / writing extended registers with the mmdio command.
The basic mechanism used for the access is not specific to Micrel, it is defined in the IEEE standard [1], "Annex 22D: Clause 22 access to Clause 45 MMD registers"
A presentation for this can be found when you ask google for it [2]
[1]: IEEE Std 802.3-2008 (or 2012): http://standards.ieee.org/about/get/802/802.3.html [2]: https://www.google.de/search?q=%22Clause+22+Access+to+Clause+45+Registers%22
So maybe it will be useful to have the access functions in the common phy lib?
I assume that most current PHYs have these method implemented, as also some extended standard registers needs this. For example, the EEE (Energy Efficient Ethernet) registers are located in this extended register range.
Probably this can be extended in some follow-up patches, I just wanted to share my thoughts ;-) I currently have not platform on my desk which uses phylib (and current code), so I cannot provide a patch on my own, sorry.
Best Regards, Thomas

Hi Thomas,
On 02/09/2013 16:59, thomas.langer@lantiq.com wrote:
Hello Stefano,
Stefano Babic wrote on 2013-09-02:
Some phys have additional registers that are not covered by standard. Access to this registers can be done via specific sequence according to the phy datasheet. The driver for Micrel phy contains some additional function, that the board maintainer can call to tune the phy. However, these registers cannot be accessed with "mmdio" command.
Add calback to the phy API to allow to call the function for reading / writing extended registers with the mmdio command.
The basic mechanism used for the access is not specific to Micrel, it is defined in the IEEE standard [1], "Annex 22D: Clause 22 access to Clause 45 MMD registers"
Interesting - my changes were done according to the Micrel's KSZ9031 datasheet.
A presentation for this can be found when you ask google for it [2]
[1]: IEEE Std 802.3-2008 (or 2012): http://standards.ieee.org/about/get/802/802.3.html [2]: https://www.google.de/search?q=%22Clause+22+Access+to+Clause+45+Registers%22
So maybe it will be useful to have the access functions in the common phy lib?
Maybe it is enough to move the extread / extwrite functions inside phylib. However, if the behavior of the KSZ9031 matches the documentation you have posted, the KSZ9021 seems to slightly deviate. Or is the 9021 supporting another "clause" ?
I assume that most current PHYs have these method implemented, as also some extended standard registers needs this. For example, the EEE (Energy Efficient Ethernet) registers are located in this extended register range.
This is also a nice to have feature.
Probably this can be extended in some follow-up patches,
Let's wait for Joe's opinion.
I just wanted to share my thoughts ;-)
Thanks, I was not aware of the standard. Learned something new...
Best regards, Stefano
participants (2)
-
Stefano Babic
-
thomas.langer@lantiq.com