From aafe03561694271e9d07bc0696b40d9a01dbcdbf Mon Sep 17 00:00:00 2001 From: Russell King Date: Fri, 10 Apr 2020 16:27:26 +0100 Subject: libmii: add a bunch of additional PHY support (this commit will be broken up) Signed-off-by: Russell King --- libmii.c | 2015 ++++++++++++++++++++++++++++++++++++++++++++++++++++++------ mii-diag.c | 36 +- 2 files changed, 1877 insertions(+), 174 deletions(-) diff --git a/libmii.c b/libmii.c index 96f0060..49e7934 100644 --- a/libmii.c +++ b/libmii.c @@ -31,20 +31,24 @@ int monitor_mii(long ioaddr, int phy_id); /* This library expects to be able to call the following functions: */ extern int mdio_read(long ioaddr, int phy_id, int mii_reg_num); +extern void mdio_write(long ioaddr, int phy_id, int mii_reg_num, int value); #include #include +#include +#include #include #include #include + typedef u_int32_t u32; typedef u_int16_t u16; typedef u_int8_t u8; static const char *media_names[] = { "10baseT", "10baseT-FD", "100baseTx", "100baseTx-FD", "100baseT4", - "Flow-control", 0, + "Pause", "AsymPause", 0, }; static void ns83843(long ioaddr, int phy_id); @@ -63,177 +67,1636 @@ static void via_tahoe(long ioaddr, int phy_id); static void via_vt6103(long ioaddr, int phy_id); static void via_vt6105(long ioaddr, int phy_id); static void intel(long ioaddr, int phy_id); +static void mv88x3310_xs(long ioaddr, int phy_id); +static void mv88x3310(long ioaddr, int phy_id); +static void mv88e1111(long ioaddr, int phy_id); +static void mv88e151x(long ioaddr, int phy_id); + +enum { + MDIO_MMD_PMAPMD = 1, + MDIO_MMD_WIS = 2, + MDIO_MMD_PCS = 3, + MDIO_MMD_PHY_XS = 4, + MDIO_MMD_DTE_XS = 5, + MDIO_MMD_TC = 6, + MDIO_MMD_AN = 7, + MDIO_MMD_VENDOR1 = 30, + MDIO_MMD_VENDOR2 = 31, + + MMD_CTRL1 = 0, + MMD_STAT1 = 1, + MMD_DEVID1 = 2, + MMD_DEVID2 = 3, + MMD_SPEED = 4, + MMD_DEVPKG1 = 5, + MMD_DEVPKG2 = 6, + MMD_CTRL2 = 7, + MMD_STAT2 = 8, + MMD_PKGID1 = 14, + MMD_PKGID2 = 15, + + MMD_PMA_EXT_ABL = 11, + MMD_PMA_40G100G_EXT_ABL = 13, + MMD_PMA_EEE_CAPABILITY = 16, + MMD_PMA_25G_EXT_ABL = 19, + MMD_PMA_NBASE_EXT_ABL = 21, + MMD_PMA_10GBT_STATUS = 129, + MMD_PMA_10GBT_SWAPPOL = 130, + + MMD_PCS_EEE_CTRL_CAPAB = 20, + MMD_PCS_EEE_WAKE_ERR = 22, + MMD_PCS_BASERT_STAT1 = 32, + MMD_PCS_BASERT_STAT2 = 33, + + MMD_PHY_XS_EEE_CAPAB = 20, + MMD_PHY_XS_EEE_WAKE_ERR = 22, + MMD_PHY_XS_LANE_STATUS = 24, + + MMD_DTE_XS_EEE_CAPAB = 20, + MMD_DTE_XS_EEE_WAKE_ERR = 22, + + MMD_AN_BASE_ADV0 = 16, + MMD_AN_BASE_ADV1 = 17, + MMD_AN_BASE_ADV2 = 18, + MMD_AN_BASE_LPA0 = 19, + MMD_AN_BASE_LPA1 = 20, + MMD_AN_BASE_LPA2 = 21, + MMD_AN_XNP_ADV0 = 22, + MMD_AN_XNP_ADV1 = 23, + MMD_AN_XNP_ADV2 = 24, + MMD_AN_XNP_LPA0 = 25, + MMD_AN_XNP_LPA1 = 26, + MMD_AN_XNP_LPA2 = 27, + MMD_AN_MULTI_GBT_CTRL = 32, + MMD_AN_MULTI_GBT_STAT = 33, + MMD_AN_BASER_STAT = 48, + MMD_AN_EEE_ADV = 60, + MMD_AN_EEE_LPA = 61, +}; + +#define BIT(x) (1 << (x)) + +static const char *mmd_name[32] = { + [MDIO_MMD_PMAPMD] = "PMA/PMD", + [MDIO_MMD_WIS] = "WIS", + [MDIO_MMD_PCS] = "PCS", + [MDIO_MMD_PHY_XS] = "PHY XS", + [MDIO_MMD_DTE_XS] = "DTE XS", + [MDIO_MMD_TC] = "TC", + [MDIO_MMD_AN] = "AN", + [MDIO_MMD_VENDOR1] = "Vendor Specific 1", + [MDIO_MMD_VENDOR2] = "Vendor Specific 2", +}; + +struct phy { + long ioaddr; + int phy_id; + int devad; + unsigned int page; + unsigned int offset; + int (*mdio_read)(const struct phy *phy, unsigned int reg); +}; + +#define DEFINE_C22_PHY(name, addr, id) \ + struct phy name = { \ + .ioaddr = addr, \ + .phy_id = id, \ + .mdio_read = __mdio_read_phy, \ + } +#define DEFINE_C45_PHY(name, addr, id, __devad) \ + struct phy name = { \ + .ioaddr = addr, \ + .phy_id = id, \ + .devad = __devad, \ + .mdio_read = __mdio_read_phy, \ + } + +struct c22_decoder { + void (*show_advert)(struct phy *phy, u16 *regs); + void (*show_lpar)(struct phy *phy, u16 *regs); +}; struct mii_partnum { - const char *vendor; /* Vendor name. */ - u16 phy_id0; /* Vendor ID (alternate ver. of ieee_oui[]) */ - u16 phy_id1; /* Vendor ID (alternate ver. of ieee_oui[]) */ - unsigned char ieee_oui[3]; /* IEEE-assigned organizationally unique ID */ - char flags; + u32 phy_id; /* Vendor ID */ + u32 msk_id; /* Vendor ID mask */ + const char *vendor; /* Vendor name. */ void (*(func))(long xcvr_if, int phy_id);/* Function to emit more info. */ } static oui_map[] = { - {"Unknown transceiver type", 0x0000, 0x0000, {0,}, 0, NULL,}, - {"National Semiconductor 83840A", 0x2000, 0x5c01, {0,}, 0, NULL,}, - {"National Semiconductor 83843", 0x2000, 0x5c10, {0,}, 0, ns83843, }, - {"Level One LXT970", 0x7810, 0x0000, {0,}, 0, NULL, }, - {"Level One LXT971", 0x7810, 0x0001, {0,}, 0, NULL, }, - {"Level One LXT971A",0x7810, 0x0003, {0,}, 0, NULL, }, - {"Level One (unknown type)", 0, 0, {0x1e,0x04,0x00}, 0, NULL, }, - {"Davicom DM9101", 0x0181, 0xB800, {0,}, 0, davicom_dm9101, }, - {"Davicom (unknown type)", 0, 0, {0x00, 0x60, 0x6e}, 0, davicom_dm9101, }, - {"Quality Semiconductor QS6612", 0x0181, 0x4410, {0,}, 0, qs6612}, - {"Quality Semiconductor (unknown type)", 0,0, {0x00, 0x60, 0x51}, 0, NULL}, - {"SMSC 83c180", 0x0282, 0x1C51, {0}, 0, smsc83c180, }, - {"TDK Semi 78Q2120 rev. 2", 0x0300, 0xE542, {0,}, 0, tdk78q2120, }, - {"TDK Semi 78Q2120 rev. 3", 0x0300, 0xE543, {0,}, 0, tdk78q2120, }, - {"TDK Semi 78Q2120 rev. 11", 0x0300, 0xE54B, {0,}, 0, tdk78q2120, }, - {"TDK transceiver (unknown type)", 0,0, {0x00, 0xc0, 0x39}, 0, tdk78q2120}, - {"Intel (unknown type)", 0,0, {0x00, 0xf8, 0x00}, 0, intel_i553}, - {"Enable Semiconductor EL40-331", 0x0043, 0x7411, {0,}, 0, enablesemi}, - {"AMD 79c901A.1 HomePNA", 0x0000, 0x6B91, {0,}, 0, amd_pna}, - {"AMD 79c901A.2 HomePNA", 0x0000, 0x6B92, {0,}, 0, amd_pna}, - {"AMD 79c901A.3 HomePNA", 0x0000, 0x6B93, {0,}, 0, amd_pna}, - {"AMD 79c901A.3 10baseT", 0x0000, 0x6B71, {0,}, 0, amd_tx}, - {"AdHoc Technology AH101LF", 0x0022, 0x561B, {0,}, 0, tdk78q2120}, - {"Altimata Communications AC101LF", 0x0022, 0x5523, {0,}, 0, tdk78q2120}, - {"Altimata Comm (unknown type)", 0, 0, {0x00,0x10,0xA9}, 0, tdk78q2120}, - {"ASIX (unknown type)", 0, 0, {0x00,0xC0,0xB4}, 0, tdk78q2120}, - {"ADMtek AN983 Comet", 0x0022, 0x5410, {0,}, 0, admtek}, - {"ADMtek AN985 Comet", 0x0022, 0x5513, {0,}, 0, admtek}, - {"ADMtek (unknown type)", 0, 0, {0x00,0xe0,0x92}, 0, admtek}, - {"Lucent LU6612", 0x0180, 0x7641, {0,}, 0, qs6612}, - {"Lucent LU3X31", 0x0043, 0x7411, {0,}, 0, lu3x31}, - {"LSI Logic (Seeq) 80225", 0, 0, {0x00,0xA0,0x7D}, 0, NULL}, - {"Myson MTD981", 0x0302, 0xD000, {0,}, 0, myson981}, - {"Myson (unknown type)", 0, 0, {0x00,0xC0,0xB4,}, 0, myson981}, - {"Alta/Kendin Sundance", 0x0022, 0x1720, {0,}, 0, NULL}, - {"Alta/Kendin Sundance", 0, 0, {0x00,0x08,0x85}, 0, NULL}, - {"VIA Tahoe VT6103", 0x0101, 0x8f20, {0,}, 0, via_vt6103}, - {"VIA Tahoe VT6104", 0x0101, 0x8f30, {0,}, 0, via_tahoe}, - {"VIA Rhine VT6105", 0x0101, 0x8f22, {0,}, 0, via_vt6105}, - {"Intel 82557 series", 0x02a8, 0x0150, {0,}, 0, intel}, - {"Intel 82555 rev 1", 0x02a8, 0x0151, {0,}, 0, intel}, - {"Intel 82559 transceiver", 0x02a8, 0x0154, {0,}, 0, intel}, - {"Intel 82555 series transceiver", 0,0, {0x00,0xaa,0x00}, 0, intel}, +#define OUI(a,b,c) ((a) << 26 | (b) << 18 | (c) << 10), 0xfffffc00 + /* Table sorted in numerical order, but with regard to the mask */ + {0x00000000, 0xffffffff, "Unknown transceiver type"}, + {0x00006b91, 0xffffffff, "AMD 79c901A.1 HomePNA", amd_pna }, + {0x00006b92, 0xffffffff, "AMD 79c901A.2 HomePNA", amd_pna }, + {0x00006b93, 0xffffffff, "AMD 79c901A.3 HomePNA", amd_pna }, + {0x00007b71, 0xffffffff, "AMD 79c901A.3 10baseT", amd_tx }, + {0x00221720, 0xffffffff, "Alta/Kendin Sundance"}, + {OUI(0x00, 0x08, 0x85), "Alta/Kendin Sundance"}, + {0x002b09a0, 0xfffffff0, "Marvell Semiconductor 88X3310", mv88x3310 }, + {0x002bdc00, 0xfffffc00, "Broadcom BCM63XX"}, + {0x00406000, 0xfffffc00, "Broadcom BCM63XX"}, + {0x00437411, 0xffffffff, "Enable Semiconductor EL40-331", enablesemi }, + {0x004dd076, 0xffffffef, "Atheros AT8030"}, + {0x004dd074, 0xffffffef, "Atheros AT8031"}, + {0x004dd072, 0xffffffef, "Atheros AT8035"}, + {0x01018f20, 0xffffffff, "VIA Tahoe VT6103", via_vt6103 }, + {0x01018f30, 0xffffffff, "VIA Tahoe VT6104", via_tahoe }, + {0x01018f22, 0xffffffff, "VIA Rhine VT6105", via_vt6105 }, + {0x01410c60, 0xfffffff0, "Marvell Semiconductor 88E1101"}, + {0x01410c90, 0xfffffff0, "Marvell Semiconductor 88E1112"}, + {0x01410cb0, 0xfffffff0, "Marvell Semiconductor 88E1121R"}, + {0x01410cc0, 0xfffffff0, "Marvell Semiconductor 88E1111", mv88e1111 }, + {0x01410cd0, 0xfffffff0, "Marvell Semiconductor 88E1145"}, + {0x01410da0, 0xfffffff0, "Marvell Semiconductor 88X3310", mv88x3310_xs}, + {0x01410dd0, 0xfffffff0, "Marvell Semiconductor 88E151x", mv88e151x }, + {0x01410e10, 0xfffffff0, "Marvell Semiconductor 88E1118"}, + {0x01410e30, 0xfffffff0, "Marvell Semiconductor 88E1240"}, + {0x01410e40, 0xfffffff0, "Marvell Semiconductor 88E1116R"}, + {0x01410e50, 0xfffffff0, "Marvell Semiconductor 88E1149R"}, + {0x01410e60, 0xfffffff0, "Marvell Semiconductor 88E3016"}, + {0x01410e90, 0xfffffff0, "Marvell Semiconductor 88E1318S"}, + {0x01410ea0, 0xfffffff0, "Marvell Semiconductor 88E1545"}, + {0x01410eb0, 0xfffffff0, "Marvell Semiconductor 88E1540"}, + {OUI(0x00, 0x50, 0x43), "Marvell Semiconductor (unknown type)"}, + {0x0143bc30, 0xfffffff0, "Broadcom BCM5241"}, + {0x0143bc70, 0xfffffff0, "Broadcom BCMAC131"}, + {0x0143bca0, 0xfffffff0, "Broadcom BCM5481"}, + {0x0143bcf0, 0xfffffff0, "Broadcom BCM5395"}, + {0x0143bd60, 0xfffffff0, "Broadcom BCM50610"}, + {0x0143bd70, 0xfffffff0, "Broadcom BCM50610M"}, + {0x01814410, 0xffffffff, "Quality Semiconductor QS6612", qs6612 }, + {OUI(0x00, 0x60, 0x51), "Quality Semiconductor (unknown type)" }, + {0x0181b800, 0xffffffff, "Davicom DM9101", davicom_dm9101 }, + {OUI(0x00, 0x60, 0x6e), "Davicom (unknown type)", davicom_dm9101 }, + {0x02821c51, 0xffffffff, "SMSC 83c180", smsc83c180 }, + {0x02a80150, 0xffffffff, "Intel 82557 series", intel }, + {0x02a80151, 0xffffffff, "Intel 82555 rev 1", intel }, + {0x02a80154, 0xffffffff, "Intel 82559 transceiver", intel }, + {OUI(0x00, 0xaa, 0x00), "Intel 82555 series transceiver", intel }, + {0x0300e542, 0xffffffff, "TDK Semi 78Q2120 rev. 2", tdk78q2120 }, + {0x0300e543, 0xffffffff, "TDK Semi 78Q2120 rev. 3", tdk78q2120 }, + {0x0300e54b, 0xffffffff, "TDK Semi 78Q2120 rev. 11", tdk78q2120 }, + {OUI(0x00, 0xc0, 0x39), "TDK transceiver (unknown type)", tdk78q2120 }, + {0x0302d000, 0xffffffff, "Myson MTD981", myson981 }, + {OUI(0x00, 0xc0, 0xb4), "Myson (unknown type)", myson981 }, + {OUI(0x00, 0xf8, 0x00), "Intel (unknown type)", intel_i553 }, + {0x20005c01, 0xffffffff, "National Semiconductor 83840A"}, + {0x20005c10, 0xffffffff, "National Semiconductor 83843", ns83843 }, + {0x600d8480, 0xfffffff0, "Broadcom BCM7439"}, + {0x600d8490, 0xfffffff0, "Broadcom BCM7366"}, + {0x600d84b0, 0xfffffff0, "Broadcom BCM7362"}, + {0x600d8510, 0xfffffff0, "Broadcom BCM7445"}, + {0x600d8650, 0xfffffff0, "Broadcom BCM7346"}, + {0x600d86b0, 0xfffffff0, "Broadcom BCM7425"}, + {0x600d8730, 0xfffffff0, "Broadcom BCM7429"}, + {0x600d8750, 0xfffffff0, "Broadcom BCM7435"}, + {0x78100000, 0xffffffff, "Level One LXT970"}, + {0x78100001, 0xffffffff, "Level One LXT971"}, + {0x78100003, 0xffffffff, "Level One LXT971A"}, + {OUI(0x1e, 0x04, 0x00), "Level One (unknown type)"}, + {0xae025080, 0xfffffff0, "Broadcom BCM7439"}, + {0xae025090, 0xfffffff0, "Broadcom BCM7268"}, + {0xae025190, 0xfffffff0, "Broadcom BCM7260"}, + {0xae0251a0, 0xfffffff0, "Broadcom BCM7278"}, + {0xae025260, 0xfffffff0, "Broadcom BCM7364"}, + {0xae025280, 0xfffffff0, "Broadcom BCM7250"}, + {0xae0252e0, 0xfffffff0, "Broadcom BCM74371"}, + {0xae0253b0, 0xfffffff0, "Broadcom BCM7271"}, + + /* Unsorted entries */ + {0x0022561b, 0xffffffff, "AdHoc Technology AH101LF", tdk78q2120 }, + {0x00225523, 0xffffffff, "Altimata Communications AC101LF", tdk78q2120 }, + {OUI(0x00, 0x10, 0xa9), "Altimata Comm (unknown type)", tdk78q2120 }, + {OUI(0x00, 0xc0, 0xb4), "ASIX (unknown type)", tdk78q2120 }, + {0x00225410, 0xffffffff, "ADMtek AN983 Comet", admtek }, + {0x00225513, 0xffffffff, "ADMtek AN985 Comet", admtek }, + {OUI(0x00, 0xe0, 0x92), "ADMtek (unknown type)", admtek }, + {0x01807641, 0xffffffff, "Lucent LU6612", qs6612 }, + {0x00437411, 0xffffffff, "Lucent LU3X31", lu3x31 }, + {OUI(0x00, 0xa0, 0x7d), "LSI Logic (Seeq) 80225"}, {0, }, }; -static u16 mii_val[32]; +static int __mdio_read_phy(const struct phy *phy, unsigned int reg) +{ + return mdio_read(phy->ioaddr, phy->phy_id | phy->devad, + phy->offset + reg); +} -void show_mii_details(long ioaddr, int phy_id) +static int mdio_read_phy(const struct phy *phy, unsigned int reg) { - int mii_reg, i, vendor = 0; - u16 bmcr, bmsr, new_bmsr; + return phy->mdio_read(phy, reg); +} - /* This may not be omitted from the output. */ - printf("%s", version_msg); - printf(" MII PHY #%d transceiver registers:", phy_id); - for (mii_reg = 0; mii_reg < 32; mii_reg++) { - mii_val[mii_reg] = mdio_read(ioaddr, phy_id, mii_reg); +static int lookup_vendor(int reg2, int reg3) +{ + u32 id = reg2 << 16 | reg3; + unsigned char oui_0 = id >> 26; + unsigned char oui_1 = id >> 18; + unsigned char oui_2 = id >> 10; + unsigned char model = (id >> 4) & 0x3f; + unsigned char rev = id & 0x0f; + int i, vendor = 0; + + printf(" Vendor ID is %2.2x:%2.2x:%2.2x:--:--:--, model %d rev. %d.\n", + oui_0, oui_1, oui_2, model, rev); + + for (i = 0; oui_map[i].vendor; i++) + if (((oui_map[i].phy_id ^ id) & oui_map[i].msk_id) == 0) { + printf(" Vendor/Part: %s.\n", oui_map[i].vendor); + vendor = i; + break; + } + if (oui_map[i].vendor == NULL) + printf(" No specific information is known about this transceiver" + " type.\n"); + + return vendor; +} + +struct msg_tbl { int bitmask; char *msg; }; + +static void msg_if_set(const int val, const struct msg_tbl msg_tbl[]) +{ + int i; + for (i = 0; msg_tbl[i].bitmask; i++) + if (msg_tbl[i].bitmask & val) + printf(" %s\n", msg_tbl[i].msg); +} + +static void msg_if_set_fmt(const int val, const struct msg_tbl msg_tbl[], + const char *fmt) +{ + int i; + for (i = 0; msg_tbl[i].bitmask; i++) + if (msg_tbl[i].bitmask & val) + printf(fmt, msg_tbl[i].msg); +} + +static u16 mii_val[64], c45_an_ctrl; + +void read_mii_regs(long ioaddr, int phy_id, unsigned int first, unsigned int num_regs) +{ + int mii_reg; + + for (mii_reg = 0; mii_reg < num_regs; mii_reg++) { + int val = mdio_read(ioaddr, phy_id, first + mii_reg); + mii_val[mii_reg] = val < 0 ? 0 : val; + } +} + +static void show_mii_regs(int num_regs) +{ + int mii_reg; + + for (mii_reg = 0; mii_reg < num_regs; mii_reg++) printf("%s %4.4x", (mii_reg % 8) == 0 ? "\n " : "", mii_val[mii_reg]); +} + +struct bit_decoder { + u16 mask; + u16 val; + const char *str; +}; + +static const char **bit_decoder(const char **str, u16 val, + const struct bit_decoder *decoder) +{ + int i; + + for (i = 0; decoder[i].str; i++) + if ((val & decoder[i].mask) == decoder[i].val) + *str++ = decoder[i].str; + *str = NULL; + + return str; +} + +struct multi_bit_decoder { + unsigned int offset; + const struct bit_decoder *bits; +}; + +static const char **multi_bit_decoder(const char **str, const u16 *vals, + const struct multi_bit_decoder *mdec) +{ + const char **p = str; + int i; + + for (i = 0; mdec[i].bits; i++) + p = bit_decoder(p, vals[mdec[i].offset], mdec[i].bits); + + return p; +} + +#if 0 +static char *strconcat(const char *separator, const char **strings) +{ + size_t length, sep_length = strlen(separator); + char *string, *p; + int i; + + if (!strings[0]) + return NULL; + + for (i = 0, length = 0; strings[i]; i++) + length += strlen(strings[i]); + length += (i - 1) * sep_length; + + string = malloc(length); + if (!string) + return NULL; + + for (i = 0, p = string; strings[i]; i++) { + if (i) { + strcpy(p, separator); + p += sep_length; + } + strcpy(p, strings[i]); + p += strlen(p); + } + + return string; +} +#endif + +static void print_strings(const char **strs, const char *prefix, + const char *separator) +{ + int need_separator = 0; + int need_prefix = 1; + int i; + + for (i = 0; strs[i]; i++) { + const char *pfx = ""; + const char *str = strs[i]; + + if (need_prefix) { + pfx = prefix; + need_prefix = 0; + } else if (need_separator) { + pfx = separator; + } + + printf("%s%s", pfx, str); + if (str[strlen(str) - 1] == '\n') + need_prefix = 1; + need_separator = 1; } + if (!need_separator || !need_prefix) + printf(".\n"); +} + +static void show_mii_bits(u16 val, const struct bit_decoder *decoder, + const char *prefix, const char *separator) +{ + const char *strings[64]; + + bit_decoder(strings, val, decoder); + print_strings(strings, prefix, separator); +} + +static void show_mii_reg(const char *name, u16 val, + const struct bit_decoder *decoder, + const char *prefix, const char *separator) +{ + printf(" %s %4.4x", name, val); + show_mii_bits(val, decoder, prefix, separator); +} + +static void show_mii_multibits(u16 *vals, + const struct multi_bit_decoder *decoder, + const char *prefix, const char *separator) +{ + const char *strings[64]; + + multi_bit_decoder(strings, vals, decoder); + print_strings(strings, prefix, separator); +} + +static const char *protocol(unsigned int proto) +{ + if (proto == 1) + return "IEEE 802.3 CSMA/CD protocol"; + else + return "Using an unknown (non 802.3) encapsulation"; +} + +static void show_protocol(unsigned int proto) +{ + printf(" %s.\n", protocol(proto)); +} + + +/* AN advertisements etc */ +// Clause 22 base page advert +static const struct bit_decoder c22_base_advert[] = { + { BIT(11), BIT(11), "AsymPause" }, + { BIT(10), BIT(10), "Pause" }, + { BIT(9), BIT(9), "100baseT4" }, + { BIT(8), BIT(8), "100baseTx-FD" }, + { BIT(7), BIT(7), "100baseTx" }, + { BIT(6), BIT(6), "10baseT-FD" }, + { BIT(5), BIT(5), "10baseT" }, + { }, +}; + +static void show_advert(u16 advert) +{ + show_mii_reg("I'm advertising", advert, c22_base_advert, + ".\n Advertising ", " "); + printf(" Advertising %sadditional info pages.\n", + advert & BIT(15) ? "" : "no "); + show_protocol(advert & 31); +} + +static void show_lpar(u16 lpar) +{ + show_mii_reg("Link partner advertisment is", lpar, c22_base_advert, + ".\n Advertising ", " "); + if (lpar & BIT(13)) + printf(" Remote fault.\n"); + printf(" Negotiation %s.\n", + lpar & BIT(14) ? "completed" : "did not complete"); +} + +static void show_xnp(const char *prefix, u16 adv) +{ + printf(" %s is %4.4x: ", prefix, adv); + if (adv & BIT(13)) + printf("message page %u\n", adv & 0x07ff); + else + printf("unformatted code %3.3x\n", adv & 0x07ff); +} + +static void show_xnp_advert(u16 adv) +{ + show_xnp("XNP advert", adv); +} + +static void show_xnp_lpar(u16 lpa) +{ + show_xnp("XNP link partner", lpa); +} + +// Clause 28 base page and 1000Base-T control/status +static void show_c28_advert(struct phy *phy, u16 *regs) +{ + show_advert(regs[4]); + if (regs[1] & BIT(8)) { + printf(" 1000baseT control %4.4x:", regs[9]); + if (regs[9] & BIT(8)) + printf(" 1000baseT-HD"); + if (regs[9] & BIT(9)) + printf(" 1000baseT-FD"); + printf("\n"); + } +} + +static void show_c28_lpar(struct phy *phy, u16 *regs) +{ + show_lpar(regs[5]); + if (regs[1] & BIT(8)) { + printf(" 1000baseT status %4.4x:", regs[10]); + if (regs[10] & BIT(10)) + printf(" 1000baseT-HD"); + if (regs[10] & BIT(11)) + printf(" 1000baseT-FD"); + if (regs[10] & BIT(12)) + printf(" RemoteRxOK"); + if (regs[10] & BIT(13)) + printf(" LocalRxOK"); + printf("\n"); + } +} + +static const struct c22_decoder c28_linkword = { + .show_advert = show_c28_advert, + .show_lpar = show_c28_lpar, +}; + +// Clause 37 1000baseX advert +static void show_c37_advert(struct phy *phy, u16 *regs) +{ + u16 base = regs[4]; + + printf(" I'm advertising %4.4x:", base); + if (base & BIT(6)) + printf(" 1000baseX"); + if (base & BIT(5)) + printf(" 1000baseX-FD"); + if (base & BIT(7)) + printf(" Pause"); + if (base & BIT(8)) + printf(" AsymPause"); printf(".\n"); +} + +static void show_c37_lpar(struct phy *phy, u16 *regs) +{ + u16 base = regs[5]; + if (base) { + printf(" Link partner advertisment is %4.4x.\n Advertising", base); + if (base & BIT(6)) + printf(" 1000baseX"); + if (base & BIT(5)) + printf(" 1000baseX-FD"); + if (base & BIT(7)) + printf(" Pause"); + if (base & BIT(8)) + printf(" AsymPause"); + printf(".\n"); + } +} + +static const struct c22_decoder c37_linkword = { + .show_advert = show_c37_advert, + .show_lpar = show_c37_lpar, +}; + +// Cisco SGMII advert +static const struct bit_decoder cisco_sgmii_adv_decoder[] = { + { 0x8000, 0x0000, "Link Down" }, + { 0x8000, 0x8000, "Link Up, " }, + { 0x8c00, 0x8000, "10M" }, + { 0x8c00, 0x8400, "100M" }, + { 0x8c00, 0x8800, "1000M" }, + { 0x8c00, 0x8c00, "?" }, + { 0x9000, 0x8000, "/Half" }, + { 0x9000, 0x9000, "/Full" }, + { 0, 0, NULL }, +}; + +static void cisco_sgmii_advert(struct phy *phy, u16 *regs) +{ + const char *strings[17], **p = strings; + u16 base = regs[4]; + + printf(" SGMII advertisement %4.4x", base); + if (base & 1) + p = bit_decoder(p, base, cisco_sgmii_adv_decoder); + else + *p++ = " Link down"; + if (base & 0x63fe) + *p++ = ", reserved bits are set"; + *p = NULL; + print_strings(strings, ": ", ""); +} + +static void cisco_sgmii_lpar(struct phy *phy, u16 *regs) +{ + u16 base = regs[5]; + + printf(" SGMII acknowledgement %4.4x: ", base); + if (base & 1) + printf("%s.", base == 0x4001 ? "MAC acknowledged" : "unknown"); + else if (base == 0) + printf("Unacknowledged."); + + printf("\n"); +} + +static const struct c22_decoder cisco_sgmii_linkword = { + .show_advert = cisco_sgmii_advert, + .show_lpar = cisco_sgmii_lpar, +}; + +// Clause 73.6 (baseR) advert +static const struct bit_decoder c73_linkword_high16[] = { + { BIT(15), BIT(15), "FEC requested" }, /* F1 */ + { BIT(14), BIT(14), "FEC ability" }, /* F0 */ + { }, +}; + +static const struct bit_decoder c73_linkword_mid16[] = { + { BIT(13), BIT(13), "100GbaseCR4" }, /* A8 */ + { BIT(12), BIT(12), "100GbaseKR4" }, /* A7 */ + { BIT(11), BIT(11), "100GbaseKP4" }, /* A6 */ + { BIT(10), BIT(10), "100GbaseCR10" }, /* A5 */ + { BIT(9), BIT(9), "40GbaseCR4" }, /* A4 */ + { BIT(8), BIT(8), "40GbaseKR4" }, /* A3 */ + { BIT(7), BIT(7), "10GbaseKR" }, /* A2 */ + { BIT(6), BIT(6), "10GbaseKX4" }, /* A1 */ + { BIT(5), BIT(5), "1000baseKX" }, /* A0 */ + { }, +}; + +static const struct bit_decoder c73_linkword_low16[] = { + { BIT(11), BIT(11), "AsmPause" }, /* C1 */ + { BIT(10), BIT(10), "Pause" }, /* C0 */ + { }, +}; + +static const struct multi_bit_decoder c73_linkword[] = { + { 2, c73_linkword_high16 }, /* D32:D47 */ + { 1, c73_linkword_mid16 }, /* D16:D31 */ + { 0, c73_linkword_low16 }, /* D0:D15 */ + { }, +}; + +static void show_c73_linkword(const char *desc, u16 *lwp) +{ + printf(" %s %4.4x%4.4x%4.4x.\n", desc, lwp[2], lwp[1], lwp[0]); + show_mii_multibits(lwp, c73_linkword, " Advertising ", " "); + if (lwp[0] & BIT(13)) + printf(" Remote fault.\n"); + show_protocol(lwp[0] & 31); +} + + +/* PMA/PMD tables */ +// 1.0.5:2: Control register 1: Speed selection +static const char *pma_speed[16] = { + [0x0] = "10Gb/s", + [0x1] = "10PASS-TS/2base-TL", + [0x2] = "40Gb/s", + [0x3] = "100Gb/s", + [0x4] = "25Gb/s", + [0x6] = "2.5Gb/s", // 802.3bz + [0x7] = "5Gb/s", // 802.3bz +}; + +// 1.4: PMA/PMD speed ability +static const struct bit_decoder mii_c45_spd_cap_pma[] = { + { BIT(8), BIT(8), "100G" }, + { BIT(7), BIT(7), "40G" }, + { BIT(0), BIT(0), "10G" }, + { BIT(6), BIT(6), "10G/1G" }, + { BIT(14), BIT(14), "5G" }, // 802.3bz + { BIT(13), BIT(13), "2.5G" }, // 802.3bz + { BIT(3), BIT(3), "1G" }, + { BIT(4), BIT(4), "100M" }, + { BIT(5), BIT(5), "10M" }, + { BIT(2), BIT(2), "10PASS-TS" }, + { BIT(1), BIT(1), "2base-TL" }, + { }, +}; + +// 1.7: PMA/PMD control 2 register +static const char *mii_c45_type_pma[128] = { + [0x00] = "10GbaseCX4", [0x01] = "10GbaseEW", + [0x02] = "10GbaseLW", [0x03] = "10GbaseSW", + [0x04] = "10GbaseLX4", [0x05] = "10GbaseER", + [0x06] = "10GbaseLR", [0x07] = "10GbaseSR", + [0x08] = "10GbaseLRM", [0x09] = "10GbaseT", + [0x0a] = "10GbaseKX4", [0x0b] = "10GbaseKR", + [0x0c] = "1000baseT", [0x0d] = "1000baseKX", + [0x0e] = "100baseTX", [0x0f] = "10baseT", + [0x10] = "10/1GbasePRX-D1", [0x11] = "10/1GbasePRX-D2", + [0x12] = "10/1GbasePRX-D3", [0x13] = "10GbasePR-D1", + [0x14] = "10GbasePR-D2", [0x15] = "10GbasePR-D3", + [0x16] = "10/1GbasePRX-U1", [0x17] = "10/1GbasePRX-U2", + [0x18] = "10/1GbasePRX-U3", [0x19] = "10GbasePR-U1", + [0x1a] = "10GbasePR-U3", + [0x26] = "40GbaseT", // 802.3bq + [0x30] = "2.5GbaseT", [0x31] = "5GbaseT", // 802.3bz + [0x37] = "25GbaseT", // 802.3bq + [0x40] = "40GbaseKR4", [0x41] = "40GbaseCR4", + [0x42] = "40GbaseSR4", [0x43] = "40GbaseLR4", + [0x44] = "40GbaseFR", + [0x50] = "100GbaseCR10", [0x51] = "100GbaseSR10", + [0x52] = "100GbaseLR4", [0x53] = "100GbaseER4", +}; + +// 1.8: PMA/PMD status 2 register +static const struct bit_decoder mii_c45_stat_2_pma[] = { + { BIT(0), BIT(0), "Local loopback" }, + { BIT(1), BIT(1), "10GbaseEW" }, + { BIT(2), BIT(2), "10GbaseLW" }, + { BIT(3), BIT(3), "10GbaseSW" }, + { BIT(4), BIT(4), "10GbaseLX4" }, + { BIT(5), BIT(5), "10GbaseER" }, + { BIT(6), BIT(6), "10GbaseLR" }, + { BIT(7), BIT(7), "10GbaseSR" }, + { BIT(8), BIT(8), "Transmit disable" }, + { BIT(12), BIT(12), "Receive fault" }, + { BIT(13), BIT(13), "Transmit fault" }, + { }, +}; + +// 1.11: PMA/PMD extended ability register +static const struct bit_decoder mii_c45_ext_ability_pma[] = { + { BIT(10), BIT(10), "40G/100G (below)" }, + { BIT(0), BIT(0), "10GbaseCX4" }, + { BIT(1), BIT(1), "10GbaseLRM" }, + { BIT(2), BIT(2), "10GbaseT" }, + { BIT(3), BIT(3), "10GbaseKX4" }, + { BIT(4), BIT(4), "10GbaseKR" }, + { BIT(14), BIT(14), "2.5G/5G (below)" }, // 802.3bz + { BIT(5), BIT(5), "1000baseT" }, + { BIT(6), BIT(6), "1000baseKX" }, + { BIT(7), BIT(7), "100baseTX" }, + { BIT(8), BIT(8), "10baseT" }, + { BIT(9), BIT(9), "P2MP" }, + { }, +}; + +// 1.13: 40G/100G PMA/PMD extended ability register +static const struct bit_decoder mii_c45_40g_ext_abl_pma[] = { + { BIT(7), BIT(7), "100GbaseSR4" }, + { BIT(8), BIT(8), "100GbaseCR10" }, + { BIT(9), BIT(9), "100GbaseSR10" }, + { BIT(10), BIT(10), "100GbaseLR4" }, + { BIT(11), BIT(11), "100GbaseER4" }, + { BIT(12), BIT(12), "100GbaseKP4" }, + { BIT(13), BIT(13), "100GbaseKR4" }, + { BIT(14), BIT(14), "100GbaseCR4" }, + { BIT(0), BIT(0), "40GbaseKR4" }, + { BIT(1), BIT(1), "40GbaseCR4" }, + { BIT(2), BIT(2), "40GbaseSR4" }, + { BIT(3), BIT(3), "40GbaseLR4" }, + { BIT(4), BIT(4), "40GbaseFR" }, + { BIT(5), BIT(5), "40GbaseER4" }, + { BIT(6), BIT(6), "40GbaseT" }, // 802.3bq + { BIT(15), BIT(15), "PMA remote loopback" }, + { }, +}; + +// 1.16: EEE capability +static const struct bit_decoder mii_c45_eee_capability_pma[] = { + { BIT(11), BIT(11), "100GBASE-CR4" }, + { BIT(10), BIT(10), "100GBASE-KR4" }, + { BIT(9), BIT(9), "100GBASE-KP4" }, + { BIT(8), BIT(8), "100GBASE-CR10" }, + { BIT(1), BIT(1), "40GBASE-CR4" }, + { BIT(0), BIT(0), "40GBASE-KR4" }, + { }, +}; + +// 1.19: 25G PMA/PMD extended ability register +static const struct bit_decoder mii_c45_25g_ext_abl_pma[] = { + { BIT(5), BIT(5), "25GbaseT" }, // 802.3bq + { }, +}; + +// 1.21: 2.5G/5G PMA/PMD extended ability register +static const struct bit_decoder mii_c45_nbase_ext_abl_pma[] = { + { BIT(1), BIT(1), "5GbaseT" }, // 802.3bz + { BIT(0), BIT(0), "2.5GbaseT" }, // 802.3bz + { }, +}; + + +/* PCS */ +// 3.0.5:2: Control register 1: Speed selection +static const char *pcs_speed[16] = { + [0x0] = "10Gb/s", + [0x1] = "10PASS-TS/2base-TL", + [0x2] = "10/1Gb/s", + [0x3] = "40Gb/s", + [0x4] = "100Gb/s", + [0x7] = "2.5Gb/s", // 802.3bz + [0x8] = "5Gb/s", // 802.3bz +}; + +// 3.1: PCS status 1 register +static const struct bit_decoder mii_c45_pcs_stat[] = { + { BIT(11), BIT(11), "Tx LPI received" }, + { BIT(10), BIT(10), "Rx LPI received" }, + { BIT(9), BIT(9), "Tx LPI indication" }, + { BIT(8), BIT(8), "Rx LPI indication" }, + { BIT(6), BIT(6), "Clock stop capable" }, + { BIT(1), BIT(1), "Supports low-power mode" }, + { }, +}; + +// 3.4: PCS speed ability register +static const struct bit_decoder mii_c45_spd_cap_pcs[] = { + { BIT(3), BIT(3), "100G" }, + { BIT(2), BIT(2), "40G" }, + { BIT(0), BIT(0), "10G" }, + { BIT(7), BIT(7), "5G" }, // 802.3bz + { BIT(6), BIT(6), "2.5G" }, // 802.3bz + { BIT(1), BIT(1), "10PASS-TS/2base-TL" }, + { }, +}; + +// 3.7.3:0 PCS type selection +static const char *mii_c45_type_pcs[16] = { + [0x0] = "10GbaseR", + [0x1] = "10GbaseX", + [0x2] = "10GbaseW", + [0x3] = "10GbaseT", + [0x4] = "40GbaseR", + [0x5] = "100GbaseR", + [0x6] = "40GbaseT", // 802.3bq + [0x7] = "25GbaseR", // 802.3bq + [0x9] = "25GbaseT", // 802.3bq + [0xa] = "2.5GbaseT", // 802.3bz + [0xb] = "5GbaseT", // 802.3bz +}; + +// 3.8: PCS status 2 register +static const struct bit_decoder mii_c45_stat_2_pcs[] = { + { BIT(5), BIT(5), "100GbaseR" }, + { BIT(4), BIT(4), "40GbaseR" }, + { BIT(6), BIT(6), "40GbaseT" }, // 802.3bq + { BIT(9), BIT(9), "25GbaseT" }, // 802.3bq + { BIT(0), BIT(0), "10GbaseR" }, + { BIT(1), BIT(1), "10GbaseX" }, + { BIT(2), BIT(2), "10GbaseW" }, + { BIT(3), BIT(3), "10GbaseT" }, + { BIT(12), BIT(12), "5GbaseT" }, // 802.3bz + { BIT(13), BIT(13), "2.5GbaseT" }, // 802.3bz + { }, +}; + +// 3.20: EEE control and capability +static const struct bit_decoder mii_c45_eee_ctrl_capab_pcs[] = { + { BIT(13), BIT(13), "100GBASE-R deep sleep" }, + { BIT(12), BIT(12), "100GBASE-R fast wake" }, + { BIT(9), BIT(9), "40GBASE-R deep sleep" }, + { BIT(8), BIT(8), "40GBASE-R fast wake" }, + { BIT(6), BIT(6), "10GBASE-KR" }, + { BIT(5), BIT(5), "10GBASE-KX4" }, + { BIT(4), BIT(4), "1000BASE-KX" }, + { BIT(3), BIT(3), "10GBASE-T" }, + { BIT(2), BIT(2), "1000BASE-T" }, + { BIT(1), BIT(1), "100BASE-TX" }, + { BIT(0), BIT(0), "FastWake" }, + { }, +}; + +// 3.32: baseR and MultiGbaseT PCS status 1 register +static const struct bit_decoder mii_pcs_basert_stat1[] = { + { BIT(12), BIT(12), "PCS receive link up" }, + { BIT(3), BIT(3), "PRBS9 pattern testing" }, + { BIT(2), BIT(2), "PRBS31 pattern testing" }, + { BIT(1), BIT(1), "High BER" }, + { BIT(0), BIT(0), "Block lock" }, + { }, +}; + +// 3.33: baseR and MultiGbaseT PCS status 2 register +static const struct bit_decoder mii_pcs_basert_stat2[] = { + { BIT(15), BIT(15), "Block lock (latched)" }, + { BIT(14), BIT(14), "High BER (latched)" }, + { }, +}; + +static const struct multi_bit_decoder mii_pcs_basert_stat[] = { + { MMD_PCS_BASERT_STAT1, mii_pcs_basert_stat1 }, + { MMD_PCS_BASERT_STAT2, mii_pcs_basert_stat2 }, + { }, +}; + + +/* PHY XGXS / DTE XGXS */ +// 4.0.5:2: PHY XGXS Control register 1: Speed selection +// 5.0.5:2: DTE XGXS Control register 1: Speed selection +static const char *xs_speed[16] = { + [0x0] = "10Gb/s", +}; + +// 4.1: PHYXS status 1 register +static const struct bit_decoder mii_c45_phyxs_stat[] = { + { BIT(11), BIT(11), "Tx LPI received" }, + { BIT(10), BIT(10), "Rx LPI received" }, + { BIT(9), BIT(9), "Tx LPI indication" }, + { BIT(8), BIT(8), "Rx LPI indication" }, + { BIT(6), BIT(6), "Clock stop capable" }, + { BIT(1), BIT(1), "Supports low-power mode" }, + { }, +}; + +// 4.4: PHY XGXS speed ability register +// 5.4: DTE XGXS speed ability register +static const struct bit_decoder mii_c45_spd_cap_xs[] = { + { BIT(0), BIT(0), "10G" }, + { }, +}; + +// 4.20: EEE capability +// 5.20: EEE capability +static const struct bit_decoder mii_c45_eee_ctrl_capab_xs[] = { + { BIT(4), BIT(4), "EEE" }, + { BIT(0), BIT(0), "XAUI stop capable" }, + { }, +}; + +// 4.24: PHY XGXS lane status register +static const struct bit_decoder mii_phyxs_lane_status[] = { + { BIT(12), BIT(12), "lanes synchronized and aligned" }, + { BIT(11), BIT(11), "pattern testing supported" }, + { BIT(10), BIT(10), "loopback support" }, + { BIT(3), BIT(3), "lane 3 synchronized" }, + { BIT(2), BIT(2), "lane 2 synchronized" }, + { BIT(1), BIT(1), "lane 2 synchronized" }, + { BIT(0), BIT(0), "lane 2 synchronized" }, + { }, +}; + +/* AN */ +// 7.1: AN status +static const struct bit_decoder mii_c45_an_stat[] = { + { BIT(3), 0, "Unable to perform Auto-negotiation.\n" }, + { BIT(3) | BIT(5), BIT(3), "Able to perform Auto-negotiation, negotiation not complete.\n" }, + { BIT(3) | BIT(5), BIT(3) | BIT(5), "Able to perform Auto-negotiation, negotiation complete.\n" }, + { BIT(0), BIT(0), "LP Auto-negotiation" }, + { BIT(4), BIT(4), "Remote fault" }, + { BIT(6), BIT(6), "Page Rcvd" }, + { BIT(7), BIT(7), "Ext Next Pg" }, + { BIT(9), BIT(9), "Parallel detection fault" }, + { }, +}; + +// 7.32: 10GbaseT AN control register (tech bits) +static const struct bit_decoder mii_c45_an_multi_gbt_tech[] = { + /* 0..2 below */ + { BIT(3), BIT(3), "40GbaseT/fast" }, // 802.3bq + { BIT(11), BIT(11), "40GbaseT" }, // 802.3bq + { BIT(9), BIT(9), "25GbaseT/fast" }, // 802.3bq + { BIT(10), BIT(10), "25GbaseT" }, // 802.3bq + { BIT(12), BIT(12), "10GbaseT" }, + { BIT(6), BIT(6), "5GbaseT/fast" }, // 802.3bz + { BIT(8), BIT(8), "5GbaseT" }, // 802.3bz + { BIT(5), BIT(5), "2.5GbaseT/fast" }, // 802.3bz + { BIT(7), BIT(7), "2.5GbaseT" }, // 802.3bz + /* 13..15 below */ + { }, +}; + +// 7.32: 10GbaseT AN control register (other bits) +static const struct bit_decoder mii_c45_an_multi_gbt_other[] = { + { 0, 0, "\n" }, + { BIT(15) | BIT(14), BIT(15), "I'm configured to be the slave phy.\n" }, + { BIT(15) | BIT(14), BIT(15) | BIT(14), "I'm configured to be the master phy.\n" }, + { BIT(13), BIT(13), "I'm part of a multi-port device.\n" }, + { BIT(13), 0, "I'm part of a single-port device.\n" }, + { BIT(2), BIT(2), "10GbaseT LD frame reset" }, + { BIT(1), BIT(1), "10GbaseT Fast retrain" }, + { BIT(0), BIT(0), "10GbaseT LD loop timing" }, + { }, +}; + +static const struct multi_bit_decoder mii_c45_an_advert[] = { + { MMD_AN_MULTI_GBT_CTRL, mii_c45_an_multi_gbt_tech }, + { MMD_AN_BASE_ADV0, c22_base_advert }, + { MMD_AN_MULTI_GBT_CTRL, mii_c45_an_multi_gbt_other }, + { }, +}; + +// 7.33: 10GbaseT AN status register +static const struct bit_decoder mii_an_multi_gbt_stat[] = { + { BIT(15), BIT(15), "Master/slave config fault" }, + { BIT(14), BIT(14), "Local PHY master" }, + { BIT(14), 0 << 14, "Local PHY slave" }, + { BIT(13), 0 << 13, "Local RX not ok" }, + { BIT(12), 0 << 13, "Remote RX not ok" }, + { BIT(10), BIT(10), "LP loop timing" }, + { BIT(8), BIT(8), "LP 40GbaseT" }, // 802.3bq + { BIT(2), BIT(2), "LP 25GbaseT/fast" }, // 802.3bq + { BIT(7), BIT(7), "LP 25GbaseT" }, // 802.3bq + { BIT(9), BIT(9), "LP 10GbaseT PMA retrain req" }, // 802.3bq + { BIT(11), BIT(11), "LP 10GbaseT" }, + { BIT(4), BIT(4), "LP 5GbaseT/fast" }, // 802.3bz + { BIT(6), BIT(6), "LP 5GbaseT" }, // 802.3bz + { BIT(3), BIT(3), "LP 2.5GbaseT/fast" }, // 802.3bz + { BIT(5), BIT(5), "LP 2.5GbaseT" }, // 802.3bz + { BIT(1), BIT(1), "LP Fast retrain" }, + { } +}; + +// 7.48: Backplane Ethernet, baseR copper status +#define BASE_R_PORT_TYPE_MASK 0x0f6e +static const struct bit_decoder mii_an_baser_status[] = { + { BASE_R_PORT_TYPE_MASK, BIT(1), "1000baseKX" }, + { BASE_R_PORT_TYPE_MASK, BIT(2), "10GbaseKX4 or CX4" }, + { BASE_R_PORT_TYPE_MASK, BIT(3), "10GbaseKR" }, + { BASE_R_PORT_TYPE_MASK, BIT(5), "40GbaseKR4" }, + { BASE_R_PORT_TYPE_MASK, BIT(6), "40GbaseCR4" }, + { BASE_R_PORT_TYPE_MASK, BIT(8), "100GbaseCR10" }, + { BASE_R_PORT_TYPE_MASK, BIT(9), "100GbaseKP4" }, + { BASE_R_PORT_TYPE_MASK, BIT(10), "100GbaseKR4" }, + { BASE_R_PORT_TYPE_MASK, BIT(11), "100GbaseCR4" }, + { BIT(4), BIT(4), "FEC" }, + { } +}; + +// 7.60: EEE advertisement +// 7.61: EEE link partner ability +static const struct bit_decoder mii_an_eee_advertisement[] = { + { BIT(13), BIT(13), "100GBASE-CR4" }, + { BIT(12), BIT(12), "100GBASE-KR4" }, + { BIT(11), BIT(11), "100GBASE-KP4" }, + { BIT(10), BIT(10), "100GBASE-CR10" }, + { BIT(8), BIT(8), "40GBASE-CR4" }, + { BIT(7), BIT(7), "40GBASE-KR4" }, + { BIT(6), BIT(6), "10GBASE-KR" }, + { BIT(5), BIT(5), "10GBASE-KX4" }, + { BIT(4), BIT(4), "1000BASE-KX" }, + { BIT(3), BIT(3), "10GBASE-T" }, + { BIT(2), BIT(2), "1000BASE-T" }, + { BIT(1), BIT(1), "100BASE-TX" }, + { } +}; + + +static const char *mii_c45_speed(int devad) +{ + const char **speed_table = NULL; + unsigned int speed; + + switch (devad) { + case MDIO_MMD_PMAPMD: + speed_table = pma_speed; + break; + case MDIO_MMD_PCS: + speed_table = pcs_speed; + break; + case MDIO_MMD_PHY_XS: + case MDIO_MMD_DTE_XS: + speed_table = xs_speed; + break; + } + switch (mii_val[MMD_CTRL1] & 0x2040) { + case 0x0000: + return "10Mb/s"; + case 0x0040: + return "100Mb/s"; + case 0x2000: + return "1Gb/s"; + case 0x2040: + speed = (mii_val[MMD_CTRL1] & 0x003c) >> 2; + if (speed_table && speed_table[speed]) + return speed_table[speed]; + default: + return "Reserved"; + } +} + +static void show_mii_c45_status2(const struct phy *phy, + u16 stat2, const struct bit_decoder *decoder) +{ + u16 new_stat2 = mdio_read_phy(phy, MMD_STAT2); + + printf(" Status 2 register %4.4x ... %4.4x\n", stat2, new_stat2); + + if (decoder) + show_mii_bits(stat2, decoder, " Abilities: ", ", "); + + if (new_stat2 & BIT(11)) + printf(" *Transmit fault reported*.\n"); + else if (stat2 & BIT(11)) + printf(" Transmit fault previously reported, but now cleared.\n"); + if (new_stat2 & BIT(10)) + printf(" *Receive fault reported*.\n"); + else if (stat2 & BIT(10)) + printf(" Receive fault previously reported, but now cleared.\n"); +} + +static void show_mii_c45_pmapmd_10gbt_status(const struct phy *phy) +{ + static const char *mdi[4] = { "AB and CD crossed", "Reserved", "Reserved", "no crossover" }; + u16 rval[18]; + int val, i; + + for (i = 0; i < sizeof(rval) / 2; i++) { + val = mdio_read_phy(phy, MMD_PMA_10GBT_SWAPPOL + i); + rval[i] = val < 0 ? 0 : val; + } + + printf(" Pair swap %4.4x: MDI %s, A %s, B %s, C %s, D %s.\n", + rval[0], + mdi[rval[0] & 3], + rval[0] & BIT(8) ? "reversed" : "normal", + rval[0] & BIT(9) ? "reversed" : "normal", + rval[0] & BIT(10) ? "reversed" : "normal", + rval[0] & BIT(11) ? "reversed" : "normal"); + if (rval[1]) + printf(" TX backoff %4.4x: LP TX backoff %udB TX backoff %udB%s.\n", + rval[1], + 2 * ((rval[1] & 0xe000) >> 13), + 2 * ((rval[1] & 0x1c00) >> 10), + rval[1] & 1 ? " short reach" : ""); + +#define SNR(x) (((x) - 0x8000) / 10.f) + printf(" Operating SNR: A: %5.1fdB B: %5.1fdB C: %5.1fdB D: %5.1fdB.\n", + SNR(rval[3]), SNR(rval[4]), SNR(rval[5]), SNR(rval[6])); + printf(" Minimum SNR : A: %5.1fdB B: %5.1fdB C: %5.1fdB D: %5.1fdB.\n", + SNR(rval[7]), SNR(rval[8]), SNR(rval[9]), SNR(rval[10])); + if (rval[11] || rval[12] || rval[13] || rval[14]) + printf(" RX power : A: %5.1fdBm B: %5.1fdBm C: %5.1fdBm D: %5.1fdBm.\n", + SNR(rval[11]), SNR(rval[12]), SNR(rval[13]), SNR(rval[14])); +#define SKEW(x) (((int)((x) & 0x7f) - 64) * 1.25f) + printf(" Skew A>B %6.2fns A>C %6.2fns A>D %6.2fns\n", + SKEW(rval[15] >> 8), SKEW(rval[16]), SKEW(rval[16] >> 8)); + if (rval[17]) + printf(" Fast retrain %4.4x.\n", + rval[17]); +} + +static const char *c45_mmd_name(unsigned int devad) +{ + const char *name = mmd_name[devad]; + + return name ? name : ""; +} + +static void show_mii_c45_details(const struct phy *phy) +{ + const struct bit_decoder *decoder; + const char *type; + const char *col_end = "\033[m"; + const char *col_red = "\033[31m"; + const char *col_grn = "\033[32m"; + int new_stat, val; + + if (mii_val[MMD_CTRL1] == 0xffff) { + printf(" No MII transceiver present!.\n"); + return; + } + + if (phy->devad == MDIO_MMD_VENDOR1 || + phy->devad == MDIO_MMD_VENDOR2) + return; + + if ((mii_val[MMD_STAT2] & 0xc000) != 0x8000 && + (phy->phy_id & 0xc000) != 0xc000 && + phy->devad != MDIO_MMD_AN) { + printf(" No device responding at this address!\n"); + return; + } + + printf(" Control 1 register %4.4x: ", mii_val[MMD_CTRL1]); + switch (phy->devad) { + case MDIO_MMD_PMAPMD: + if (c45_an_ctrl & BIT(12)) { + printf("Speed determined by auto-negotiation.\n"); + break; + } + /*FALLTHROUGH*/ + default: + printf("Speed %s.\n", mii_c45_speed(phy->devad)); + break; + case MDIO_MMD_AN: + printf("Auto-negotiation %sabled.\n", + mii_val[MMD_CTRL1] & BIT(12) ? "en" : "dis"); + break; + } + + new_stat = mdio_read_phy(phy, MMD_STAT1); + printf(" Status register %4.4x ... %4.4x.\n", + mii_val[MMD_STAT1], new_stat); + switch (phy->devad) { + case MDIO_MMD_PMAPMD: /* has fault */ + case MDIO_MMD_PCS: /* has fault */ + case MDIO_MMD_DTE_XS: /* has fault */ + type = "Receive link"; + break; + case MDIO_MMD_PHY_XS: /* has fault */ + type = "Transmit link"; + break; + case MDIO_MMD_WIS: /* no fault */ + case MDIO_MMD_AN: /* no fault */ + default: + type = NULL; + break; + } + if (!type) { + printf(" Link status: %s%sestablished%s.\n", + /* transmit on #4 */ + new_stat & BIT(2) ? col_grn : col_red, + mii_val[MMD_STAT1] & BIT(2) ? "" : + new_stat & BIT(2) ? "previously broken, but now re" : + "not ", col_end); + } else { + printf(" %s status: %s%sestablished%s.%s\n", type, + /* transmit on #4 */ + new_stat & BIT(2) ? col_grn : col_red, + mii_val[MMD_STAT1] & BIT(2) ? "" : + new_stat & BIT(2) ? "previously broken, but now re" : + "not ", col_end, + new_stat & BIT(7) ? "\n *Fault condition detected*" : + mii_val[MMD_STAT1] & BIT(7) ? "\n Fault condition previously reported, but now cleared" : ""); + } + switch (phy->devad) { + case MDIO_MMD_PCS: + decoder = mii_c45_pcs_stat; + break; + case MDIO_MMD_PHY_XS: + decoder = mii_c45_phyxs_stat; + break; + case MDIO_MMD_AN: + decoder = mii_c45_an_stat; + break; + default: + decoder = NULL; + break; + } + if (decoder) + show_mii_bits(new_stat, decoder, " ", ", "); + + switch (phy->devad) { + case MDIO_MMD_PMAPMD: + decoder = mii_c45_spd_cap_pma; + type = mii_c45_type_pma[mii_val[MMD_CTRL2] & 0x007f]; + if (c45_an_ctrl & BIT(12)) + type = "determined by auto-negotiation"; + break; + case MDIO_MMD_PCS: + decoder = mii_c45_spd_cap_pcs; + type = mii_c45_type_pcs[mii_val[MMD_CTRL2] & 0x000f]; + break; + case MDIO_MMD_PHY_XS: + case MDIO_MMD_DTE_XS: + decoder = mii_c45_spd_cap_xs; + type = NULL; + break; + default: + decoder = NULL; + type = NULL; + break; + } + if (decoder) + show_mii_reg("Speed capability", mii_val[MMD_SPEED], + decoder, ": ", ", "); + + if (phy->devad == MDIO_MMD_PMAPMD || phy->devad == MDIO_MMD_PCS) { + if (!type) + type = "unknown"; + printf(" Control 2 register %4.4x: Type %s.\n", + mii_val[MMD_CTRL2], type); + } + + switch (phy->devad) { + case MDIO_MMD_PMAPMD: + show_mii_c45_status2(phy, mii_val[MMD_STAT2], + mii_c45_stat_2_pma); + + if (mii_val[MMD_PMA_EXT_ABL]) + show_mii_reg("Extended Ability register", + mii_val[MMD_PMA_EXT_ABL], + mii_c45_ext_ability_pma, + "\n Abilities: ", ", "); + + // Don't have the information for this yet... + if (mii_val[MMD_PMA_25G_EXT_ABL]) + show_mii_reg("25G Extended Ability register", + mii_val[MMD_PMA_25G_EXT_ABL], + mii_c45_25g_ext_abl_pma, + "\n Abilities: ", ", "); + + if (mii_val[MMD_PMA_EXT_ABL] & BIT(10)) + show_mii_reg("40G/100G Extended Ability register", + mii_val[MMD_PMA_40G100G_EXT_ABL], + mii_c45_40g_ext_abl_pma, + "\n Abilities: ", ", "); + + // This should be predicated on a bit in MMD_PMA_EXT_ABL + // but 88x3310 doesn't do this! + if (mii_val[MMD_PMA_NBASE_EXT_ABL]) + show_mii_reg("2.5G/5G Extended Ability register", + mii_val[MMD_PMA_NBASE_EXT_ABL], + mii_c45_nbase_ext_abl_pma, + "\n Abilities: ", ", "); + + val = mdio_read_phy(phy, MMD_PMA_10GBT_STATUS); + if (val & 1) + show_mii_c45_pmapmd_10gbt_status(phy); + + if (mii_val[MMD_PMA_EEE_CAPABILITY]) + show_mii_reg("EEE capabilities", + mii_val[MMD_PMA_EEE_CAPABILITY], + mii_c45_eee_capability_pma, + "\n ", ", "); + break; + + case MDIO_MMD_PCS: + show_mii_c45_status2(phy, mii_val[MMD_STAT2], + mii_c45_stat_2_pcs); + printf(" baseR or 10GbaseT status %4.4x %4.4x", + mii_val[MMD_PCS_BASERT_STAT1], + mii_val[MMD_PCS_BASERT_STAT2]); + show_mii_multibits(mii_val, mii_pcs_basert_stat, + "\n ", "\n "); + if (mii_val[MMD_PCS_EEE_CTRL_CAPAB]) { + show_mii_reg("EEE control and capabilities", + mii_val[MMD_PCS_EEE_CTRL_CAPAB], + mii_c45_eee_ctrl_capab_pcs, + "\n ", ", "); + printf(" EEE wake error counter: %4.4x\n", + mii_val[MMD_PCS_EEE_WAKE_ERR]); + } + break; + + case MDIO_MMD_PHY_XS: + if (mii_val[MMD_PHY_XS_EEE_CAPAB]) { + show_mii_reg("EEE capabilities", + mii_val[MMD_PHY_XS_EEE_CAPAB], + mii_c45_eee_ctrl_capab_xs, + "\n ", ", "); + printf(" EEE wake error counter: %4.4x\n", + mii_val[MMD_PHY_XS_EEE_WAKE_ERR]); + } + val = mii_val[MMD_PHY_XS_LANE_STATUS]; + show_mii_reg("Lane Status", val, mii_phyxs_lane_status, + ":\n ", "\n "); + break; + + case MDIO_MMD_DTE_XS: + if (mii_val[MMD_DTE_XS_EEE_CAPAB]) { + show_mii_reg("EEE capabilities", + mii_val[MMD_DTE_XS_EEE_CAPAB], + mii_c45_eee_ctrl_capab_xs, + "\n ", ", "); + printf(" EEE wake error counter: %4.4x\n", + mii_val[MMD_DTE_XS_EEE_WAKE_ERR]); + } + break; + + case MDIO_MMD_AN: + val = mdio_read_phy(phy, MMD_AN_BASER_STAT); + if (val < 0) + val = 0; + + if (val & 1) { + /* bits defined as per 73.6. */ + show_c73_linkword("I'm advertising", + mii_val + MMD_AN_BASE_ADV0); + if (mii_val[MMD_AN_BASE_LPA2] | + mii_val[MMD_AN_BASE_LPA1] | + mii_val[MMD_AN_BASE_LPA0]) + show_c73_linkword("Link partner advertisment is", + mii_val + MMD_AN_BASE_LPA0); + + show_mii_reg("baseR Status", val, mii_an_baser_status, + ": ", " "); + } else { + /* bits defined as per 28.2.1.2. */ + printf(" I'm advertising %4.4x %4.4x.\n", + mii_val[MMD_AN_BASE_ADV0], + mii_val[MMD_AN_MULTI_GBT_CTRL]); + show_mii_multibits(mii_val, mii_c45_an_advert, + " ", " "); + printf(" Advertising %sadditional info pages.\n", + mii_val[MMD_AN_BASE_ADV0] & BIT(15) ? "" : "no "); + show_protocol(mii_val[MMD_AN_BASE_ADV0] & 31); + show_lpar(mii_val[MMD_AN_BASE_LPA0]); + } + + if (mii_val[MMD_AN_MULTI_GBT_CTRL]) + show_mii_reg("10GbaseT status", + mii_val[MMD_AN_MULTI_GBT_STAT], + mii_an_multi_gbt_stat, "\n ", ", "); + + if (mii_val[MMD_CTRL1] & BIT(13)) { + show_xnp_advert(mii_val[MMD_AN_XNP_ADV0]); + show_xnp_lpar(mii_val[MMD_AN_XNP_LPA0]); + } + + if (mii_val[MMD_AN_EEE_ADV]) { + show_mii_reg("EEE advertisement", + mii_val[MMD_AN_EEE_ADV], + mii_an_eee_advertisement, "\n ", ", "); + } + if (mii_val[MMD_AN_EEE_LPA]) { + show_mii_reg("EEE link partner advertisement", + mii_val[MMD_AN_EEE_LPA], + mii_an_eee_advertisement, "\n ", ", "); + } + break; + } +} + + +static void show_mii_c22_details(struct phy *phy, const struct c22_decoder *dec) +{ + const char *col_end = "\033[m"; + const char *col_red = "\033[31m"; + const char *col_grn = "\033[32m"; + int i; + u16 bmcr, bmsr, new_bmsr; + if (mii_val[0] == 0xffff) { printf(" No MII transceiver present!.\n"); return; } bmcr = mii_val[0]; bmsr = mii_val[1]; - printf(" Basic mode control register 0x%4.4x:", bmcr); - if (bmcr & 0x1000) + printf(" Basic mode control register %4.4x:", bmcr); + if (bmcr & BIT(12)) printf(" Auto-negotiation enabled.\n"); else printf(" Auto-negotiation disabled!\n" " Speed fixed at 10%s mbps, %s-duplex.\n", - bmcr & 0x2000 ? "0" : "", - bmcr & 0x0100 ? "full":"half"); - if (bmcr & 0x8000) + bmcr & BIT(6) ? "00" : bmcr & BIT(13) ? "0" : "", + bmcr & BIT(8) ? "full":"half"); + if (bmcr & BIT(15)) printf(" Transceiver currently being reset!\n"); - if (bmcr & 0x4000) + if (bmcr & BIT(14)) printf(" Transceiver in loopback mode!\n"); - if (bmcr & 0x0800) + if (bmcr & BIT(11)) printf(" Transceiver powered down!\n"); - if (bmcr & 0x0400) + if (bmcr & BIT(10)) printf(" Transceiver isolated from the MII!\n"); - if (bmcr & 0x0200) + if (bmcr & BIT(9)) printf(" Restarted auto-negotiation in progress!\n"); - if (bmcr & 0x0080) + if (bmcr & BIT(7)) printf(" Internal Collision-Test enabled!\n"); - new_bmsr = mdio_read(ioaddr, phy_id, 1); - printf(" Basic mode status register 0x%4.4x ... %4.4x.\n" - " Link status: %sestablished.\n" - " Capable of ", - bmsr, new_bmsr, - bmsr & 0x0004 ? "" : - (new_bmsr & 0x0004) ? "previously broken, but now re" : "not "); - if (bmsr & 0xF800) { + new_bmsr = mdio_read_phy(phy, 1); + printf(" Basic mode status register %4.4x ... %4.4x.\n", + bmsr, new_bmsr); + if (bmsr & BIT(8)) + printf(" With extended status register %4.4x.\n", + mii_val[15]); + printf(" Link status: %s%sestablished%s.\n" + " Capable of", + new_bmsr & BIT(2) ? col_grn : col_red, + bmsr & BIT(2) ? "" : + (new_bmsr & BIT(2)) ? "previously broken, but now re" : "not ", + col_end); + if (bmsr & 0xF800 || (bmsr & BIT(8) && mii_val[15] & 0xf000)) { for (i = 15; i >= 11; i--) if (bmsr & (1<"); + printf(" "); printf(".\n" " %s to perform Auto-negotiation, negotiation %scomplete.\n", - bmsr & 0x0008 ? "Able" : "Unable", - bmsr & 0x0020 ? "" : "not "); + bmsr & BIT(3) ? "Able" : "Unable", + bmsr & BIT(5) ? "" : "not "); - if (bmsr & 0x0010) + if (bmsr & BIT(4)) printf(" Remote fault detected!\n"); - if (bmsr & 0x0002) + if (bmsr & BIT(1)) printf(" *** Link Jabber! ***\n"); - if (mii_val[2] ^ mii_val[3]) { /* Eliminate 0x0000 and 0xffff IDs. */ - unsigned char oui_0 = mii_val[2] >> 10; - unsigned char oui_1 = mii_val[2] >> 2; - unsigned char oui_2 = (mii_val[2] << 6) | (mii_val[3] >> 10); - - printf(" Vendor ID is %2.2x:%2.2x:%2.2x:--:--:--, model %d rev. %d.\n", - oui_0, oui_1, oui_2, - ((mii_val[3] >> 4) & 0x3f), mii_val[3] & 0x0f); - for ( i = 0; oui_map[i].vendor; i++) - /* We match either the Phy ID or the IEEE OUI. */ - if ((oui_map[i].phy_id0 == mii_val[2] && - oui_map[i].phy_id1 == mii_val[3]) || - (oui_map[i].ieee_oui[0] == oui_0 && - oui_map[i].ieee_oui[1] == oui_1 && - oui_map[i].ieee_oui[2] == oui_2)) { - printf(" Vendor/Part: %s.\n", oui_map[i].vendor); - vendor = i; - break; - } - if (oui_map[i].vendor == NULL) - printf(" No specific information is known about this transceiver" - " type.\n"); - } else + dec->show_advert(phy, mii_val); + dec->show_lpar(phy, mii_val); +} + +static int show_c22_subdev(struct phy *phy, int subdev, unsigned int off, + const struct c22_decoder *dec) +{ + int vendor = 0; + + phy->offset = off; + + if (phy->phy_id & 0x8000) + printf("\n MII PHY #%d:%d %s", + (phy->phy_id >> 5) & 31, phy->devad, + c45_mmd_name(phy->devad)); + else + printf("\n MII PHY #%d", phy->phy_id); + + if (subdev) + printf(" Subdevice #%d", subdev); + + printf(" transceiver registers:"); + read_mii_regs(phy->ioaddr, phy->phy_id | phy->devad, off, 32); + show_mii_regs(32); + printf(".\n"); + if (mii_val[2] ^ mii_val[3]) { /* Eliminate 0x0000 and 0xffff IDs. */ + vendor = lookup_vendor(mii_val[2], mii_val[3]); + } else { printf(" This transceiver has no vendor identification.\n"); + } + show_mii_c22_details(phy, dec); + + return vendor; +} + +static int show_c45_subdev(struct phy *phy, int subdev, unsigned int off) +{ + const char *name = c45_mmd_name(phy->devad); + int vendor = 0; - { - int nway_advert = mii_val[4]; - int lkpar = mii_val[5]; - printf(" I'm advertising %4.4x:", nway_advert); - for (i = 10; i >= 5; i--) - if (nway_advert & (1<= 5; i--) - if (lkpar & (1<offset = off; + if (subdev) + printf("\n MII PHY #%d:%d %s Subdevice #%d transceiver registers:", + (phy->phy_id >> 5) & 31, phy->devad, name, subdev); + else + printf("\n MII PHY #%d:%d %s transceiver registers:", + (phy->phy_id >> 5) & 31, phy->devad, name); + read_mii_regs(phy->ioaddr, phy->phy_id | phy->devad, off, 64); + show_mii_regs(64); + printf(".\n"); + + if (phy->devad != MDIO_MMD_VENDOR1 && phy->devad != MDIO_MMD_VENDOR2) { + if (mii_val[MMD_DEVID1] ^ mii_val[MMD_DEVID2]) { + vendor = lookup_vendor(mii_val[MMD_DEVID1], + mii_val[MMD_DEVID2]); + } else { + printf(" This transceiver has no vendor identification.\n"); + } + } + + show_mii_c45_details(phy); + + return vendor; +} + + +void show_mii_details(long ioaddr, int phy_id) +{ + DEFINE_C22_PHY(phy, ioaddr, phy_id); + int vendor, i; + + /* This may not be omitted from the output. */ + printf("%s", version_msg); + if ((phy_id & 0x801f) == 0x8000) { + int val; + u32 v, mmds = 0; + for (i = 1; i < 32; i++) { + val = mdio_read(ioaddr, phy_id | i, MMD_DEVPKG1); + if (val < 0) + continue; + v = val; + val = mdio_read(ioaddr, phy_id | i, MMD_DEVPKG2); + if (val < 0) + continue; + v |= val << 16; + if (v == 0xffffffff) + continue; + mmds = v; + break; + } + + if (mmds & BIT(7)) { + val = mdio_read(ioaddr, phy_id | MDIO_MMD_AN, MMD_CTRL1); + if (val >= 0) + c45_an_ctrl = val; + } + + for (i = 1; i < 32; i++) { + if (mmds & (1 << i)) { + phy.devad = i; + vendor = show_c45_subdev(&phy, 0, 0); + if (oui_map[vendor].func) + oui_map[vendor].func(ioaddr, phy_id | i); + } + } + } else if (phy_id & 0x8000) { + phy.devad = phy.phy_id & 31; + phy.phy_id &= ~31; + + vendor = show_c45_subdev(&phy, 0, 0); + if (oui_map[vendor].func) + oui_map[vendor].func(ioaddr, phy_id); + } else { + vendor = show_c22_subdev(&phy, 0, 0, &c28_linkword); + if (oui_map[vendor].func) + oui_map[vendor].func(ioaddr, phy_id); + } +} + +static void dump_mii_range(long ioaddr, int phy_id, unsigned int start, + unsigned int len) +{ + unsigned int i, j; + int gap = 0; + + for (i = start; i < start + len; i += 8) { + u16 vals[8], v; + + for (j = 0, v = 0; j < 8; j++) { + int val = mdio_read(ioaddr, phy_id, i + j); + if (val < 0) + val = 0; + vals[j] = val; + v |= val; + } + + if (v) { + if (gap) { + printf("\n ..."); + gap = 0; + } + printf("\n %4.4x:", i); + for (j = 0; j < 8; j++) + printf(" %4.4x", vals[j]); + } else { + gap = 1; + } + } + printf("\n"); +} + +void dump_mii(long ioaddr, int phy_id) +{ + unsigned int max; + + if (phy_id & 0x8000) + max = 65536; + else + max = 32; + + printf(" MII PHY #%d transceiver registers:", phy_id); + dump_mii_range(ioaddr, phy_id, 0, max); } int monitor_mii(long ioaddr, int phy_id) @@ -312,25 +1775,6 @@ int monitor_mii(long ioaddr, int phy_id) /* Emit transceiver-specific info. */ -struct msg_tbl { int bitmask; char *msg; }; - -static void msg_if_set(const int val, const struct msg_tbl msg_tbl[]) -{ - int i; - for (i = 0; msg_tbl[i].bitmask; i++) - if (msg_tbl[i].bitmask & val) - printf(" %s\n", msg_tbl[i].msg); -} - -static void msg_if_set_fmt(const int val, const struct msg_tbl msg_tbl[], - const char *fmt) -{ - int i; - for (i = 0; msg_tbl[i].bitmask; i++) - if (msg_tbl[i].bitmask & val) - printf(fmt, msg_tbl[i].msg); -} - static void qs6612(long ioaddr, int phy_id) { printf(" QS6612 extra registers: Mode %4.4x.\n" @@ -352,13 +1796,13 @@ static void ns83843(long ioaddr, int phy_id) " Receive errors %d\n" " Link beat is currently %sstable\n", mii_val[0x10], - mii_val[10] & 0x0001 ? "Valid" : "Invalid", - mii_val[10] & 0x0002 ? 10 : 100, - mii_val[10] & 0x0004 ? "full" : "half", - mii_val[0x11] & 0x0002 ? "en":"dis", - mii_val[0x10] & 0x0100 ? "interrupt": "none", + mii_val[10] & BIT(0) ? "Valid" : "Invalid", + mii_val[10] & BIT(1) ? 10 : 100, + mii_val[10] & BIT(2) ? "full" : "half", + mii_val[0x11] & BIT(1) ? "en":"dis", + mii_val[0x10] & BIT(8) ? "interrupt": "none", mii_val[0x13], mii_val[0x14], mii_val[0x15], - mii_val[0x16] & 0x0010 ? "UN" : ""); + mii_val[0x16] & BIT(4) ? "UN" : ""); return; } static void smsc83c180(long ioaddr, int phy_id) @@ -371,11 +1815,11 @@ static void smsc83c180(long ioaddr, int phy_id) " Auto-negotiation %scomplete, 1%s0Mbps %s duplex.\n" " Rx symbol errors since last read %d.\n", mii_reg25, - mii_reg25 & 0x2000 ? "normal" : "reversed", + mii_reg25 & BIT(13) ? "normal" : "reversed", (mii_reg25>>8) & 0x1F, - mii_reg25 & 0x0080 ? "did not " : "", - mii_reg25 & 0x0020 ? "0" : "", - mii_reg25 & 0x0040 ? "full" : "half", + mii_reg25 & BIT(7) ? "did not " : "", + mii_reg25 & BIT(5) ? "0" : "", + mii_reg25 & BIT(6) ? "full" : "half", mdio_read(ioaddr, phy_id, 26)); return; } @@ -407,16 +1851,16 @@ static void tdk78q2120(long ioaddr, int phy_id) "%s%s" " Auto-negotiation %s, 1%s0Mbps %s duplex.\n" " Rx link in %s state, PLL %s.\n", - mii_reg16 & 0x0020 ? "OVERRIDDEN to" : "detected as", - mii_reg16 & 0x0010 ? "reversed" : "normal", - mii_reg16 & 0x0002 ? + mii_reg16 & BIT(5) ? "OVERRIDDEN to" : "detected as", + mii_reg16 & BIT(4) ? "reversed" : "normal", + mii_reg16 & BIT(1) ? " 100baseTx Coding and scrambling is disabled!\n":"", - mii_reg16 & 0x0001 ? " Rx_CLK power-save mode is enabled!\n":"", - mii_reg18 & 0x1000 ? "had no common media" : "complete", - mii_reg18 & 0x0400 ? "0" : "", - mii_reg18 & 0x0800 ? "full" : "half", - mii_reg18 & 0x0200 ? "pass" : "fail", - mii_reg18 & 0x0100 ? "slipped since last read" : "locked"); + mii_reg16 & BIT(0) ? " Rx_CLK power-save mode is enabled!\n":"", + mii_reg18 & BIT(12) ? "had no common media" : "complete", + mii_reg18 & BIT(10) ? "0" : "", + mii_reg18 & BIT(11) ? "full" : "half", + mii_reg18 & BIT(9) ? "pass" : "fail", + mii_reg18 & BIT(8) ? "slipped since last read" : "locked"); msg_if_set(mii_reg16, tdk_reg16); if (mii_reg17 & 0x00ff) { @@ -453,8 +1897,8 @@ static void enablesemi(long ioaddr, int phy_id) printf(" Isolated %d times, %d false carrier events, %d Rx errors.\n", mii_val[18], mii_val[19], mii_val[21]); printf(" Cable polarity is %s, 100Mb PLL is %slocked.\n", - mii_val[28]&0x8000 ? "reversed" : "normal", - mii_val[27]&0x2000 ? "" : "un"); + mii_val[28]&BIT(15) ? "reversed" : "normal", + mii_val[27]&BIT(13) ? "" : "un"); } /* The amd79c901 contains both PNA and 10/100 management registers. http://www.amd.com/products/npd/techdocs/22304.pdf @@ -570,9 +2014,9 @@ static void via_tahoe(long ioaddr, int phy_id) mii_reg16, mii_reg17, mii_reg18); msg_if_set_fmt(mii_reg17, via_reg17, " %s\n"); printf(" Link %s 10%s Mbps %s duplex\n", - mii_reg18 & 0x2000 ? "up" : "down", - mii_reg18 & 0x0400 ? "0" : "", - mii_reg18 & 0x0800 ? "full" : "half"); + mii_reg18 & BIT(13) ? "up" : "down", + mii_reg18 & BIT(10) ? "0" : "", + mii_reg18 & BIT(11) ? "full" : "half"); } /* Information from @@ -596,8 +2040,8 @@ static void via_vt6105(long ioaddr, int phy_id) { printf(" VIA vt6105 PHY status:\n" " Duplex %s speed %s\n", - mii_val[20] & 0x0001 ? "full" : "half", - mii_val[20] & 0x0002 ? "100" : "10"); + mii_val[20] & BIT(0) ? "full" : "half", + mii_val[20] & BIT(1) ? "100" : "10"); } /* Information from @@ -619,6 +2063,233 @@ static void intel(long ioaddr, int phy_id) mii_val[24], mii_val[25]); } +static void mv88x3310_xs(long ioaddr, int phy_id) +{ + DEFINE_C45_PHY(phy, ioaddr, phy_id & ~31, phy_id & 31); + + if (phy.devad != 4) + return; + + show_c45_subdev(&phy, 2, 0x1000); + show_c22_subdev(&phy, 3, 0x2000, &cisco_sgmii_linkword); +} + +static void mv88x3310(long ioaddr, int phy_id) +{ + DEFINE_C45_PHY(phy, ioaddr, phy_id & ~31, phy_id & 31); + + switch (phy.devad) { + case MDIO_MMD_PCS: + show_c45_subdev(&phy, 2, 0x1000); + show_c22_subdev(&phy, 3, 0x2000, &c37_linkword); + break; + + case MDIO_MMD_AN: + show_c45_subdev(&phy, 2, 0x1000); + show_c45_subdev(&phy, 3, 0x1800); + show_c45_subdev(&phy, 4, 0x2000); + + printf("\n Other registers"); + dump_mii_range(ioaddr, phy_id, 0x8000, 32); + dump_mii_range(ioaddr, phy_id, 0x9000, 16); + dump_mii_range(ioaddr, phy_id, 0x9800, 16); + dump_mii_range(ioaddr, phy_id, 0xa000, 64); + break; + } +} + + + +static int marvell_read_paged(const struct phy *phy, unsigned int addr) +{ + u16 val, old_page; + long ioaddr = phy->ioaddr; + int phy_id = phy->phy_id; + + old_page = mdio_read(ioaddr, phy_id, 22); + mdio_write(ioaddr, phy_id, 22, phy->page); + val = mdio_read(ioaddr, phy_id, addr); + mdio_write(ioaddr, phy_id, 22, old_page); + + return val; +} + +static const struct bit_decoder mv88e1111_pss_decoder[] = { + { 0xc800, 0x8800, "1000Mbps" }, + { 0xc800, 0x4800, "100Mbps" }, + { 0xc800, 0x0800, "10Mbps" }, + { 0x2800, 0x2800, "Full" }, + { 0x2800, 0x0800, "Half" }, + { 0x0400, 0x0400, "Link Up" }, + { 0x0440, 0x0440, "MDIX" }, + { 0x0440, 0x0400, "MDI" }, + { 0x0420, 0x0420, "Downshift" }, + { 0x0808, 0x0808, "TX Pause" }, + { 0x0804, 0x0804, "RX Pause" }, + { }, +}; + +static const struct bit_decoder mv88e1111_epss_decoder[] = { + { 0x8000, 0x8000, "Fiber/copper auto-selection disabled" }, + { 0x8000, 0x0000, "Fiber/copper auto-selection enabled" }, + { 0x2000, 0x2000, "Fiber link selected" }, + { 0x2000, 0x0000, "Copper link selected" }, + { 0x1000, 0x1000, "AN bypass enabled" }, + { 0x1000, 0x0000, "AN bypass disabled" }, + { 0x0800, 0x0800, "AN is bypassed" }, + { 0x0200, 0x0200, "Auto-medium register selection enabled" }, + { 0x000f, 0x0000, "SGMII w/ clock w/ SGMII AN to copper" }, + { 0x000f, 0x0003, "RGMII to fiber" }, + { 0x000f, 0x0004, "SGMII w/o clock w/ SGMII AN to copper" }, + { 0x000f, 0x0006, "RGMII to SGMII" }, + { 0x000f, 0x0007, "GMII to fiber" }, + { 0x000f, 0x0008, "1000baseX w/o clock w/ 1000baseX AN to copper" }, + { 0x000f, 0x0009, "RTBI to copper" }, + { 0x000f, 0x000b, "RGMII/Modified MII to copper" }, + { 0x000f, 0x000c, "1000baseX w/o clock w/o 1000baseX AN to copper" }, + { 0x000f, 0x000d, "TBI to copper" }, + { 0x000f, 0x000e, "GMII to SGMII" }, + { 0x000f, 0x000f, "GMII to copper" }, + { }, +}; + +static void mv88e1111(long ioaddr, int phy_id) +{ + DEFINE_C22_PHY(phy, ioaddr, phy_id); + int i; + + show_mii_reg("PHY Specific Status", mii_val[17], + mv88e1111_pss_decoder, "\n ", ", "); + + show_mii_reg("Extended PHY Specific Status", mii_val[27], + mv88e1111_epss_decoder, " ", ".\n "); + + phy.page = 1; + phy.mdio_read = marvell_read_paged; + for (i = 0; i < 32; i++) + mii_val[i] = mdio_read_phy(&phy, i); + printf("\n MII PHY #%d fiber registers:", phy_id); + show_mii_regs(32); + printf(".\n"); + + switch (mii_val[27] & 15) { + case 0: + case 4: /* SGMII to copper */ + show_mii_c22_details(&phy, &cisco_sgmii_linkword); + break; + case 8: + case 12: + /* GMII mode */ + show_mii_c22_details(&phy, &c37_linkword); + break; + default: + break; + } +} + +static const struct bit_decoder mv88e151x_gcr[] = { + { 0x0040, 0x0040, "Auto-media:" }, + { 0x0070, 0x0040, "Link on first media" }, + { 0x0070, 0x0050, "Copper preferred" }, + { 0x0070, 0x0060, "Fiber preferred" }, + { 0x0070, 0x0070, "Reserved" }, + { 0x0007, 0x0000, "RGMII(sys) to Copper" }, + { 0x0007, 0x0001, "SGMII(sys) to Copper" }, + { 0x0007, 0x0002, "RGMII(sys) to 1000baseX" }, + { 0x0007, 0x0003, "SGMII(sys) to 100baseFX" }, + { 0x0007, 0x0004, "SGMII(sys) to SGMII(media)" }, + { 0x0007, 0x0005, "Reserved" }, + { 0x0007, 0x0006, "RGMII(sys) to Auto" }, + { 0x0007, 0x0007, "RGMII(sys) to Auto" }, + { }, +}; + +static const struct bit_decoder marvell_sgmii_adv_decoder[] = { + { 0x8000, 0x0000, "Link Down" }, + { 0x8000, 0x8000, "Link Up, " }, + { 0x8c00, 0x8000, "10M/" }, + { 0x8c00, 0x8400, "100M/" }, + { 0x8c00, 0x8800, "1000M/" }, + { 0x8c00, 0x8c00, "?/" }, + { 0x9000, 0x8000, "Half, " }, + { 0x9000, 0x9000, "Full, " }, + /* These are the Marvell additions */ + { 0x8200, 0x8200, "TxPause, " }, + { 0x8100, 0x8100, "RxPause, " }, + { 0x8080, 0x8000, "Copper" }, + { 0x8080, 0x8080, "Fiber" }, + { }, +}; + +static void marvell_sgmii_advert(struct phy *phy, u16 *regs) +{ + const char *strings[17], **p = strings; + u16 base = regs[4]; + + printf(" SGMII advertisement %4.4x", base); + if (base & 1) { + p = bit_decoder(p, base, marvell_sgmii_adv_decoder); + } else { + *p++ = " Link down"; + *p = NULL; + } + if (base & 0x607e) { + *p++ = ", reserved bits are set"; + *p = NULL; + } + print_strings(strings, ": ", ""); +} + +static const struct c22_decoder marvell_system_sgmii_linkword = { + .show_advert = marvell_sgmii_advert, + .show_lpar = cisco_sgmii_lpar, +}; + +static const struct bit_decoder marvell_fscr2_decoder[] = { + { 0x4000, 0x4000, "1000baseX noise filter" }, + { 0x0200, 0x0200, "100baseFX FEFI enabled" }, + { 0x0040, 0x0040, "AN bypass enabled" }, + { 0x0040, 0x0000, "AN bypass disabled" }, + { 0x0020, 0x0020, "AN was bypassed" }, + { 0x0008, 0x0008, "Fiber TX disabled" }, + { }, +}; + +static void mv88e151x(long ioaddr, int phy_id) +{ + DEFINE_C22_PHY(phy, ioaddr, phy_id); + u16 gcr; + int i; + + phy.page = 18; + gcr = mdio_read_phy(&phy, 20); + show_mii_reg("General Control Register 1", gcr, mv88e151x_gcr, ": ", ", "); + + phy.page = 1; + phy.mdio_read = marvell_read_paged; + for (i = 0; i < 32; i++) + mii_val[i] = mdio_read_phy(&phy, i); + printf("\n MII PHY #%d fiber registers:", phy_id); + show_mii_regs(32); + printf(".\n"); + + switch (mii_val[16] & 3) { + case 0: /* 100baseFX */ + break; + case 1: /* 1000baseX */ + show_mii_c22_details(&phy, &c37_linkword); + break; + case 2: /* SGMII System mode */ + show_mii_c22_details(&phy, &marvell_system_sgmii_linkword); + break; + case 3: /* SGMII Media mode */ + show_mii_c22_details(&phy, &cisco_sgmii_linkword); + break; + } + show_mii_reg("Fiber Specific Control Register 2", mii_val[26], + marvell_fscr2_decoder, ": ", ", "); +} + /* * Local variables: diff --git a/mii-diag.c b/mii-diag.c index f3f3f2f..5426603 100644 --- a/mii-diag.c +++ b/mii-diag.c @@ -403,7 +403,7 @@ int do_one_xcvr(int skfd) return 0; } -int mdio_read(int skfd, int phy_id, int location) +static int __mdio_read(int skfd, int phy_id, int location) { u16 *data = (u16 *)(&ifr.ifr_data); @@ -418,7 +418,7 @@ int mdio_read(int skfd, int phy_id, int location) return data[3]; } -void mdio_write(int skfd, int phy_id, int location, int value) +static void __mdio_write(int skfd, int phy_id, int location, int value) { u16 *data = (u16 *)(&ifr.ifr_data); @@ -432,6 +432,38 @@ void mdio_write(int skfd, int phy_id, int location, int value) } } +#define MII_MMD_CTRL 0x0d +#define MII_MMD_DATA 0x0e +#define MII_MMD_CTRL_NOINCR 0x4000 + +int mdio_read(int skfd, int phy_id, int location) +{ + if ((phy_id & 0xc000) == 0xc000) { + unsigned int devad = phy_id & 31; + int pkgad = (phy_id >> 5) & 31; + __mdio_write(skfd, pkgad, MII_MMD_CTRL, devad); + __mdio_write(skfd, pkgad, MII_MMD_DATA, location); + __mdio_write(skfd, pkgad, MII_MMD_CTRL, devad | MII_MMD_CTRL_NOINCR); + return __mdio_read(skfd, pkgad, MII_MMD_DATA); + } else { + return __mdio_read(skfd, phy_id, location); + } +} + +void mdio_write(int skfd, int phy_id, int location, int value) +{ + if ((phy_id & 0xc000) == 0xc000) { + unsigned int devad = phy_id & 31; + int pkgad = (phy_id >> 5) & 31; + __mdio_write(skfd, pkgad, MII_MMD_CTRL, devad); + __mdio_write(skfd, pkgad, MII_MMD_DATA, location); + __mdio_write(skfd, pkgad, MII_MMD_CTRL, devad | MII_MMD_CTRL_NOINCR); + __mdio_write(skfd, pkgad, MII_MMD_DATA, value); + } else { + __mdio_write(skfd, phy_id, location, value); + } +} + /* Parse the command line argument for advertised capabilities. */ static int parse_advertise(const char *capabilities) { -- cgit