Chuanhong Guo c057db94f8 siflower: sf21: new subtarget for sf21a6826/sf21h8898
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>
2025-02-05 11:08:37 +08:00

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");