From 9042b46eda33ef5db3cdfc9e12b3c8cabb196141 Mon Sep 17 00:00:00 2001 From: Jerome Brunet Date: Mon, 18 Dec 2017 10:44:40 +0100 Subject: [PATCH 1/7] net: phy: meson-gxl: check phy_write return value Always check phy_write return values. Better to be safe than sorry Reviewed-by: Andrew Lunn Signed-off-by: Jerome Brunet Signed-off-by: David S. Miller --- drivers/net/phy/meson-gxl.c | 50 ++++++++++++++++++++++++++++--------- 1 file changed, 38 insertions(+), 12 deletions(-) diff --git a/drivers/net/phy/meson-gxl.c b/drivers/net/phy/meson-gxl.c index 4ee630afe43a..900606204c0a 100644 --- a/drivers/net/phy/meson-gxl.c +++ b/drivers/net/phy/meson-gxl.c @@ -26,27 +26,53 @@ static int meson_gxl_config_init(struct phy_device *phydev) { + int ret; + /* Enable Analog and DSP register Bank access by */ - phy_write(phydev, 0x14, 0x0000); - phy_write(phydev, 0x14, 0x0400); - phy_write(phydev, 0x14, 0x0000); - phy_write(phydev, 0x14, 0x0400); + ret = phy_write(phydev, 0x14, 0x0000); + if (ret) + return ret; + ret = phy_write(phydev, 0x14, 0x0400); + if (ret) + return ret; + ret = phy_write(phydev, 0x14, 0x0000); + if (ret) + return ret; + ret = phy_write(phydev, 0x14, 0x0400); + if (ret) + return ret; /* Write Analog register 23 */ - phy_write(phydev, 0x17, 0x8E0D); - phy_write(phydev, 0x14, 0x4417); + ret = phy_write(phydev, 0x17, 0x8E0D); + if (ret) + return ret; + ret = phy_write(phydev, 0x14, 0x4417); + if (ret) + return ret; /* Enable fractional PLL */ - phy_write(phydev, 0x17, 0x0005); - phy_write(phydev, 0x14, 0x5C1B); + ret = phy_write(phydev, 0x17, 0x0005); + if (ret) + return ret; + ret = phy_write(phydev, 0x14, 0x5C1B); + if (ret) + return ret; /* Program fraction FR_PLL_DIV1 */ - phy_write(phydev, 0x17, 0x029A); - phy_write(phydev, 0x14, 0x5C1D); + ret = phy_write(phydev, 0x17, 0x029A); + if (ret) + return ret; + ret = phy_write(phydev, 0x14, 0x5C1D); + if (ret) + return ret; /* Program fraction FR_PLL_DIV1 */ - phy_write(phydev, 0x17, 0xAAAA); - phy_write(phydev, 0x14, 0x5C1C); + ret = phy_write(phydev, 0x17, 0xAAAA); + if (ret) + return ret; + ret = phy_write(phydev, 0x14, 0x5C1C); + if (ret) + return ret; return 0; } From 00fd73eb29205c1c94805fe5e39a09135d870e96 Mon Sep 17 00:00:00 2001 From: Jerome Brunet Date: Mon, 18 Dec 2017 10:44:41 +0100 Subject: [PATCH 2/7] net: phy: meson-gxl: define control registers Define registers and bits in meson-gxl PHY driver to make a bit more human friendly. No functional change. Signed-off-by: Neil Armstrong Signed-off-by: Jerome Brunet Signed-off-by: David S. Miller --- drivers/net/phy/meson-gxl.c | 86 ++++++++++++++++++++++++++++--------- 1 file changed, 66 insertions(+), 20 deletions(-) diff --git a/drivers/net/phy/meson-gxl.c b/drivers/net/phy/meson-gxl.c index 900606204c0a..61bcc17098d7 100644 --- a/drivers/net/phy/meson-gxl.c +++ b/drivers/net/phy/meson-gxl.c @@ -24,53 +24,95 @@ #include #include +#define TSTCNTL 20 +#define TSTCNTL_READ BIT(15) +#define TSTCNTL_WRITE BIT(14) +#define TSTCNTL_REG_BANK_SEL GENMASK(12, 11) +#define TSTCNTL_TEST_MODE BIT(10) +#define TSTCNTL_READ_ADDRESS GENMASK(9, 5) +#define TSTCNTL_WRITE_ADDRESS GENMASK(4, 0) +#define TSTREAD1 21 +#define TSTWRITE 23 + +#define BANK_ANALOG_DSP 0 +#define BANK_WOL 1 +#define BANK_BIST 3 + +/* Analog/DSP Registers */ +#define A6_CONFIG_REG 0x17 + +/* WOL Registers */ +#define LPI_STATUS 0xc +#define LPI_STATUS_RSV12 BIT(12) + +/* BIST Registers */ +#define FR_PLL_CONTROL 0x1b +#define FR_PLL_DIV0 0x1c +#define FR_PLL_DIV1 0x1d + static int meson_gxl_config_init(struct phy_device *phydev) { int ret; /* Enable Analog and DSP register Bank access by */ - ret = phy_write(phydev, 0x14, 0x0000); + ret = phy_write(phydev, TSTCNTL, 0); if (ret) return ret; - ret = phy_write(phydev, 0x14, 0x0400); + ret = phy_write(phydev, TSTCNTL, TSTCNTL_TEST_MODE); if (ret) return ret; - ret = phy_write(phydev, 0x14, 0x0000); + ret = phy_write(phydev, TSTCNTL, 0); if (ret) return ret; - ret = phy_write(phydev, 0x14, 0x0400); + ret = phy_write(phydev, TSTCNTL, TSTCNTL_TEST_MODE); if (ret) return ret; - /* Write Analog register 23 */ - ret = phy_write(phydev, 0x17, 0x8E0D); + /* Write CONFIG_A6*/ + ret = phy_write(phydev, TSTWRITE, 0x8e0d); if (ret) return ret; - ret = phy_write(phydev, 0x14, 0x4417); + ret = phy_write(phydev, TSTCNTL, + TSTCNTL_WRITE + | FIELD_PREP(TSTCNTL_REG_BANK_SEL, BANK_ANALOG_DSP) + | TSTCNTL_TEST_MODE + | FIELD_PREP(TSTCNTL_WRITE_ADDRESS, A6_CONFIG_REG)); if (ret) return ret; /* Enable fractional PLL */ - ret = phy_write(phydev, 0x17, 0x0005); + ret = phy_write(phydev, TSTWRITE, 0x0005); if (ret) return ret; - ret = phy_write(phydev, 0x14, 0x5C1B); + ret = phy_write(phydev, TSTCNTL, + TSTCNTL_WRITE + | FIELD_PREP(TSTCNTL_REG_BANK_SEL, BANK_BIST) + | TSTCNTL_TEST_MODE + | FIELD_PREP(TSTCNTL_WRITE_ADDRESS, FR_PLL_CONTROL)); if (ret) return ret; /* Program fraction FR_PLL_DIV1 */ - ret = phy_write(phydev, 0x17, 0x029A); + ret = phy_write(phydev, TSTWRITE, 0x029a); if (ret) return ret; - ret = phy_write(phydev, 0x14, 0x5C1D); + ret = phy_write(phydev, TSTCNTL, + TSTCNTL_WRITE + | FIELD_PREP(TSTCNTL_REG_BANK_SEL, BANK_BIST) + | TSTCNTL_TEST_MODE + | FIELD_PREP(TSTCNTL_WRITE_ADDRESS, FR_PLL_DIV1)); if (ret) return ret; /* Program fraction FR_PLL_DIV1 */ - ret = phy_write(phydev, 0x17, 0xAAAA); + ret = phy_write(phydev, TSTWRITE, 0xaaaa); if (ret) return ret; - ret = phy_write(phydev, 0x14, 0x5C1C); + ret = phy_write(phydev, TSTCNTL, + TSTCNTL_WRITE + | FIELD_PREP(TSTCNTL_REG_BANK_SEL, BANK_BIST) + | TSTCNTL_TEST_MODE + | FIELD_PREP(TSTCNTL_WRITE_ADDRESS, FR_PLL_DIV0)); if (ret) return ret; @@ -105,26 +147,30 @@ static int meson_gxl_read_status(struct phy_device *phydev) goto read_status_continue; /* Need to access WOL bank, make sure the access is open */ - ret = phy_write(phydev, 0x14, 0x0000); + ret = phy_write(phydev, TSTCNTL, 0); if (ret) return ret; - ret = phy_write(phydev, 0x14, 0x0400); + ret = phy_write(phydev, TSTCNTL, TSTCNTL_TEST_MODE); if (ret) return ret; - ret = phy_write(phydev, 0x14, 0x0000); + ret = phy_write(phydev, TSTCNTL, 0); if (ret) return ret; - ret = phy_write(phydev, 0x14, 0x0400); + ret = phy_write(phydev, TSTCNTL, TSTCNTL_TEST_MODE); if (ret) return ret; /* Request LPI_STATUS WOL register */ - ret = phy_write(phydev, 0x14, 0x8D80); + ret = phy_write(phydev, TSTCNTL, + TSTCNTL_READ + | FIELD_PREP(TSTCNTL_REG_BANK_SEL, BANK_WOL) + | TSTCNTL_TEST_MODE + | FIELD_PREP(TSTCNTL_READ_ADDRESS, LPI_STATUS)); if (ret) return ret; /* Read LPI_STATUS value */ - wol = phy_read(phydev, 0x15); + wol = phy_read(phydev, TSTREAD1); if (wol < 0) return wol; @@ -136,7 +182,7 @@ static int meson_gxl_read_status(struct phy_device *phydev) if (exp < 0) return exp; - if (!(wol & BIT(12)) || + if (!(wol & LPI_STATUS_RSV12) || ((exp & EXPANSION_NWAY) && !(lpa & LPA_LPACK))) { /* Looks like aneg failed after all */ phydev_dbg(phydev, "LPA corruption - aneg restart\n"); From fdaa84c37151f7f8080dc2d7842f681d9790fb87 Mon Sep 17 00:00:00 2001 From: Jerome Brunet Date: Mon, 18 Dec 2017 10:44:42 +0100 Subject: [PATCH 3/7] net: phy: meson-gxl: add read and write helpers for banked registers Add read and write helpers to manipulate banked registers on this PHY This helps clarify the settings applied to these registers and what the driver actually does Signed-off-by: Neil Armstrong Signed-off-by: Jerome Brunet Signed-off-by: David S. Miller --- drivers/net/phy/meson-gxl.c | 150 +++++++++++++++++++----------------- 1 file changed, 79 insertions(+), 71 deletions(-) diff --git a/drivers/net/phy/meson-gxl.c b/drivers/net/phy/meson-gxl.c index 61bcc17098d7..a52645566d0d 100644 --- a/drivers/net/phy/meson-gxl.c +++ b/drivers/net/phy/meson-gxl.c @@ -50,69 +50,100 @@ #define FR_PLL_DIV0 0x1c #define FR_PLL_DIV1 0x1d +static int meson_gxl_open_banks(struct phy_device *phydev) +{ + int ret; + + /* Enable Analog and DSP register Bank access by + * toggling TSTCNTL_TEST_MODE bit in the TSTCNTL register + */ + ret = phy_write(phydev, TSTCNTL, 0); + if (ret) + return ret; + ret = phy_write(phydev, TSTCNTL, TSTCNTL_TEST_MODE); + if (ret) + return ret; + ret = phy_write(phydev, TSTCNTL, 0); + if (ret) + return ret; + return phy_write(phydev, TSTCNTL, TSTCNTL_TEST_MODE); +} + +static void meson_gxl_close_banks(struct phy_device *phydev) +{ + phy_write(phydev, TSTCNTL, 0); +} + +static int meson_gxl_read_reg(struct phy_device *phydev, + unsigned int bank, unsigned int reg) +{ + int ret; + + ret = meson_gxl_open_banks(phydev); + if (ret) + goto out; + + ret = phy_write(phydev, TSTCNTL, TSTCNTL_READ | + FIELD_PREP(TSTCNTL_REG_BANK_SEL, bank) | + TSTCNTL_TEST_MODE | + FIELD_PREP(TSTCNTL_READ_ADDRESS, reg)); + if (ret) + goto out; + + ret = phy_read(phydev, TSTREAD1); +out: + /* Close the bank access on our way out */ + meson_gxl_close_banks(phydev); + return ret; +} + +static int meson_gxl_write_reg(struct phy_device *phydev, + unsigned int bank, unsigned int reg, + uint16_t value) +{ + int ret; + + ret = meson_gxl_open_banks(phydev); + if (ret) + goto out; + + ret = phy_write(phydev, TSTWRITE, value); + if (ret) + goto out; + + ret = phy_write(phydev, TSTCNTL, TSTCNTL_WRITE | + FIELD_PREP(TSTCNTL_REG_BANK_SEL, bank) | + TSTCNTL_TEST_MODE | + FIELD_PREP(TSTCNTL_WRITE_ADDRESS, reg)); + +out: + /* Close the bank access on our way out */ + meson_gxl_close_banks(phydev); + return ret; +} + static int meson_gxl_config_init(struct phy_device *phydev) { int ret; - /* Enable Analog and DSP register Bank access by */ - ret = phy_write(phydev, TSTCNTL, 0); - if (ret) - return ret; - ret = phy_write(phydev, TSTCNTL, TSTCNTL_TEST_MODE); - if (ret) - return ret; - ret = phy_write(phydev, TSTCNTL, 0); - if (ret) - return ret; - ret = phy_write(phydev, TSTCNTL, TSTCNTL_TEST_MODE); - if (ret) - return ret; - /* Write CONFIG_A6*/ - ret = phy_write(phydev, TSTWRITE, 0x8e0d); - if (ret) - return ret; - ret = phy_write(phydev, TSTCNTL, - TSTCNTL_WRITE - | FIELD_PREP(TSTCNTL_REG_BANK_SEL, BANK_ANALOG_DSP) - | TSTCNTL_TEST_MODE - | FIELD_PREP(TSTCNTL_WRITE_ADDRESS, A6_CONFIG_REG)); + ret = meson_gxl_write_reg(phydev, BANK_ANALOG_DSP, A6_CONFIG_REG, + 0x8e0d); if (ret) return ret; /* Enable fractional PLL */ - ret = phy_write(phydev, TSTWRITE, 0x0005); - if (ret) - return ret; - ret = phy_write(phydev, TSTCNTL, - TSTCNTL_WRITE - | FIELD_PREP(TSTCNTL_REG_BANK_SEL, BANK_BIST) - | TSTCNTL_TEST_MODE - | FIELD_PREP(TSTCNTL_WRITE_ADDRESS, FR_PLL_CONTROL)); + ret = meson_gxl_write_reg(phydev, BANK_BIST, FR_PLL_CONTROL, 0x5); if (ret) return ret; /* Program fraction FR_PLL_DIV1 */ - ret = phy_write(phydev, TSTWRITE, 0x029a); - if (ret) - return ret; - ret = phy_write(phydev, TSTCNTL, - TSTCNTL_WRITE - | FIELD_PREP(TSTCNTL_REG_BANK_SEL, BANK_BIST) - | TSTCNTL_TEST_MODE - | FIELD_PREP(TSTCNTL_WRITE_ADDRESS, FR_PLL_DIV1)); + ret = meson_gxl_write_reg(phydev, BANK_BIST, FR_PLL_DIV1, 0x029a); if (ret) return ret; /* Program fraction FR_PLL_DIV1 */ - ret = phy_write(phydev, TSTWRITE, 0xaaaa); - if (ret) - return ret; - ret = phy_write(phydev, TSTCNTL, - TSTCNTL_WRITE - | FIELD_PREP(TSTCNTL_REG_BANK_SEL, BANK_BIST) - | TSTCNTL_TEST_MODE - | FIELD_PREP(TSTCNTL_WRITE_ADDRESS, FR_PLL_DIV0)); + ret = meson_gxl_write_reg(phydev, BANK_BIST, FR_PLL_DIV0, 0xaaaa); if (ret) return ret; @@ -146,31 +177,8 @@ static int meson_gxl_read_status(struct phy_device *phydev) else if (!ret) goto read_status_continue; - /* Need to access WOL bank, make sure the access is open */ - ret = phy_write(phydev, TSTCNTL, 0); - if (ret) - return ret; - ret = phy_write(phydev, TSTCNTL, TSTCNTL_TEST_MODE); - if (ret) - return ret; - ret = phy_write(phydev, TSTCNTL, 0); - if (ret) - return ret; - ret = phy_write(phydev, TSTCNTL, TSTCNTL_TEST_MODE); - if (ret) - return ret; - - /* Request LPI_STATUS WOL register */ - ret = phy_write(phydev, TSTCNTL, - TSTCNTL_READ - | FIELD_PREP(TSTCNTL_REG_BANK_SEL, BANK_WOL) - | TSTCNTL_TEST_MODE - | FIELD_PREP(TSTCNTL_READ_ADDRESS, LPI_STATUS)); - if (ret) - return ret; - - /* Read LPI_STATUS value */ - wol = phy_read(phydev, TSTREAD1); + /* Aneg is done, let's check everything is fine */ + wol = meson_gxl_read_reg(phydev, BANK_WOL, LPI_STATUS); if (wol < 0) return wol; From c1e535510f6e9f8b64814aa2ddf2347610b3396d Mon Sep 17 00:00:00 2001 From: Jerome Brunet Date: Mon, 18 Dec 2017 10:44:43 +0100 Subject: [PATCH 4/7] net: phy: meson-gxl: use genphy_config_init Use the generic init function to populate some of the phydev structure fields Reviewed-by: Andrew Lunn Signed-off-by: Jerome Brunet Signed-off-by: David S. Miller --- drivers/net/phy/meson-gxl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/phy/meson-gxl.c b/drivers/net/phy/meson-gxl.c index a52645566d0d..0a34656a2086 100644 --- a/drivers/net/phy/meson-gxl.c +++ b/drivers/net/phy/meson-gxl.c @@ -147,7 +147,7 @@ static int meson_gxl_config_init(struct phy_device *phydev) if (ret) return ret; - return 0; + return genphy_config_init(phydev); } /* This function is provided to cope with the possible failures of this phy From 30e43f13342bbe1dd96fa032030063ed5bfb6d04 Mon Sep 17 00:00:00 2001 From: Jerome Brunet Date: Mon, 18 Dec 2017 10:44:44 +0100 Subject: [PATCH 5/7] net: phy: meson-gxl: leave CONFIG_A6 untouched The PHY performs just as well when left in its default configuration and it makes senses because this poke gets reset just after init. According to the documentation, all registers in the Analog/DSP bank are reset when there is a mode switch from 10BT to 100BT. The bank is also reset on power down and soft reset, so we will never see the value which may have been set by the bootloader. In the end, we have used the default configuration so far and there is no reason to change now. Remove CONFIG_A6 poke to make this clear. Reviewed-by: Andrew Lunn Signed-off-by: Jerome Brunet Signed-off-by: David S. Miller --- drivers/net/phy/meson-gxl.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/drivers/net/phy/meson-gxl.c b/drivers/net/phy/meson-gxl.c index 0a34656a2086..ddc92424e8de 100644 --- a/drivers/net/phy/meson-gxl.c +++ b/drivers/net/phy/meson-gxl.c @@ -38,9 +38,6 @@ #define BANK_WOL 1 #define BANK_BIST 3 -/* Analog/DSP Registers */ -#define A6_CONFIG_REG 0x17 - /* WOL Registers */ #define LPI_STATUS 0xc #define LPI_STATUS_RSV12 BIT(12) @@ -126,12 +123,6 @@ static int meson_gxl_config_init(struct phy_device *phydev) { int ret; - /* Write CONFIG_A6*/ - ret = meson_gxl_write_reg(phydev, BANK_ANALOG_DSP, A6_CONFIG_REG, - 0x8e0d); - if (ret) - return ret; - /* Enable fractional PLL */ ret = meson_gxl_write_reg(phydev, BANK_BIST, FR_PLL_CONTROL, 0x5); if (ret) From cf127ff20af1a9f410fde58917bd17a66d1425c5 Mon Sep 17 00:00:00 2001 From: Jerome Brunet Date: Mon, 18 Dec 2017 10:44:45 +0100 Subject: [PATCH 6/7] net: phy: meson-gxl: add interrupt support Enable interrupt support in meson-gxl PHY driver Signed-off-by: Jerome Brunet Signed-off-by: David S. Miller --- drivers/net/phy/meson-gxl.c | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/drivers/net/phy/meson-gxl.c b/drivers/net/phy/meson-gxl.c index ddc92424e8de..ee0aa18af631 100644 --- a/drivers/net/phy/meson-gxl.c +++ b/drivers/net/phy/meson-gxl.c @@ -33,6 +33,14 @@ #define TSTCNTL_WRITE_ADDRESS GENMASK(4, 0) #define TSTREAD1 21 #define TSTWRITE 23 +#define INTSRC_FLAG 29 +#define INTSRC_ANEG_PR BIT(1) +#define INTSRC_PARALLEL_FAULT BIT(2) +#define INTSRC_ANEG_LP_ACK BIT(3) +#define INTSRC_LINK_DOWN BIT(4) +#define INTSRC_REMOTE_FAULT BIT(5) +#define INTSRC_ANEG_COMPLETE BIT(6) +#define INTSRC_MASK 30 #define BANK_ANALOG_DSP 0 #define BANK_WOL 1 @@ -193,16 +201,43 @@ static int meson_gxl_read_status(struct phy_device *phydev) return genphy_read_status(phydev); } +static int meson_gxl_ack_interrupt(struct phy_device *phydev) +{ + int ret = phy_read(phydev, INTSRC_FLAG); + + return ret < 0 ? ret : 0; +} + +static int meson_gxl_config_intr(struct phy_device *phydev) +{ + u16 val; + + if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { + val = INTSRC_ANEG_PR + | INTSRC_PARALLEL_FAULT + | INTSRC_ANEG_LP_ACK + | INTSRC_LINK_DOWN + | INTSRC_REMOTE_FAULT + | INTSRC_ANEG_COMPLETE; + } else { + val = 0; + } + + return phy_write(phydev, INTSRC_MASK, val); +} + static struct phy_driver meson_gxl_phy[] = { { .phy_id = 0x01814400, .phy_id_mask = 0xfffffff0, .name = "Meson GXL Internal PHY", .features = PHY_BASIC_FEATURES, - .flags = PHY_IS_INTERNAL, + .flags = PHY_IS_INTERNAL | PHY_HAS_INTERRUPT, .config_init = meson_gxl_config_init, .aneg_done = genphy_aneg_done, .read_status = meson_gxl_read_status, + .ack_interrupt = meson_gxl_ack_interrupt, + .config_intr = meson_gxl_config_intr, .suspend = genphy_suspend, .resume = genphy_resume, }, From afb4fa47fe94b5f0e97f9afb7251b34e2eb8fbdc Mon Sep 17 00:00:00 2001 From: Jerome Brunet Date: Mon, 18 Dec 2017 10:44:46 +0100 Subject: [PATCH 7/7] net: phy: meson-gxl: join the authors Following previous changes, join the other authors of this driver and take the blame with them Reviewed-by: Andrew Lunn Signed-off-by: Jerome Brunet Signed-off-by: David S. Miller --- drivers/net/phy/meson-gxl.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/phy/meson-gxl.c b/drivers/net/phy/meson-gxl.c index ee0aa18af631..ddc2c5ea3787 100644 --- a/drivers/net/phy/meson-gxl.c +++ b/drivers/net/phy/meson-gxl.c @@ -255,4 +255,5 @@ MODULE_DEVICE_TABLE(mdio, meson_gxl_tbl); MODULE_DESCRIPTION("Amlogic Meson GXL Internal PHY driver"); MODULE_AUTHOR("Baoqi wang"); MODULE_AUTHOR("Neil Armstrong "); +MODULE_AUTHOR("Jerome Brunet "); MODULE_LICENSE("GPL");