From fcab00d3a8a7c9c249b2eb4352dc5c07c94a39fc Mon Sep 17 00:00:00 2001 From: Jani Paalijarvi Date: Wed, 21 Aug 2024 16:29:52 +0300 Subject: [PATCH] drivers/net/ksz9477: Implement errata fixes From the ksz9477 errata, setup the configuration which is tested to work. - Improve PHY receive performance - Disable EEE for ports 1-5 - Fix supply current values Signed-off-by: Jani Paalijarvi --- drivers/net/ksz9477.c | 294 +++++++++++++++++++++++++++++++++++++- drivers/net/ksz9477_reg.h | 20 +++ 2 files changed, 312 insertions(+), 2 deletions(-) diff --git a/drivers/net/ksz9477.c b/drivers/net/ksz9477.c index d2b3aadda5d..b41a4c71313 100644 --- a/drivers/net/ksz9477.c +++ b/drivers/net/ksz9477.c @@ -34,6 +34,9 @@ * Pre-processor Definitions ****************************************************************************/ +#define ERRATA_MOD1_REGS 7 +#define ERRATA_MOD9_REGS 13 + /**************************************************************************** * Private Data ****************************************************************************/ @@ -58,6 +61,13 @@ static uint8_t g_port_mirror_config[7] = #endif +struct errata +{ + uint8_t dev; + uint16_t reg; + uint16_t value; +}; + /**************************************************************************** * Private Functions ****************************************************************************/ @@ -126,7 +136,6 @@ static int ksz9477_reg_write32(uint16_t reg, uint32_t data) return ksz9477_write(&write_msg); } -#if 0 /* Enable when needed */ static int ksz9477_reg_write16(uint16_t reg, uint16_t data) { int ret; @@ -177,7 +186,6 @@ static int ksz9477_reg_write16(uint16_t reg, uint16_t data) return ksz9477_write(&write_msg); } -#endif static int ksz9477_sgmii_read_indirect(uint32_t address, uint16_t *value, unsigned len) @@ -213,6 +221,260 @@ static int ksz9477_sgmii_write_indirect(uint32_t address, uint16_t *value, return ret; } +static int ksz9477_mmd_read_indirect(ksz9477_port_t port, uint16_t dev, + uint16_t reg, uint16_t *value, + uint16_t len) +{ + int ret; + + /* Set up MMD device address */ + + ret = ksz9477_reg_write16(KSZ9477_PHY_MMD_SETUP(port), + KSZ9477_MMD_OP_MODE_REGISTER | dev); + if (ret != OK) + { + goto out; + } + + /* Select register */ + + ret = ksz9477_reg_write16(KSZ9477_PHY_MMD_DATA(port), reg); + if (ret != OK) + { + goto out; + } + + /* Set MMD operation mode */ + + if (len <= 1) + { + /* no post increment */ + + ret = ksz9477_reg_write16(KSZ9477_PHY_MMD_SETUP(port), + KSZ9477_MMD_OP_MODE_NO_INCREMENT | dev); + if (ret != OK) + { + goto out; + } + } + else + { + /* post increment on reads and writes */ + + ret = ksz9477_reg_write16(KSZ9477_PHY_MMD_SETUP(port), + KSZ9477_MMD_OP_MODE_RW_INCREMENT | dev); + if (ret != OK) + { + goto out; + } + } + + /* Read value */ + + while (len-- && ret == OK) + { + ret = ksz9477_reg_read16(KSZ9477_PHY_MMD_DATA(port), value); + value++; + } + +out: + + return ret; +} + +static int ksz9477_mmd_write_indirect(ksz9477_port_t port, uint8_t dev, + uint16_t reg, const uint16_t *value, + uint16_t len) +{ + int ret; + + /* Set up MMD device address */ + + ret = ksz9477_reg_write16(KSZ9477_PHY_MMD_SETUP(port), + KSZ9477_MMD_OP_MODE_REGISTER | dev); + if (ret != OK) + { + goto out; + } + + /* Select register */ + + ret = ksz9477_reg_write16(KSZ9477_PHY_MMD_DATA(port), reg); + if (ret != OK) + { + goto out; + } + + /* Set MMD operation mode */ + + if (len <= 1) + { + /* no post increment */ + + ret = ksz9477_reg_write16(KSZ9477_PHY_MMD_SETUP(port), + KSZ9477_MMD_OP_MODE_NO_INCREMENT | dev); + if (ret != OK) + { + goto out; + } + } + else + { + /* post increment on reads and writes */ + + ret = ksz9477_reg_write16(KSZ9477_PHY_MMD_SETUP(port), + KSZ9477_MMD_OP_MODE_RW_INCREMENT | dev); + if (ret != OK) + { + goto out; + } + } + + /* Write value */ + + while (len-- && ret == OK) + { + ret = ksz9477_reg_write16(KSZ9477_PHY_MMD_DATA(port), *value); + value++; + } + +out: + + return ret; +} + +static int ksz9477_custom_error_fixes(ksz9477_port_t port) +{ + int ret = OK; + int j; + uint16_t regval16; + const struct errata err1[] = { + {KSZ9477_MMD_DEV_SIGNAL_QUALITY, 0xce, 0x0100}, + {KSZ9477_MMD_DEV_SIGNAL_QUALITY, 0xcc, 0x0ff0}, + {KSZ9477_MMD_DEV_SIGNAL_QUALITY, 0xca, 0x0141}, + {KSZ9477_MMD_DEV_SIGNAL_QUALITY, 0xcb, 0x0fcf}, + {KSZ9477_MMD_DEV_SIGNAL_QUALITY, 0xc8, 0x0010}, + {KSZ9477_MMD_DEV_SIGNAL_QUALITY, 0xd9, 0x0100}, + {KSZ9477_MMD_DEV_SIGNAL_QUALITY, 0xc9, 0x0280}, + {KSZ9477_MMD_DEV_SIGNAL_QUALITY, 0x8f, 0x6032}, + {KSZ9477_MMD_DEV_SIGNAL_QUALITY, 0x9d, 0x248c}, + {KSZ9477_MMD_DEV_SIGNAL_QUALITY, 0x75, 0x0060}, + {KSZ9477_MMD_DEV_SIGNAL_QUALITY, 0xd3, 0x7777}, + }; + + const struct errata err2[] = { + {KSZ9477_MMD_DEV_QUIET_WIRE, 0x0, 0x9400}, + {KSZ9477_MMD_DEV_QUIET_WIRE, 0x4, 0x00e2}, + {KSZ9477_MMD_DEV_QUIET_WIRE, 0x6, 0x3100}, + {KSZ9477_MMD_DEV_QUIET_WIRE, 0x9, 0xe01c}, + }; + + const struct errata err3[] = { + {KSZ9477_MMD_DEV_SIGNAL_QUALITY, 0x79, 0x010a}, + {KSZ9477_MMD_DEV_SIGNAL_QUALITY, 0x7a, 0x00ed}, + {KSZ9477_MMD_DEV_SIGNAL_QUALITY, 0x7b, 0x00d3}, + {KSZ9477_MMD_DEV_SIGNAL_QUALITY, 0x7c, 0x00bc}, + {KSZ9477_MMD_DEV_SIGNAL_QUALITY, 0x7d, 0x00a8}, + {KSZ9477_MMD_DEV_SIGNAL_QUALITY, 0x7e, 0x0096}, + {KSZ9477_MMD_DEV_SIGNAL_QUALITY, 0x7f, 0x0085}, + {KSZ9477_MMD_DEV_SIGNAL_QUALITY, 0x80, 0x0077}, + {KSZ9477_MMD_DEV_SIGNAL_QUALITY, 0x81, 0x006a}, + {KSZ9477_MMD_DEV_SIGNAL_QUALITY, 0x82, 0x005e}, + {KSZ9477_MMD_DEV_SIGNAL_QUALITY, 0x83, 0x0054}, + {KSZ9477_MMD_DEV_SIGNAL_QUALITY, 0x84, 0x004b}, + {KSZ9477_MMD_DEV_SIGNAL_QUALITY, 0x85, 0x0043}, + {KSZ9477_MMD_DEV_SIGNAL_QUALITY, 0x86, 0x003c}, + {KSZ9477_MMD_DEV_SIGNAL_QUALITY, 0x87, 0x0035}, + {KSZ9477_MMD_DEV_SIGNAL_QUALITY, 0x88, 0x002f}, + {KSZ9477_MMD_DEV_SIGNAL_QUALITY, 0x89, 0x002a}, + {KSZ9477_MMD_DEV_SIGNAL_QUALITY, 0x8a, 0x0026}, + }; + + struct errata mod9[] = { + {KSZ9477_MMD_DEV_QUIET_WIRE, 0x13, 0x6eff}, + {KSZ9477_MMD_DEV_QUIET_WIRE, 0x14, 0xe6ff}, + {KSZ9477_MMD_DEV_QUIET_WIRE, 0x15, 0x6eff}, + {KSZ9477_MMD_DEV_QUIET_WIRE, 0x16, 0xe6ff}, + {KSZ9477_MMD_DEV_QUIET_WIRE, 0x17, 0x00ff}, + {KSZ9477_MMD_DEV_QUIET_WIRE, 0x18, 0x43ff}, + {KSZ9477_MMD_DEV_QUIET_WIRE, 0x19, 0xc3ff}, + {KSZ9477_MMD_DEV_QUIET_WIRE, 0x1a, 0x6fff}, + {KSZ9477_MMD_DEV_QUIET_WIRE, 0x1b, 0x07ff}, + {KSZ9477_MMD_DEV_QUIET_WIRE, 0x1c, 0x0fff}, + {KSZ9477_MMD_DEV_QUIET_WIRE, 0x1d, 0xe7ff}, + {KSZ9477_MMD_DEV_QUIET_WIRE, 0x1e, 0xefff}, + }; + + /* First turn off autoneg */ + + ret = ksz9477_reg_write16(KSZ9477_PHY_CONTROL(port), 0x2100); + + if (ret != OK) + { + nerr("PHY Control register write failure, ret %d\n", ret); + return ret ? ret : -EINVAL; + } + + /* Set Remote Loopback register */ + + ret = ksz9477_reg_write16(KSZ9477_PHY_REMOTE_LP(port), 0x00f0); + if (ret != OK) + { + nerr("PHY Remote Loopback register write failure, ret %d\n", ret); + return ret ? ret : -EINVAL; + } + + /* Needed configurations for MMD Signal Quality */ + + for (j = 0; ret == OK && j < 11; j++) + { + ret = ksz9477_mmd_write_indirect(port, err1[j].dev, err1[j].reg, + &(err1[j].value), 1); + } + + /* Needed configurations for MMD Quiet-WIRE */ + + for (j = 0; ret == OK && j < 4; j++) + { + ret = ksz9477_mmd_write_indirect(port, err2[j].dev, err2[j].reg, + &(err2[j].value), 1); + } + + /* More configurations for MMD Signal Quality */ + + for (j = 0x0; ret == OK && j < 18; j++) + { + ret = ksz9477_mmd_write_indirect(port, err3[j].dev, err3[j].reg, + &(err3[j].value), 1); + } + + /* Module 9: Set various registers to get correct supply current values */ + + for (j = 0; ret == OK && j < 12; j++) + { + ret = ksz9477_mmd_write_indirect(port, mod9[j].dev, mod9[j].reg, + &(mod9[j].value), 1); + } + + /* Disable EEE */ + + regval16 = 0x0; + ret = ksz9477_mmd_write_indirect(port, KSZ9477_MMD_DEV_EEE_ADVERTISEMENT, + 0x3c, ®val16, 1); + + /* Turn on autoneg */ + + ret = ksz9477_reg_write16(KSZ9477_PHY_CONTROL(port), 0x1140); + + if (ret != OK) + { + nerr("PHY Control register write failure, ret %d\n", ret); + return ret ? ret : -EINVAL; + } + + return ret; +} + /**************************************************************************** * Public Functions ****************************************************************************/ @@ -377,6 +639,34 @@ int ksz9477_init(ksz9477_port_t master_port) return ret ? ret : -EINVAL; } + /* Check that indirect access to PHY MMD works. + * Default value of MMD EEE ADVERTISEMENT REGISTER is 0x6 + */ + + ret = ksz9477_mmd_read_indirect(KSZ9477_PORT_PHY1, + KSZ9477_MMD_DEV_EEE_ADVERTISEMENT, + 0x3c, ®val16, 1); + if (ret != OK || regval16 != 0x6) + { + nerr("MMD access failure, ret %d\n", ret); + return ret ? ret : -EINVAL; + } + + /* Handle some erratas */ + + for (i = KSZ9477_PORT_PHY1; i <= KSZ9477_PORT_PHY5; i++) + { + ret = ksz9477_custom_error_fixes(i); + } + + if (ret != OK) + { + nerr("Errata handling failure, ret %d\n", ret); + return ret ? ret : -EINVAL; + } + + /* Set up SGMII port */ + if (master_port == KSZ9477_PORT_SGMII) { /* Set the switch's SGMII port into "PHY" mode and enable link */ diff --git a/drivers/net/ksz9477_reg.h b/drivers/net/ksz9477_reg.h index 43a631eb7d1..001a78a0e90 100644 --- a/drivers/net/ksz9477_reg.h +++ b/drivers/net/ksz9477_reg.h @@ -61,6 +61,11 @@ #define KSZ9477_PHY_CONTROL(p) KSZ9477_PORT_REG(p, 0x100) #define KSZ9477_PHY_STATUS(p) KSZ9477_PORT_REG(p, 0x102) +#define KSZ9477_PHY_MMD_SETUP(p) KSZ9477_PORT_REG(p, 0x11A) +#define KSZ9477_PHY_MMD_DATA(p) KSZ9477_PORT_REG(p, 0x11C) + +#define KSZ9477_PHY_REMOTE_LP(p) KSZ9477_PORT_REG(p, 0x122) + /* Note! Unlike in data sheet, the indirect data register reads and * writes must be done with 32-bit accesses and the address is * 0x204 @@ -120,6 +125,21 @@ #define SGMII_AUTONEG_CONTROL_TC_MASTER (1 << 3) #define SGMII_AUTONEG_CONTROL_LINK_STATUS (1 << 4) +/* MMD device addresses */ + +#define KSZ9477_MMD_DEV_SIGNAL_QUALITY 0x1 +#define KSZ9477_MMD_DEV_LED_MODE 0x2 +#define KSZ9477_MMD_DEV_EEE_ADVERTISEMENT 0x7 +#define KSZ9477_MMD_DEV_QUIET_WIRE 0x1C + +/* MMD operation modes */ + +#define KSZ9477_MMD_OP_MODE_SHIFT 14 +#define KSZ9477_MMD_OP_MODE_REGISTER (0 << KSZ9477_MMD_OP_MODE_SHIFT) +#define KSZ9477_MMD_OP_MODE_NO_INCREMENT (1 << KSZ9477_MMD_OP_MODE_SHIFT) +#define KSZ9477_MMD_OP_MODE_RW_INCREMENT (2 << KSZ9477_MMD_OP_MODE_SHIFT) +#define KSZ9477_MMD_OP_MODE_W_INCREMENT (3 << KSZ9477_MMD_OP_MODE_SHIFT) + /* Port Mirroring Control Register */ #define KSZ9477_PORT_MIRROR_CONTROL(p) KSZ9477_PORT_REG(p, 0x800)