[U-Boot] [PATCH 1/3] net: phy: mscc: add support for VSC8584 PHY

Joe Hershberger
2018-10-22T21:52:38Z

On Fri, Sep 14, 2018 at 7:50 AM Quentin Schulz
<quentin.schulz@bootlin.com> wrote:
>
> The VSC8584 PHY is a 4-port PHY that is 10/100/1000BASE-T, 100BASE-FX,
> 1000BASE-X and triple-speed copper SFP capable, can communicate with the
> MAC via SGMII, QSGMII or 1000BASE-X, supports downshifting and can set
> the blinking pattern of each of its 4 LEDs, supports hardware offloading
> of MACsec and supports SyncE as well as HP Auto-MDIX detection.
>
> This adds support for 10/100/1000BASE-T and SGMII/QSGMII link with the
> MAC.
>
> The VSC8584 has also an internal Intel 8051 microcontroller whose
> firmware needs to be patched when the PHY is reset. If the 8051's
> firmware has the expected CRC, its patching can be skipped. The
> microcontroller can be accessed from any port of the PHY, though the CRC
> function can only be done through the PHY that is the base PHY of the
> package (internal address 0) due to a limitation of the firmware.
>
> The GPIO register bank is a set of registers that are common to all PHYs
> in the package. So any modification in any register of this bank affects
> all PHYs of the package.
>
> The revA of the VSC8584 PHY (which is not and will not be publicly
> released) should NOT patch the firmware of the microcontroller or it'll
> make things worse, the easiest way is just to not support it.
>
> Signed-off-by: Quentin Schulz <quentin.schulz@bootlin.com>
> ---
> drivers/net/phy/mscc.c | 542 +++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 542 insertions(+)
>
> diff --git a/drivers/net/phy/mscc.c b/drivers/net/phy/mscc.c
> index 439f5e3c5c..0e54fe0eff 100644
> --- a/drivers/net/phy/mscc.c
> +++ b/drivers/net/phy/mscc.c
> @@ -11,12 +11,15 @@
>
> #include <miiphy.h>
> #include <bitfield.h>
> +#include <time.h>
> +#include <linux/delay.h>
>
> /* Microsemi PHY ID's */
> #define PHY_ID_VSC8530 0x00070560
> #define PHY_ID_VSC8531 0x00070570
> #define PHY_ID_VSC8540 0x00070760
> #define PHY_ID_VSC8541 0x00070770
> +#define PHY_ID_VSC8584 0x000707c0
>
> /* Microsemi VSC85xx PHY Register Pages */
> #define MSCC_EXT_PAGE_ACCESS 31 /* Page Access Register */
> @@ -29,6 +32,14 @@
> #define MSCC_PHY_PAGE_TEST 0x2A30 /* TEST Page registers */
> #define MSCC_PHY_PAGE_TR 0x52B5 /* Token Ring Page registers */
>
> +/* Std Page Register 18 */
> +#define MSCC_PHY_BYPASS_CONTROL 18
> +#define PARALLEL_DET_IGNORE_ADVERTISED 0x0008
> +
> +/* Std Page Register 22 */
> +#define MSCC_PHY_EXT_CNTL_STATUS 22
> +#define SMI_BROADCAST_WR_EN 0x0001
> +
> /* Std Page Register 28 - PHY AUX Control/Status */
> #define MIIM_AUX_CNTRL_STAT_REG 28
> #define MIIM_AUX_CNTRL_STAT_ACTIPHY_TO (0x0004)
> @@ -47,6 +58,37 @@
> #define MAC_IF_SELECTION_RGMII (2)
> #define MAC_IF_SELECTION_POS (11)
> #define MAC_IF_SELECTION_WIDTH (2)
> +#define VSC8584_MAC_IF_SELECTION_MASK 0x1000
> +#define VSC8584_MAC_IF_SELECTION_SGMII 0
> +#define VSC8584_MAC_IF_SELECTION_1000BASEX 1
> +#define VSC8584_MAC_IF_SELECTION_POS 12
> +#define MEDIA_OP_MODE_MASK 0x0700

Please use GENMASK() or BIT() for masks.

> +#define MEDIA_OP_MODE_COPPER 0
> +#define MEDIA_OP_MODE_SERDES 1
> +#define MEDIA_OP_MODE_1000BASEX 2
> +#define MEDIA_OP_MODE_100BASEFX 3
> +#define MEDIA_OP_MODE_AMS_COPPER_SERDES 5
> +#define MEDIA_OP_MODE_AMS_COPPER_1000BASEX 6
> +#define MEDIA_OP_MODE_AMS_COPPER_100BASEFX 7
> +#define MEDIA_OP_MODE_POS 8
> +
> +/* Extended Page 1 Register 20E1 */
> +#define MSCC_PHY_ACTIPHY_CNTL 20
> +#define PHY_ADDR_REVERSED 0x0200
> +
> +/* Extended Page 1 Register 23E1 */
> +
> +#define MSCC_PHY_EXT_PHY_CNTL_4 23
> +#define PHY_CNTL_4_ADDR_POS 11
> +
> +/* Extended Page 1 Register 25E1 */
> +#define MSCC_PHY_VERIPHY_CNTL_2 25
> +
> +/* Extended Page 1 Register 26E1 */
> +#define MSCC_PHY_VERIPHY_CNTL_3 26
> +
> +/* Extended Page 2 Register 16E2 */
> +#define MSCC_PHY_CU_PMD_TX_CNTL 16
>
> /* Extended Page 2 Register 20E2 */
> #define MSCC_PHY_RGMII_CNTL_REG 20
> @@ -72,6 +114,74 @@
> #define RMII_CLK_OUT_ENABLE_WIDTH (1)
> #define RMII_CLK_OUT_ENABLE_MASK (0x10)
>
> +/* Extended Page 3 Register 22E3 */
> +#define MSCC_PHY_SERDES_TX_CRC_ERR_CNT 22
> +
> +/* Extended page GPIO register 00G */
> +#define MSCC_DW8051_CNTL_STATUS 0
> +#define MICRO_NSOFT_RESET 0x8000

Bit definitions such as these should use BIT().

> +#define RUN_FROM_INT_ROM 0x4000
> +#define AUTOINC_ADDR 0x2000
> +#define PATCH_RAM_CLK 0x1000
> +#define MICRO_PATCH_EN 0x0080
> +#define DW8051_CLK_EN 0x0010
> +#define MICRO_CLK_EN 0x0008
> +#define MICRO_CLK_DIVIDE(x) ((x) >> 1)
> +
> +/* Extended page GPIO register 09G */
> +#define MSCC_TRAP_ROM_ADDR(x) ((x) * 2 + 1)
> +
> +/* Extended page GPIO register 10G */
> +#define MSCC_PATCH_RAM_ADDR(x) (((x) + 1) * 2)
> +
> +/* Extended page GPIO register 11G */
> +#define MSCC_INT_MEM_ADDR 11
> +
> +/* Extended page GPIO register 12G */
> +#define MSCC_INT_MEM_CNTL 12
> +#define READ_SFR 0x6000
> +#define READ_PRAM 0x4000
> +#define READ_ROM 0x2000
> +#define READ_RAM 0x0000
> +#define INT_MEM_WRITE_EN 0x1000
> +#define EN_PATCH_RAM_TRAP_ADDR(x) (0x0100 << ((x) - 1))
> +#define INT_MEM_DATA_M 0x00ff
> +#define INT_MEM_DATA(x) (INT_MEM_DATA_M & (x))
> +
> +/* Extended page GPIO register 18G */
> +#define MSCC_PHY_PROC_CMD 18
> +#define PROC_CMD_NCOMPLETED 0x8000
> +#define PROC_CMD_FAILED 0x4000
> +#define PROC_CMD_SGMII_PORT(x) ((x) << 8)
> +#define PROC_CMD_FIBER_PORT(x) (0x0100 << (x) % 4)
> +#define PROC_CMD_QSGMII_PORT 0x0c00
> +#define PROC_CMD_RST_CONF_PORT 0x0080
> +#define PROC_CMD_RECONF_PORT 0x0000
> +#define PROC_CMD_READ_MOD_WRITE_PORT 0x0040
> +#define PROC_CMD_WRITE 0x0040
> +#define PROC_CMD_READ 0x0000
> +#define PROC_CMD_FIBER_DISABLE 0x0020
> +#define PROC_CMD_FIBER_100BASE_FX 0x0010
> +#define PROC_CMD_FIBER_1000BASE_X 0x0000
> +#define PROC_CMD_SGMII_MAC 0x0030
> +#define PROC_CMD_QSGMII_MAC 0x0020
> +#define PROC_CMD_NO_MAC_CONF 0x0000
> +#define PROC_CMD_NOP 0x000f
> +#define PROC_CMD_CRC16 0x0008
> +#define PROC_CMD_FIBER_MEDIA_CONF 0x0001
> +#define PROC_CMD_MCB_ACCESS_MAC_CONF 0x0000
> +#define PROC_CMD_NCOMPLETED_TIMEOUT_MS 500
> +
> +/* Extended page GPIO register 19G */
> +#define MSCC_PHY_MAC_CFG_FASTLINK 19
> +#define MAC_CFG_MASK 0xc000
> +#define MAC_CFG_SGMII 0x0000
> +#define MAC_CFG_QSGMII 0x4000
> +
> +/* Test Registers */
> +#define MSCC_PHY_TEST_PAGE_5 5
> +#define MSCC_PHY_TEST_PAGE_8 8
> +
> /* Token Ring Page 0x52B5 Registers */
> #define MSCC_PHY_REG_TR_ADDR_16 16
> #define MSCC_PHY_REG_TR_DATA_17 17
> @@ -110,6 +220,12 @@
> #define MSCC_PHY_RESET_TIMEOUT (100)
> #define MSCC_PHY_MICRO_TIMEOUT (500)
>
> +#define VSC8584_REVB 0x0001
> +#define MSCC_DEV_REV_MASK GENMASK(3, 0)
> +
> +#define MSCC_VSC8584_REVB_INT8051_FW_START_ADDR 0xe800
> +#define MSCC_VSC8584_REVB_INT8051_FW_CRC 0xfb48
> +
> /* RGMII/GMII Clock Delay (Skew) Options */ enum vsc_phy_rgmii_skew {
> VSC_PHY_RGMII_DELAY_200_PS,
> VSC_PHY_RGMII_DELAY_800_PS,
> @@ -133,6 +249,331 @@ vsc_phy_clk_slew {
> VSC_PHY_CLK_SLEW_RATE_7,
> };
>
> +static void vsc8584_csr_write(struct mii_dev *bus, int phy0, u16 addr, u32 val)
> +{
> + bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_PHY_REG_TR_DATA_18,
> + val >> 16);
> + bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_PHY_REG_TR_DATA_17,
> + val & GENMASK(15, 0));
> + bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_PHY_REG_TR_ADDR_16,
> + MSCC_PHY_TR_16_WRITE | addr);
> +}
> +
> +static int vsc8584_cmd(struct mii_dev *bus, int phy, u16 val)
> +{
> + unsigned long deadline;
> + u16 reg_val;
> +
> + bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
> + MSCC_PHY_PAGE_GPIO);
> +
> + bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_PHY_PROC_CMD,
> + PROC_CMD_NCOMPLETED | val);
> +
> + deadline = timer_get_us() + PROC_CMD_NCOMPLETED_TIMEOUT_MS * 1000;
> + do {
> + reg_val = bus->read(bus, phy, MDIO_DEVAD_NONE,
> + MSCC_PHY_PROC_CMD);
> + } while (timer_get_us() <= deadline &&
> + (reg_val & PROC_CMD_NCOMPLETED) &&
> + !(reg_val & PROC_CMD_FAILED));
> +
> + bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
> + MSCC_PHY_PAGE_STD);
> +
> + if (reg_val & PROC_CMD_FAILED)
> + return -EIO;
> + if (reg_val & PROC_CMD_NCOMPLETED)
> + return -ETIMEDOUT;
> +
> + return 0;
> +}
> +
> +static int vsc8584_micro_deassert_reset(struct mii_dev *bus, int phy,
> + bool patch_en)
> +{
> + u32 enable, release;
> +
> + bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
> + MSCC_PHY_PAGE_GPIO);
> +
> + enable = RUN_FROM_INT_ROM | MICRO_CLK_EN | DW8051_CLK_EN;
> + release = MICRO_NSOFT_RESET | RUN_FROM_INT_ROM | DW8051_CLK_EN |
> + MICRO_CLK_EN;
> +
> + if (patch_en) {
> + enable |= MICRO_PATCH_EN;
> + release |= MICRO_PATCH_EN;
> +
> + /* Clear all patches */
> + bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_INT_MEM_CNTL,
> + READ_RAM);
> + }
> +
> + /* Enable 8051 Micro clock; CLEAR/SET patch present; disable PRAM clock

Please correct the comment style... "/*" on its own line got
multi-line comments.

> + * override and addr. auto-incr; operate at 125 MHz
> + */
> + bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_DW8051_CNTL_STATUS, enable);
> + /* Release 8051 Micro SW reset */
> + bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_DW8051_CNTL_STATUS, release);
> +
> + bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
> + MSCC_PHY_PAGE_STD);
> +
> + return 0;
> +}
> +
> +static int vsc8584_micro_assert_reset(struct mii_dev *bus, int phy)
> +{
> + int ret;
> + u16 reg;
> +
> + ret = vsc8584_cmd(bus, phy, PROC_CMD_NOP);
> + if (ret)
> + return ret;
> +
> + bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
> + MSCC_PHY_PAGE_GPIO);
> +
> + reg = bus->read(bus, phy, MDIO_DEVAD_NONE, MSCC_INT_MEM_CNTL);
> + reg &= ~EN_PATCH_RAM_TRAP_ADDR(4);
> + bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_INT_MEM_CNTL, reg);
> +
> + bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_TRAP_ROM_ADDR(4), 0x005b);
> + bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_PATCH_RAM_ADDR(4), 0x005b);
> +
> + reg = bus->read(bus, phy, MDIO_DEVAD_NONE, MSCC_INT_MEM_CNTL);
> + reg |= EN_PATCH_RAM_TRAP_ADDR(4);
> + bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_INT_MEM_CNTL, reg);
> +
> + bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_PHY_PROC_CMD, PROC_CMD_NOP);
> +
> + reg = bus->read(bus, phy, MDIO_DEVAD_NONE, MSCC_DW8051_CNTL_STATUS);
> + reg &= ~MICRO_NSOFT_RESET;
> + bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_DW8051_CNTL_STATUS, reg);
> +
> + bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_PHY_PROC_CMD,
> + PROC_CMD_MCB_ACCESS_MAC_CONF | PROC_CMD_SGMII_PORT(0) |
> + PROC_CMD_NO_MAC_CONF | PROC_CMD_READ);
> +
> + reg = bus->read(bus, phy, MDIO_DEVAD_NONE, MSCC_INT_MEM_CNTL);
> + reg &= ~EN_PATCH_RAM_TRAP_ADDR(4);
> + bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_INT_MEM_CNTL, reg);
> +
> + bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
> + MSCC_PHY_PAGE_STD);
> +
> + return 0;
> +}
> +
> +static const u8 fw_patch_vsc8584[] = {
> + 0xe8, 0x59, 0x02, 0xe8, 0x12, 0x02, 0xe8, 0x42, 0x02, 0xe8, 0x5a, 0x02,
> + 0xe8, 0x5b, 0x02, 0xe8, 0x5c, 0xe5, 0x69, 0x54, 0x0f, 0x24, 0xf7, 0x60,
> + 0x27, 0x24, 0xfc, 0x60, 0x23, 0x24, 0x08, 0x70, 0x14, 0xe5, 0x69, 0xae,
> + 0x68, 0x78, 0x04, 0xce, 0xa2, 0xe7, 0x13, 0xce, 0x13, 0xd8, 0xf8, 0x7e,
> + 0x00, 0x54, 0x0f, 0x80, 0x00, 0x7b, 0x01, 0x7a, 0x00, 0x7d, 0xee, 0x7f,
> + 0x92, 0x12, 0x50, 0xee, 0x22, 0xe4, 0xf5, 0x10, 0x85, 0x10, 0xfb, 0x7d,
> + 0x1c, 0xe4, 0xff, 0x12, 0x59, 0xea, 0x05, 0x10, 0xe5, 0x10, 0xc3, 0x94,
> + 0x04, 0x40, 0xed, 0x22, 0x22, 0x22, 0x22, 0x22,
> +};
> +
> +static int vsc8584_get_fw_crc(struct mii_dev *bus, int phy, u16 start,
> + u16 *crc, const u8 *fw_patch, int fw_size)
> +{
> + int ret;
> +
> + bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
> + MSCC_PHY_PAGE_EXT1);
> +
> + bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_PHY_VERIPHY_CNTL_2, start);
> + // Add one byte to size for the one added by the patch_fw function

Please use /* */ style comments.

> + bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_PHY_VERIPHY_CNTL_3,
> + fw_size + 1);
> +
> + ret = vsc8584_cmd(bus, phy, PROC_CMD_CRC16);
> + if (ret)
> + goto out;
> +
> + bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
> + MSCC_PHY_PAGE_EXT1);
> +
> + *crc = bus->read(bus, phy, MDIO_DEVAD_NONE, MSCC_PHY_VERIPHY_CNTL_2);
> +
> +out:
> + bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
> + MSCC_PHY_PAGE_STD);
> +
> + return ret;
> +}
> +
> +static int vsc8584_patch_fw(struct mii_dev *bus, int phy, const u8 *fw_patch,
> + int fw_size)
> +{
> + int i, ret;
> +
> + ret = vsc8584_micro_assert_reset(bus, phy);
> + if (ret) {
> + pr_err("%s: failed to assert reset of micro\n", __func__);
> + return ret;
> + }
> +
> + bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
> + MSCC_PHY_PAGE_GPIO);
> +
> + /*
> + * Hold 8051 Micro in SW Reset, Enable auto incr address and patch clock
> + * Disable the 8051 Micro clock
> + */
> + bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_DW8051_CNTL_STATUS,
> + RUN_FROM_INT_ROM | AUTOINC_ADDR | PATCH_RAM_CLK |
> + MICRO_CLK_EN | MICRO_CLK_DIVIDE(2));
> + bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_INT_MEM_CNTL, READ_PRAM |
> + INT_MEM_WRITE_EN | INT_MEM_DATA(2));
> + bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_INT_MEM_ADDR, 0x0000);
> +
> + for (i = 0; i < fw_size; i++)
> + bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_INT_MEM_CNTL,
> + READ_PRAM | INT_MEM_WRITE_EN | fw_patch[i]);
> +
> + /* Clear internal memory access */
> + bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_INT_MEM_CNTL, READ_RAM);
> +
> + bus->write(bus, phy, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
> + MSCC_PHY_PAGE_STD);
> +
> + return 0;
> +}
> +
> +static int vsc8584_config_pre_init(struct mii_dev *bus, int phy0)
> +{
> + u16 reg, crc;
> + int ret;
> +
> + bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
> + MSCC_PHY_PAGE_STD);
> +
> + reg = bus->read(bus, phy0, MDIO_DEVAD_NONE, MSCC_PHY_EXT_CNTL_STATUS);
> + reg |= SMI_BROADCAST_WR_EN;
> + bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_PHY_EXT_CNTL_STATUS, reg);
> +
> + reg = bus->read(bus, phy0, MDIO_DEVAD_NONE, MSCC_PHY_BYPASS_CONTROL);
> + reg |= PARALLEL_DET_IGNORE_ADVERTISED;
> + bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_PHY_BYPASS_CONTROL, reg);
> +
> + bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
> + MSCC_PHY_PAGE_EXT3);
> +
> + bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_PHY_SERDES_TX_CRC_ERR_CNT,
> + 0x2000);
> +
> + bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
> + MSCC_PHY_PAGE_TEST);
> +
> + bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_PHY_TEST_PAGE_5, 0x1f20);
> +
> + reg = bus->read(bus, phy0, MDIO_DEVAD_NONE, MSCC_PHY_TEST_PAGE_8);
> + reg |= 0x8000;

Comment, use BIT(31), and use #define

> + bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_PHY_TEST_PAGE_8, reg);
> +
> + bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
> + MSCC_PHY_PAGE_TR);
> +
> + bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_PHY_REG_TR_ADDR_16, 0xafa4);
> +
> + reg = bus->read(bus, phy0, MDIO_DEVAD_NONE, MSCC_PHY_REG_TR_DATA_18);
> + reg &= ~0x007f;

Use GENMASK() and #define

> + reg |= 0x0019;

Magic number... use #define.

> + bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_PHY_REG_TR_DATA_18, reg);
> +
> + bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_PHY_REG_TR_ADDR_16, 0x8fa4);

Magic number... use #define

> +

Comment what this is doing and link the errata that it came from...

> + vsc8584_csr_write(bus, phy0, 0x07fa, 0x0050100f);
> + vsc8584_csr_write(bus, phy0, 0x1688, 0x00049f81);
> + vsc8584_csr_write(bus, phy0, 0x0f90, 0x00688980);
> + vsc8584_csr_write(bus, phy0, 0x03a4, 0x0000d8f0);
> + vsc8584_csr_write(bus, phy0, 0x0fc0, 0x00000400);
> + vsc8584_csr_write(bus, phy0, 0x0f82, 0x0012b002);
> + vsc8584_csr_write(bus, phy0, 0x1686, 0x00000004);
> + vsc8584_csr_write(bus, phy0, 0x168c, 0x00d2c46f);
> + vsc8584_csr_write(bus, phy0, 0x17a2, 0x00000620);
> + vsc8584_csr_write(bus, phy0, 0x16a0, 0x00eeffdd);
> + vsc8584_csr_write(bus, phy0, 0x16a6, 0x00071448);
> + vsc8584_csr_write(bus, phy0, 0x16a4, 0x0013132f);
> + vsc8584_csr_write(bus, phy0, 0x16a8, 0x00000000);
> + vsc8584_csr_write(bus, phy0, 0x0ffc, 0x00c0a028);
> + vsc8584_csr_write(bus, phy0, 0x0fe8, 0x0091b06c);
> + vsc8584_csr_write(bus, phy0, 0x0fea, 0x00041600);
> + vsc8584_csr_write(bus, phy0, 0x0f80, 0x00fffaff);
> + vsc8584_csr_write(bus, phy0, 0x0fec, 0x00901809);
> + vsc8584_csr_write(bus, phy0, 0x0ffe, 0x00b01007);
> + vsc8584_csr_write(bus, phy0, 0x16b0, 0x00eeff00);
> + vsc8584_csr_write(bus, phy0, 0x16b2, 0x00007000);
> + vsc8584_csr_write(bus, phy0, 0x16b4, 0x00000814);
> +
> + bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
> + MSCC_PHY_PAGE_EXT2);
> +
> + bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_PHY_CU_PMD_TX_CNTL, 0x028e);
> +
> + bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
> + MSCC_PHY_PAGE_TR);
> +
> + vsc8584_csr_write(bus, phy0, 0x0486, 0x0008a518);
> + vsc8584_csr_write(bus, phy0, 0x0488, 0x006dc696);
> + vsc8584_csr_write(bus, phy0, 0x048a, 0x00000912);
> +
> + bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
> + MSCC_PHY_PAGE_TEST);
> +
> + reg = bus->read(bus, phy0, MDIO_DEVAD_NONE, MSCC_PHY_TEST_PAGE_8);
> + reg &= ~0x8000;
> + bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_PHY_TEST_PAGE_8, reg);
> +
> + bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
> + MSCC_PHY_PAGE_STD);
> +
> + reg = bus->read(bus, phy0, MDIO_DEVAD_NONE, MSCC_PHY_EXT_CNTL_STATUS);
> + reg &= ~SMI_BROADCAST_WR_EN;
> + bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_PHY_EXT_CNTL_STATUS, reg);
> +
> + ret = vsc8584_get_fw_crc(bus, phy0,
> + MSCC_VSC8584_REVB_INT8051_FW_START_ADDR, &crc,
> + fw_patch_vsc8584,
> + ARRAY_SIZE(fw_patch_vsc8584));
> + if (ret)
> + goto out;
> +
> + if (crc != MSCC_VSC8584_REVB_INT8051_FW_CRC) {
> + debug("FW CRC is not the expected one, patching FW...\n");
> + if (vsc8584_patch_fw(bus, phy0, fw_patch_vsc8584,
> + ARRAY_SIZE(fw_patch_vsc8584)))
> + pr_warn("failed to patch FW, expect non-optimal device\n");
> + }
> +
> + vsc8584_micro_deassert_reset(bus, phy0, false);
> +
> + ret = vsc8584_get_fw_crc(bus, phy0,
> + MSCC_VSC8584_REVB_INT8051_FW_START_ADDR, &crc,
> + fw_patch_vsc8584,
> + ARRAY_SIZE(fw_patch_vsc8584));
> + if (ret)
> + goto out;
> +
> + if (crc != MSCC_VSC8584_REVB_INT8051_FW_CRC)
> + pr_warn("FW CRC after patching is not the expected one, expect non-optimal device\n");
> +
> + ret = vsc8584_micro_assert_reset(bus, phy0);
> + if (ret)
> + goto out;
> +
> + vsc8584_micro_deassert_reset(bus, phy0, true);
> +
> +out:
> + bus->write(bus, phy0, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
> + MSCC_PHY_PAGE_STD);
> +
> + return ret;
> +}
>
> static int mscc_vsc8531_vsc8541_init_scripts(struct phy_device *phydev)
> {
> @@ -457,6 +898,96 @@ static int vsc8541_config(struct phy_device *phydev)
> return genphy_config_aneg(phydev);
> }
>
> +static int vsc8584_config(struct phy_device *phydev)
> +{
> + int ret;
> + u16 addr;
> + u16 reg_val;
> + u16 val;
> + u16 base_addr;
> +
> + phy_write(phydev, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
> + MSCC_PHY_PAGE_EXT1);
> + addr = phy_read(phydev, MDIO_DEVAD_NONE, MSCC_PHY_EXT_PHY_CNTL_4);
> + addr >>= PHY_CNTL_4_ADDR_POS;
> +
> + val = phy_read(phydev, MDIO_DEVAD_NONE, MSCC_PHY_ACTIPHY_CNTL);
> + if (val & PHY_ADDR_REVERSED)
> + base_addr = phydev->addr + addr;
> + else
> + base_addr = phydev->addr - addr;
> +
> + if ((phydev->phy_id & MSCC_DEV_REV_MASK) != VSC8584_REVB) {
> + pr_warn("VSC8584 revA not officially supported, skipping firmware patching. Use at your own risk.\n");
> + } else {
> + ret = vsc8584_config_pre_init(phydev->bus, base_addr);
> + if (ret)
> + return ret;
> + }
> +
> + phy_write(phydev, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
> + MSCC_PHY_PAGE_GPIO);
> +
> + if (phydev->interface == PHY_INTERFACE_MODE_QSGMII)
> + val = MAC_CFG_QSGMII;
> + else
> + val = MAC_CFG_SGMII;
> +
> + reg_val = phy_read(phydev, MDIO_DEVAD_NONE, MSCC_PHY_MAC_CFG_FASTLINK);
> + reg_val &= ~MAC_CFG_MASK;
> + reg_val |= val;
> + ret = phy_write(phydev, MDIO_DEVAD_NONE, MSCC_PHY_MAC_CFG_FASTLINK,
> + reg_val);
> + if (ret)
> + return ret;
> +
> + reg_val = PROC_CMD_MCB_ACCESS_MAC_CONF | PROC_CMD_RST_CONF_PORT |
> + PROC_CMD_READ_MOD_WRITE_PORT;
> + if (phydev->interface == PHY_INTERFACE_MODE_QSGMII)
> + reg_val |= PROC_CMD_QSGMII_MAC;
> + else
> + reg_val |= PROC_CMD_SGMII_MAC;
> +
> + ret = vsc8584_cmd(phydev->bus, phydev->addr, reg_val);
> + if (ret)
> + return ret;
> +
> + mdelay(10);
> +
> + /* Disable SerDes for 100Base-FX */
> + ret = vsc8584_cmd(phydev->bus, phydev->addr, PROC_CMD_FIBER_MEDIA_CONF |
> + PROC_CMD_FIBER_PORT(addr) | PROC_CMD_FIBER_DISABLE |
> + PROC_CMD_READ_MOD_WRITE_PORT |
> + PROC_CMD_RST_CONF_PORT | PROC_CMD_FIBER_100BASE_FX);
> + if (ret)
> + return ret;
> +
> + /* Disable SerDes for 1000Base-X */
> + ret = vsc8584_cmd(phydev->bus, phydev->addr, PROC_CMD_FIBER_MEDIA_CONF |
> + PROC_CMD_FIBER_PORT(addr) | PROC_CMD_FIBER_DISABLE |
> + PROC_CMD_READ_MOD_WRITE_PORT |
> + PROC_CMD_RST_CONF_PORT | PROC_CMD_FIBER_1000BASE_X);
> + if (ret)
> + return ret;
> +
> + phy_write(phydev, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
> + MSCC_PHY_PAGE_STD);
> + reg_val = phy_read(phydev, MDIO_DEVAD_NONE,
> + MSCC_PHY_EXT_PHY_CNTL_1_REG);
> + reg_val &= ~(MEDIA_OP_MODE_MASK | VSC8584_MAC_IF_SELECTION_MASK);
> + reg_val |= MEDIA_OP_MODE_COPPER |
> + (VSC8584_MAC_IF_SELECTION_SGMII <<
> + VSC8584_MAC_IF_SELECTION_POS);
> + ret = phy_write(phydev, MDIO_DEVAD_NONE, MSCC_PHY_EXT_PHY_CNTL_1_REG,
> + reg_val);
> +
> + ret = mscc_phy_soft_reset(phydev);
> + if (ret != 0)
> + return ret;
> +
> + return genphy_config(phydev);
> +}
> +
> static struct phy_driver VSC8530_driver = {
> .name = "Microsemi VSC8530",
> .uid = PHY_ID_VSC8530,
> @@ -497,12 +1028,23 @@ static struct phy_driver VSC8541_driver = {
> .shutdown = &genphy_shutdown,
> };
>
> +static struct phy_driver VSC8584_driver = {
> + .name = "Microsemi VSC8584",
> + .uid = PHY_ID_VSC8584,
> + .mask = 0x000ffff0,
> + .features = PHY_GBIT_FEATURES,
> + .config = &vsc8584_config,
> + .startup = &mscc_startup,
> + .shutdown = &genphy_shutdown,
> +};
> +
> int phy_mscc_init(void)
> {
> phy_register(&VSC8530_driver);
> phy_register(&VSC8531_driver);
> phy_register(&VSC8540_driver);
> phy_register(&VSC8541_driver);
> + phy_register(&VSC8584_driver);
>
> return 0;
> }
> --
> 2.17.1
>
> _______________________________________________
> U-Boot mailing list
> U-Boot at lists.denx.de
> https://lists.denx.de/listinfo/u-boot