From d44762f33fb08c7eb4a0adca0b5f3e33ae8f07d0 Mon Sep 17 00:00:00 2001 From: Jani Paalijarvi Date: Wed, 21 Aug 2024 16:29:52 +0300 Subject: [PATCH] drivers/net/ksz9477: Handle Silicon Erratas Improve PHY receive performance Improve transmit waveform amplitude Disable EEE for ports 1-5 Fix supply current values Signed-off-by: Jani Paalijarvi --- drivers/net/ksz9477.c | 265 +++++++++++++++++++++++++++++++++++++- drivers/net/ksz9477_reg.h | 18 +++ 2 files changed, 281 insertions(+), 2 deletions(-) diff --git a/drivers/net/ksz9477.c b/drivers/net/ksz9477.c index b7c2a5101d946..117feb3b9f236 100644 --- a/drivers/net/ksz9477.c +++ b/drivers/net/ksz9477.c @@ -32,6 +32,9 @@ * Pre-processor Definitions ****************************************************************************/ +#define ERRATA_MOD1_REGS 7 +#define ERRATA_MOD9_REGS 13 + /**************************************************************************** * Private Data ****************************************************************************/ @@ -56,6 +59,13 @@ static uint8_t g_port_mirror_config[7] = #endif +struct errata +{ + uint8_t dev; + uint16_t reg; + uint16_t value; +}; + /**************************************************************************** * Private Functions ****************************************************************************/ @@ -124,7 +134,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; @@ -175,7 +184,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) @@ -211,6 +219,231 @@ 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, 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_handle_erratas(ksz9477_port_t port) +{ + int ret; + uint16_t regval16; + int j; + struct errata mod1[ERRATA_MOD1_REGS]; + struct errata mod9[ERRATA_MOD9_REGS]; + + /* 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; + } + + /* Module 1: Improve PHY receive performance */ + + mod1[0].dev = KSZ9477_MMD_DEV_SIGNAL_QUALITY; + mod1[0].reg = 0x6f; + mod1[0].value = 0xdd0b; + mod1[1].dev = KSZ9477_MMD_DEV_SIGNAL_QUALITY; + mod1[1].reg = 0x8f; + mod1[1].value = 0x6032; + mod1[2].dev = KSZ9477_MMD_DEV_SIGNAL_QUALITY; + mod1[2].reg = 0x9d; + mod1[2].value = 0x248c; + mod1[3].dev = KSZ9477_MMD_DEV_SIGNAL_QUALITY; + mod1[3].reg = 0x75; + mod1[3].value = 0x0060; + mod1[4].dev = KSZ9477_MMD_DEV_SIGNAL_QUALITY; + mod1[4].reg = 0xd3; + mod1[4].value = 0x7777; + mod1[5].dev = KSZ9477_MMD_DEV_QUIET_WIRE; + mod1[5].reg = 0x06; + mod1[5].value = 0x3008; + mod1[6].dev = KSZ9477_MMD_DEV_QUIET_WIRE; + mod1[6].reg = 0x08; + mod1[6].value = 0x2001; + + for (j = 0; ret == OK && j < ERRATA_MOD1_REGS; j++) + { + ret = ksz9477_mmd_write_indirect(port, mod1[j].dev, mod1[j].reg, + &(mod1[j].value), 1); + } + + /* Module 2: Improve transmit waveform amplitude */ + + regval16 = 0x00d0; + ret = ksz9477_mmd_write_indirect(port, KSZ9477_MMD_DEV_QUIET_WIRE, + 0x4, ®val16, 1); + + /* Module 9: Set various registers to get correct supply current values */ + + mod9[0].reg = 0x13; + mod9[0].value = 0x6eff; + mod9[1].reg = 0x14; + mod9[1].value = 0xe6ff; + mod9[2].reg = 0x15; + mod9[2].value = 0x6eff; + mod9[3].reg = 0x16; + mod9[3].value = 0xe6ff; + mod9[4].reg = 0x17; + mod9[4].value = 0x00ff; + mod9[5].reg = 0x18; + mod9[5].value = 0x43ff; + mod9[6].reg = 0x19; + mod9[6].value = 0xC3ff; + mod9[7].reg = 0x1a; + mod9[7].value = 0x6fff; + mod9[8].reg = 0x1b; + mod9[8].value = 0x07ff; + mod9[9].reg = 0x1c; + mod9[9].value = 0x0fff; + mod9[10].reg = 0x1d; + mod9[10].value = 0xe7ff; + mod9[11].reg = 0x1e; + mod9[11].value = 0xefff; + mod9[12].reg = 0x20; + mod9[12].value = 0xeeee; + + for (j = 0; ret == OK && j < ERRATA_MOD9_REGS; j++) + { + ret = ksz9477_mmd_write_indirect(port, KSZ9477_MMD_DEV_QUIET_WIRE, + mod9[j].reg, &(mod9[j].value), 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 ****************************************************************************/ @@ -375,6 +608,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 KSZ9477S Silicon Erratas */ + + for (i = KSZ9477_PORT_PHY1; i <= KSZ9477_PORT_PHY5; i++) + { + ret = ksz9477_handle_erratas(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 a40c52e37a402..3d897fd874778 100644 --- a/drivers/net/ksz9477_reg.h +++ b/drivers/net/ksz9477_reg.h @@ -59,6 +59,9 @@ #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) + /* Note! Unlike in data sheet, the indirect data register reads and * writes must be done with 32-bit accesses and the address is * 0x204 @@ -118,6 +121,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)