mirror of
https://github.com/openwrt/openwrt.git
synced 2025-03-13 07:54:14 +00:00
Siflower SF21A6826/SF21H8898 are a family of RISC-V SoCs with: * Quad-core T-Head C908 (1.125G for SF21A6826, 1.25G for SF21H8898) * DDR3/DDR4 memory controller * 1 QSGMII 4x1G * 1 SGMII/2500Base-X 2.5G * 1 additional RGMII on SF21H8898 * Network offloading engine for L2 switching and L3 NAT * 2 PCIE Gen2 lanes, operating in either one PCIE Gen2x2 or two PCIE Gen2x1 mode * 1 USB2.0 Link: https://github.com/openwrt/openwrt/pull/17115 Signed-off-by: Chuanhong Guo <gch981213@gmail.com>
878 lines
25 KiB
C
878 lines
25 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
* drivers/net/phy/siflower.c
|
|
*
|
|
* Driver for Siflower PHYs
|
|
*
|
|
* Copyright (c) 2023 Siflower, Inc.
|
|
*
|
|
* Support : Siflower Phys:
|
|
* Giga phys: p1211f, p1240
|
|
*/
|
|
#include <linux/bitops.h>
|
|
#include <linux/of.h>
|
|
#include <linux/phy.h>
|
|
#include <linux/module.h>
|
|
#include <linux/delay.h>
|
|
|
|
/* for wol feature */
|
|
#include <linux/netdevice.h>
|
|
|
|
/* WOL Enable Flag:
|
|
* disable by default to enable system WOL feature of phy
|
|
* please define this phy to 1 otherwise, define it to 0.
|
|
*/
|
|
#define SIFLOWER_PHY_WOL_FEATURE_ENABLE 0
|
|
#define SIFLOWER_PHY_WOL_PASSWD_ENABLE 0
|
|
|
|
#define SIFLOWER_PHY_MODE_SET_ENABLE 0
|
|
#define SIFLOWER_PHY_RXC_DELAY_SET_ENABLE 0
|
|
#define SIFLOWER_PHY_RXC_DELAY_VAL 0x40
|
|
#define SIFLOWER_PHY_TXC_DELAY_VAL 0x40
|
|
#define SIFLOWER_PHY_CLK_OUT_125M_ENABLE 1
|
|
|
|
#define SFPHY_GLB_DISABLE 0
|
|
#define SFPHY_GLB_ENABLE 1
|
|
#define SFPHY_LINK_DOWN 0
|
|
#define SFPHY_LINK_UP 1
|
|
/* Mask used for ID comparisons */
|
|
#define SIFLOWER_PHY_ID_MASK 0xffffffff
|
|
|
|
/* SF1211F PHY IDs */
|
|
#define SF1211F_PHY_ID 0xADB40412
|
|
/* SF1240 PHY IDs */
|
|
#define SF1240_PHY_ID 0xADB40411
|
|
|
|
/* SF1211F PHY LED */
|
|
#define SF1211F_EXTREG_LED0 0x1E33 // 0
|
|
#define SF1211F_EXTREG_LED1 0x1E34 // 00101111
|
|
#define SF1211F_EXTREG_LED2 0x1E35 // 0x40
|
|
/* SF1240 PHY BX LED */
|
|
#define SF1240_EXTREG_LEDCTRL 0x0621
|
|
#define SF1240_EXTREG_LED0_1 0x0700
|
|
#define SF1240_EXTREG_LED0_2 0x0701
|
|
#define SF1240_EXTREG_LED1_1 0x0702
|
|
#define SF1240_EXTREG_LED1_2 0x0703
|
|
#define SF1240_EXTREG_LED2_1 0x0706
|
|
#define SF1240_EXTREG_LED2_2 0x0707
|
|
#define SF1240_EXTREG_LED3_1 0x0708
|
|
#define SF1240_EXTREG_LED3_2 0x0709
|
|
#define SF1240_EXTREG_LED4_1 0x070C
|
|
#define SF1240_EXTREG_LED4_2 0x070D
|
|
#define SF1240_EXTREG_LED5_1 0x070E
|
|
#define SF1240_EXTREG_LED5_2 0x070F
|
|
#define SF1240_EXTREG_LED6_1 0x0712
|
|
#define SF1240_EXTREG_LED6_2 0x0713
|
|
#define SF1240_EXTREG_LED7_1 0x0714
|
|
#define SF1240_EXTREG_LED7_2 0x0715
|
|
|
|
/* PHY MODE OPSREG*/
|
|
#define SF1211F_EXTREG_GET_PORT_PHY_MODE 0x062B
|
|
#define SF1211F_EXTREG_PHY_MODE_MASK 0x0070
|
|
/* Magic Packet MAC address registers */
|
|
#define SIFLOWER_MAGIC_PACKET_MAC_ADDR 0x0229
|
|
/* Magic Packet MAC Passwd registers */
|
|
#define SIFLOWER_MAGIC_PACKET_PASSWD_ADDR 0x022F
|
|
#define SIFLOWER_PHY_WOL_PULSE_MODE_SET 0x062a
|
|
|
|
/* Magic Packet MAC Passwd Val*/
|
|
#define SIFLOWER_MAGIC_PACKET_PASSWD1 0x11
|
|
#define SIFLOWER_MAGIC_PACKET_PASSWD2 0x22
|
|
#define SIFLOWER_MAGIC_PACKET_PASSWD3 0x33
|
|
#define SIFLOWER_MAGIC_PACKET_PASSWD4 0x44
|
|
#define SIFLOWER_MAGIC_PACKET_PASSWD5 0x55
|
|
#define SIFLOWER_MAGIC_PACKET_PASSWD6 0x66
|
|
|
|
/* Siflower wol config register */
|
|
#define SIFLOWER_WOL_CFG_REG0 0x0220
|
|
#define SIFLOWER_WOL_CFG_REG1 0x0221
|
|
#define SIFLOWER_WOL_CFG_REG2 0x0222
|
|
#define SIFLOWER_WOL_STA_REG 0x0223
|
|
/* 8 PHY MODE */
|
|
#define SF1211F_EXTREG_PHY_MODE_UTP_TO_RGMII 0x00
|
|
#define SF1211F_EXTREG_PHY_MODE_FIBER_TO_RGMII 0x10
|
|
#define SF1211F_EXTREG_PHY_MODE_UTP_OR_FIBER_TO_RGMII 0x20
|
|
#define SF1211F_EXTREG_PHY_MODE_UTP_TO_SGMII 0x30
|
|
#define SF1211F_EXTREG_PHY_MODE_SGMII_PHY_TO_RGMII_MAC 0x40
|
|
#define SF1211F_EXTREG_PHY_MODE_SGMII_MAC_TO_RGMII_PHY 0x50
|
|
#define SF1211F_EXTREG_PHY_MODE_UTP_TO_FIBER_AUTO 0x60
|
|
#define SF1211F_EXTREG_PHY_MODE_UTP_TO_FIBER_FORCE 0x70
|
|
|
|
/* PHY EXTRW OPSREG */
|
|
#define SF1211F_EXTREG_ADDR 0x0E
|
|
#define SF1211F_EXTREG_DATA 0x0D
|
|
/* PHY PAGE SPACE */
|
|
#define SFPHY_REG_UTP_SPACE 0
|
|
#define SFPHY_REG_FIBER_SPACE 1
|
|
|
|
/* PHY PAGE SELECT */
|
|
#define SF1211F_EXTREG_PHY_MODE_PAGE_SELECT 0x0016
|
|
#define SFPHY_REG_UTP_SPACE_SETADDR 0x0000
|
|
#define SFPHY_REG_FIBER_SPACE_SETADDR 0x0100
|
|
//utp
|
|
#define UTP_REG_PAUSE_CAP 0x0400 /* Can pause */
|
|
#define UTP_REG_PAUSE_ASYM 0x0800 /* Can pause asymetrically */
|
|
//fiber
|
|
#define FIBER_REG_PAUSE_CAP 0x0080 /* Can pause */
|
|
#define FIBER_REG_PAUSE_ASYM 0x0100 /* Can pause asymetrically */
|
|
|
|
/* specific status register */
|
|
#define SIFLOWER_SPEC_REG 0x0011
|
|
|
|
/* Interrupt Enable Register */
|
|
#define SIFLOWER_INTR_REG 0x0017
|
|
/* WOL TYPE */
|
|
#define SIFLOWER_WOL_TYPE BIT(0)
|
|
/* WOL Pulse Width */
|
|
#define SIFLOWER_WOL_WIDTH1 BIT(1)
|
|
#define SIFLOWER_WOL_WIDTH2 BIT(2)
|
|
/* WOL dest addr check enable */
|
|
#define SIFLOWER_WOL_SECURE_CHECK BIT(5)
|
|
/* WOL crc check enable */
|
|
#define SIFLOWER_WOL_CRC_CHECK BIT(4)
|
|
/* WOL dest addr check enable */
|
|
#define SIFLOWER_WOL_DESTADDR_CHECK BIT(5)
|
|
/* WOL Event Interrupt Enable */
|
|
#define SIFLOWER_WOL_INTR_EN BIT(2)
|
|
/* WOL Enable */
|
|
#define SIFLOWER_WOL_EN BIT(7)
|
|
|
|
#define SIFLOWER_WOL_RESTARTANEG BIT(9)
|
|
/* GET PHY MODE */
|
|
#define SFPHY_MODE_CURR sfphy_get_port_type(phydev)
|
|
|
|
enum siflower_port_type_e
|
|
{
|
|
SFPHY_PORT_TYPE_UTP,
|
|
SFPHY_PORT_TYPE_FIBER,
|
|
SFPHY_PORT_TYPE_COMBO,
|
|
SFPHY_PORT_TYPE_EXT
|
|
};
|
|
enum siflower_wol_type_e
|
|
{
|
|
SFPHY_WOL_TYPE_LEVEL,
|
|
SFPHY_WOL_TYPE_PULSE,
|
|
SFPHY_WOL_TYPE_EXT
|
|
};
|
|
|
|
enum siflower_wol_width_e
|
|
{
|
|
SFPHY_WOL_WIDTH_84MS,
|
|
SFPHY_WOL_WIDTH_168MS,
|
|
SFPHY_WOL_WIDTH_336MS,
|
|
SFPHY_WOL_WIDTH_672MS,
|
|
SFPHY_WOL_WIDTH_EXT
|
|
};
|
|
|
|
typedef struct siflower_wol_cfg_s
|
|
{
|
|
int wolen;
|
|
int type;
|
|
int width;
|
|
int secure;
|
|
int checkcrc;
|
|
int checkdst;
|
|
}siflower_wol_cfg_t;
|
|
|
|
static int sf1211f_phy_ext_read(struct phy_device *phydev, u32 regnum)
|
|
{
|
|
int ret, val, oldpage = 0, oldval = 0;
|
|
|
|
phy_lock_mdio_bus(phydev);
|
|
|
|
ret = __phy_read(phydev, SF1211F_EXTREG_ADDR);
|
|
if (ret < 0)
|
|
goto err_handle;
|
|
oldval = ret;
|
|
|
|
/* Force change to utp page */
|
|
ret = __phy_read(phydev, SF1211F_EXTREG_PHY_MODE_PAGE_SELECT);//get old page
|
|
if (ret < 0)
|
|
goto err_handle;
|
|
oldpage = ret;
|
|
|
|
ret = __phy_write(phydev, SF1211F_EXTREG_PHY_MODE_PAGE_SELECT, SFPHY_REG_UTP_SPACE_SETADDR);
|
|
if (ret < 0)
|
|
goto err_handle;
|
|
|
|
/* Default utp ext rw */
|
|
ret = __phy_write(phydev, SF1211F_EXTREG_ADDR, regnum);
|
|
if (ret < 0)
|
|
goto err_handle;
|
|
|
|
ret = __phy_read(phydev, SF1211F_EXTREG_DATA);
|
|
if (ret < 0)
|
|
goto err_handle;
|
|
val = ret;
|
|
|
|
/* Recover to old page */
|
|
ret = __phy_write(phydev, SF1211F_EXTREG_PHY_MODE_PAGE_SELECT, oldpage);
|
|
if (ret < 0)
|
|
goto err_handle;
|
|
|
|
ret = __phy_write(phydev, SF1211F_EXTREG_ADDR, oldval);
|
|
if (ret < 0)
|
|
goto err_handle;
|
|
ret = val;
|
|
|
|
err_handle:
|
|
phy_unlock_mdio_bus(phydev);
|
|
return ret;
|
|
}
|
|
|
|
static int sf1211f_phy_ext_write(struct phy_device *phydev, u32 regnum, u16 val)
|
|
{
|
|
int ret, oldpage = 0, oldval = 0;
|
|
|
|
phy_lock_mdio_bus(phydev);
|
|
|
|
ret = __phy_read(phydev, SF1211F_EXTREG_ADDR);
|
|
if (ret < 0)
|
|
goto err_handle;
|
|
oldval = ret;
|
|
|
|
/* Force change to utp page */
|
|
ret = __phy_read(phydev, SF1211F_EXTREG_PHY_MODE_PAGE_SELECT); //get old page
|
|
if (ret < 0)
|
|
goto err_handle;
|
|
oldpage = ret;
|
|
|
|
ret = __phy_write(phydev, SF1211F_EXTREG_PHY_MODE_PAGE_SELECT, SFPHY_REG_UTP_SPACE_SETADDR);
|
|
if (ret < 0)
|
|
goto err_handle;
|
|
|
|
/* Default utp ext rw */
|
|
ret = __phy_write(phydev, SF1211F_EXTREG_ADDR, regnum);
|
|
if (ret < 0)
|
|
goto err_handle;
|
|
|
|
ret = __phy_write(phydev, SF1211F_EXTREG_DATA, val);
|
|
if (ret < 0)
|
|
goto err_handle;
|
|
|
|
/* Recover to old page */
|
|
ret = __phy_write(phydev, SF1211F_EXTREG_PHY_MODE_PAGE_SELECT, oldpage);
|
|
if (ret < 0)
|
|
goto err_handle;
|
|
|
|
ret = __phy_write(phydev, SF1211F_EXTREG_ADDR, oldval);
|
|
if (ret < 0)
|
|
goto err_handle;
|
|
|
|
err_handle:
|
|
phy_unlock_mdio_bus(phydev);
|
|
return ret;
|
|
|
|
}
|
|
|
|
static int siflower_phy_select_reg_page(struct phy_device *phydev, int space)
|
|
{
|
|
int ret;
|
|
if (space == SFPHY_REG_UTP_SPACE)
|
|
ret = phy_write(phydev, SF1211F_EXTREG_PHY_MODE_PAGE_SELECT, SFPHY_REG_UTP_SPACE_SETADDR);
|
|
else
|
|
ret = phy_write(phydev, SF1211F_EXTREG_PHY_MODE_PAGE_SELECT, SFPHY_REG_FIBER_SPACE_SETADDR);
|
|
return ret;
|
|
}
|
|
|
|
static int siflower_phy_get_reg_page(struct phy_device *phydev)
|
|
{
|
|
return phy_read(phydev, SF1211F_EXTREG_PHY_MODE_PAGE_SELECT);
|
|
}
|
|
|
|
static int siflower_phy_ext_read(struct phy_device *phydev, u32 regnum)
|
|
{
|
|
return sf1211f_phy_ext_read(phydev, regnum);
|
|
}
|
|
|
|
|
|
static int siflower_phy_ext_write(struct phy_device *phydev, u32 regnum, u16 val)
|
|
{
|
|
return sf1211f_phy_ext_write(phydev, regnum, val);
|
|
}
|
|
|
|
static int sfphy_page_read(struct phy_device *phydev, int page, u32 regnum)
|
|
{
|
|
int ret, val, oldpage = 0, oldval = 0;
|
|
|
|
phy_lock_mdio_bus(phydev);
|
|
|
|
ret = __phy_read(phydev, SF1211F_EXTREG_ADDR);
|
|
if (ret < 0)
|
|
goto err_handle;
|
|
oldval = ret;
|
|
|
|
ret = __phy_read(phydev, SF1211F_EXTREG_PHY_MODE_PAGE_SELECT);
|
|
if (ret < 0)
|
|
goto err_handle;
|
|
oldpage = ret;
|
|
|
|
//Select page
|
|
ret = __phy_write(phydev, SF1211F_EXTREG_PHY_MODE_PAGE_SELECT, (page << 8));
|
|
if (ret < 0)
|
|
goto err_handle;
|
|
|
|
ret = __phy_read(phydev, regnum);
|
|
if (ret < 0)
|
|
goto err_handle;
|
|
val = ret;
|
|
|
|
/* Recover to old page */
|
|
ret = __phy_write(phydev, SF1211F_EXTREG_PHY_MODE_PAGE_SELECT, oldpage);
|
|
if (ret < 0)
|
|
goto err_handle;
|
|
|
|
ret = __phy_write(phydev, SF1211F_EXTREG_ADDR, oldval);
|
|
if (ret < 0)
|
|
goto err_handle;
|
|
ret = val;
|
|
|
|
err_handle:
|
|
phy_unlock_mdio_bus(phydev);
|
|
return ret;
|
|
}
|
|
|
|
static int sfphy_page_write(struct phy_device *phydev, int page, u32 regnum, u16 value)
|
|
{
|
|
int ret, oldpage = 0, oldval = 0;
|
|
|
|
phy_lock_mdio_bus(phydev);
|
|
|
|
ret = __phy_read(phydev, SF1211F_EXTREG_ADDR);
|
|
if (ret < 0)
|
|
goto err_handle;
|
|
oldval = ret;
|
|
|
|
ret = __phy_read(phydev, SF1211F_EXTREG_PHY_MODE_PAGE_SELECT);
|
|
if (ret < 0)
|
|
goto err_handle;
|
|
oldpage = ret;
|
|
|
|
//Select page
|
|
ret = __phy_write(phydev, SF1211F_EXTREG_PHY_MODE_PAGE_SELECT, (page << 8));
|
|
if(ret<0)
|
|
goto err_handle;
|
|
|
|
ret = __phy_write(phydev, regnum, value);
|
|
if(ret<0)
|
|
goto err_handle;
|
|
|
|
/* Recover to old page */
|
|
ret = __phy_write(phydev, SF1211F_EXTREG_PHY_MODE_PAGE_SELECT, oldpage);
|
|
if (ret < 0)
|
|
goto err_handle;
|
|
|
|
ret = __phy_write(phydev, SF1211F_EXTREG_ADDR, oldval);
|
|
if (ret < 0)
|
|
goto err_handle;
|
|
|
|
err_handle:
|
|
phy_unlock_mdio_bus(phydev);
|
|
return ret;
|
|
}
|
|
|
|
//get port type
|
|
static int sfphy_get_port_type(struct phy_device *phydev)
|
|
{
|
|
int ret, mode;
|
|
|
|
ret = siflower_phy_ext_read(phydev, SF1211F_EXTREG_GET_PORT_PHY_MODE);
|
|
if (ret < 0)
|
|
return ret;
|
|
ret &= SF1211F_EXTREG_PHY_MODE_MASK;
|
|
|
|
if (ret == SF1211F_EXTREG_PHY_MODE_UTP_TO_RGMII ||
|
|
ret == SF1211F_EXTREG_PHY_MODE_UTP_TO_SGMII) {
|
|
mode = SFPHY_PORT_TYPE_UTP;
|
|
} else if (ret == SF1211F_EXTREG_PHY_MODE_FIBER_TO_RGMII ||
|
|
ret == SF1211F_EXTREG_PHY_MODE_SGMII_PHY_TO_RGMII_MAC ||
|
|
ret == SF1211F_EXTREG_PHY_MODE_SGMII_MAC_TO_RGMII_PHY) {
|
|
mode = SFPHY_PORT_TYPE_FIBER;
|
|
} else {
|
|
mode = SFPHY_PORT_TYPE_COMBO;
|
|
}
|
|
|
|
return mode;
|
|
}
|
|
|
|
static int sfphy_restart_aneg(struct phy_device *phydev)
|
|
{
|
|
int ret, ctl;
|
|
|
|
ctl = sfphy_page_read(phydev, SFPHY_REG_FIBER_SPACE, MII_BMCR);
|
|
if (ctl < 0)
|
|
return ctl;
|
|
ctl |= BMCR_ANENABLE;
|
|
ret = sfphy_page_write(phydev, SFPHY_REG_FIBER_SPACE, MII_BMCR, ctl);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int sf1211f_config_aneg(struct phy_device *phydev)
|
|
{
|
|
int ret, phymode, oldpage = 0;
|
|
|
|
phymode = SFPHY_MODE_CURR;
|
|
|
|
if (phymode == SFPHY_PORT_TYPE_UTP || phymode == SFPHY_PORT_TYPE_COMBO) {
|
|
oldpage = siflower_phy_get_reg_page(phydev);
|
|
if (oldpage < 0)
|
|
return oldpage;
|
|
ret = siflower_phy_select_reg_page(phydev, SFPHY_REG_UTP_SPACE);
|
|
if (ret < 0)
|
|
return ret;
|
|
ret = genphy_config_aneg(phydev);
|
|
if (ret < 0)
|
|
return ret;
|
|
ret = siflower_phy_select_reg_page(phydev, oldpage);
|
|
if (ret < 0)
|
|
return ret;
|
|
}
|
|
|
|
if (phymode == SFPHY_PORT_TYPE_FIBER || phymode == SFPHY_PORT_TYPE_COMBO) {
|
|
oldpage = siflower_phy_get_reg_page(phydev);
|
|
if (oldpage < 0)
|
|
return oldpage;
|
|
ret = siflower_phy_select_reg_page(phydev, SFPHY_REG_FIBER_SPACE);
|
|
if (ret < 0)
|
|
return ret;
|
|
if (AUTONEG_ENABLE != phydev->autoneg)
|
|
return genphy_setup_forced(phydev);
|
|
ret = sfphy_restart_aneg(phydev);
|
|
if (ret < 0)
|
|
return ret;
|
|
ret = siflower_phy_select_reg_page(phydev, oldpage);
|
|
if (ret < 0)
|
|
return ret;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int sf1211f_aneg_done(struct phy_device *phydev)
|
|
{
|
|
int val = 0;
|
|
|
|
val = phy_read(phydev, 0x16);
|
|
|
|
if (val == SFPHY_REG_FIBER_SPACE_SETADDR) {
|
|
val = phy_read(phydev, 0x1);
|
|
val = phy_read(phydev, 0x1);
|
|
return (val < 0) ? val : (val & BMSR_LSTATUS);
|
|
}
|
|
|
|
return genphy_aneg_done(phydev);
|
|
}
|
|
|
|
#if (SIFLOWER_PHY_WOL_FEATURE_ENABLE)
|
|
static void siflower_get_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol)
|
|
{
|
|
int val = 0;
|
|
wol->supported = WAKE_MAGIC;
|
|
wol->wolopts = 0;
|
|
|
|
val = siflower_phy_ext_read(phydev, SIFLOWER_WOL_CFG_REG1);
|
|
if (val < 0)
|
|
return;
|
|
|
|
if (val & SIFLOWER_WOL_EN)
|
|
wol->wolopts |= WAKE_MAGIC;
|
|
|
|
return;
|
|
}
|
|
|
|
static int siflower_wol_en_cfg(struct phy_device *phydev, siflower_wol_cfg_t wol_cfg)
|
|
{
|
|
int ret, val0,val1;
|
|
|
|
val0 = siflower_phy_ext_read(phydev, SIFLOWER_WOL_CFG_REG0);
|
|
if (val0 < 0)
|
|
return val0;
|
|
val1 = siflower_phy_ext_read(phydev, SIFLOWER_WOL_CFG_REG1);
|
|
if (val1 < 0)
|
|
return val1;
|
|
if (wol_cfg.wolen) {
|
|
val1 |= SIFLOWER_WOL_EN;
|
|
if (wol_cfg.type == SFPHY_WOL_TYPE_LEVEL) {
|
|
val0 |= SIFLOWER_WOL_TYPE;
|
|
} else if (wol_cfg.type == SFPHY_WOL_TYPE_PULSE) {
|
|
ret = siflower_phy_ext_write(phydev, SIFLOWER_PHY_WOL_PULSE_MODE_SET, 0x04);//set int pin pulse
|
|
if (ret < 0)
|
|
return ret;
|
|
val0 &= ~SIFLOWER_WOL_TYPE;
|
|
if (wol_cfg.width == SFPHY_WOL_WIDTH_84MS) {
|
|
val0 &= ~SIFLOWER_WOL_WIDTH1;
|
|
val0 &= ~SIFLOWER_WOL_WIDTH2;
|
|
} else if (wol_cfg.width == SFPHY_WOL_WIDTH_168MS) {
|
|
val0 |= SIFLOWER_WOL_WIDTH1;
|
|
val0 &= ~SIFLOWER_WOL_WIDTH2;
|
|
} else if (wol_cfg.width == SFPHY_WOL_WIDTH_336MS) {
|
|
val0 &= ~SIFLOWER_WOL_WIDTH1;
|
|
val0 |= SIFLOWER_WOL_WIDTH2;
|
|
} else if (wol_cfg.width == SFPHY_WOL_WIDTH_672MS) {
|
|
val0 |= SIFLOWER_WOL_WIDTH1;
|
|
val0 |= SIFLOWER_WOL_WIDTH2;
|
|
}
|
|
}
|
|
if (wol_cfg.secure == SFPHY_GLB_ENABLE)
|
|
val1 |= SIFLOWER_WOL_SECURE_CHECK;
|
|
else
|
|
val1 &= ~SIFLOWER_WOL_SECURE_CHECK;
|
|
if (wol_cfg.checkcrc == SFPHY_GLB_ENABLE)
|
|
val0 |= SIFLOWER_WOL_CRC_CHECK;
|
|
else
|
|
val0 &= ~SIFLOWER_WOL_CRC_CHECK;
|
|
if (wol_cfg.checkdst == SFPHY_GLB_ENABLE)
|
|
val0 |= SIFLOWER_WOL_DESTADDR_CHECK;
|
|
else
|
|
val0 &= ~SIFLOWER_WOL_DESTADDR_CHECK;
|
|
} else {
|
|
val1 &= ~SIFLOWER_WOL_EN;
|
|
}
|
|
|
|
ret = siflower_phy_ext_write(phydev, SIFLOWER_WOL_CFG_REG0, val0);
|
|
if (ret < 0)
|
|
return ret;
|
|
ret = siflower_phy_ext_write(phydev, SIFLOWER_WOL_CFG_REG1, val1);
|
|
if (ret < 0)
|
|
return ret;
|
|
return 0;
|
|
}
|
|
|
|
static int siflower_set_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol)
|
|
{
|
|
int ret, val, i, phymode;
|
|
siflower_wol_cfg_t wol_cfg;
|
|
|
|
phymode = SFPHY_MODE_CURR;
|
|
memset(&wol_cfg,0,sizeof(siflower_wol_cfg_t));
|
|
|
|
if (wol->wolopts & WAKE_MAGIC) {
|
|
if (phymode == SFPHY_PORT_TYPE_UTP || phymode == SFPHY_PORT_TYPE_COMBO) {
|
|
/* Enable the WOL interrupt */
|
|
val = sfphy_page_read(phydev, SFPHY_REG_UTP_SPACE, SIFLOWER_INTR_REG);
|
|
val |= SIFLOWER_WOL_INTR_EN;
|
|
ret = sfphy_page_write(phydev, SFPHY_REG_UTP_SPACE, SIFLOWER_INTR_REG, val);
|
|
if (ret < 0)
|
|
return ret;
|
|
}
|
|
if (phymode == SFPHY_PORT_TYPE_FIBER || phymode == SFPHY_PORT_TYPE_COMBO) {
|
|
/* Enable the WOL interrupt */
|
|
val = sfphy_page_read(phydev, SFPHY_REG_FIBER_SPACE, SIFLOWER_INTR_REG);
|
|
val |= SIFLOWER_WOL_INTR_EN;
|
|
ret = sfphy_page_write(phydev, SFPHY_REG_FIBER_SPACE, SIFLOWER_INTR_REG, val);
|
|
if (ret < 0)
|
|
return ret;
|
|
}
|
|
/* Set the WOL config */
|
|
wol_cfg.wolen = SFPHY_GLB_ENABLE;
|
|
wol_cfg.type = SFPHY_WOL_TYPE_PULSE;
|
|
wol_cfg.width = SFPHY_WOL_WIDTH_672MS;
|
|
wol_cfg.checkdst = SFPHY_GLB_ENABLE;
|
|
wol_cfg.checkcrc = SFPHY_GLB_ENABLE;
|
|
ret = siflower_wol_en_cfg(phydev, wol_cfg);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
/* Store the device address for the magic packet */
|
|
for(i = 0; i < 6; ++i) {
|
|
ret = siflower_phy_ext_write(phydev, SIFLOWER_MAGIC_PACKET_MAC_ADDR - i,
|
|
((phydev->attached_dev->dev_addr[i])));
|
|
if (ret < 0)
|
|
return ret;
|
|
}
|
|
#if SIFLOWER_PHY_WOL_PASSWD_ENABLE
|
|
/* Set passwd for the magic packet */
|
|
ret = siflower_phy_ext_write(phydev, SIFLOWER_MAGIC_PACKET_PASSWD_ADDR, SIFLOWER_MAGIC_PACKET_PASSWD1);
|
|
if (ret < 0)
|
|
return ret;
|
|
ret = siflower_phy_ext_write(phydev, SIFLOWER_MAGIC_PACKET_PASSWD_ADDR - 1, SIFLOWER_MAGIC_PACKET_PASSWD2);
|
|
if (ret < 0)
|
|
return ret;
|
|
ret = siflower_phy_ext_write(phydev, SIFLOWER_MAGIC_PACKET_PASSWD_ADDR - 2, SIFLOWER_MAGIC_PACKET_PASSWD3);
|
|
if (ret < 0)
|
|
return ret;
|
|
ret = siflower_phy_ext_write(phydev, SIFLOWER_MAGIC_PACKET_PASSWD_ADDR - 3, SIFLOWER_MAGIC_PACKET_PASSWD4);
|
|
if (ret < 0)
|
|
return ret;
|
|
ret = siflower_phy_ext_write(phydev, SIFLOWER_MAGIC_PACKET_PASSWD_ADDR - 4, SIFLOWER_MAGIC_PACKET_PASSWD5);
|
|
if (ret < 0)
|
|
return ret;
|
|
ret = siflower_phy_ext_write(phydev, SIFLOWER_MAGIC_PACKET_PASSWD_ADDR - 5, SIFLOWER_MAGIC_PACKET_PASSWD6);
|
|
if (ret < 0)
|
|
return ret;
|
|
#endif
|
|
} else {
|
|
wol_cfg.wolen = SFPHY_GLB_DISABLE;
|
|
wol_cfg.type = SFPHY_WOL_TYPE_EXT;
|
|
wol_cfg.width = SFPHY_WOL_WIDTH_EXT;
|
|
wol_cfg.checkdst = SFPHY_GLB_DISABLE;
|
|
wol_cfg.checkcrc = SFPHY_GLB_DISABLE;
|
|
ret = siflower_wol_en_cfg(phydev, wol_cfg);
|
|
if (ret < 0)
|
|
return ret;
|
|
}
|
|
|
|
if (val == SF1211F_EXTREG_PHY_MODE_UTP_TO_SGMII) {
|
|
val = sfphy_page_read(phydev, SFPHY_REG_UTP_SPACE, MII_BMCR);
|
|
val |= SIFLOWER_WOL_RESTARTANEG;
|
|
ret = sfphy_page_write(phydev, SFPHY_REG_UTP_SPACE, MII_BMCR, val);
|
|
if (ret < 0)
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
static int sf1211f_rxc_txc_init(struct phy_device *phydev)
|
|
{
|
|
int ret;
|
|
|
|
ret = (siflower_phy_ext_read(phydev, SF1211F_EXTREG_GET_PORT_PHY_MODE) &
|
|
SF1211F_EXTREG_PHY_MODE_MASK);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
if ((ret == SF1211F_EXTREG_PHY_MODE_UTP_TO_SGMII) ||
|
|
(ret == SF1211F_EXTREG_PHY_MODE_UTP_TO_FIBER_AUTO) ||
|
|
(ret == SF1211F_EXTREG_PHY_MODE_UTP_TO_FIBER_FORCE))
|
|
return 0;
|
|
|
|
// Init rxc and enable rxc
|
|
if (ret == SF1211F_EXTREG_PHY_MODE_UTP_TO_RGMII) {
|
|
ret = phy_read(phydev, 0x11);
|
|
if ((ret & 0x4) == 0x0) {
|
|
ret = siflower_phy_ext_write(phydev,0x1E0C, 0x17);
|
|
if (ret < 0)
|
|
return ret;
|
|
ret = siflower_phy_ext_write(phydev,0x1E58, 0x00);
|
|
if (ret < 0)
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID){
|
|
// Init rxc delay
|
|
ret = siflower_phy_ext_write(phydev,0x0282, SIFLOWER_PHY_RXC_DELAY_VAL);
|
|
if (ret < 0)
|
|
return ret;
|
|
}
|
|
else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID){
|
|
// Init txc delay
|
|
ret = siflower_phy_ext_write(phydev,0x0281, SIFLOWER_PHY_TXC_DELAY_VAL);
|
|
if (ret < 0)
|
|
return ret;
|
|
}
|
|
else if(phydev->interface == PHY_INTERFACE_MODE_RGMII_ID){
|
|
ret = siflower_phy_ext_write(phydev,0x0282, SIFLOWER_PHY_RXC_DELAY_VAL);
|
|
if (ret < 0)
|
|
return ret;
|
|
ret = siflower_phy_ext_write(phydev,0x0281, SIFLOWER_PHY_TXC_DELAY_VAL);
|
|
if (ret < 0)
|
|
return ret;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int sf1211f_config_opt(struct phy_device *phydev)
|
|
{
|
|
int ret;
|
|
//100M utp optimise
|
|
ret = siflower_phy_ext_write(phydev, 0x0149, 0x84);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
ret = siflower_phy_ext_write(phydev, 0x014A, 0x86);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
ret = siflower_phy_ext_write(phydev, 0x023C, 0x81);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
//1000M utp optimise
|
|
ret = siflower_phy_ext_write(phydev, 0x0184, 0x85);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
ret = siflower_phy_ext_write(phydev, 0x0185, 0x86);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
ret = siflower_phy_ext_write(phydev, 0x0186, 0x85);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
ret = siflower_phy_ext_write(phydev, 0x0187, 0x86);
|
|
if (ret < 0)
|
|
return ret;
|
|
return ret;
|
|
}
|
|
#if SIFLOWER_PHY_CLK_OUT_125M_ENABLE
|
|
static int sf1211f_clkout_init(struct phy_device *phydev)
|
|
{
|
|
int ret;
|
|
|
|
ret = siflower_phy_ext_write(phydev, 0x0272 , 0x09);
|
|
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
#if SIFLOWER_PHY_MODE_SET_ENABLE
|
|
//set mode
|
|
static int phy_mode_set(struct phy_device *phydev, u16 phyMode)
|
|
{
|
|
int ret, num = 0;
|
|
|
|
ret = siflower_phy_ext_read(phydev, 0xC417);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
ret = (ret & 0xF0) | (0x8 | phyMode);
|
|
|
|
ret = siflower_phy_ext_write(phydev, 0xC417, ret);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
while ((siflower_phy_ext_read(phydev, 0xC415) & 0x07) != phyMode) {
|
|
msleep(10);
|
|
if(++num == 5) {
|
|
printk("Phy Mode Set Time Out!\r\n");
|
|
break;
|
|
}
|
|
}
|
|
|
|
while (siflower_phy_ext_read(phydev, 0xC413) != 0) {
|
|
msleep(10);
|
|
if(++num == 10) {
|
|
printk("Phy Mode Set Time Out!\r\n");
|
|
break;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
int sf1240_config_init(struct phy_device *phydev)
|
|
{
|
|
int ret;
|
|
ret = genphy_read_abilities(phydev);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
|
|
phydev->supported, ESTATUS_1000_TFULL);
|
|
linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
|
|
phydev->advertising, ESTATUS_1000_TFULL);
|
|
return 0;
|
|
}
|
|
|
|
int sf1211f_config_init(struct phy_device *phydev)
|
|
{
|
|
int ret, phymode;
|
|
|
|
#if SIFLOWER_PHY_WOL_FEATURE_ENABLE
|
|
struct ethtool_wolinfo wol;
|
|
#endif
|
|
|
|
#if SIFLOWER_PHY_MODE_SET_ENABLE
|
|
ret = phy_mode_set(phydev, 0x0);
|
|
if (ret < 0)
|
|
return ret;
|
|
#endif
|
|
phymode = SFPHY_MODE_CURR;
|
|
|
|
if (phymode == SFPHY_PORT_TYPE_UTP || phymode == SFPHY_PORT_TYPE_COMBO) {
|
|
siflower_phy_select_reg_page(phydev, SFPHY_REG_UTP_SPACE);
|
|
ret = genphy_read_abilities(phydev);
|
|
if (ret < 0)
|
|
return ret;
|
|
} else {
|
|
siflower_phy_select_reg_page(phydev, SFPHY_REG_FIBER_SPACE);
|
|
ret = genphy_read_abilities(phydev);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
|
|
phydev->supported, ESTATUS_1000_TFULL);
|
|
linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
|
|
phydev->advertising, ESTATUS_1000_TFULL);
|
|
}
|
|
|
|
ret = sf1211f_rxc_txc_init(phydev);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
ret = sf1211f_config_opt(phydev);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
#if SIFLOWER_PHY_CLK_OUT_125M_ENABLE
|
|
ret = sf1211f_clkout_init(phydev);
|
|
if (ret < 0)
|
|
return ret;
|
|
#endif
|
|
|
|
#if SIFLOWER_PHY_WOL_FEATURE_ENABLE
|
|
wol.wolopts = 0;
|
|
wol.supported = WAKE_MAGIC;
|
|
wol.wolopts |= WAKE_MAGIC;
|
|
siflower_set_wol(phydev, &wol);
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct phy_driver sf_phy_drivers[] = {
|
|
{
|
|
.phy_id = SF1211F_PHY_ID,
|
|
.phy_id_mask = SIFLOWER_PHY_ID_MASK,
|
|
.name = "SF1211F Gigabit Ethernet",
|
|
.features = PHY_GBIT_FEATURES,
|
|
.flags = PHY_POLL,
|
|
.config_init = sf1211f_config_init,
|
|
.config_aneg = sf1211f_config_aneg,
|
|
.aneg_done = sf1211f_aneg_done,
|
|
.write_mmd = genphy_write_mmd_unsupported,
|
|
.read_mmd = genphy_read_mmd_unsupported,
|
|
.suspend = genphy_suspend,
|
|
.resume = genphy_resume,
|
|
#if SIFLOWER_PHY_WOL_FEATURE_ENABLE
|
|
.get_wol = &siflower_get_wol,
|
|
.set_wol = &siflower_set_wol,
|
|
#endif
|
|
},
|
|
|
|
{
|
|
.phy_id = SF1240_PHY_ID,
|
|
.phy_id_mask = SIFLOWER_PHY_ID_MASK,
|
|
.name = "SF1240 Gigabit Ethernet",
|
|
.features = PHY_GBIT_FEATURES,
|
|
.flags = PHY_POLL,
|
|
.config_init = sf1240_config_init,
|
|
.config_aneg = genphy_config_aneg,
|
|
.write_mmd = genphy_write_mmd_unsupported,
|
|
.read_mmd = genphy_read_mmd_unsupported,
|
|
.suspend = genphy_suspend,
|
|
.resume = genphy_resume,
|
|
},
|
|
};
|
|
|
|
/* for linux 4.x */
|
|
module_phy_driver(sf_phy_drivers);
|
|
|
|
static struct mdio_device_id __maybe_unused siflower_phy_tbl[] = {
|
|
{ SF1211F_PHY_ID, SIFLOWER_PHY_ID_MASK },
|
|
{ SF1240_PHY_ID, SIFLOWER_PHY_ID_MASK },
|
|
{},
|
|
};
|
|
|
|
MODULE_DEVICE_TABLE(mdio, siflower_phy_tbl);
|
|
|
|
MODULE_DESCRIPTION("Siflower PHY driver");
|
|
MODULE_LICENSE("GPL");
|