From ff0a75fa9f1506b62b6b3820774340ad331df26a Mon Sep 17 00:00:00 2001 From: Florin Chiculita Date: Mon, 27 Apr 2020 16:59:04 +0300 Subject: [PATCH] net: phy: inphi in112525 gen1/gen2 retimers support Add support for Inphi retimers used on LX2160A-RDB/QDS boards Add support for M13,M8 mezzanine cards using Inphi retimers Gen1 configurations: 10/25 Gen2 configurations: 10/25 + 40/50/100G (using 2 retimers) Speeds are configurable via menuconfig options Signed-off-by: Florin Chiculita Signed-off-by: Priyanka Jain # rebased Signed-off-by: Ioana Ciornei # rebased over v2021.04 --- configs/lx2160aqds_tfa_SECURE_BOOT_defconfig | 1 + configs/lx2160aqds_tfa_defconfig | 1 + configs/lx2160ardb_tfa_SECURE_BOOT_defconfig | 1 + configs/lx2160ardb_tfa_defconfig | 1 + drivers/net/phy/Kconfig | 63 + drivers/net/phy/Makefile | 1 + drivers/net/phy/in112525.c | 1089 ++++++++++++++++++ drivers/net/phy/phy.c | 3 + include/configs/lx2160aqds.h | 12 +- include/configs/lx2160ardb.h | 14 +- include/in112525.h | 236 ++++ include/linux/ethtool.h | 4 + include/phy.h | 3 +- 13 files changed, 1426 insertions(+), 3 deletions(-) create mode 100644 drivers/net/phy/in112525.c create mode 100644 include/in112525.h diff --git a/configs/lx2160aqds_tfa_SECURE_BOOT_defconfig b/configs/lx2160aqds_tfa_SECURE_BOOT_defconfig index 5a7ffe9c4a..eb1aa453fe 100644 --- a/configs/lx2160aqds_tfa_SECURE_BOOT_defconfig +++ b/configs/lx2160aqds_tfa_SECURE_BOOT_defconfig @@ -57,6 +57,7 @@ CONFIG_PHYLIB=y CONFIG_PHY_AQUANTIA=y CONFIG_PHY_CORTINA=y CONFIG_SYS_CORTINA_NO_FW_UPLOAD=y +CONFIG_PHY_INPHI=y CONFIG_PHY_REALTEK=y CONFIG_PHY_VITESSE=y CONFIG_DM_ETH=y diff --git a/configs/lx2160aqds_tfa_defconfig b/configs/lx2160aqds_tfa_defconfig index ed6f78b099..403c101485 100644 --- a/configs/lx2160aqds_tfa_defconfig +++ b/configs/lx2160aqds_tfa_defconfig @@ -64,6 +64,7 @@ CONFIG_PHYLIB=y CONFIG_PHY_AQUANTIA=y CONFIG_PHY_CORTINA=y CONFIG_SYS_CORTINA_NO_FW_UPLOAD=y +CONFIG_PHY_INPHI=y CONFIG_PHY_REALTEK=y CONFIG_PHY_VITESSE=y CONFIG_DM_ETH=y diff --git a/configs/lx2160ardb_tfa_SECURE_BOOT_defconfig b/configs/lx2160ardb_tfa_SECURE_BOOT_defconfig index bb92fcb6c5..8c915b516b 100644 --- a/configs/lx2160ardb_tfa_SECURE_BOOT_defconfig +++ b/configs/lx2160ardb_tfa_SECURE_BOOT_defconfig @@ -53,6 +53,7 @@ CONFIG_PHY_AQUANTIA=y CONFIG_PHY_ATHEROS=y CONFIG_PHY_CORTINA=y CONFIG_SYS_CORTINA_NO_FW_UPLOAD=y +CONFIG_PHY_INPHI=y CONFIG_DM_ETH=y CONFIG_DM_MDIO=y CONFIG_E1000=y diff --git a/configs/lx2160ardb_tfa_defconfig b/configs/lx2160ardb_tfa_defconfig index 1d3b59c849..2aa5c0e841 100644 --- a/configs/lx2160ardb_tfa_defconfig +++ b/configs/lx2160ardb_tfa_defconfig @@ -62,6 +62,7 @@ CONFIG_PHY_AQUANTIA=y CONFIG_PHY_ATHEROS=y CONFIG_PHY_CORTINA=y CONFIG_SYS_CORTINA_NO_FW_UPLOAD=y +CONFIG_PHY_INPHI=y CONFIG_DM_ETH=y CONFIG_DM_MDIO=y CONFIG_E1000=y diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index 070ffa82cb..2d53cd5d56 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -144,6 +144,69 @@ config PHY_DAVICOM config PHY_ET1011C bool "LSI TruePHY ET1011C support" +config PHY_INPHI + bool "Inphi IN112525 gen1/gen2 retimers support" + help + This enables the IN112525 Retimer support + Supported speeds: + gen1(S03): 10G/25G + gen2(S05): 10G/25G & 40G/50G/100G using multiple chips + Phy initialization sequence differs depending on target + configurations: 10G or 25G. Higher speeds refer to use-cases + where multiple retimers are used on multiple data-paths and + speed adds up (e.g. 100G = 4x25G lanes -> 2 retimers). + The driver also uses a microcode firmware in the form of + address-data tuples for phy initialization +choice + prompt "Speed selection for INPHI in112525 s03 retimer" + default IN112525_S03_25G + help + Configure the speed settings for the Inphi S03 retimer + Default speed is 25G, but 10G speed can be set using + a half-rate configuration + +config IN112525_S03_10G + bool "10G" + help + 10G settings (half-rate) for Inphi in112525 gen1 retimer +config IN112525_S03_25G + bool "25G" + help + 25G settings for Inphi in112525 gen1 retimer +endchoice + +choice + prompt "Speed selection for INPHI in112525 s05 retimer" + default IN112525_S05_100G + help + This configures the IN112525_S05 driver for different speeds. + For 40G and 100G speeds two Inphi retimers must be used. Each + chip has two data paths and each path can support 10G or 25G. + +config IN112525_S05_10G + bool "10G" + help + 10G settings for Inphi in112525 gen2 retimer +config IN112525_S05_25G + bool "25G" + help + 25G settings for Inphi in112525 gen2 retimer + Two lanes are shutdown for individual port operation +config IN112525_S05_40G + bool "40G" + help + 40G settings for Inphi in112525 gen2 retimer +config IN112525_S05_50G + bool "50G" + help + 50G settings for Inphi in112525 gen2 retimer + +config IN112525_S05_100G + bool "100G" + help + 100G best settings for Inphi in112525 gen2 retimer +endchoice + config PHY_LXT bool "LXT971 Ethernet PHY support" diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index b56a672b06..fa7d9e20a6 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_MV88E6352_SWITCH) += mv88e6352.o obj-$(CONFIG_PHYLIB) += phy.o obj-$(CONFIG_PHYLIB_10G) += generic_10g.o obj-$(CONFIG_PHY_AQUANTIA) += aquantia.o +obj-$(CONFIG_PHY_INPHI) += in112525.o obj-$(CONFIG_PHY_ATHEROS) += atheros.o obj-$(CONFIG_PHY_BROADCOM) += broadcom.o obj-$(CONFIG_PHY_CORTINA) += cortina.o cortina_api.o diff --git a/drivers/net/phy/in112525.c b/drivers/net/phy/in112525.c new file mode 100644 index 0000000000..026a82e979 --- /dev/null +++ b/drivers/net/phy/in112525.c @@ -0,0 +1,1089 @@ +/* + * Copyright 2018-2021 NXP + * Copyright 2018 INPHI + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Inphi is a registered trademark of Inphi Corporation + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_SYS_IN112525_FW_IN_MMC +#include +#endif + +#ifndef CONFIG_PHYLIB_10G +#error The INPHI PHY needs 10G support +#endif + +/* lookup table to map the multiply options and MUX selects to the bit values */ +unsigned char tx_pll_mpy_map[][2] = { + /* MPY MS, LS */ + [10] = {0, 0}, + [20] = {1, 0}, + [40] = {2, 0}, + [8] = {0, 1}, + [16] = {1, 1}, + [32] = {2, 1}, + [33] = {1, 6}, + [66] = {2, 6}, + [15] = {0, 7}, + [30] = {1, 7}, + [60] = {2, 7}, +}; + +static struct in112525_config inphi_s03_config[] = { + [INIT_OC192] = { .enable_otu_protocol = 0, + .enable_external_refclk = 0, + .enable_prescaler = 0, + .tx_pll_mpy_ratio = 20, + .enable_half_rate = 1, + .enable_extended_range = 1, + .tx_pll_refclk_source = RECOV_CLK, + .ctle_mode = MODE_25_25_10, + .rx_common_mode = 3, + .rx_odt_override = 0, + .l0_phase_adjust_val = IN112525_PHASE_ADJUST_VAL, + .l1_phase_adjust_val = IN112525_PHASE_ADJUST_VAL, + .l2_phase_adjust_val = IN112525_PHASE_ADJUST_VAL, + .l3_phase_adjust_val = IN112525_PHASE_ADJUST_VAL + }, + + [INIT_10GE] = { .enable_otu_protocol = 0, + .enable_external_refclk = 0, + .enable_prescaler = 0, + .tx_pll_mpy_ratio = 20, + .enable_half_rate = 1, + .enable_extended_range = 1, + .tx_pll_refclk_source = RECOV_CLK, + .ctle_mode = MODE_25_25_10, + .rx_common_mode = 3, + .rx_odt_override = 0, + .l0_phase_adjust_val = IN112525_PHASE_ADJUST_VAL, + .l1_phase_adjust_val = IN112525_PHASE_ADJUST_VAL, + .l2_phase_adjust_val = IN112525_PHASE_ADJUST_VAL, + .l3_phase_adjust_val = IN112525_PHASE_ADJUST_VAL + }, + + [INIT_16GFC] = { .enable_otu_protocol = 0, + .enable_external_refclk = 0, + .enable_prescaler = 0, + .tx_pll_mpy_ratio = 20, + .enable_half_rate = 1, + .enable_extended_range = 1, + .tx_pll_refclk_source = RECOV_CLK, + .ctle_mode = MODE_25_25_10, + .rx_common_mode = 3, + .rx_odt_override = 0, + .l0_phase_adjust_val = IN112525_PHASE_ADJUST_VAL, + .l1_phase_adjust_val = IN112525_PHASE_ADJUST_VAL, + .l2_phase_adjust_val = IN112525_PHASE_ADJUST_VAL, + .l3_phase_adjust_val = IN112525_PHASE_ADJUST_VAL + }, + + [INIT_20GE] = { .enable_otu_protocol = 0, + .enable_external_refclk = 0, + .enable_prescaler = 0, + .tx_pll_mpy_ratio = 10, + .enable_half_rate = 0, + .enable_extended_range = 1, + .tx_pll_refclk_source = RECOV_CLK, + .ctle_mode = MODE_25_25_10, + .rx_common_mode = 3, + .rx_odt_override = 0, + .l0_phase_adjust_val = IN112525_PHASE_ADJUST_VAL, + .l1_phase_adjust_val = IN112525_PHASE_ADJUST_VAL, + .l2_phase_adjust_val = IN112525_PHASE_ADJUST_VAL, + .l3_phase_adjust_val = IN112525_PHASE_ADJUST_VAL + }, + + [INIT_100GE] = { .enable_otu_protocol = 0, + .enable_external_refclk = 0, + .enable_prescaler = 0, + .tx_pll_mpy_ratio = 10, + .enable_half_rate = 0, + .enable_extended_range = 0, + .tx_pll_refclk_source = RECOV_CLK, + .ctle_mode = MODE_25_25_10, + .rx_common_mode = 3, + .rx_odt_override = 0, + .l0_phase_adjust_val = IN112525_PHASE_ADJUST_VAL, + .l1_phase_adjust_val = IN112525_PHASE_ADJUST_VAL, + .l2_phase_adjust_val = IN112525_PHASE_ADJUST_VAL, + .l3_phase_adjust_val = IN112525_PHASE_ADJUST_VAL + }, + + [INIT_25GE] = { .enable_otu_protocol = 0, + .enable_external_refclk = 0, + .enable_prescaler = 0, + .tx_pll_mpy_ratio = 10, + .enable_half_rate = 0, + .enable_extended_range = 0, + .tx_pll_refclk_source = RECOV_CLK, + .ctle_mode = MODE_25_25_10, + .rx_common_mode = 3, + .rx_odt_override = 0, + .l0_phase_adjust_val = IN112525_PHASE_ADJUST_VAL, + .l1_phase_adjust_val = IN112525_PHASE_ADJUST_VAL, + .l2_phase_adjust_val = IN112525_PHASE_ADJUST_VAL, + .l3_phase_adjust_val = IN112525_PHASE_ADJUST_VAL + }, + + [INIT_OTU4] = { .enable_otu_protocol = 1, + .enable_external_refclk = 0, + .enable_prescaler = 0, + .tx_pll_mpy_ratio = 10, + .enable_half_rate = 0, + .enable_extended_range = 0, + .tx_pll_refclk_source = RECOV_CLK, + .ctle_mode = MODE_25_25_10, + .rx_common_mode = 3, + .rx_odt_override = 0, + .l0_phase_adjust_val = IN112525_PHASE_ADJUST_VAL, + .l1_phase_adjust_val = IN112525_PHASE_ADJUST_VAL, + .l2_phase_adjust_val = IN112525_PHASE_ADJUST_VAL, + .l3_phase_adjust_val = IN112525_PHASE_ADJUST_VAL + }, + + [INIT_32GFC] = { .enable_otu_protocol = 0, + .enable_external_refclk = 0, + .enable_prescaler = 0, + .tx_pll_mpy_ratio = 10, + .enable_half_rate = 0, + .enable_extended_range = 0, + .tx_pll_refclk_source = RECOV_CLK, + .ctle_mode = MODE_25_25_10, + .rx_common_mode = 3, + .rx_odt_override = 0, + .l0_phase_adjust_val = IN112525_PHASE_ADJUST_VAL, + .l1_phase_adjust_val = IN112525_PHASE_ADJUST_VAL, + .l2_phase_adjust_val = IN112525_PHASE_ADJUST_VAL, + .l3_phase_adjust_val = IN112525_PHASE_ADJUST_VAL + }, + + [INIT_F28P2G] = { .enable_otu_protocol = 0, + .enable_external_refclk = 0, + .enable_prescaler = 0, + .tx_pll_mpy_ratio = 10, + .enable_half_rate = 0, + .enable_extended_range = 0, + .tx_pll_refclk_source = RECOV_CLK, + .ctle_mode = MODE_25_25_10, + .rx_common_mode = 3, + .rx_odt_override = 0, + .l0_phase_adjust_val = IN112525_PHASE_ADJUST_VAL, + .l1_phase_adjust_val = IN112525_PHASE_ADJUST_VAL, + .l2_phase_adjust_val = IN112525_PHASE_ADJUST_VAL, + .l3_phase_adjust_val = IN112525_PHASE_ADJUST_VAL + } +}; + +static struct in112525_s03_vco_codes s03_vco_codes; + +#ifdef CONFIG_IN112525_S03_25G +#define CURRENT_CONFIG (inphi_s03_config[INIT_25GE]) +#else +#define CURRENT_CONFIG (inphi_s03_config[INIT_10GE]) +#endif + +#define ALL_LANES 4 +#define mdio_wr(a, b) phy_write(inphi_phydev, MDIO_MMD_VEND1, (a), (b)) +#define mdio_rd(a) phy_read(inphi_phydev, MDIO_MMD_VEND1, (a)) + +struct phy_device *inphi_phydev; + +int in112525_upload_firmware(struct phy_device *phydev) +{ + char line_temp[0x50] = {0}; + char reg_addr[0x50] = {0}; + char reg_data[0x50] = {0}; + int i, line_cnt = 0, column_cnt = 0; + struct in112525_reg_config fw_temp; + char *addr = NULL; + + addr = (char *)IN112525_FW_ADDR; + +#if defined(CONFIG_SYS_IN112525_FW_IN_MMC) + int dev = CONFIG_SYS_MMC_ENV_DEV; + u32 cnt = IN112525_FW_LENGTH / 512; + u32 blk = IN112525_FW_ADDR / 512; + struct mmc *mmc = find_mmc_device(CONFIG_SYS_MMC_ENV_DEV); + + if (!mmc) { + puts("Failed to find MMC device for IN112525 ucode\n"); + } else { + addr = malloc(IN112525_FW_LENGTH); + printf("MMC read: dev # %u, block # %u, count %u ...\n", + dev, blk, cnt); + mmc_init(mmc); + (void)mmc->block_dev.block_read(&mmc->block_dev, blk, cnt, + addr); + /* flush cache after read */ + flush_cache((ulong)addr, cnt * 512); + } +#endif + while (*addr != 'Q') { + i = 0; + + while (*addr != 0xa) { + line_temp[i++] = *addr++; + if (i > 0x50) { + printf("IN112525 ucode not found @ 0x%p\n", + (char *)IN112525_FW_ADDR); + return -1; + } + } + + addr++; /* skip '\n' */ + line_cnt++; + column_cnt = i; + line_temp[column_cnt] = '\0'; + + if (line_cnt > IN112525_FW_LENGTH) { + printf("IN112525 ucode not found @ 0x%p\n", + (char *)IN112525_FW_ADDR); + return -1; + } + for (i = 0; i < column_cnt; i++) { + if (isspace(line_temp[i++])) + break; + } + + memcpy(reg_addr, line_temp, i); + memcpy(reg_data, &line_temp[i], column_cnt - i); + strim(reg_addr); + strim(reg_data); + fw_temp.reg_addr = (simple_strtoul(reg_addr, NULL, 0)) & 0xffff; + fw_temp.reg_value = (simple_strtoul(reg_data, NULL, 0)) & + 0xffff; + /* check if garbage is present at ucode location */ + if (fw_temp.reg_addr < 0x700) { + printf("IN112525 ucode not found @ 0x%p\n", + (char *)IN112525_FW_ADDR); + return -1; + } + phy_write(phydev, MDIO_MMD_VEND1, fw_temp.reg_addr, + fw_temp.reg_value); + } + return 0; +} + +int in112525_s05_phy_init(struct phy_device *phydev) +{ + u32 reg_value, ret; + u32 l0_vco_code, l1_vco_code, l2_vco_code, l3_vco_code; + + phy_write(phydev, MDIO_MMD_VEND1, PHYMISC_REG11, 0); + phy_write(phydev, MDIO_MMD_VEND1, PHYMISC_REG11, IN112525_FORCE_PC); + phy_write(phydev, MDIO_MMD_VEND1, PHYMISC_REG11, IN112525_LOL_CTRL); + + /* The S05 retimer seems to work only when ALL lanes are locked. + * Datapath is Lane0<->Lane1, Lane2<->Lane3. + * For individual lane operation to work, pair 0-1 must be disabled + */ + phy_write(phydev, MDIO_MMD_VEND1, PHYCTRL_REG0, + IN112525_MDIOINIT | IN112525_HRESET | +#if defined(CONFIG_IN112525_S05_10G) || defined(CONFIG_IN112525_S05_25G) + IN112525_LANE0_DISABLE | IN112525_LANE1_DISABLE | +#endif + IN112525_SRESET + ); + + phy_write(phydev, MDIO_MMD_VEND1, PHYCTRL_REG0, + IN112525_HRESET | +#if defined(CONFIG_IN112525_S05_10G) || defined(CONFIG_IN112525_S05_25G) + IN112525_LANE0_DISABLE | IN112525_LANE1_DISABLE | +#endif + IN112525_SRESET + ); + + phy_write(phydev, MDIO_MMD_VEND1, PHYMISC_REG3, + IN112525_EXT_REFCLK_EN | IN112525_CTLE_10G); + + phy_write(phydev, MDIO_MMD_VEND1, PHYCTRL_REG0, +#if defined(CONFIG_IN112525_S05_10G) || defined(CONFIG_IN112525_S05_25G) + IN112525_LANE0_DISABLE | IN112525_LANE1_DISABLE | +#endif + IN112525_SRESET); + + mdelay(10); + + reg_value = phy_read(phydev, MDIO_MMD_VEND1, IN112525_EFUSE_REG); + if (!(reg_value & IN112525_EFUSE_DONE)) { + printf("IN112525 phy init failed: EFUSE Done not set\n"); + return -1; + } + + udelay(100); + + reg_value = phy_read(phydev, MDIO_MMD_VEND1, PHYMISC_REG2); + if (!(reg_value & IN112525_CALIBRATION_DONE)) { + printf("IN112525 phy init failed: CAL DONE not set\n"); + return -1; + } + + phy_write(phydev, MDIO_MMD_VEND1, PHYMISC_REG2, + (IN112525_RX_PLL_RESET | IN112525_TX_PLL_RESET | + IN112525_CORE_DATAPATH_RESET | IN112525_TX_SERDES_RESET)); + + phy_write(phydev, MDIO_MMD_VEND1, PHYCTRL_REG0, + IN112525_MANUALRESET_SELECT | +#if defined(CONFIG_IN112525_S05_10G) || defined(CONFIG_IN112525_S05_25G) + IN112525_LANE0_DISABLE | IN112525_LANE1_DISABLE | +#endif + IN112525_SRESET); + +#if defined(CONFIG_IN112525_S05_25G) || defined(CONFIG_IN112525_S05_50G) + phy_write(phydev, MDIO_MMD_VEND1, PHYCTRL_REG15, PHYCTRL_REG15_VAL); + phy_write(phydev, MDIO_MMD_VEND1, PHYCTRL_REG17, PHYCTRL_REG17_VAL); +#else + /* 100G requires specific extended-range settings */ + phy_write(phydev, MDIO_MMD_VEND1, PHYCTRL_REG15, PHYCTRL_REG15_VAL_EXT); + phy_write(phydev, MDIO_MMD_VEND1, PHYCTRL_REG17, PHYCTRL_REG17_VAL_EXT); +#endif + + /* chip internals */ + phy_write(phydev, MDIO_MMD_VEND1, PHYCTRL_REG18, 0xff); + phy_write(phydev, MDIO_MMD_VEND1, PHYCTRL_REG19, 0x2d); + phy_write(phydev, MDIO_MMD_VEND1, PHYCTRL_REG19, 0x802d); + phy_write(phydev, MDIO_MMD_VEND1, PHYCTRL_REG19, 0x0); + + phy_write(phydev, MDIO_MMD_VEND1, PHYCTRL_REG18, 0xe9); + phy_write(phydev, MDIO_MMD_VEND1, PHYCTRL_REG19, 0x8); + phy_write(phydev, MDIO_MMD_VEND1, PHYCTRL_REG19, 0x8008); + phy_write(phydev, MDIO_MMD_VEND1, PHYCTRL_REG19, 0x0); + + phy_write(phydev, MDIO_MMD_VEND1, PHYCTRL_REG11, + IN112525_TXPLL_MSDIV | IN112525_TXPLL_IQDIV); + +#if defined(CONFIG_IN112525_S05_10G) || defined(CONFIG_IN112525_S05_40G) + phy_write(phydev, MDIO_MMD_VEND1, PHYCTRL_REG14, + IN112525_RX_HALFRATE_EN); + + phy_write(phydev, MDIO_MMD_VEND1, PHYCTRL_REG13, PHYCTRL_REG13_VAL); + + phy_write(phydev, MDIO_MMD_VEND1, PHYCTRL_REG20, + IN112525_RX_LOS_EN | IN112525_RX_LOS_10G_THRESHOLD); + + phy_write(phydev, MDIO_MMD_VEND1, PHYMISC_REG30, + IN112525_RX_MISC_TRIM1_VAL); +#else + phy_write(phydev, MDIO_MMD_VEND1, PHYCTRL_REG13, + PHYCTRL_REG13_VAL | IN112525_LOSD_HYSTERESIS_EN); + + phy_write(phydev, MDIO_MMD_VEND1, PHYCTRL_REG20, + IN112525_RX_LOS_EN | IN112525_RX_LOS_100G_THRESHOLD); +#endif + phy_write(phydev, MDIO_MMD_VEND1, PHYMISC_REG8, IN112525_FA_WIN_SIZE); + +#if !defined(CONFIG_IN112525_S05_10G) && !defined(CONFIG_IN112525_S05_40G) + /* specific stuff required when not in half-rate operation before + * reading VCO codes + */ + phy_write(phydev, MDIO_MMD_VEND1, PHYCTRL_REG2, 0x5000); + phy_write(phydev, MDIO_MMD_VEND1, 0x501, 0x0200); + phy_write(phydev, MDIO_MMD_VEND1, 0x510, 0x001F); + phy_write(phydev, MDIO_MMD_VEND1, 0x517, 0x803F); +#endif + + /* actual VCO codes reading; save codes for later */ + l0_vco_code = phy_read(phydev, MDIO_MMD_VEND1, PHYMISC_REG7); + l1_vco_code = phy_read(phydev, MDIO_MMD_VEND1, PHYMISC_REG7 + 0x100); + l2_vco_code = phy_read(phydev, MDIO_MMD_VEND1, PHYMISC_REG7 + 0x200); + l3_vco_code = phy_read(phydev, MDIO_MMD_VEND1, PHYMISC_REG7 + 0x300); + + +#if defined(CONFIG_IN112525_S05_10G) || defined(CONFIG_IN112525_S05_40G) + /* adjust VCOs with 20/25 ratio when in half-rate operation */ + l0_vco_code *= 0.8; + l1_vco_code *= 0.8; + l2_vco_code *= 0.8; + l3_vco_code *= 0.8; + +#endif + + ret = in112525_upload_firmware(phydev); + if (ret) { + printf("IN112525: upload firmware failed\n"); + return -1; + } + + phy_write(phydev, MDIO_MMD_VEND1, 0x73b, l0_vco_code); + phy_write(phydev, MDIO_MMD_VEND1, 0x73c, l1_vco_code); + phy_write(phydev, MDIO_MMD_VEND1, 0x73d, l2_vco_code); + phy_write(phydev, MDIO_MMD_VEND1, 0x73e, l3_vco_code); + + phy_write(phydev, MDIO_MMD_VEND1, 0x737, IN112525_PHASE_ADJUST_VAL); + phy_write(phydev, MDIO_MMD_VEND1, 0x738, IN112525_PHASE_ADJUST_VAL); + phy_write(phydev, MDIO_MMD_VEND1, 0x739, IN112525_PHASE_ADJUST_VAL); + phy_write(phydev, MDIO_MMD_VEND1, 0x73a, IN112525_PHASE_ADJUST_VAL); + + phy_write(phydev, MDIO_MMD_VEND1, PHYMISC_REG12, IN112525_USEQ_FL); + + printf("IN112525: starting ucode...\n"); + phy_write(phydev, MDIO_MMD_VEND1, PHYMISC_REG11, + IN112525_LOL_CTRL | IN112525_USEQ_EN); + + return 0; +} + +int in112525_s05_config(struct phy_device *phydev) +{ + return in112525_s05_phy_init(phydev); +} + +int tx_pll_lock_test(int lane) +{ + int i, val, locked = 1; + + if (lane == ALL_LANES) { + for (i = 0; i < ALL_LANES; i++) { + val = mdio_rd(i * 0x100 + PHYSTAT_REG3); + locked = locked & bit_test(val, 15); + } + } else { + val = mdio_rd(lane * 0x100 + PHYSTAT_REG3); + locked = locked & bit_test(val, 15); + } + + return locked; +} + +void tx_pll_assert(int lane) +{ + int val, recal; + + if (lane == ALL_LANES) { + val = mdio_rd(PHYMISC_REG2); + recal = (1 << 12); + mdio_wr(PHYMISC_REG2, val | recal); + } else { + val = mdio_rd(lane * 0x100 + PHYCTRL_REG4); + recal = (1 << 15); + mdio_wr(lane * 0x100 + PHYCTRL_REG4, val | recal); + } +} + +void tx_pll_de_assert(int lane) +{ + int recal, val; + + if (lane == ALL_LANES) { + val = mdio_rd(PHYMISC_REG2); + recal = 0xefff; + mdio_wr(PHYMISC_REG2, val & recal); + } else { + val = mdio_rd(lane * 0x100 + PHYCTRL_REG4); + recal = 0x7fff; + mdio_wr(lane * 0x100 + PHYCTRL_REG4, val & recal); + } +} + +void tx_core_assert(int lane) +{ + int recal, val, val2, core_reset; + + if (lane == 4) { + val = mdio_rd(PHYMISC_REG2); + recal = 1 << 10; + mdio_wr(PHYMISC_REG2, val | recal); + } else { + val2 = mdio_rd(PHYMISC_REG3); + core_reset = (1 << (lane + 8)); + mdio_wr(PHYMISC_REG3, val2 | core_reset); + } +} + +void lol_disable(int lane) +{ + int val, mask; + + val = mdio_rd(PHYMISC_REG3); + mask = 1 << (lane + 4); + mdio_wr(PHYMISC_REG3, val | mask); +} + +void tx_core_de_assert(int lane) +{ + int val, recal, val2, core_reset; + + if (lane == ALL_LANES) { + val = mdio_rd(PHYMISC_REG2); + recal = 0xffff - (1 << 10); + mdio_wr(PHYMISC_REG2, val & recal); + } else { + val2 = mdio_rd(PHYMISC_REG3); + core_reset = 0xffff - (1 << (lane + 8)); + mdio_wr(PHYMISC_REG3, val2 & core_reset); + } +} + +void tx_restart(int lane) +{ + tx_core_assert(lane); + tx_pll_assert(lane); + tx_pll_de_assert(lane); + WAIT(150); + tx_core_de_assert(lane); +} + +void disable_lane(int lane) +{ + rx_reset_assert(lane); + rx_powerdown_assert(lane); + tx_core_assert(lane); + lol_disable(lane); +} + +void WAIT(int delay_cycles) +{ + udelay(delay_cycles * 10); +} + +int bit_test(int value, int bit_field) +{ + int bit_mask = (1 << bit_field); + int result; + + result = ((value & bit_mask) == bit_mask); + return result; +} + +void toggle_reset(int lane) +{ + int reg, val, orig; + + if (lane == ALL_LANES) { + mdio_wr(PHYMISC_REG2, 0x8000); + WAIT(10); + mdio_wr(PHYMISC_REG2, 0x0000); + } else { + reg = lane * 0x100 + PHYCTRL_REG8; + val = (1 << 6); + orig = mdio_rd(reg); + mdio_wr(reg, orig + val); + WAIT(10); + mdio_wr(reg, orig); + } +} + +int az_complete_test(int lane) +{ + int success = 1, value; + + if (lane == 0 || lane == ALL_LANES) { + value = mdio_rd(PHYCTRL_REG5); + success = success & bit_test(value, 2); + } + if (lane == 1 || lane == ALL_LANES) { + value = mdio_rd(PHYCTRL_REG5 + 0x100); + success = success & bit_test(value, 2); + } + if (lane == 2 || lane == ALL_LANES) { + value = mdio_rd(PHYCTRL_REG5 + 0x200); + success = success & bit_test(value, 2); + } + if (lane == 3 || lane == ALL_LANES) { + value = mdio_rd(PHYCTRL_REG5 + 0x300); + success = success & bit_test(value, 2); + } + + return success; +} + +void rx_reset_assert(int lane) +{ + int mask, val; + + if (lane == ALL_LANES) { + val = mdio_rd(PHYMISC_REG2); + mask = (1 << 15); + mdio_wr(PHYMISC_REG2, val + mask); + } else { + val = mdio_rd(lane * 0x100 + PHYCTRL_REG8); + mask = (1 << 6); + mdio_wr(lane * 0x100 + PHYCTRL_REG8, val + mask); + } +} + +void rx_reset_de_assert(int lane) +{ + int mask, val; + + if (lane == ALL_LANES) { + val = mdio_rd(PHYMISC_REG2); + mask = 0xffff - (1 << 15); + mdio_wr(PHYMISC_REG2, val & mask); + } else { + val = mdio_rd(lane*0x100 + PHYCTRL_REG8); + mask = 0xffff - (1 << 6); + mdio_wr(lane*0x100 + PHYCTRL_REG8, val & mask); + } +} + +void rx_powerdown_assert(int lane) +{ + int mask, val; + + val = mdio_rd(lane * 0x100 + PHYCTRL_REG8); + mask = (1 << 5); + mdio_wr(lane * 0x100 + PHYCTRL_REG8, val + mask); +} + +void rx_powerdown_de_assert(int lane) +{ + int mask, val; + + val = mdio_rd(lane * 0x100 + PHYCTRL_REG8); + mask = 0xffff - (1 << 5); + mdio_wr(lane * 0x100 + PHYCTRL_REG8, val & mask); +} + +void save_vco_codes(int lane) +{ + int value0, value1, value2, value3; + + if (lane == 0 || lane == ALL_LANES) { + value0 = mdio_rd(PHYMISC_REG5); + mdio_wr(PHYMISC_REG7, value0 + IN112525_RX_VCO_CODE_OFFSET); + s03_vco_codes.l0_vco_code = value0; + } + if (lane == 1 || lane == ALL_LANES) { + value1 = mdio_rd(PHYMISC_REG5 + 0x100); + mdio_wr(PHYMISC_REG7 + 0x100, + value1 + IN112525_RX_VCO_CODE_OFFSET); + s03_vco_codes.l1_vco_code = value1; + } + if (lane == 2 || lane == ALL_LANES) { + value2 = mdio_rd(PHYMISC_REG5 + 0x200); + mdio_wr(PHYMISC_REG7 + 0x200, + value2 + IN112525_RX_VCO_CODE_OFFSET); + s03_vco_codes.l2_vco_code = value2; + } + if (lane == 3 || lane == ALL_LANES) { + value3 = mdio_rd(PHYMISC_REG5 + 0x300); + mdio_wr(PHYMISC_REG7 + 0x300, + value3 + IN112525_RX_VCO_CODE_OFFSET); + s03_vco_codes.l3_vco_code = value3; + } +} + +void save_az_offsets(int lane) +{ + int i; + +#define AZ_OFFSET_LANE_UPDATE(reg, lane) \ + mdio_wr((reg) + (lane) * 0x100, \ + (mdio_rd((reg) + (lane) * 0x100) >> 8)) + + if (lane == ALL_LANES) { + for (i = 0; i < ALL_LANES; i++) { + AZ_OFFSET_LANE_UPDATE(PHYMISC_REG20, i); + AZ_OFFSET_LANE_UPDATE(PHYMISC_REG20 + 1, i); + AZ_OFFSET_LANE_UPDATE(PHYMISC_REG20 + 2, i); + AZ_OFFSET_LANE_UPDATE(PHYMISC_REG20 + 3, i); + AZ_OFFSET_LANE_UPDATE(PHYMISC_REG21, i); + AZ_OFFSET_LANE_UPDATE(PHYMISC_REG21 + 1, i); + AZ_OFFSET_LANE_UPDATE(PHYMISC_REG21 + 2, i); + AZ_OFFSET_LANE_UPDATE(PHYMISC_REG21 + 3, i); + AZ_OFFSET_LANE_UPDATE(PHYMISC_REG22, i); + } + } else { + AZ_OFFSET_LANE_UPDATE(PHYMISC_REG20, lane); + AZ_OFFSET_LANE_UPDATE(PHYMISC_REG20 + 1, lane); + AZ_OFFSET_LANE_UPDATE(PHYMISC_REG20 + 2, lane); + AZ_OFFSET_LANE_UPDATE(PHYMISC_REG20 + 3, lane); + AZ_OFFSET_LANE_UPDATE(PHYMISC_REG21, lane); + AZ_OFFSET_LANE_UPDATE(PHYMISC_REG21 + 1, lane); + AZ_OFFSET_LANE_UPDATE(PHYMISC_REG21 + 2, lane); + AZ_OFFSET_LANE_UPDATE(PHYMISC_REG21 + 3, lane); + AZ_OFFSET_LANE_UPDATE(PHYMISC_REG22, lane); + } + + mdio_wr(PHYCTRL_REG7, 0x0001); +} + +int in112525_s03_lane_recovery(int lane) +{ + int i, value, az_pass; + + switch (lane) { + case 0: + case 1: + case 2: + case 3: + rx_reset_assert(lane); + WAIT(2000); + break; + case ALL_LANES: + mdio_wr(PHYMISC_REG2, 0x9C00); + WAIT(2000); + while (1) { + value = mdio_rd(PHYMISC_REG2); + if (bit_test(value, 4)) + break; + } + break; + default: + dev_err(inphi_phydev->dev, + "Incorrect usage of APIs in %s driver\n", + inphi_phydev->drv->name); + break; + } + + if (lane == 0 || lane == ALL_LANES) + mdio_wr(PHYMISC_REG7, L0_VCO_CODE_trim); + if (lane == 1 || lane == ALL_LANES) + mdio_wr(PHYMISC_REG7 + 0x100, L1_VCO_CODE_trim); + if (lane == 2 || lane == ALL_LANES) + mdio_wr(PHYMISC_REG7 + 0x200, L2_VCO_CODE_trim); + if (lane == 3 || lane == ALL_LANES) + mdio_wr(PHYMISC_REG7 + 0x300, L3_VCO_CODE_trim); + + if (lane == 0 || lane == 4) + mdio_wr(PHYCTRL_REG5, 0x0418); + if (lane == 1 || lane == 4) + mdio_wr(PHYCTRL_REG5 + 0x100, 0x0418); + if (lane == 2 || lane == 4) + mdio_wr(PHYCTRL_REG5 + 0x200, 0x0418); + if (lane == 3 || lane == 4) + mdio_wr(PHYCTRL_REG5 + 0x300, 0x0418); + + mdio_wr(PHYCTRL_REG7, 0x0000); + rx_reset_de_assert(lane); + + if (lane == 0 || lane == 4) { + mdio_wr(PHYCTRL_REG5, 0x0410); + mdio_wr(PHYCTRL_REG5, 0x0412); + } + if (lane == 1 || lane == 4) { + mdio_wr(PHYCTRL_REG5 + 0x100, 0x0410); + mdio_wr(PHYCTRL_REG5 + 0x100, 0x0412); + } + if (lane == 2 || lane == 4) { + mdio_wr(PHYCTRL_REG5 + 0x200, 0x0410); + mdio_wr(PHYCTRL_REG5 + 0x200, 0x0412); + } + if (lane == 3 || lane == 4) { + mdio_wr(PHYCTRL_REG5 + 0x300, 0x0410); + mdio_wr(PHYCTRL_REG5 + 0x300, 0x0412); + } + + for (i = 0; i < 64; i++) { + /* wait 1000 times 10us */ + WAIT(10000); + az_pass = az_complete_test(lane); + if (az_pass) { + save_az_offsets(lane); + break; + } + } + + if (!az_pass) { + debug("auto-zero calibration timed out for lane %d\n", lane); + return 0; + } + + mdio_wr(lane * 0x100 + PHYMISC_REG4, 0x0002); + mdio_wr(lane * 0x100 + PHYMISC_REG6, 0x2028); + mdio_wr(lane * 0x100 + PHYCTRL_REG5, 0x0010); + WAIT(100); + mdio_wr(lane * 0x100 + PHYCTRL_REG5, 0x0110); + WAIT(3000); + mdio_wr(lane * 0x100 + PHYMISC_REG6, 0x3020); + + if (lane == ALL_LANES) { + mdio_wr(PHYMISC_REG2, 0x1C00); + mdio_wr(PHYMISC_REG2, 0x0C00); + } else { + tx_restart(lane); + /* delay > 10ms is required */ + WAIT(1100); + } + + if (lane == ALL_LANES) { + if (bit_test(mdio_rd(PHYMISC_REG2), 6) == 0) + debug("TX PLL not locked on ALL lanes\n"); + } else { + if (tx_pll_lock_test(lane) == 0) { + debug("TX PLL not locked on lane %d\n", lane); + return -1; + } + } + + save_vco_codes(lane); + + if (lane == ALL_LANES) { + mdio_wr(PHYMISC_REG2, 0x0400); + mdio_wr(PHYMISC_REG2, 0x0000); + value = mdio_rd(PHYCTRL_REG1); + value = value & 0xffbf; + mdio_wr(PHYCTRL_REG2, value); + } else { + tx_core_de_assert(lane); + } + + if (lane == ALL_LANES) { + mdio_wr(PHYMISC_REG1, 0x8000); + mdio_wr(PHYMISC_REG1, 0x0000); + } + + mdio_rd(PHYMISC_REG1); + mdio_rd(PHYMISC_REG1); + + WAIT(100); + mdio_rd(PHYSTAT_REG1); + mdio_rd(PHYSTAT_REG2); + + return 0; +} + +int in112525_s03_phy_init(struct phy_device *phydev) +{ + u32 reg_value; + u32 reg; + int i; + int tx_pll_MSDIV_value; + int tx_pll_LSDIV_value; + int tx_pll_iqdiv; + int tx_pll_ctrl2_value; + + /* put the chip in hw/sw and MDIO reset */ + mdio_wr(PHYCTRL_REG0, + IN112525_HRESET | IN112525_SRESET | IN112525_MDIOINIT); + + /* de-assert MDIO init */ + mdio_wr(PHYCTRL_REG0, IN112525_HRESET | IN112525_SRESET); + + /* apply configuration */ + if (CURRENT_CONFIG.enable_prescaler) + mdio_wr(IN112525_PRESCALE_20M, 0x0001); + + if (CURRENT_CONFIG.enable_external_refclk) + mdio_wr(PHYMISC_REG3, (1 << 15)); + else + mdio_wr(PHYMISC_REG3, 0x0); + + mdio_wr(PHYCTRL_REG0, IN112525_SRESET); + + WAIT(1000); + + reg_value = phy_read(phydev, MDIO_MMD_VEND1, IN112525_EFUSE_REG); + if (!(reg_value & IN112525_EFUSE_DONE)) { + puts("IN112525_s03 init failed: EFUSE Done not set\n"); + return -1; + } + WAIT(1000); + + reg_value = phy_read(phydev, MDIO_MMD_VEND1, PHYMISC_REG2); + if (!(reg_value & IN112525_CALIBRATION_DONE)) { + puts("IN112525_s03 init failed: CALIBRATION_DONE not set\n"); + return -1; + } + + mdio_wr(PHYMISC_REG2, + IN112525_RX_PLL_RESET | + IN112525_TX_PLL_RESET | + IN112525_TX_SERDES_RESET | + IN112525_CORE_DATAPATH_RESET); + + if (CURRENT_CONFIG.enable_otu_protocol) + mdio_wr(PHYCTRL_REG0, 0x8C00); + else + mdio_wr(PHYCTRL_REG0, 0x8200); + + if (CURRENT_CONFIG.enable_extended_range) { + mdio_wr(PHYCTRL_REG10, 0x2032); + printf("IN112525_s03 possible misconfig [ext range]\n"); + mdio_wr(PHYCTRL_REG12, 0x0007); + } else { + mdio_wr(PHYCTRL_REG10, 0xA02D); + mdio_wr(PHYCTRL_REG12, 0x0005); + } + + mdio_wr(PHYCTRL_REG18, 0x00ff); + mdio_wr(PHYCTRL_REG19, 0x002d); + mdio_wr(PHYCTRL_REG19, 0x802d); + mdio_wr(PHYCTRL_REG19, 0x0000); + mdio_wr(PHYCTRL_REG18, 0x00e9); + mdio_wr(PHYCTRL_REG19, 0x0008); + mdio_wr(PHYCTRL_REG19, 0x8008); + mdio_wr(PHYCTRL_REG19, 0x0000); + + tx_pll_MSDIV_value = tx_pll_mpy_map[CURRENT_CONFIG.tx_pll_mpy_ratio][0]; + tx_pll_LSDIV_value = tx_pll_mpy_map[CURRENT_CONFIG.tx_pll_mpy_ratio][1]; + tx_pll_iqdiv = (CURRENT_CONFIG.enable_half_rate) ? 1 : 0; + tx_pll_ctrl2_value = + (CURRENT_CONFIG.tx_pll_refclk_source << 11) + + (tx_pll_iqdiv << 8) + + (tx_pll_MSDIV_value << 4) + + tx_pll_LSDIV_value; + + mdio_wr(PHYCTRL_REG11, tx_pll_ctrl2_value); + + if (CURRENT_CONFIG.enable_half_rate) + mdio_wr(PHYCTRL_REG14, 0x0020); + + /* set the CTLE mode (bw on the front end stages) + * for example '25:25:10', '10:10:10' etc. + */ + for (i = 0; i < ALL_LANES; i++) { + reg = i * 0x100 + PHYCTRL_REG8; + reg_value = phy_read(phydev, MDIO_MMD_VEND1, reg); + reg_value = reg_value & 0xFF7C; + /* put bits 7,1,0 for EQ */ + reg_value = reg_value | CURRENT_CONFIG.ctle_mode; + mdio_wr(reg, reg_value); + } + + + /* rx common code settings */ + mdio_wr(PHYMISC_REG32, CURRENT_CONFIG.rx_common_mode); + mdio_wr(PHYMISC_REG32 + 0x100, CURRENT_CONFIG.rx_common_mode); + mdio_wr(PHYMISC_REG32 + 0x200, CURRENT_CONFIG.rx_common_mode - 1); + mdio_wr(PHYMISC_REG32 + 0x300, CURRENT_CONFIG.rx_common_mode - 1); + /* mdio_wr(PHYMISC_REG30, CURRENT_CONFIG.rx_common_mode); */ + + if (CURRENT_CONFIG.rx_odt_override) + mdio_wr(PHYMISC_REG31, CURRENT_CONFIG.rx_odt_override); + + s03_vco_codes.l0_vco_code = mdio_rd(PHYMISC_REG7); + s03_vco_codes.l1_vco_code = mdio_rd(PHYMISC_REG7 + 0x100); + s03_vco_codes.l2_vco_code = mdio_rd(PHYMISC_REG7 + 0x200); + s03_vco_codes.l3_vco_code = mdio_rd(PHYMISC_REG7 + 0x300); + + if (CURRENT_CONFIG.enable_extended_range) { + s03_vco_codes.l0_vco_code = + (int)(s03_vco_codes.l0_vco_code * 0.8); + s03_vco_codes.l1_vco_code = + (int)(s03_vco_codes.l1_vco_code * 0.8); + s03_vco_codes.l2_vco_code = + (int)(s03_vco_codes.l2_vco_code * 0.8); + s03_vco_codes.l3_vco_code = + (int)(s03_vco_codes.l3_vco_code * 0.8); + } + + mdio_wr(PHYMISC_REG2, 0x0); + WAIT(10000); + + /* start fresh */ + in112525_s03_lane_recovery(ALL_LANES); + + return 0; +} + +int in112525_s03_config(struct phy_device *phydev) +{ + inphi_phydev = phydev; + return in112525_s03_phy_init(phydev); +} + +int in112525_probe(struct phy_device *phydev) +{ + phydev->flags = PHY_FLAG_BROKEN_RESET; + return 0; +} + +int in112525_s03_startup(struct phy_device *phydev) +{ + int reg_value, i; + + phydev->link = 1; + +#ifdef CONFIG_IN112525_S03_10G + phydev->speed = SPEED_10000; +#else + phydev->speed = SPEED_25000; +#endif + phydev->duplex = DUPLEX_FULL; + + for (i = 0; i < ALL_LANES; i++) { + reg_value = mdio_rd(PHYSTAT_REG3 + i * 0x100); + if (!bit_test(reg_value, 15)) { + debug("starting recovery for lane %d\n", i); + in112525_s03_lane_recovery(i); + } + } + + return 0; +} + +int in112525_s05_startup(struct phy_device *phydev) +{ + phydev->link = 1; + +#if defined(CONFIG_IN112525_S05_10G) + phydev->speed = SPEED_10000; +#elif defined(CONFIG_IN112525_S05_25G) + phydev->speed = SPEED_25000; +#elif defined(CONFIG_IN112525_S05_40G) + phydev->speed = SPEED_40000; +#elif defined(CONFIG_IN112525_S05_50G) + phydev->speed = SPEED_50000; +#elif defined(CONFIG_IN112525_S05_100G) + phydev->speed = SPEED_100000; +#endif + phydev->duplex = DUPLEX_FULL; + return 0; +} + +struct phy_driver in112525_s05_driver = { + .name = "Inphi in112525_S05P", + .uid = PHY_UID_IN112525_S05, + .mask = 0x0ff0ffff, + .features = PHY_10G_FEATURES, + .mmds = MDIO_DEVS_VEND1, + .config = &in112525_s05_config, + .probe = &in112525_probe, + .startup = &in112525_s05_startup, + .shutdown = &gen10g_shutdown, +}; + +struct phy_driver in112525_s03_driver = { + .name = "Inphi in112525_S03P", + .uid = PHY_UID_IN112525_S03, + .mask = 0x0ff0fff0, + .features = PHY_10G_FEATURES, + .mmds = MDIO_DEVS_VEND1, + .config = &in112525_s03_config, + .probe = &in112525_probe, + .startup = &in112525_s03_startup, + .shutdown = &gen10g_shutdown, +}; + +int phy_in112525_init(void) +{ + phy_register(&in112525_s05_driver); + phy_register(&in112525_s03_driver); + return 0; +} diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index dcdef9e661..57b5c4b852 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -511,6 +511,9 @@ int phy_init(void) #ifdef CONFIG_PHY_ET1011C phy_et1011c_init(); #endif +#ifdef CONFIG_PHY_INPHI + phy_in112525_init(); +#endif #ifdef CONFIG_PHY_LXT phy_lxt_init(); #endif diff --git a/include/configs/lx2160aqds.h b/include/configs/lx2160aqds.h index ea1b1635fe..748cfd2639 100644 --- a/include/configs/lx2160aqds.h +++ b/include/configs/lx2160aqds.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0+ */ /* - * Copyright 2018-2020 NXP + * Copyright 2018-2021 NXP */ #ifndef __LX2_QDS_H @@ -31,6 +31,16 @@ u8 qixis_esdhc_detect_quirk(void); #if defined(CONFIG_FSL_MC_ENET) #define CONFIG_MII #define CONFIG_ETHPRIME "DPMAC17@rgmii-id" + +#define INPHI_PHY_ADDR1 0x0 +#define INPHI_PHY_ADDR2 0x1 +#ifdef CONFIG_SD_BOOT +#define IN112525_FW_ADDR 0x980000 +#else +#define IN112525_FW_ADDR 0x20980000 +#endif +#define IN112525_FW_LENGTH 0x40000 + #endif /* EEPROM */ diff --git a/include/configs/lx2160ardb.h b/include/configs/lx2160ardb.h index 6a0ab95786..8a90f8f3eb 100644 --- a/include/configs/lx2160ardb.h +++ b/include/configs/lx2160ardb.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0+ */ /* - * Copyright 2018,2020 NXP + * Copyright 2018-2021 NXP */ #ifndef __LX2_RDB_H @@ -23,6 +23,18 @@ #define CONFIG_ETHPRIME "DPMAC1@xgmii" #define AQR113C_PHY_ADDR1 0x0 #define AQR113C_PHY_ADDR2 0x08 + +#define INPHI_PHY_ADDR1 0x0 +#ifdef CONFIG_SD_BOOT +#define IN112525_FW_ADDR 0x980000 +#else +#define IN112525_FW_ADDR 0x20980000 +#endif +#define IN112525_FW_LENGTH 0x40000 + +#define RGMII_PHY_ADDR1 0x01 +#define RGMII_PHY_ADDR2 0x02 + #endif /* EMC2305 */ diff --git a/include/in112525.h b/include/in112525.h new file mode 100644 index 0000000000..146a94f7c2 --- /dev/null +++ b/include/in112525.h @@ -0,0 +1,236 @@ +/* + * Copyright 2018-2021 NXP + * Copyright 2018 INPHI + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Inphi is a registered trademark of Inphi Corporation + * + */ + +#ifndef _IN112525_H_ +#define _IN112525_H_ + +#define PHYCTRL_REG0 0x0000 +#define PHYCTRL_REG1 0x0012 +#define PHYCTRL_REG2 0x0014 +#define PHYCTRL_REG4 0x0121 +#define PHYCTRL_REG5 0x0180 +#define PHYCTRL_REG6 0x0580 +#define PHYCTRL_REG7 0x05C4 +#define PHYCTRL_REG8 0x01C8 +#define PHYCTRL_REG10 0x0520 +#define PHYCTRL_REG11 0x0521 +#define PHYCTRL_REG12 0x0522 +#define PHYCTRL_REG13 0x05c8 +#define PHYCTRL_REG14 0x059b +#define PHYCTRL_REG15 0x0520 +#define PHYCTRL_REG16 0x0521 +#define PHYCTRL_REG17 0x0522 +#define PHYCTRL_REG18 0x0525 +#define PHYCTRL_REG19 0x0524 +#define PHYCTRL_REG20 0x05ad + +#define PHYSTAT_REG1 0x0021 +#define PHYSTAT_REG2 0x0022 +#define PHYSTAT_REG3 0x0123 + +#define PHYMISC_REG1 0x0025 +#define PHYMISC_REG2 0x002c +#define PHYMISC_REG3 0x00b3 +#define PHYMISC_REG4 0x0181 +#define PHYMISC_REG5 0x019d +#define PHYMISC_REG6 0x0198 +#define PHYMISC_REG7 0x0199 +#define PHYMISC_REG8 0x0581 +#define PHYMISC_REG9 0x0598 +#define PHYMISC_REG11 0x00f0 +#define PHYMISC_REG12 0x00f4 +#define PHYMISC_REG20 0x01B0 +#define PHYMISC_REG21 0x01BC +#define PHYMISC_REG22 0x01C0 +#define PHYMISC_REG30 0x0584 +#define PHYMISC_REG31 0x0585 +#define PHYMISC_REG32 0x0184 + +#define IN112525_HRESET 0x20 +#define IN112525_SRESET 0x200 +#define IN112525_MDIOINIT 0x1000 +#define IN112525_MANUALRESET_SELECT 0x8000 +#define IN112525_LANE0_DISABLE BIT(0) +#define IN112525_LANE1_DISABLE BIT(1) +#define IN112525_CALIBRATION_DONE 0x10 +#define IN112525_PLL_POWERDOWN 0x8 +#define IN112525_CORE_DATAPATH_RESET 0x0400 +#define IN112525_TX_SERDES_RESET 0x0800 +#define IN112525_TX_PLL_RESET 0x1000 +#define IN112525_RX_PLL_RESET 0x8000 + +#if defined(CONFIG_IN112525_S05_10G) || defined(CONFIG_IN112525_S05_40G) +#define IN112525_EXT_REFCLK_EN 0x8000 +#define IN112525_CTLE_10G 0x8 +#else +#define IN112525_EXT_REFCLK_EN 0x0000 +#define IN112525_CTLE_10G 0x0 +#endif + +#define IN112525_FORCE_PC 0x1000 +#define IN112525_LOL_CTRL 0x2000 +#define IN112525_USEQ_EN 0x8000 +#define IN112525_USEQ_FL 0x4 +#define IN112525_USEQ_LOS 0x2 +#define IN112525_USEQ_LOL 0x1 + +#define PHYCTRL_REG15_VAL 0x2032 +#define PHYCTRL_REG17_VAL 0x0007 +#define PHYCTRL_REG15_VAL_EXT 0xA02D +#define PHYCTRL_REG17_VAL_EXT 0x0005 + +#if defined(CONFIG_IN112525_S05_10G) || defined(CONFIG_IN112525_S05_40G) +#define IN112525_TXPLL_MSDIV 0x10 +#define IN112525_TXPLL_IQDIV 0x100 +#else +#define IN112525_TXPLL_MSDIV 0x0 +#define IN112525_TXPLL_IQDIV 0x0 +#endif + +#define IN112525_FA_WIN_SIZE 0x2 +#define IN112525_RX_MISC_TRIM1_VAL 0xB + +#define IN112525_RX_HALFRATE_EN 0x20 + +#define IN112525_RX_LOS_EN 0x8000 +#define IN112525_RX_LOS_10G_THRESHOLD 0x1 +#define IN112525_RX_LOS_100G_THRESHOLD 0x2 + +#if defined(CONFIG_IN112525_S05_10G) || defined(CONFIG_IN112525_S05_40G) +#define PHYCTRL_REG13_VAL 0x8 +#define IN112525_LOSD_HYSTERESIS_EN 0x11 +#else +#define PHYCTRL_REG13_VAL 0x0 +#define IN112525_LOSD_HYSTERESIS_EN 0x01 +#endif + +#define IN112525_EFUSE_REG 0x600 +#define IN112525_EFUSE_DONE 0x2000 +#define IN112525_AUTOZERO_COMPLETION_DONE 0x4 +#define IN112525_RX_PLL_LOCK_ACQUIRED 0x20 +#define IN112525_TX_PLL_LOCK_ACQUIRED 0x40 + +#define IN112525_PRESCALE_20M 0xB5 + +#define IN112525_RX_VCO_CODE_OFFSET 5 +#define IN112525_RX_PHASE_ADJUST_TRACK_VAL 36 + +#define L0_VCO_CODE_trim 390 +#define L1_VCO_CODE_trim 390 +#define L2_VCO_CODE_trim 390 +#define L3_VCO_CODE_trim 390 + +/* Micro-Sequencer Data Memory 0x700 - 0x73F */ +#define IN112525_US_DATA_MEM_ADDR 0x73E +#define IN112525_PHASE_ADJUST_VAL 0x3 + +struct in112525_reg_config { + unsigned short reg_addr; + unsigned short reg_value; +}; + +/* define mapping for CTLE codes to rx_ctl registers 0x1c8,...,0x4c8 */ +enum in112525_eq_mode { + MODE_25_10_10 = 0x0, + MODE_25_10_25 = 0x1, + MODE_25_25_10 = 0x2, + MODE_25_25_25 = 0x3, + MODE_10_10_10 = 0x80, + MODE_10_10_25 = 0x81, + MODE_10_25_10 = 0x82, + MODE_10_25_25 = 0x83 +}; + +enum in112525_s03_mode { + INIT_OC192, + INIT_10GE, + INIT_16GFC, + INIT_20GE, + INIT_100GE, + INIT_25GE, + INIT_OTU4, + INIT_32GFC, + INIT_F28P2G +}; + +enum in112525_refclock { + RECOV_CLK, /* Normal Mission Mode */ + EXTERNAL, /* BERT Mode */ + RXMUX_CLK, + LPBK, + SEC_REFCLK = 5 +}; + +struct in112525_s03_vco_codes { + int l0_vco_code; + int l1_vco_code; + int l2_vco_code; + int l3_vco_code; +}; + +struct in112525_config { + /* OTU protocol check */ + unsigned char enable_otu_protocol:1; + /* external refclk buffer */ + unsigned char enable_external_refclk:1; + /* clk prescaler */ + unsigned char enable_prescaler:1; + /* set half-rate mode */ + unsigned char enable_half_rate:1; + /* FA sweep range for lower rates */ + unsigned char enable_extended_range:1; + /* PLL multiplier ratio settings */ + unsigned short tx_pll_mpy_ratio; + /* src for TX PLL (external or recovered clk */ + enum in112525_refclock tx_pll_refclk_source; + /* equalizer preset mode (25:25:25, 25:25:10, 10:10:10 */ + enum in112525_eq_mode ctle_mode; + /* common mode settings */ + unsigned char rx_common_mode; + /* rx termination adjustments for non-matched tx */ + unsigned char rx_odt_override; + /* default phase adjustments per lanes */ + unsigned char l0_phase_adjust_val; + unsigned char l1_phase_adjust_val; + unsigned char l2_phase_adjust_val; + unsigned char l3_phase_adjust_val; +}; + +int bit_test(int value, int bit_field); +void WAIT(int delay_cycles); +void tx_restart(int lane); +void disable_lane(int lane); +void rx_powerdown_assert(int lane); +void rx_powerdown_de_assert(int lane); +void rx_reset_assert(int lane); +void rx_reset_de_assert(int lane); +void toggle_reset(int lane); +int in112525_s03_phy_init(struct phy_device *phydev); +int in112525_s03_lane_recovery(int lane); +#endif diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h index f6dbdb096d..ccb5b8c123 100644 --- a/include/linux/ethtool.h +++ b/include/linux/ethtool.h @@ -620,6 +620,10 @@ enum ethtool_sfeatures_retval_bits { #define SPEED_1000 1000 #define SPEED_2500 2500 #define SPEED_10000 10000 +#define SPEED_25000 25000 +#define SPEED_40000 40000 +#define SPEED_50000 50000 +#define SPEED_100000 100000 /* Duplex, half or full. */ #define DUPLEX_HALF 0x00 diff --git a/include/phy.h b/include/phy.h index 2754421ed4..acc1c34a92 100644 --- a/include/phy.h +++ b/include/phy.h @@ -517,6 +517,7 @@ int phy_cortina_init(void); int phy_cortina_access_init(void); int phy_davicom_init(void); int phy_et1011c_init(void); +int phy_in112525_init(void); int phy_lxt_init(void); int phy_marvell_init(void); int phy_micrel_ksz8xxx_init(void); @@ -574,5 +575,5 @@ static inline bool phy_interface_is_sgmii(struct phy_device *phydev) #define PHY_UID_CS4223 0x03e57003 #define PHY_UID_TN2020 0x00a19410 #define PHY_UID_IN112525_S03 0x02107440 - +#define PHY_UID_IN112525_S05 0x02107441 #endif -- 2.17.1