From 3b60e97e8cf8a1ae78ec68a2fed37cd763675e56 Mon Sep 17 00:00:00 2001 From: baiywt Date: Fri, 18 Feb 2022 16:38:43 +0800 Subject: [PATCH] Add yt8531c support. Adapted from orangepi-xunlong/openwrt - 600-Add-yt8531c-support.patch by schwar3kat --- drivers/net/phy/Kconfig | 5 + drivers/net/phy/motorcomm.c | 1540 +++++++++++++++++++++++++++++++++ drivers/net/phy/yt8614-phy.h | 491 +++++++++++ include/linux/motorcomm_phy.h | 119 +++ 5 files changed, 2156 insertions(+) create mode 100644 drivers/net/phy/motorcomm.c create mode 100644 drivers/net/phy/yt8614-phy.h create mode 100644 include/linux/motorcomm_phy.h diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index ce030fcb1..ff4861847 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -297,6 +297,11 @@ config MICROSEMI_PHY help Currently supports VSC8514, VSC8530, VSC8531, VSC8540 and VSC8541 PHYs +config MOTORCOMM_PHY + tristate "Motorcomm PHYs" + help + Supports the YT8010, YT8510, YT8511, YT8512 YT8521 YT8531 PHYs. + config NATIONAL_PHY tristate "National Semiconductor PHYs" help diff --git a/drivers/net/phy/motorcomm.c b/drivers/net/phy/motorcomm.c new file mode 100644 index 000000000..74eef3dfa --- /dev/null +++ b/drivers/net/phy/motorcomm.c @@ -0,0 +1,1540 @@ +/* + * drivers/net/phy/motorcomm.c + * + * Driver for Motorcomm PHYs + * + * Author: Leilei Zhao + * + * Copyright (c) 2019 Motorcomm, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * Support : Motorcomm Phys: + * Giga phys: yt8511, yt8521 + * 100/10 Phys : yt8512, yt8512b, yt8510 + * Automotive 100Mb Phys : yt8010 + * Automotive 100/10 hyper range Phys: yt8510 + */ + +#include +#include +#include +#include +#include +#include +#include +#ifndef LINUX_VERSION_CODE +#include +#else +#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c)) +#endif +/*for wol, 20210604*/ +#include + +#include "yt8614-phy.h" + +/**** configuration section begin ***********/ + +/* if system depends on ethernet packet to restore from sleep, please define this macro to 1 + * otherwise, define it to 0. + */ +#define SYS_WAKEUP_BASED_ON_ETH_PKT 1 + +/* to enable system WOL of phy, please define this macro to 1 + * otherwise, define it to 0. + */ +#define YTPHY_ENABLE_WOL 0 + +/* some GMAC need clock input from PHY, for eg., 125M, please enable this macro + * by degault, it is set to 0 + * NOTE: this macro will need macro SYS_WAKEUP_BASED_ON_ETH_PKT to set to 1 + */ +#define GMAC_CLOCK_INPUT_NEEDED 1 + + +#define YT8521_PHY_MODE_FIBER 1 //fiber mode only +#define YT8521_PHY_MODE_UTP 2 //utp mode only +#define YT8521_PHY_MODE_POLL 3 //fiber and utp, poll mode + +/* please make choice according to system design + * for Fiber only system, please define YT8521_PHY_MODE_CURR 1 + * for UTP only system, please define YT8521_PHY_MODE_CURR 2 + * for combo system, please define YT8521_PHY_MODE_CURR 3 + */ +#define YT8521_PHY_MODE_CURR 3 + +/**** configuration section end ***********/ + + +/* no need to change below */ + +#if (YTPHY_ENABLE_WOL) +#undef SYS_WAKEUP_BASED_ON_ETH_PKT +#define SYS_WAKEUP_BASED_ON_ETH_PKT 1 +#endif + +/* workaround for 8521 fiber 100m mode */ +static int link_mode_8521 = 0; //0: no link; 1: utp; 32: fiber. traced that 1000m fiber uses 32. +static int link_mode_8614[4] = {0}; //0: no link; 1: utp; 32: fiber. traced that 1000m fiber uses 32. + +/* for multiple port phy, base phy address */ +static unsigned int yt_mport_base_phy_addr = 0xff; //0xff: invalid; for 8618 +static unsigned int yt_mport_base_phy_addr_8614 = 0xff; //0xff: invalid; + +int phy_yt8531_led_fixup(struct mii_bus *bus, int addr); +int yt8511_config_out_125m(struct mii_bus *bus, int phy_id); + +#if ( LINUX_VERSION_CODE > KERNEL_VERSION(5,0,0) ) +int genphy_config_init(struct phy_device *phydev) +{ + int ret; + + printk (KERN_INFO "yzhang..read phyaddr=%d, phyid=%08x\n",phydev->mdio.addr, phydev->phy_id); + + if(phydev->phy_id == 0x4f51e91b) + { + printk (KERN_INFO "yzhang..get YT8511, abt to set 125m clk out, phyaddr=%d, phyid=%08x\n",phydev->mdio.addr, phydev->phy_id); + ret = yt8511_config_out_125m(phydev->mdio.bus, phydev->mdio.addr); + printk (KERN_INFO "yzhang..8511 set 125m clk out, reg=%#04x\n",phydev->mdio.bus->read(phydev->mdio.bus,phydev->mdio.addr,0x1f)/*double check as delay*/); + if (ret<0) + printk (KERN_INFO "yzhang..failed to set 125m clk out, ret=%d\n",ret); + + phy_yt8531_led_fixup(phydev->mdio.bus, phydev->mdio.addr); + } + return genphy_read_abilities(phydev); +} +#endif + +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(4,0,0) ) +static int ytphy_config_init(struct phy_device *phydev) +{ + return 0; +} +#endif + +static int ytphy_read_ext(struct phy_device *phydev, u32 regnum) +{ + int ret; + int val; + + ret = phy_write(phydev, REG_DEBUG_ADDR_OFFSET, regnum); + if (ret < 0) + return ret; + + val = phy_read(phydev, REG_DEBUG_DATA); + + return val; +} + +static int ytphy_write_ext(struct phy_device *phydev, u32 regnum, u16 val) +{ + int ret; + + ret = phy_write(phydev, REG_DEBUG_ADDR_OFFSET, regnum); + if (ret < 0) + return ret; + + ret = phy_write(phydev, REG_DEBUG_DATA, val); + + return ret; +} + +static int yt8010_config_aneg(struct phy_device *phydev) +{ + phydev->speed = SPEED_100; + return 0; +} + +static int yt8512_clk_init(struct phy_device *phydev) +{ + int ret; + int val; + + val = ytphy_read_ext(phydev, YT8512_EXTREG_AFE_PLL); + if (val < 0) + return val; + + val |= YT8512_CONFIG_PLL_REFCLK_SEL_EN; + + ret = ytphy_write_ext(phydev, YT8512_EXTREG_AFE_PLL, val); + if (ret < 0) + return ret; + + val = ytphy_read_ext(phydev, YT8512_EXTREG_EXTEND_COMBO); + if (val < 0) + return val; + + val |= YT8512_CONTROL1_RMII_EN; + + ret = ytphy_write_ext(phydev, YT8512_EXTREG_EXTEND_COMBO, val); + if (ret < 0) + return ret; + + val = phy_read(phydev, MII_BMCR); + if (val < 0) + return val; + + val |= YT_SOFTWARE_RESET; + ret = phy_write(phydev, MII_BMCR, val); + + return ret; +} + +static int yt8512_led_init(struct phy_device *phydev) +{ + int ret; + int val; + int mask; + + val = ytphy_read_ext(phydev, YT8512_EXTREG_LED0); + if (val < 0) + return val; + + val |= YT8512_LED0_ACT_BLK_IND; + + mask = YT8512_LED0_DIS_LED_AN_TRY | YT8512_LED0_BT_BLK_EN | + YT8512_LED0_HT_BLK_EN | YT8512_LED0_COL_BLK_EN | + YT8512_LED0_BT_ON_EN; + val &= ~mask; + + ret = ytphy_write_ext(phydev, YT8512_EXTREG_LED0, val); + if (ret < 0) + return ret; + + val = ytphy_read_ext(phydev, YT8512_EXTREG_LED1); + if (val < 0) + return val; + + val |= YT8512_LED1_BT_ON_EN; + + mask = YT8512_LED1_TXACT_BLK_EN | YT8512_LED1_RXACT_BLK_EN; + val &= ~mask; + + ret = ytphy_write_ext(phydev, YT8512_LED1_BT_ON_EN, val); + + return ret; +} + +static int yt8512_config_init(struct phy_device *phydev) +{ + int ret; + int val; +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(4,0,0) ) + ret = ytphy_config_init(phydev); +#else + ret = genphy_config_init(phydev); +#endif + if (ret < 0) + return ret; + + ret = yt8512_clk_init(phydev); + if (ret < 0) + return ret; + + ret = yt8512_led_init(phydev); + + /* disable auto sleep */ + val = ytphy_read_ext(phydev, YT8512_EXTREG_SLEEP_CONTROL1); + if (val < 0) + return val; + + val &= (~BIT(YT8512_EN_SLEEP_SW_BIT)); + + ret = ytphy_write_ext(phydev, YT8512_EXTREG_SLEEP_CONTROL1, val); + if (ret < 0) + return ret; + + return ret; +} + +static int yt8512_read_status(struct phy_device *phydev) +{ + int ret; + int val; + int speed, speed_mode, duplex; + + ret = genphy_update_link(phydev); + if (ret) + return ret; + + val = phy_read(phydev, REG_PHY_SPEC_STATUS); + if (val < 0) + return val; + + duplex = (val & YT8512_DUPLEX) >> YT8512_DUPLEX_BIT; + speed_mode = (val & YT8512_SPEED_MODE) >> YT8512_SPEED_MODE_BIT; + switch (speed_mode) { + case 0: + speed = SPEED_10; + break; + case 1: + speed = SPEED_100; + break; + case 2: + case 3: + default: +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(4,0,0) ) + speed = -1; +#else + speed = SPEED_UNKNOWN; +#endif + break; + } + + phydev->speed = speed; + phydev->duplex = duplex; + + return 0; +} +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(4,0,0) ) +#else +#if 0 +int yt8521_soft_reset(struct phy_device *phydev) +{ + int ret; + + ytphy_write_ext(phydev, 0xa000, 0); + ret = genphy_soft_reset(phydev); + if (ret < 0) + return ret; + + ytphy_write_ext(phydev, 0xa000, 2); + ret = genphy_soft_reset(phydev); + if (ret < 0) { + ytphy_write_ext(phydev, 0xa000, 0); + return ret; + } + + return 0; +} +#else +/* qingsong feedback 2 genphy_soft_reset will cause problem. + * and this is the reduction version + */ +int yt8521_soft_reset(struct phy_device *phydev) +{ + int ret, val; + + val = ytphy_read_ext(phydev, 0xa001); + ytphy_write_ext(phydev, 0xa001, (val & ~0x8000)); + + ret = genphy_soft_reset(phydev); + if (ret < 0) + return ret; + + return 0; +} +#endif + +#endif + +#if GMAC_CLOCK_INPUT_NEEDED +static int ytphy_mii_rd_ext(struct mii_bus *bus, int phy_id, u32 regnum) +{ + int ret; + int val; + + ret = bus->write(bus, phy_id, REG_DEBUG_ADDR_OFFSET, regnum); + if (ret < 0) + return ret; + + val = bus->read(bus, phy_id, REG_DEBUG_DATA); + + return val; +} + +static int ytphy_mii_wr_ext(struct mii_bus *bus, int phy_id, u32 regnum, u16 val) +{ + int ret; + + ret = bus->write(bus, phy_id, REG_DEBUG_ADDR_OFFSET, regnum); + if (ret < 0) + return ret; + + ret = bus->write(bus, phy_id, REG_DEBUG_DATA, val); + + return ret; +} + +int yt8511_config_dis_txdelay(struct mii_bus *bus, int phy_id) +{ + int ret; + int val; + + /* disable auto sleep */ + val = ytphy_mii_rd_ext(bus, phy_id, 0x27); + if (val < 0) + return val; + + val &= (~BIT(15)); + + ret = ytphy_mii_wr_ext(bus, phy_id, 0x27, val); + if (ret < 0) + return ret; + + /* enable RXC clock when no wire plug */ + val = ytphy_mii_rd_ext(bus, phy_id, 0xc); + if (val < 0) + return val; + + /* ext reg 0xc b[7:4] + Tx Delay time = 150ps * N - 250ps + */ + val &= ~(0xf << 4); + ret = ytphy_mii_wr_ext(bus, phy_id, 0xc, val); + printk("yt8511_config_dis_txdelay..phy txdelay, val=%#08x\n",val); + + return ret; +} + +int phy_yt8531_led_fixup(struct mii_bus *bus, int addr) +{ + printk("%s in\n", __func__); + + ytphy_mii_wr_ext(bus, addr, 0xa00d, 0x670); + ytphy_mii_wr_ext(bus, addr, 0xa00e, 0x2070); + ytphy_mii_wr_ext(bus, addr, 0xa00f, 0x7e); + + return 0; +} + +int yt8511_config_out_125m(struct mii_bus *bus, int addr) +{ + int ret; + int val; + + mdelay(50); + ret = ytphy_mii_wr_ext(bus, addr, 0xa012, 0xd0); + + mdelay(100); + val = ytphy_mii_rd_ext(bus, addr, 0xa012); + + if(val != 0xd0) + { + printk("yt8511_config_out_125m error\n"); + return -1; + } + + /* disable auto sleep */ + val = ytphy_mii_rd_ext(bus, addr, 0x27); + if (val < 0) + return val; + + val &= (~BIT(15)); + + ret = ytphy_mii_wr_ext(bus, addr, 0x27, val); + if (ret < 0) + return ret; + + /* enable RXC clock when no wire plug */ + val = ytphy_mii_rd_ext(bus, addr, 0xc); + if (val < 0) + return val; + + /* ext reg 0xc.b[2:1] + 00-----25M from pll; + 01---- 25M from xtl;(default) + 10-----62.5M from pll; + 11----125M from pll(here set to this value) + */ + val |= (3 << 1); + ret = ytphy_mii_wr_ext(bus, addr, 0xc, val); + printk("yt8511_config_out_125m, phy clk out, val=%#08x\n",val); + +#if 0 + /* for customer, please enable it based on demand. + * configure to master + */ + val = bus->read(bus, phy_id, 0x9/*master/slave config reg*/); + val |= (0x3<<11); //to be manual config and force to be master + ret = bus->write(bus, phy_id, 0x9, val); //take effect until phy soft reset + if (ret < 0) + return ret; + + printk("yt8511_config_out_125m, phy to be master, val=%#08x\n",val); +#endif + + return ret; +} + +EXPORT_SYMBOL(yt8511_config_out_125m); + +static int yt8511_config_init(struct phy_device *phydev) +{ + int ret; + +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(4,0,0) ) + ret = ytphy_config_init(phydev); +#else + ret = genphy_config_init(phydev); +#endif + + return ret; +} +#endif /*GMAC_CLOCK_INPUT_NEEDED*/ + +#if (YTPHY_ENABLE_WOL) +static int ytphy_switch_reg_space(struct phy_device *phydev, int space) +{ + int ret; + + if (space == YTPHY_REG_SPACE_UTP){ + ret = ytphy_write_ext(phydev, 0xa000, 0); + }else{ + ret = ytphy_write_ext(phydev, 0xa000, 2); + } + + return ret; +} + +static int ytphy_wol_en_cfg(struct phy_device *phydev, ytphy_wol_cfg_t wol_cfg) +{ + int ret=0; + int val=0; + + val = ytphy_read_ext(phydev, YTPHY_WOL_CFG_REG); + if (val < 0) + return val; + + if(wol_cfg.enable) { + val |= YTPHY_WOL_CFG_EN; + + if(wol_cfg.type == YTPHY_WOL_TYPE_LEVEL) { + val &= ~YTPHY_WOL_CFG_TYPE; + val &= ~YTPHY_WOL_CFG_INTR_SEL; + } else if(wol_cfg.type == YTPHY_WOL_TYPE_PULSE) { + val |= YTPHY_WOL_CFG_TYPE; + val |= YTPHY_WOL_CFG_INTR_SEL; + + if(wol_cfg.width == YTPHY_WOL_WIDTH_84MS) { + val &= ~YTPHY_WOL_CFG_WIDTH1; + val &= ~YTPHY_WOL_CFG_WIDTH2; + } else if(wol_cfg.width == YTPHY_WOL_WIDTH_168MS) { + val |= YTPHY_WOL_CFG_WIDTH1; + val &= ~YTPHY_WOL_CFG_WIDTH2; + } else if(wol_cfg.width == YTPHY_WOL_WIDTH_336MS) { + val &= ~YTPHY_WOL_CFG_WIDTH1; + val |= YTPHY_WOL_CFG_WIDTH2; + } else if(wol_cfg.width == YTPHY_WOL_WIDTH_672MS) { + val |= YTPHY_WOL_CFG_WIDTH1; + val |= YTPHY_WOL_CFG_WIDTH2; + } + } + } else { + val &= ~YTPHY_WOL_CFG_EN; + val &= ~YTPHY_WOL_CFG_INTR_SEL; + } + + ret = ytphy_write_ext(phydev, YTPHY_WOL_CFG_REG, val); + if (ret < 0) + return ret; + + return 0; +} + +static void ytphy_get_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol) +{ + int val = 0; + + wol->supported = WAKE_MAGIC; + wol->wolopts = 0; + + val = ytphy_read_ext(phydev, YTPHY_WOL_CFG_REG); + if (val < 0) + return; + + if (val & YTPHY_WOL_CFG_EN) + wol->wolopts |= WAKE_MAGIC; + + return; +} + +static int ytphy_set_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol) +{ + int ret, pre_page, val; + ytphy_wol_cfg_t wol_cfg; + struct net_device *p_attached_dev = phydev->attached_dev; + + memset(&wol_cfg,0,sizeof(ytphy_wol_cfg_t)); + pre_page = ytphy_read_ext(phydev, 0xa000); + if (pre_page < 0) + return pre_page; + + /* Switch to phy UTP page */ + ret = ytphy_switch_reg_space(phydev, YTPHY_REG_SPACE_UTP); + if (ret < 0) + return ret; + + if (wol->wolopts & WAKE_MAGIC) { + + /* Enable the WOL interrupt */ + val = phy_read(phydev, YTPHY_UTP_INTR_REG); + val |= YTPHY_WOL_INTR; + ret = phy_write(phydev, YTPHY_UTP_INTR_REG, val); + if (ret < 0) + return ret; + + /* Set the WOL config */ + wol_cfg.enable = 1; //enable + wol_cfg.type= YTPHY_WOL_TYPE_PULSE; + wol_cfg.width= YTPHY_WOL_WIDTH_672MS; + ret = ytphy_wol_en_cfg(phydev, wol_cfg); + if (ret < 0) + return ret; + + /* Store the device address for the magic packet */ + ret = ytphy_write_ext(phydev, YTPHY_MAGIC_PACKET_MAC_ADDR2, + ((p_attached_dev->dev_addr[0] << 8) | + p_attached_dev->dev_addr[1])); + if (ret < 0) + return ret; + ret = ytphy_write_ext(phydev, YTPHY_MAGIC_PACKET_MAC_ADDR1, + ((p_attached_dev->dev_addr[2] << 8) | + p_attached_dev->dev_addr[3])); + if (ret < 0) + return ret; + ret = ytphy_write_ext(phydev, YTPHY_MAGIC_PACKET_MAC_ADDR0, + ((p_attached_dev->dev_addr[4] << 8) | + p_attached_dev->dev_addr[5])); + if (ret < 0) + return ret; + } else { + wol_cfg.enable = 0; //disable + wol_cfg.type= YTPHY_WOL_TYPE_MAX; + wol_cfg.width= YTPHY_WOL_WIDTH_MAX; + ret = ytphy_wol_en_cfg(phydev, wol_cfg); + if (ret < 0) + return ret; + } + + /* Recover to previous register space page */ + ret = ytphy_switch_reg_space(phydev, pre_page); + if (ret < 0) + return ret; + + return 0; +} + +#endif /*(YTPHY_ENABLE_WOL)*/ + +static int yt8521_config_init(struct phy_device *phydev) +{ + int ret; + int val; + + phydev->irq = PHY_POLL; + + ytphy_write_ext(phydev, 0xa000, 0); +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(4,0,0) ) + ret = ytphy_config_init(phydev); +#else + ret = genphy_config_init(phydev); +#endif + if (ret < 0) + return ret; + + /* disable auto sleep */ + val = ytphy_read_ext(phydev, YT8521_EXTREG_SLEEP_CONTROL1); + if (val < 0) + return val; + + val &= (~BIT(YT8521_EN_SLEEP_SW_BIT)); + + ret = ytphy_write_ext(phydev, YT8521_EXTREG_SLEEP_CONTROL1, val); + if (ret < 0) + return ret; + + /* enable RXC clock when no wire plug */ + ret = ytphy_write_ext(phydev, 0xa000, 0); + if (ret < 0) + return ret; + + val = ytphy_read_ext(phydev, 0xc); + if (val < 0) + return val; + val &= ~(1 << 12); + ret = ytphy_write_ext(phydev, 0xc, val); + if (ret < 0) + return ret; + + printk (KERN_INFO "yt8521_config_init, 8521 init call out.\n"); + return ret; +} + +/* + * for fiber mode, there is no 10M speed mode and + * this function is for this purpose. + */ +static int yt8521_adjust_status(struct phy_device *phydev, int val, int is_utp) +{ + int speed_mode, duplex; +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(4,0,0) ) + int speed = -1; +#else + int speed = SPEED_UNKNOWN; +#endif + + duplex = (val & YT8512_DUPLEX) >> YT8521_DUPLEX_BIT; + speed_mode = (val & YT8521_SPEED_MODE) >> YT8521_SPEED_MODE_BIT; + switch (speed_mode) { + case 0: + if (is_utp) + speed = SPEED_10; + break; + case 1: + speed = SPEED_100; + break; + case 2: + speed = SPEED_1000; + break; + case 3: + break; + default: +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(4,0,0) ) + speed = -1; +#else + speed = SPEED_UNKNOWN; +#endif + break; + } + + phydev->speed = speed; + phydev->duplex = duplex; + //printk (KERN_INFO "yt8521_adjust_status call out,regval=0x%04x,mode=%s,speed=%dm...\n", val,is_utp?"utp":"fiber", phydev->speed); + + return 0; +} + +/* + * for fiber mode, when speed is 100M, there is no definition for autonegotiation, and + * this function handles this case and return 1 per linux kernel's polling. + */ +int yt8521_aneg_done (struct phy_device *phydev) +{ + + //printk("yt8521_aneg_done callin,speed=%dm,linkmoded=%d\n", phydev->speed,link_mode_8521); + + if((32 == link_mode_8521) && (SPEED_100 == phydev->speed)) + { + return 1/*link_mode_8521*/; + } + +#if ( LINUX_VERSION_CODE > KERNEL_VERSION(3,11,0) ) + return genphy_aneg_done(phydev); +#else + return 1; +#endif +} + +static int yt8521_read_status(struct phy_device *phydev) +{ + int ret; + volatile int val, yt8521_fiber_latch_val, yt8521_fiber_curr_val; + volatile int link; + int link_utp = 0, link_fiber = 0; + +#if (YT8521_PHY_MODE_CURR != YT8521_PHY_MODE_FIBER) + /* reading UTP */ + ret = ytphy_write_ext(phydev, 0xa000, 0); + if (ret < 0) + return ret; + + val = phy_read(phydev, REG_PHY_SPEC_STATUS); + if (val < 0) + return val; + + link = val & (BIT(YT8521_LINK_STATUS_BIT)); + if (link) { + link_utp = 1; + link_mode_8521 = 1; + yt8521_adjust_status(phydev, val, 1); + } else { + link_utp = 0; + } +#endif //(YT8521_PHY_MODE_CURR != YT8521_PHY_MODE_FIBER) + +#if (YT8521_PHY_MODE_CURR != YT8521_PHY_MODE_UTP) + /* reading Fiber */ + ret = ytphy_write_ext(phydev, 0xa000, 2); + if (ret < 0) + return ret; + + val = phy_read(phydev, REG_PHY_SPEC_STATUS); + if (val < 0) + return val; + + //note: below debug information is used to check multiple PHy ports. + //printk (KERN_INFO "yt8521_read_status, fiber status=%04x,macbase=0x%08lx\n", val,(unsigned long)phydev->attached_dev); + + /* for fiber, from 1000m to 100m, there is not link down from 0x11, and check reg 1 to identify such case + * this is important for Linux kernel for that, missing linkdown event will cause problem. + */ + yt8521_fiber_latch_val = phy_read(phydev, MII_BMSR); + yt8521_fiber_curr_val = phy_read(phydev, MII_BMSR); + link = val & (BIT(YT8521_LINK_STATUS_BIT)); + if((link) && (yt8521_fiber_latch_val != yt8521_fiber_curr_val)) + { + link = 0; + printk (KERN_INFO "yt8521_read_status, fiber link down detect,latch=%04x,curr=%04x\n", yt8521_fiber_latch_val,yt8521_fiber_curr_val); + } + + if (link) { + link_fiber = 1; + yt8521_adjust_status(phydev, val, 0); + link_mode_8521 = 32; //fiber mode + + + } else { + link_fiber = 0; + } +#endif //(YT8521_PHY_MODE_CURR != YT8521_PHY_MODE_UTP) + + if (link_utp || link_fiber) { + phydev->link = 1; + } else { + phydev->link = 0; + link_mode_8521 = 0; + } + +#if (YT8521_PHY_MODE_CURR != YT8521_PHY_MODE_FIBER) + if (link_utp) { + ytphy_write_ext(phydev, 0xa000, 0); + } +#endif + + //printk (KERN_INFO "yzhang..8521 read status call out,link=%d,linkmode=%d\n", phydev->link, link_mode_8521 ); + return 0; +} + +int yt8521_suspend(struct phy_device *phydev) +{ +#if !(SYS_WAKEUP_BASED_ON_ETH_PKT) + int value; + +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(4,0,0) ) + mutex_lock(&phydev->lock); +#else + /* no need lock in 4.19 */ +#endif + + ytphy_write_ext(phydev, 0xa000, 0); + value = phy_read(phydev, MII_BMCR); + phy_write(phydev, MII_BMCR, value | BMCR_PDOWN); + + ytphy_write_ext(phydev, 0xa000, 2); + value = phy_read(phydev, MII_BMCR); + phy_write(phydev, MII_BMCR, value | BMCR_PDOWN); + + ytphy_write_ext(phydev, 0xa000, 0); + +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(4,0,0) ) + mutex_unlock(&phydev->lock); +#else + /* no need lock/unlock in 4.19 */ +#endif +#endif /*!(SYS_WAKEUP_BASED_ON_ETH_PKT)*/ + + return 0; +} + +int yt8521_resume(struct phy_device *phydev) +{ +#if !(SYS_WAKEUP_BASED_ON_ETH_PKT) + int value; + int ret; + +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(4,0,0) ) + mutex_lock(&phydev->lock); +#else + /* no need lock/unlock in 4.19 */ +#endif + + ytphy_write_ext(phydev, 0xa000, 0); + value = phy_read(phydev, MII_BMCR); + phy_write(phydev, MII_BMCR, value & ~BMCR_PDOWN); + + /* disable auto sleep */ + value = ytphy_read_ext(phydev, YT8521_EXTREG_SLEEP_CONTROL1); + if (value < 0) + return value; + + value &= (~BIT(YT8521_EN_SLEEP_SW_BIT)); + ret = ytphy_write_ext(phydev, YT8521_EXTREG_SLEEP_CONTROL1, value); + if (ret < 0) + return ret; + + /* enable RXC clock when no wire plug */ + value = ytphy_read_ext(phydev, 0xc); + if (value < 0) + return value; + value &= ~(1 << 12); + ret = ytphy_write_ext(phydev, 0xc, value); + if (ret < 0) + return ret; + + ytphy_write_ext(phydev, 0xa000, 2); + value = phy_read(phydev, MII_BMCR); + phy_write(phydev, MII_BMCR, value & ~BMCR_PDOWN); + +#if (YT8521_PHY_MODE_CURR != YT8521_PHY_MODE_FIBER) + ytphy_write_ext(phydev, 0xa000, 0); +#endif + +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(4,0,0) ) + mutex_unlock(&phydev->lock); +#else + /* no need lock/unlock in 4.19 */ +#endif +#endif /*!(SYS_WAKEUP_BASED_ON_ETH_PKT)*/ + + return 0; +} + + +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(4,0,0) ) +#else +int yt8618_soft_reset(struct phy_device *phydev) +{ + int ret; + + ytphy_write_ext(phydev, 0xa000, 0); + ret = genphy_soft_reset(phydev); + if (ret < 0) + return ret; + + return 0; +} + +int yt8614_soft_reset(struct phy_device *phydev) +{ + int ret; + + /* utp */ + ytphy_write_ext(phydev, 0xa000, 0); + ret = genphy_soft_reset(phydev); + if (ret < 0) + return ret; + + /* qsgmii */ + ytphy_write_ext(phydev, 0xa000, 2); + ret = genphy_soft_reset(phydev); + if (ret < 0) { + ytphy_write_ext(phydev, 0xa000, 0); //back to utp mode + return ret; + } + + /* sgmii */ + ytphy_write_ext(phydev, 0xa000, 3); + ret = genphy_soft_reset(phydev); + if (ret < 0) { + ytphy_write_ext(phydev, 0xa000, 0); //back to utp mode + return ret; + } + + return 0; +} + +#endif + +static int yt8618_config_init(struct phy_device *phydev) +{ + int ret; + int val; + + phydev->irq = PHY_POLL; + + if(0xff == yt_mport_base_phy_addr) + /* by default, we think the first phy should be the base phy addr. for mul */ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(4,0,0) ) + { + yt_mport_base_phy_addr = phydev->addr; + }else if (yt_mport_base_phy_addr > phydev->addr) { + printk (KERN_INFO "yzhang..8618 init, phy address mismatch, base=%d, cur=%d\n", yt_mport_base_phy_addr, phydev->addr); + } +#else + { + yt_mport_base_phy_addr = phydev->mdio.addr; + }else if (yt_mport_base_phy_addr > phydev->mdio.addr) { + printk (KERN_INFO "yzhang..8618 init, phy address mismatch, base=%d, cur=%d\n", yt_mport_base_phy_addr, phydev->mdio.addr); + } +#endif + + ytphy_write_ext(phydev, 0xa000, 0); +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(4,0,0) ) + ret = ytphy_config_init(phydev); +#else + ret = genphy_config_init(phydev); +#endif + if (ret < 0) + return ret; + + /* for utp to optimize signal */ + ret = ytphy_write_ext(phydev, 0x41, 0x33); + if (ret < 0) + return ret; + ret = ytphy_write_ext(phydev, 0x42, 0x66); + if (ret < 0) + return ret; + ret = ytphy_write_ext(phydev, 0x43, 0xaa); + if (ret < 0) + return ret; + ret = ytphy_write_ext(phydev, 0x44, 0xd0d); + if (ret < 0) + return ret; +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(4,0,0) ) + if((phydev->addr > yt_mport_base_phy_addr) && ((2 == phydev->addr - yt_mport_base_phy_addr) || (5 == phydev->addr - yt_mport_base_phy_addr))) +#else + if((phydev->mdio.addr > yt_mport_base_phy_addr) && ((2 == phydev->mdio.addr - yt_mport_base_phy_addr) || (5 == phydev->mdio.addr - yt_mport_base_phy_addr))) +#endif + { + ret = ytphy_write_ext(phydev, 0x44, 0x2929); + if (ret < 0) + return ret; + } + + val = phy_read(phydev, MII_BMCR); + phy_write(phydev, MII_BMCR, val | BMCR_RESET); + + printk (KERN_INFO "yt8618_config_init call out.\n"); + return ret; +} + +static int yt8614_config_init(struct phy_device *phydev) +{ + int ret = 0; + + phydev->irq = PHY_POLL; + + if(0xff == yt_mport_base_phy_addr_8614) + /* by default, we think the first phy should be the base phy addr. for mul */ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(4,0,0) ) + { + yt_mport_base_phy_addr_8614 = (unsigned int)phydev->addr; + }else if (yt_mport_base_phy_addr_8614 > (unsigned int)phydev->addr) { + printk (KERN_INFO "yzhang..8618 init, phy address mismatch, base=%u, cur=%d\n", yt_mport_base_phy_addr_8614, phydev->addr); + } +#else + { + yt_mport_base_phy_addr_8614 = (unsigned int)phydev->mdio.addr; + }else if (yt_mport_base_phy_addr_8614 > (unsigned int)phydev->mdio.addr) { + printk (KERN_INFO "yzhang..8618 init, phy address mismatch, base=%u, cur=%d\n", yt_mport_base_phy_addr_8614, phydev->mdio.addr); + } +#endif + return ret; +} + +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(4,0,0) ) +#define yt8614_get_port_from_phydev(phydev) ((0xff == yt_mport_base_phy_addr_8614) && (yt_mport_base_phy_addr_8614 <= (phydev)->addr) ? 0 : (unsigned int)((phydev)->addr) - yt_mport_base_phy_addr_8614) +#else +#define yt8614_get_port_from_phydev(phydev) ((0xff == yt_mport_base_phy_addr_8614) && (yt_mport_base_phy_addr_8614 <= (phydev)->mdio.addr) ? 0 : (unsigned int)((phydev)->mdio.addr) - yt_mport_base_phy_addr_8614) +#endif + +int yt8618_aneg_done (struct phy_device *phydev) +{ + + return genphy_aneg_done(phydev); +} + +int yt8614_aneg_done (struct phy_device *phydev) +{ + int port = yt8614_get_port_from_phydev(phydev); + + /*it should be used for 8614 fiber*/ + if((32 == link_mode_8614[port]) && (SPEED_100 == phydev->speed)) + { + return 1; + } + + return genphy_aneg_done(phydev); +} + +static int yt8614_read_status(struct phy_device *phydev) +{ + //int i; + int ret; + volatile int val, yt8614_fiber_latch_val, yt8614_fiber_curr_val; + volatile int link; + int link_utp = 0, link_fiber = 0; + int port = yt8614_get_port_from_phydev(phydev); + +#if (YT8614_PHY_MODE_CURR != YT8521_PHY_MODE_FIBER) + /* switch to utp and reading regs */ + ret = ytphy_write_ext(phydev, 0xa000, 0); + if (ret < 0) + return ret; + + val = phy_read(phydev, REG_PHY_SPEC_STATUS); + if (val < 0) + return val; + + link = val & (BIT(YT8521_LINK_STATUS_BIT)); + if (link) { + link_utp = 1; + // here is same as 8521 and re-use the function; + yt8521_adjust_status(phydev, val, 1); + } else { + link_utp = 0; + } +#endif //(YT8614_PHY_MODE_CURR != YT8521_PHY_MODE_FIBER) + +#if (YT8614_PHY_MODE_CURR != YT8521_PHY_MODE_UTP) + /* reading Fiber/sgmii */ + ret = ytphy_write_ext(phydev, 0xa000, 3); + if (ret < 0) + return ret; + + val = phy_read(phydev, REG_PHY_SPEC_STATUS); + if (val < 0) + return val; + + //printk (KERN_INFO "yzhang..8614 read fiber status=%04x,macbase=0x%08lx\n", val,(unsigned long)phydev->attached_dev); + + /* for fiber, from 1000m to 100m, there is not link down from 0x11, and check reg 1 to identify such case */ + yt8614_fiber_latch_val = phy_read(phydev, MII_BMSR); + yt8614_fiber_curr_val = phy_read(phydev, MII_BMSR); + link = val & (BIT(YT8521_LINK_STATUS_BIT)); + if((link) && (yt8614_fiber_latch_val != yt8614_fiber_curr_val)) + { + link = 0; + printk (KERN_INFO "yt8614_read_status, fiber link down detect,latch=%04x,curr=%04x\n", yt8614_fiber_latch_val,yt8614_fiber_curr_val); + } + + if (link) { + link_fiber = 1; + yt8521_adjust_status(phydev, val, 0); + link_mode_8614[port] = 32; //fiber mode + + + } else { + link_fiber = 0; + } +#endif //(YT8521_PHY_MODE_CURR != YT8521_PHY_MODE_UTP) + + if (link_utp || link_fiber) { + phydev->link = 1; + } else { + phydev->link = 0; + link_mode_8614[port] = 0; + } + +#if (YT8614_PHY_MODE_CURR != YT8521_PHY_MODE_FIBER) + if (link_utp) { + ytphy_write_ext(phydev, 0xa000, 0); + } +#endif + //printk (KERN_INFO "yt8614_read_status call out,link=%d,linkmode=%d\n", phydev->link, link_mode_8614[port] ); + + return 0; +} + +static int yt8618_read_status(struct phy_device *phydev) +{ + int ret; + volatile int val; //maybe for 8614 yt8521_fiber_latch_val, yt8521_fiber_curr_val; + volatile int link; + int link_utp = 0, link_fiber = 0; + + /* switch to utp and reading regs */ + ret = ytphy_write_ext(phydev, 0xa000, 0); + if (ret < 0) + return ret; + + val = phy_read(phydev, REG_PHY_SPEC_STATUS); + if (val < 0) + return val; + + link = val & (BIT(YT8521_LINK_STATUS_BIT)); + if (link) { + link_utp = 1; + yt8521_adjust_status(phydev, val, 1); + } else { + link_utp = 0; + } + + if (link_utp || link_fiber) { + phydev->link = 1; + } else { + phydev->link = 0; + } + + return 0; +} + +int yt8618_suspend(struct phy_device *phydev) +{ +#if !(SYS_WAKEUP_BASED_ON_ETH_PKT) + int value; + +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(4,0,0) ) + mutex_lock(&phydev->lock); +#else + /* no need lock in 4.19 */ +#endif + + ytphy_write_ext(phydev, 0xa000, 0); + value = phy_read(phydev, MII_BMCR); + phy_write(phydev, MII_BMCR, value | BMCR_PDOWN); + +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(4,0,0) ) + mutex_unlock(&phydev->lock); +#else + /* no need lock/unlock in 4.19 */ +#endif +#endif /*!(SYS_WAKEUP_BASED_ON_ETH_PKT)*/ + + return 0; +} + +int yt8618_resume(struct phy_device *phydev) +{ +#if !(SYS_WAKEUP_BASED_ON_ETH_PKT) + int value; + +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(4,0,0) ) + mutex_lock(&phydev->lock); +#else + /* no need lock/unlock in 4.19 */ +#endif + + ytphy_write_ext(phydev, 0xa000, 0); + value = phy_read(phydev, MII_BMCR); + phy_write(phydev, MII_BMCR, value & ~BMCR_PDOWN); + +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(4,0,0) ) + mutex_unlock(&phydev->lock); +#else + /* no need lock/unlock in 4.19 */ +#endif +#endif /*!(SYS_WAKEUP_BASED_ON_ETH_PKT)*/ + + return 0; +} + +int yt8614_suspend(struct phy_device *phydev) +{ +#if !(SYS_WAKEUP_BASED_ON_ETH_PKT) + int value; + +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(4,0,0) ) + mutex_lock(&phydev->lock); +#else + /* no need lock in 4.19 */ +#endif + + ytphy_write_ext(phydev, 0xa000, 0); + value = phy_read(phydev, MII_BMCR); + phy_write(phydev, MII_BMCR, value | BMCR_PDOWN); + + ytphy_write_ext(phydev, 0xa000, 3); + value = phy_read(phydev, MII_BMCR); + phy_write(phydev, MII_BMCR, value | BMCR_PDOWN); + + ytphy_write_ext(phydev, 0xa000, 0); + +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(4,0,0) ) + mutex_unlock(&phydev->lock); +#else + /* no need lock/unlock in 4.19 */ +#endif +#endif /*!(SYS_WAKEUP_BASED_ON_ETH_PKT)*/ + + return 0; +} + +int yt8614_resume(struct phy_device *phydev) +{ +#if !(SYS_WAKEUP_BASED_ON_ETH_PKT) + int value; + +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(4,0,0) ) + mutex_lock(&phydev->lock); +#else + /* no need lock/unlock in 4.19 */ +#endif + + ytphy_write_ext(phydev, 0xa000, 0); + value = phy_read(phydev, MII_BMCR); + phy_write(phydev, MII_BMCR, value & ~BMCR_PDOWN); + + ytphy_write_ext(phydev, 0xa000, 3); + value = phy_read(phydev, MII_BMCR); + phy_write(phydev, MII_BMCR, value & ~BMCR_PDOWN); + + ytphy_write_ext(phydev, 0xa000, 0); + +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(4,0,0) ) + mutex_unlock(&phydev->lock); +#else + /* no need lock/unlock in 4.19 */ +#endif +#endif /*!(SYS_WAKEUP_BASED_ON_ETH_PKT)*/ + + return 0; +} + + +static struct phy_driver ytphy_drvs[] = { + { + .phy_id = PHY_ID_YT8010, + .name = "YT8010 Automotive Ethernet", + .phy_id_mask = MOTORCOMM_PHY_ID_MASK, +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(5,4,0) ) + .features = PHY_BASIC_FEATURES, + .flags = PHY_HAS_INTERRUPT, +#endif + .config_aneg = yt8010_config_aneg, +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(4,0,0) ) + .config_init = ytphy_config_init, +#else + .config_init = genphy_config_init, +#endif + .read_status = genphy_read_status, + }, { + .phy_id = PHY_ID_YT8510, + .name = "YT8510 100/10Mb Ethernet", + .phy_id_mask = MOTORCOMM_PHY_ID_MASK, +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(5,4,0) ) + .features = PHY_BASIC_FEATURES, + .flags = PHY_HAS_INTERRUPT, +#endif + .config_aneg = genphy_config_aneg, +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(4,0,0) ) + .config_init = ytphy_config_init, +#else + .config_init = genphy_config_init, +#endif + .read_status = genphy_read_status, + }, { + .phy_id = PHY_ID_YT8511, + .name = "YT8511 Gigabit Ethernet", + .phy_id_mask = MOTORCOMM_PHY_ID_MASK, +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(5,4,0) ) + .features = PHY_GBIT_FEATURES, + .flags = PHY_HAS_INTERRUPT, +#endif + .config_aneg = genphy_config_aneg, +#if GMAC_CLOCK_INPUT_NEEDED + .config_init = yt8511_config_init, +#else +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(4,0,0) ) + .config_init = ytphy_config_init, +#else + .config_init = genphy_config_init, +#endif +#endif + .read_status = genphy_read_status, + .suspend = genphy_suspend, + .resume = genphy_resume, + }, { + .phy_id = PHY_ID_YT8512, + .name = "YT8512 Ethernet", + .phy_id_mask = MOTORCOMM_PHY_ID_MASK, +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(5,4,0) ) + .features = PHY_BASIC_FEATURES, + .flags = PHY_HAS_INTERRUPT, +#endif + .config_aneg = genphy_config_aneg, + .config_init = yt8512_config_init, + .read_status = yt8512_read_status, + .suspend = genphy_suspend, + .resume = genphy_resume, + }, { + .phy_id = PHY_ID_YT8512B, + .name = "YT8512B Ethernet", + .phy_id_mask = MOTORCOMM_PHY_ID_MASK, +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(5,4,0) ) + .features = PHY_BASIC_FEATURES, + .flags = PHY_HAS_INTERRUPT, +#endif + .config_aneg = genphy_config_aneg, + .config_init = yt8512_config_init, + .read_status = yt8512_read_status, + .suspend = genphy_suspend, + .resume = genphy_resume, + }, { + .phy_id = PHY_ID_YT8521, + .name = "YT8521 Ethernet", + .phy_id_mask = MOTORCOMM_PHY_ID_MASK, +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(5,4,0) ) + .features = PHY_BASIC_FEATURES | PHY_GBIT_FEATURES, +#endif + .flags = PHY_POLL, +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(4,0,0) ) +#else + .soft_reset = yt8521_soft_reset, +#endif + .config_aneg = genphy_config_aneg, +#if ( LINUX_VERSION_CODE > KERNEL_VERSION(3,11,0) ) + .aneg_done = yt8521_aneg_done, +#endif + .config_init = yt8521_config_init, + .read_status = yt8521_read_status, + .suspend = yt8521_suspend, + .resume = yt8521_resume, +#if (YTPHY_ENABLE_WOL) + .get_wol = &ytphy_get_wol, + .set_wol = &ytphy_set_wol, +#endif + },{ + /* same as 8521 */ + .phy_id = PHY_ID_YT8531S, + .name = "YT8531S Ethernet", + .phy_id_mask = MOTORCOMM_PHY_ID_MASK, +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(5,4,0) ) + .features = PHY_BASIC_FEATURES | PHY_GBIT_FEATURES, +#endif + .flags = PHY_POLL, +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(4,0,0) ) +#else + .soft_reset = yt8521_soft_reset, +#endif + .config_aneg = genphy_config_aneg, +#if ( LINUX_VERSION_CODE > KERNEL_VERSION(3,11,0) ) + .aneg_done = yt8521_aneg_done, +#endif + .config_init = yt8521_config_init, + .read_status = yt8521_read_status, + .suspend = yt8521_suspend, + .resume = yt8521_resume, +#if (YTPHY_ENABLE_WOL) + .get_wol = &ytphy_get_wol, + .set_wol = &ytphy_set_wol, +#endif + }, { + /* same as 8511 */ + .phy_id = PHY_ID_YT8531, + .name = "YT8531 Gigabit Ethernet", + .phy_id_mask = MOTORCOMM_PHY_ID_MASK, +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(5,4,0) ) + .features = PHY_BASIC_FEATURES | PHY_GBIT_FEATURES, + .flags = PHY_HAS_INTERRUPT, +#endif + .config_aneg = genphy_config_aneg, +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(4,0,0) ) + .config_init = ytphy_config_init, +#else + .config_init = genphy_config_init, +#endif + .read_status = genphy_read_status, + .suspend = genphy_suspend, + .resume = genphy_resume, +#if (YTPHY_ENABLE_WOL) + .get_wol = &ytphy_get_wol, + .set_wol = &ytphy_set_wol, +#endif + }, { + .phy_id = PHY_ID_YT8618, + .name = "YT8618 Ethernet", + .phy_id_mask = MOTORCOMM_MPHY_ID_MASK, +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(5,4,0) ) + .features = PHY_BASIC_FEATURES | PHY_GBIT_FEATURES, +#endif + .flags = PHY_POLL, +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(4,0,0) ) +#else + .soft_reset = yt8618_soft_reset, +#endif + .config_aneg = genphy_config_aneg, +#if ( LINUX_VERSION_CODE > KERNEL_VERSION(3,11,0) ) + .aneg_done = yt8618_aneg_done, +#endif + .config_init = yt8618_config_init, + .read_status = yt8618_read_status, + .suspend = yt8618_suspend, + .resume = yt8618_resume, + }, { + .phy_id = PHY_ID_YT8614, + .name = "YT8614 Ethernet", + .phy_id_mask = MOTORCOMM_MPHY_ID_MASK_8614, +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(5,4,0) ) + .features = PHY_BASIC_FEATURES | PHY_GBIT_FEATURES, +#endif + .flags = PHY_POLL, +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(4,0,0) ) +#else + .soft_reset = yt8614_soft_reset, +#endif + .config_aneg = genphy_config_aneg, +#if ( LINUX_VERSION_CODE > KERNEL_VERSION(3,11,0) ) + .aneg_done = yt8614_aneg_done, +#endif + .config_init = yt8614_config_init, + .read_status = yt8614_read_status, + .suspend = yt8614_suspend, + .resume = yt8614_resume, + }, +}; + +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(4,0,0) ) +static int ytphy_drivers_register(struct phy_driver* phy_drvs, int size) +{ + int i, j; + int ret; + + for (i = 0; i < size; i++) { + ret = phy_driver_register(&phy_drvs[i]); + if (ret) + goto err; + } + + return 0; + +err: + for (j = 0; j < i; j++) + phy_driver_unregister(&phy_drvs[j]); + + return ret; +} + +static void ytphy_drivers_unregister(struct phy_driver* phy_drvs, int size) +{ + int i; + + for (i = 0; i < size; i++) { + phy_driver_unregister(&phy_drvs[i]); + } +} + +static int __init ytphy_init(void) +{ + printk("motorcomm phy register\n"); + return ytphy_drivers_register(ytphy_drvs, ARRAY_SIZE(ytphy_drvs)); +} + +static void __exit ytphy_exit(void) +{ + printk("motorcomm phy unregister\n"); + ytphy_drivers_unregister(ytphy_drvs, ARRAY_SIZE(ytphy_drvs)); +} + +module_init(ytphy_init); +module_exit(ytphy_exit); +#else +/* for linux 4.x */ +module_phy_driver(ytphy_drvs); +#endif + +MODULE_DESCRIPTION("Motorcomm PHY driver"); +MODULE_AUTHOR("Leilei Zhao"); +MODULE_LICENSE("GPL"); + +static struct mdio_device_id __maybe_unused motorcomm_tbl[] = { + { PHY_ID_YT8010, MOTORCOMM_PHY_ID_MASK }, + { PHY_ID_YT8510, MOTORCOMM_PHY_ID_MASK }, + { PHY_ID_YT8511, MOTORCOMM_PHY_ID_MASK }, + { PHY_ID_YT8512, MOTORCOMM_PHY_ID_MASK }, + { PHY_ID_YT8512B, MOTORCOMM_PHY_ID_MASK }, + { PHY_ID_YT8521, MOTORCOMM_PHY_ID_MASK }, + { PHY_ID_YT8531S, MOTORCOMM_PHY_ID_8531_MASK }, + { PHY_ID_YT8531, MOTORCOMM_PHY_ID_8531_MASK }, + { PHY_ID_YT8618, MOTORCOMM_MPHY_ID_MASK }, + { PHY_ID_YT8614, MOTORCOMM_MPHY_ID_MASK_8614 }, + { } +}; + +MODULE_DEVICE_TABLE(mdio, motorcomm_tbl); + + diff --git a/drivers/net/phy/yt8614-phy.h b/drivers/net/phy/yt8614-phy.h new file mode 100644 index 000000000..56a398338 --- /dev/null +++ b/drivers/net/phy/yt8614-phy.h @@ -0,0 +1,491 @@ +#ifndef _PHY_H_ +#define _PHY_H_ + + +/* configuration for driver */ + +#define YT8614_MAX_LPORT_ID 3 + +#define YT8614_PHY_MODE_FIBER 1 //fiber mode only +#define YT8614_PHY_MODE_UTP 2 //utp mode only +#define YT8614_PHY_MODE_POLL 3 //fiber and utp, poll mode + +/* please make choice according to system design + * for Fiber only system, please define YT8614_PHY_MODE_CURR 1 + * for UTP only system, please define YT8614_PHY_MODE_CURR 2 + * for combo system, please define YT8614_PHY_MODE_CURR 3 + */ +#define YT8614_PHY_MODE_CURR 3 + + + +/* pls dont modify below lines */ + +#define PHY_ID_YT8614 0x4F51E899 //serdes +#define MOTORCOMM_MPHY_ID_MASK_8614 0xffffffff + +#ifndef BOOL +#define BOOL unsigned int +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef SPEED_1000M +#define SPEED_1000M 2 +#endif +#ifndef SPEED_100M +#define SPEED_100M 1 +#endif +#ifndef SPEED_10M +#define SPEED_10M 0 +#endif + +#ifndef SPEED_UNKNOWN +#define SPEED_UNKNOWN 0xffff +#endif + +#ifndef DUPLEX_FULL +#define DUPLEX_FULL 1 +#endif +#ifndef DUPLEX_HALF +#define DUPLEX_HALF 0 +#endif + +#ifndef BIT +#define BIT(n) (0x1<<(n)) +#endif +#ifndef s32 +typedef int s32; +typedef unsigned int u32; +typedef unsigned short u16; +typedef unsigned char u8; +#endif + +#ifndef REG_PHY_SPEC_STATUS +#define REG_PHY_SPEC_STATUS 0x11 +#define REG_DEBUG_ADDR_OFFSET 0x1e +#define REG_DEBUG_DATA 0x1f +#endif + +/**********YT8614************************************************/ + +#define YT8614_SMI_SEL_PHY 0x0 +#define YT8614_SMI_SEL_SDS_QSGMII 0x02 +#define YT8614_SMI_SEL_SDS_SGMII 0x03 + +/* yt8614 register type */ +#define YT8614_TYPE_COMMON 0x01 +#define YT8614_TYPE_UTP_MII 0x02 +#define YT8614_TYPE_UTP_EXT 0x03 +#define YT8614_TYPE_LDS_MII 0x04 +#define YT8614_TYPE_UTP_MMD 0x05 +#define YT8614_TYPE_SDS_QSGMII_MII 0x06 +#define YT8614_TYPE_SDS_SGMII_MII 0x07 +#define YT8614_TYPE_SDS_QSGMII_EXT 0x08 +#define YT8614_TYPE_SDS_SGMII_EXT 0x09 + +/* YT8614 extended common register */ +#define YT8614_REG_COM_SMI_MUX 0xA000 +#define YT8614_REG_COM_SLED_CFG0 0xA001 +#define YT8614_REG_COM_PHY_ID 0xA002 +#define YT8614_REG_COM_CHIP_VER 0xA003 +#define YT8614_REG_COM_SLED_CFG 0xA004 +#define YT8614_REG_COM_MODE_CHG_RESET 0xA005 +#define YT8614_REG_COM_SYNCE0_CFG 0xA006 +#define YT8614_REG_COM_CHIP_MODE 0xA007 + +#define YT8614_REG_COM_HIDE_SPEED 0xA009 + +#define YT8614_REG_COM_SYNCE1_CFG 0xA00E + +#define YT8614_REG_COM_HIDE_FIBER_MODE 0xA019 + + +#define YT8614_REG_COM_HIDE_SEL1 0xA054 +#define YT8614_REG_COM_HIDE_LED_CFG2 0xB8 +#define YT8614_REG_COM_HIDE_LED_CFG3 0xB9 +#define YT8614_REG_COM_HIDE_LED_CFG5 0xBB + +#define YT8614_REG_COM_HIDE_LED_CFG4 0xBA //not used currently + +#if 0 +#define YT8614_REG_COM_HIDE_LED12_CFG 0xA060 //not used currently +#define YT8614_REG_COM_HIDE_LED13_CFG 0xA061 +#define YT8614_REG_COM_HIDE_LED14_CFG 0xA062 +#define YT8614_REG_COM_HIDE_LED15_CFG 0xA063 +#define YT8614_REG_COM_HIDE_LED16_CFG 0xA064 +#define YT8614_REG_COM_HIDE_LED17_CFG 0xA065 +#define YT8614_REG_COM_HIDE_LED18_CFG 0xA066 +#define YT8614_REG_COM_HIDE_LED19_CFG 0xA067 +#define YT8614_REG_COM_HIDE_LED20_CFG 0xA068 +#define YT8614_REG_COM_HIDE_LED21_CFG 0xA069 +#define YT8614_REG_COM_HIDE_LED22_CFG 0xA06A +#define YT8614_REG_COM_HIDE_LED23_CFG 0xA06B +#define YT8614_REG_COM_HIDE_LED24_CFG 0xA06C +#define YT8614_REG_COM_HIDE_LED25_CFG 0xA06D +#define YT8614_REG_COM_HIDE_LED26_CFG 0xA06E +#define YT8614_REG_COM_HIDE_LED27_CFG 0xA06F +#endif + +#define YT8614_REG_COM_HIDE_LED28_CFG 0xA070 +#define YT8614_REG_COM_HIDE_LED29_CFG 0xA071 +#define YT8614_REG_COM_HIDE_LED30_CFG 0xA072 +#define YT8614_REG_COM_HIDE_LED31_CFG 0xA073 +#define YT8614_REG_COM_HIDE_LED32_CFG 0xA074 +#define YT8614_REG_COM_HIDE_LED33_CFG 0xA075 +#define YT8614_REG_COM_HIDE_LED34_CFG 0xA076 +#define YT8614_REG_COM_HIDE_LED35_CFG 0xA077 + +#define YT8614_REG_COM_PKG_CFG0 0xA0A0 +#define YT8614_REG_COM_PKG_CFG1 0xA0A1 +#define YT8614_REG_COM_PKG_CFG2 0xA0A2 +#define YT8614_REG_COM_PKG_RX_VALID0 0xA0A3 +#define YT8614_REG_COM_PKG_RX_VALID1 0xA0A4 +#define YT8614_REG_COM_PKG_RX_OS0 0xA0A5 +#define YT8614_REG_COM_PKG_RX_OS1 0xA0A6 +#define YT8614_REG_COM_PKG_RX_US0 0xA0A7 +#define YT8614_REG_COM_PKG_RX_US1 0xA0A8 +#define YT8614_REG_COM_PKG_RX_ERR 0xA0A9 +#define YT8614_REG_COM_PKG_RX_OS_BAD 0xA0AA +#define YT8614_REG_COM_PKG_RX_FRAG 0xA0AB +#define YT8614_REG_COM_PKG_RX_NOSFD 0xA0AC +#define YT8614_REG_COM_PKG_TX_VALID0 0xA0AD +#define YT8614_REG_COM_PKG_TX_VALID1 0xA0AE +#define YT8614_REG_COM_PKG_TX_OS0 0xA0AF + +#define YT8614_REG_COM_PKG_TX_OS1 0xA0B0 +#define YT8614_REG_COM_PKG_TX_US0 0xA0B1 +#define YT8614_REG_COM_PKG_TX_US1 0xA0B2 +#define YT8614_REG_COM_PKG_TX_ERR 0xA0B3 +#define YT8614_REG_COM_PKG_TX_OS_BAD 0xA0B4 +#define YT8614_REG_COM_PKG_TX_FRAG 0xA0B5 +#define YT8614_REG_COM_PKG_TX_NOSFD 0xA0B6 +#define YT8614_REG_COM_PKG_CFG3 0xA0B7 +#define YT8614_REG_COM_PKG_AZ_CFG 0xA0B8 +#define YT8614_REG_COM_PKG_DA_SA_CFG3 0xA0B9 + +#define YT8614_REG_COM_MANU_HW_RESET 0xA0C0 + +/* YT8614 UTP MII register: same as generic phy register definitions */ +#define REG_MII_BMCR 0x00 /* Basic mode control register */ +#define REG_MII_BMSR 0x01 /* Basic mode status register */ +#define REG_MII_PHYSID1 0x02 /* PHYS ID 1 */ +#define REG_MII_PHYSID2 0x03 /* PHYS ID 2 */ +#define REG_MII_ADVERTISE 0x04 /* Advertisement control reg */ +#define REG_MII_LPA 0x05 /* Link partner ability reg */ +#define REG_MII_EXPANSION 0x06 /* Expansion register */ +#define REG_MII_NEXT_PAGE 0x07 /* Next page register */ +#define REG_MII_LPR_NEXT_PAGE 0x08 /* LPR next page register */ +#define REG_MII_CTRL1000 0x09 /* 1000BASE-T control */ +#define REG_MII_STAT1000 0x0A /* 1000BASE-T status */ + +#define REG_MII_MMD_CTRL 0x0D /* MMD access control register */ +#define REG_MII_MMD_DATA 0x0E /* MMD access data register */ + +#define REG_MII_ESTATUS 0x0F /* Extended Status */ +#define REG_MII_SPEC_CTRL 0x10 /* PHY specific func control */ +#define REG_MII_SPEC_STATUS 0x11 /* PHY specific status */ +#define REG_MII_INT_MASK 0x12 /* Interrupt mask register */ +#define REG_MII_INT_STATUS 0x13 /* Interrupt status register */ +#define REG_MII_DOWNG_CTRL 0x14 /* Speed auto downgrade control*/ +#define REG_MII_RERRCOUNTER 0x15 /* Receive error counter */ + +#define REG_MII_EXT_ADDR 0x1E /* Extended reg's address */ +#define REG_MII_EXT_DATA 0x1F /* Extended reg's date */ + +#ifndef MII_BMSR +#define MII_BMSR REG_MII_BMSR +#endif + +#ifndef YT8614_SPEED_MODE_BIT +#define YT8614_SPEED_MODE 0xc000 +#define YT8614_DUPLEX 0x2000 +#define YT8614_SPEED_MODE_BIT 14 +#define YT8614_DUPLEX_BIT 13 +#define YT8614_LINK_STATUS_BIT 10 + +#endif + +#define YT8614_REG_COM_HIDE_SPEED_CMB_PRI 0x2000 + +/* YT8614 UTP MMD register */ +#define YT8614_REG_UTP_MMD_CTRL1 0x00 /* PCS control 1 register */ +#define YT8614_REG_UTP_MMD_STATUS1 0x01 /* PCS status 1 register */ +#define YT8614_REG_UTP_MMD_EEE_CTRL 0x14 /* EEE control and capability */ +#define YT8614_REG_UTP_MMD_EEE_WK_ERR_CNT 0x16 /* EEE wake error counter */ +#define YT8614_REG_UTP_MMD_EEE_LOCAL_ABI 0x3C /* local device EEE ability */ +#define YT8614_REG_UTP_MMD_EEE_LP_ABI 0x3D /* link partner EEE ability */ +#define YT8614_REG_UTP_MMD_EEE_AUTONEG_RES 0x8000 /* autoneg result of EEE */ + +/* YT8614 UTP EXT register */ +#define YT8614_REG_UTP_EXT_LPBK 0x0A +#define YT8614_REG_UTP_EXT_SLEEP_CTRL1 0x27 +#define YT8614_REG_UTP_EXT_DEBUG_MON1 0x5A +#define YT8614_REG_UTP_EXT_DEBUG_MON2 0x5B +#define YT8614_REG_UTP_EXT_DEBUG_MON3 0x5C +#define YT8614_REG_UTP_EXT_DEBUG_MON4 0x5D + +/* YT8614 SDS(1.25G/5G) MII register: same as YT8521S */ +#define REG_SDS_BMCR 0x00 /* Basic mode control register */ +#define REG_SDS_BMSR 0x01 /* Basic mode status register */ +#define REG_SDS_PHYSID1 0x02 /* PHYS ID 1 */ +#define REG_SDS_PHYSID2 0x03 /* PHYS ID 2 */ +#define REG_SDS_ADVERTISE 0x04 /* Advertisement control reg */ +#define REG_SDS_LPA 0x05 /* Link partner ability reg */ +#define REG_SDS_EXPANSION 0x06 /* Expansion register */ +#define REG_SDS_NEXT_PAGE 0x07 /* Next page register */ +#define REG_SDS_LPR_NEXT_PAGE 0x08 /* LPR next page register */ + +#define REG_SDS_ESTATUS 0x0F /* Extended Status */ +#define REG_SDS_SPEC_STATUS 0x11 /* SDS specific status */ + +#define REG_SDS_100FX_CFG 0x14 /* 100fx cfg */ +#define REG_SDS_RERRCOUNTER 0x15 /* Receive error counter */ +#define REG_SDS_LINT_FAIL_CNT 0x16 /* Lint fail counter mon */ + +/* YT8614 SDS(5G) EXT register */ +#define YT8614_REG_QSGMII_EXT_ANA_DIG_CFG 0x02 /* sds analog digital interface cfg */ +#define YT8614_REG_QSGMII_EXT_PRBS_CFG1 0x05 /* sds prbs cfg1 */ +#define YT8614_REG_QSGMII_EXT_PRBS_CFG2_1 0x06 /* sds prbs cfg2 */ +#define YT8614_REG_QSGMII_EXT_PRBS_CFG2_2 0x07 /* sds prbs cfg2 */ +#define YT8614_REG_QSGMII_EXT_PRBS_MON1 0x08 /* sds prbs mon1 */ +#define YT8614_REG_QSGMII_EXT_PRBS_MON2 0x09 /* sds prbs mon2 */ +#define YT8614_REG_QSGMII_EXT_PRBS_MON3 0x0A /* sds prbs mon3 */ +#define YT8614_REG_QSGMII_EXT_PRBS_MON4 0x0B /* sds prbs mon4 */ +#define YT8614_REG_QSGMII_EXT_PRBS_MON5 0x0C /* sds prbs mon5 */ +#define YT8614_REG_QSGMII_EXT_ANA_CFG2 0xA1 /* Analog cfg2 */ + +/* YT8614 SDS(1.25G) EXT register */ +#define YT8614_REG_SGMII_EXT_PRBS_CFG1 0x05 /* sds prbs cfg1 */ +#define YT8614_REG_SGMII_EXT_PRBS_CFG2 0x06 /* sds prbs cfg2 */ +#define YT8614_REG_SGMII_EXT_PRBS_MON1 0x08 /* sds prbs mon1 */ +#define YT8614_REG_SGMII_EXT_PRBS_MON2 0x09 /* sds prbs mon2 */ +#define YT8614_REG_SGMII_EXT_PRBS_MON3 0x0A /* sds prbs mon3 */ +#define YT8614_REG_SGMII_EXT_PRBS_MON4 0x0B /* sds prbs mon4 */ +#define YT8614_REG_SGMII_EXT_PRBS_MON5 0x0C /* sds prbs mon5 */ +#define YT8614_REG_SGMII_EXT_ANA_CFG2 0xA1 /* Analog cfg2 */ +#define YT8614_REG_SGMII_EXT_HIDE_AUTO_SEN 0xA5 /* Fiber auto sensing */ + +//////////////////////////////////////////////////////////////////// +#define YT8614_MMD_DEV_ADDR1 0x1 +#define YT8614_MMD_DEV_ADDR3 0x3 +#define YT8614_MMD_DEV_ADDR7 0x7 +#define YT8614_MMD_DEV_ADDR_NONE 0xFF + +/**********YT8521S************************************************/ +/* Basic mode control register(0x00) */ +#define BMCR_RESV 0x003f /* Unused... */ +#define BMCR_SPEED1000 0x0040 /* MSB of Speed (1000) */ +#define BMCR_CTST 0x0080 /* Collision test */ +#define BMCR_FULLDPLX 0x0100 /* Full duplex */ +#define BMCR_ANRESTART 0x0200 /* Auto negotiation restart */ +#define BMCR_ISOLATE 0x0400 /* Disconnect DP83840 from MII */ +#define BMCR_PDOWN 0x0800 /* Powerdown the DP83840 */ +#define BMCR_ANENABLE 0x1000 /* Enable auto negotiation */ +#define BMCR_SPEED100 0x2000 /* Select 100Mbps */ +#define BMCR_LOOPBACK 0x4000 /* TXD loopback bits */ +#define BMCR_RESET 0x8000 /* Reset the DP83840 */ + +/* Basic mode status register(0x01) */ +#define BMSR_ERCAP 0x0001 /* Ext-reg capability */ +#define BMSR_JCD 0x0002 /* Jabber detected */ +#define BMSR_LSTATUS 0x0004 /* Link status */ +#define BMSR_ANEGCAPABLE 0x0008 /* Able to do auto-negotiation */ +#define BMSR_RFAULT 0x0010 /* Remote fault detected */ +#define BMSR_ANEGCOMPLETE 0x0020 /* Auto-negotiation complete */ +#define BMSR_RESV 0x00c0 /* Unused... */ +#define BMSR_ESTATEN 0x0100 /* Extended Status in R15 */ +#define BMSR_100HALF2 0x0200 /* Can do 100BASE-T2 HDX */ +#define BMSR_100FULL2 0x0400 /* Can do 100BASE-T2 FDX */ +#define BMSR_10HALF 0x0800 /* Can do 10mbps, half-duplex */ +#define BMSR_10FULL 0x1000 /* Can do 10mbps, full-duplex */ +#define BMSR_100HALF 0x2000 /* Can do 100mbps, half-duplex */ +#define BMSR_100FULL 0x4000 /* Can do 100mbps, full-duplex */ +#define BMSR_100BASE4 0x8000 /* Can do 100mbps, 4k packets */ + +/* Advertisement control register(0x04) */ +#define ADVERTISE_SLCT 0x001f /* Selector bits */ +#define ADVERTISE_CSMA 0x0001 /* Only selector supported */ +#define ADVERTISE_10HALF 0x0020 /* Try for 10mbps half-duplex */ +#define ADVERTISE_1000XFULL 0x0020 /* Try for 1000BASE-X full-duplex */ +#define ADVERTISE_10FULL 0x0040 /* Try for 10mbps full-duplex */ +#define ADVERTISE_1000XHALF 0x0040 /* Try for 1000BASE-X half-duplex */ +#define ADVERTISE_100HALF 0x0080 /* Try for 100mbps half-duplex */ +#define ADVERTISE_1000XPAUSE 0x0080 /* Try for 1000BASE-X pause */ +#define ADVERTISE_100FULL 0x0100 /* Try for 100mbps full-duplex */ +#define ADVERTISE_1000XPSE_ASYM 0x0100 /* Try for 1000BASE-X asym pause */ +#define ADVERTISE_100BASE4 0x0200 /* Try for 100mbps 4k packets */ +#define ADVERTISE_PAUSE_CAP 0x0400 /* Try for pause */ +#define ADVERTISE_PAUSE_ASYM 0x0800 /* Try for asymetric pause */ +#define ADVERTISE_RESV 0x1000 /* Unused... */ +#define ADVERTISE_RFAULT 0x2000 /* Say we can detect faults */ +#define ADVERTISE_LPACK 0x4000 /* Ack link partners response */ +#define ADVERTISE_NPAGE 0x8000 /* Next page bit */ + +#define ADVERTISE_FULL (ADVERTISE_100FULL | ADVERTISE_10FULL | ADVERTISE_CSMA) +#define ADVERTISE_ALL (ADVERTISE_10HALF | ADVERTISE_10FULL | \ + ADVERTISE_100HALF | ADVERTISE_100FULL) + +/* Link partner ability register(0x05) */ +#define LPA_SLCT 0x001f /* Same as advertise selector */ +#define LPA_10HALF 0x0020 /* Can do 10mbps half-duplex */ +#define LPA_1000XFULL 0x0020 /* Can do 1000BASE-X full-duplex */ +#define LPA_10FULL 0x0040 /* Can do 10mbps full-duplex */ +#define LPA_1000XHALF 0x0040 /* Can do 1000BASE-X half-duplex */ +#define LPA_100HALF 0x0080 /* Can do 100mbps half-duplex */ +#define LPA_1000XPAUSE 0x0080 /* Can do 1000BASE-X pause */ +#define LPA_100FULL 0x0100 /* Can do 100mbps full-duplex */ +#define LPA_1000XPAUSE_ASYM 0x0100 /* Can do 1000BASE-X pause asym */ +#define LPA_100BASE4 0x0200 /* Can do 100mbps 4k packets */ +#define LPA_PAUSE_CAP 0x0400 /* Can pause */ +#define LPA_PAUSE_ASYM 0x0800 /* Can pause asymetrically */ +#define LPA_RESV 0x1000 /* Unused... */ +#define LPA_RFAULT 0x2000 /* Link partner faulted */ +#define LPA_LPACK 0x4000 /* Link partner acked us */ +#define LPA_NPAGE 0x8000 /* Next page bit */ + +/* 1000BASE-T Control register(0x09) */ +#define ADVERTISE_1000FULL 0x0200 /* Advertise 1000BASE-T full duplex */ +#define ADVERTISE_1000HALF 0x0100 /* Advertise 1000BASE-T half duplex */ +#define CTL1000_AS_MASTER 0x0800 +#define CTL1000_ENABLE_MASTER 0x1000 + +/* 1000BASE-T Status register(0x0A) */ +#define LPA_1000LOCALRXOK 0x2000 /* Link partner local receiver status */ +#define LPA_1000REMRXOK 0x1000 /* Link partner remote receiver status */ +#define LPA_1000FULL 0x0800 /* Link partner 1000BASE-T full duplex */ +#define LPA_1000HALF 0x0400 /* Link partner 1000BASE-T half duplex */ + +/**********YT8614************************************************/ +/* Basic mode control register(0x00) */ +#define FIBER_BMCR_RESV 0x001f /* b[4:0] Unused... */ +#define FIBER_BMCR_EN_UNIDIR 0x0020 /* b[5] Valid when bit 0.12 is zero and bit 0.8 is one */ +#define FIBER_BMCR_SPEED1000 0x0040 /* b[6] MSB of Speed (1000) */ +#define FIBER_BMCR_CTST 0x0080 /* b[7] Collision test */ +#define FIBER_BMCR_DUPLEX_MODE 0x0100 /* b[8] Duplex mode */ +#define FIBER_BMCR_ANRESTART 0x0200 /* b[9] Auto negotiation restart */ +#define FIBER_BMCR_ISOLATE 0x0400 /* b[10] Isolate phy from RGMII/SGMII/FIBER */ +#define FIBER_BMCR_PDOWN 0x0800 /* b[11] 1: Power down */ +#define FIBER_BMCR_ANENABLE 0x1000 /* b[12] Enable auto negotiation */ +#define FIBER_BMCR_SPEED100 0x2000 /* b[13] LSB of Speed (100) */ +#define FIBER_BMCR_LOOPBACK 0x4000 /* b[14] Internal loopback control */ +#define FIBER_BMCR_RESET 0x8000 /* b[15] PHY Software Reset(self-clear) */ + +/* Sds specific status register(0x11) */ +#define FIBER_SSR_ERCAP 0x0001 /* b[0] realtime syncstatus */ +#define FIBER_SSR_XMIT 0x000E /* b[3:1] realtime transmit statemachine. + 001: Xmit Idle; + 010: Xmit Config; + 100: Xmit Data. */ +#define FIBER_SSR_SER_MODE_CFG 0x0030 /* b[5:4] realtime serdes working mode. + 00: SG_MAC; + 01: SG_PHY; + 10: FIB_1000; + 11: FIB_100. */ +#define FIBER_SSR_EN_FLOWCTRL_TX 0x0040 /* b[6] realtime en_flowctrl_tx */ +#define FIBER_SSR_EN_FLOWCTRL_RX 0x0080 /* b[7] realtime en_flowctrl_rx */ +#define FIBER_SSR_DUPLEX_ERROR 0x0100 /* b[8] realtime deplex error */ +#define FIBER_SSR_RX_LPI_ACTIVE 0x0200 /* b[9] rx lpi is active */ +#define FIBER_SSR_LSTATUS 0x0400 /* b[10] Link status real-time */ +#define FIBER_SSR_PAUSE 0x1800 /* b[12:11] Pause to mac */ +#define FIBER_SSR_DUPLEX 0x2000 /* b[13] This status bit is valid only when bit10 is 1. + 1: full duplex + 0: half duplex */ +#define FIBER_SSR_SPEED_MODE 0xC000 /* b[15:14] These status bits are valid only when bit10 is 1. + 10---1000M + 01---100M */ + +/* SLED cfg0 (ext 0xA001) */ +#define FIBER_SLED_CFG0_EN_CTRL 0x00FF /* b[7:0] Control to enable the eight ports' SLED */ +#define FIBER_SLED_CFG0_BIT_MASK 0x0700 /* b[10:8] 1: enable the pin output */ +#define FIBER_SLED_CFG0_ACT_LOW 0x0800 /* b[11] control SLED's polarity. 1: active low; 0: active high */ +#define FIBER_SLED_CFG0_MANU_ST 0x7000 /* b[14:12] SLEDs' manul status, corresponding to each port's 3 SLEDs */ +#define FIBER_SLED_CFG0_MANU_EN 0x8000 /* b[15] to control serial LEDs status manually */ + +/**********YT8614************************************************/ +/* Fiber auto sensing(sgmii ext 0xA5) */ +#define FIBER_AUTO_SEN_ENABLE 0x8000 /* b[15] Enable fiber auto sensing */ + +/* Fiber force speed(common ext 0xA009) */ +#define FIBER_FORCE_1000M 0x0001 /* b[0] 1:1000BX 0:100FX */ + +#ifndef NULL +#define NULL 0 +#endif + +/* errno */ +enum ytphy_8614_errno_e +{ + SYS_E_NONE, + SYS_E_PARAM, + SYS_E_MAX +}; + +/* errno */ +enum ytphy_8614_combo_speed_e +{ + YT8614_COMBO_FIBER_1000M, + YT8614_COMBO_FIBER_100M, + YT8614_COMBO_UTP_ONLY, + YT8614_COMBO_SPEED_MAX +}; + +/* definition for porting */ +/* phy registers access */ +typedef struct +{ + u16 reg; /* the offset of the phy internal address */ + u16 val; /* the value of the register */ + u8 regType; /* register type */ +} phy_data_s; + +/* for porting use. + * pls over-write member function read/write for mdio access + */ +typedef struct phy_info_str +{ +#if 0 + struct phy_device *phydev; + int mdio_base; +#endif + unsigned int lport; + unsigned int bus_id; + unsigned int phy_addr; + + s32 (*read)(struct phy_info_str *info, phy_data_s *param); + s32 (*write)(struct phy_info_str *info, phy_data_s *param); +}phy_info_s; + +/* get phy access method */ +s32 yt8614_read_reg(struct phy_info_str *info, phy_data_s *param); +s32 yt8614_write_reg(struct phy_info_str *info, phy_data_s *param); +s32 yt8614_phy_soft_reset(u32 lport); +s32 yt8614_phy_init(u32 lport); +s32 yt8614_fiber_enable(u32 lport, BOOL enable); +s32 yt8614_utp_enable(u32 lport, BOOL enable); +s32 yt8614_fiber_unidirection_set(u32 lport, int speed, BOOL enable); +s32 yt8614_fiber_autosensing_set(u32 lport, BOOL enable); +s32 yt8614_fiber_speed_set(u32 lport, int fiber_speed); +s32 yt8614_qsgmii_autoneg_set(u32 lport, BOOL enable); +s32 yt8614_sgmii_autoneg_set(u32 lport, BOOL enable); +s32 yt8614_qsgmii_sgmii_link_status_get(u32 lport, BOOL *enable, BOOL if_qsgmii); +int yt8614_combo_media_priority_set (u32 lport, int fiber); +int yt8614_combo_media_priority_get (u32 lport, int *fiber); +s32 yt8614_utp_autoneg_set(u32 lport, BOOL enable); +s32 yt8614_utp_autoneg_get(u32 lport, BOOL *enable); +s32 yt8614_utp_autoneg_ability_set(u32 lport, unsigned int cap_mask); +s32 yt8614_utp_autoneg_ability_get(u32 lport, unsigned int *cap_mask); +s32 yt8614_utp_force_duplex_set(u32 lport, BOOL full); +s32 yt8614_utp_force_duplex_get(u32 lport, BOOL *full); +s32 yt8614_utp_force_speed_set(u32 lport, unsigned int speed); +s32 yt8614_utp_force_speed_get(u32 lport, unsigned int *speed); +int yt8614_autoneg_done_get (u32 lport, int speed, int *aneg); +int yt8614_media_status_get(u32 lport, int* speed, int* duplex, int* ret_link, int *media); + +#endif diff --git a/include/linux/motorcomm_phy.h b/include/linux/motorcomm_phy.h new file mode 100644 index 000000000..9e01fc205 --- /dev/null +++ b/include/linux/motorcomm_phy.h @@ -0,0 +1,119 @@ +/* + * include/linux/motorcomm_phy.h + * + * Motorcomm PHY IDs + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#ifndef _MOTORCOMM_PHY_H +#define _MOTORCOMM_PHY_H + +#define MOTORCOMM_PHY_ID_MASK 0x00000fff +#define MOTORCOMM_PHY_ID_8531_MASK 0xffffffff +#define MOTORCOMM_MPHY_ID_MASK 0x0000ffff + +#define PHY_ID_YT8010 0x00000309 +#define PHY_ID_YT8510 0x00000109 +#define PHY_ID_YT8511 0x0000010a +#define PHY_ID_YT8512 0x00000118 +#define PHY_ID_YT8512B 0x00000128 +#define PHY_ID_YT8521 0x0000011a +#define PHY_ID_YT8531S 0x4f51e91a +#define PHY_ID_YT8531 0x4f51e91b +//#define PHY_ID_YT8614 0x0000e899 +#define PHY_ID_YT8618 0x0000e889 + +#define REG_PHY_SPEC_STATUS 0x11 +#define REG_DEBUG_ADDR_OFFSET 0x1e +#define REG_DEBUG_DATA 0x1f + +#define YT8512_EXTREG_AFE_PLL 0x50 +#define YT8512_EXTREG_EXTEND_COMBO 0x4000 +#define YT8512_EXTREG_LED0 0x40c0 +#define YT8512_EXTREG_LED1 0x40c3 + +#define YT8512_EXTREG_SLEEP_CONTROL1 0x2027 + +#define YT_SOFTWARE_RESET 0x8000 + +#define YT8512_CONFIG_PLL_REFCLK_SEL_EN 0x0040 +#define YT8512_CONTROL1_RMII_EN 0x0001 +#define YT8512_LED0_ACT_BLK_IND 0x1000 +#define YT8512_LED0_DIS_LED_AN_TRY 0x0001 +#define YT8512_LED0_BT_BLK_EN 0x0002 +#define YT8512_LED0_HT_BLK_EN 0x0004 +#define YT8512_LED0_COL_BLK_EN 0x0008 +#define YT8512_LED0_BT_ON_EN 0x0010 +#define YT8512_LED1_BT_ON_EN 0x0010 +#define YT8512_LED1_TXACT_BLK_EN 0x0100 +#define YT8512_LED1_RXACT_BLK_EN 0x0200 +#define YT8512_SPEED_MODE 0xc000 +#define YT8512_DUPLEX 0x2000 + +#define YT8512_SPEED_MODE_BIT 14 +#define YT8512_DUPLEX_BIT 13 +#define YT8512_EN_SLEEP_SW_BIT 15 + +#define YT8521_EXTREG_SLEEP_CONTROL1 0x27 +#define YT8521_EN_SLEEP_SW_BIT 15 + +#define YT8521_SPEED_MODE 0xc000 +#define YT8521_DUPLEX 0x2000 +#define YT8521_SPEED_MODE_BIT 14 +#define YT8521_DUPLEX_BIT 13 +#define YT8521_LINK_STATUS_BIT 10 + +/* based on yt8521 wol config register */ +#define YTPHY_UTP_INTR_REG 0x12 +/* WOL Event Interrupt Enable */ +#define YTPHY_WOL_INTR BIT(6) + +/* Magic Packet MAC address registers */ +#define YTPHY_MAGIC_PACKET_MAC_ADDR2 0xa007 +#define YTPHY_MAGIC_PACKET_MAC_ADDR1 0xa008 +#define YTPHY_MAGIC_PACKET_MAC_ADDR0 0xa009 + +#define YTPHY_WOL_CFG_REG 0xa00a +#define YTPHY_WOL_CFG_TYPE BIT(0) /* WOL TYPE */ +#define YTPHY_WOL_CFG_EN BIT(3) /* WOL Enable */ +#define YTPHY_WOL_CFG_INTR_SEL BIT(6) /* WOL Event Interrupt Enable */ +#define YTPHY_WOL_CFG_WIDTH1 BIT(1) /* WOL Pulse Width */ +#define YTPHY_WOL_CFG_WIDTH2 BIT(2) + +#define YTPHY_REG_SPACE_UTP 0 +#define YTPHY_REG_SPACE_FIBER 2 + +enum ytphy_wol_type_e +{ + YTPHY_WOL_TYPE_LEVEL, + YTPHY_WOL_TYPE_PULSE, + YTPHY_WOL_TYPE_MAX +}; +typedef enum ytphy_wol_type_e ytphy_wol_type_t; + +enum ytphy_wol_width_e +{ + YTPHY_WOL_WIDTH_84MS, + YTPHY_WOL_WIDTH_168MS, + YTPHY_WOL_WIDTH_336MS, + YTPHY_WOL_WIDTH_672MS, + YTPHY_WOL_WIDTH_MAX +}; +typedef enum ytphy_wol_width_e ytphy_wol_width_t; + +struct ytphy_wol_cfg_s +{ + int enable; + int type; + int width; +}; +typedef struct ytphy_wol_cfg_s ytphy_wol_cfg_t; + +#endif /* _MOTORCOMM_PHY_H */ + + -- 2.25.1