mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-30 11:03:07 -04:00
Merge branch 'add-support-for-rtl8261c-d'
Javen Xu says: ==================== Add support for RTL8261C/D Add support for RTL8261C/D and add support for loading firmware. ==================== Link: https://patch.msgid.link/20260728073106.1515-1-javen_xu@realsil.com.cn Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
@@ -384,6 +384,119 @@ int genphy_c45_check_and_restart_aneg(struct phy_device *phydev, bool restart)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(genphy_c45_check_and_restart_aneg);
|
||||
|
||||
/**
|
||||
* genphy_c45_pma_soft_reset - software reset the PHY via Clause 45 PMA/PMD control register
|
||||
* @phydev: target phy_device struct
|
||||
*
|
||||
* Return: 0 on success, negative errno on failure.
|
||||
*/
|
||||
int genphy_c45_pma_soft_reset(struct phy_device *phydev)
|
||||
{
|
||||
int ret, val;
|
||||
|
||||
ret = phy_set_bits_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_CTRL1,
|
||||
MDIO_CTRL1_RESET);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return phy_read_mmd_poll_timeout(phydev, MDIO_MMD_PMAPMD,
|
||||
MDIO_CTRL1, val,
|
||||
!(val & MDIO_CTRL1_RESET),
|
||||
5000, 600000, true);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(genphy_c45_pma_soft_reset);
|
||||
|
||||
/**
|
||||
* genphy_c45_an_setup_master_slave - Configure Master/Slave setting for C45 PHYs
|
||||
* @phydev: target phy_device struct
|
||||
*
|
||||
* Description: Configure the forced or preferred Master/Slave role
|
||||
* 10GBASE-T control register (MMD 7, Register 0x0020) according to
|
||||
* IEEE 802.3 standards.
|
||||
*
|
||||
* Return: negative errno code on failure, 0 if Master/Slave didn't change,
|
||||
* or 1 if Master/Slave modes changed.
|
||||
*/
|
||||
static int genphy_c45_an_setup_master_slave(struct phy_device *phydev)
|
||||
{
|
||||
u16 ctl = 0;
|
||||
|
||||
switch (phydev->master_slave_set) {
|
||||
case MASTER_SLAVE_CFG_MASTER_PREFERRED:
|
||||
ctl = MDIO_AN_10GBT_CTRL_MS_PORT_TYPE;
|
||||
break;
|
||||
case MASTER_SLAVE_CFG_SLAVE_PREFERRED:
|
||||
break;
|
||||
case MASTER_SLAVE_CFG_MASTER_FORCE:
|
||||
ctl = MDIO_AN_10GBT_CTRL_MS_ENABLE | MDIO_AN_10GBT_CTRL_MS_VALUE;
|
||||
break;
|
||||
case MASTER_SLAVE_CFG_SLAVE_FORCE:
|
||||
ctl = MDIO_AN_10GBT_CTRL_MS_ENABLE;
|
||||
break;
|
||||
case MASTER_SLAVE_CFG_UNKNOWN:
|
||||
case MASTER_SLAVE_CFG_UNSUPPORTED:
|
||||
return 0;
|
||||
default:
|
||||
phydev_warn(phydev, "Unsupported Master/Slave mode\n");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
return phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_CTRL,
|
||||
MDIO_AN_10GBT_CTRL_MS_ENABLE |
|
||||
MDIO_AN_10GBT_CTRL_MS_VALUE |
|
||||
MDIO_AN_10GBT_CTRL_MS_PORT_TYPE, ctl);
|
||||
}
|
||||
|
||||
/**
|
||||
* genphy_c45_read_master_slave - read master/slave status
|
||||
* @phydev: target phy_device struct
|
||||
*
|
||||
* Description: Read the Master/Slave configuration and status
|
||||
* from 10GBASE-T control/status registers (MMD 7, Reg 0x0020 and 0x0021).
|
||||
*
|
||||
* Return: 0 on success, or a negative error code on failure.
|
||||
*/
|
||||
static int genphy_c45_read_master_slave(struct phy_device *phydev)
|
||||
{
|
||||
int val;
|
||||
|
||||
phydev->master_slave_get = MASTER_SLAVE_CFG_UNKNOWN;
|
||||
phydev->master_slave_state = MASTER_SLAVE_STATE_UNKNOWN;
|
||||
|
||||
val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_CTRL);
|
||||
if (val < 0)
|
||||
return val;
|
||||
|
||||
if (val & MDIO_AN_10GBT_CTRL_MS_ENABLE) {
|
||||
if (val & MDIO_AN_10GBT_CTRL_MS_VALUE)
|
||||
phydev->master_slave_get = MASTER_SLAVE_CFG_MASTER_FORCE;
|
||||
else
|
||||
phydev->master_slave_get = MASTER_SLAVE_CFG_SLAVE_FORCE;
|
||||
} else {
|
||||
if (val & MDIO_AN_10GBT_CTRL_MS_PORT_TYPE)
|
||||
phydev->master_slave_get = MASTER_SLAVE_CFG_MASTER_PREFERRED;
|
||||
else
|
||||
phydev->master_slave_get = MASTER_SLAVE_CFG_SLAVE_PREFERRED;
|
||||
}
|
||||
|
||||
val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_STAT);
|
||||
if (val < 0)
|
||||
return val;
|
||||
|
||||
if (val & MDIO_AN_10GBT_STAT_MS_FAULT) {
|
||||
phydev->master_slave_state = MASTER_SLAVE_STATE_ERR;
|
||||
} else if (phydev->link) {
|
||||
if (val & MDIO_AN_10GBT_STAT_MS_RES)
|
||||
phydev->master_slave_state = MASTER_SLAVE_STATE_MASTER;
|
||||
else
|
||||
phydev->master_slave_state = MASTER_SLAVE_STATE_SLAVE;
|
||||
} else {
|
||||
phydev->master_slave_state = MASTER_SLAVE_STATE_UNKNOWN;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* genphy_c45_aneg_done - return auto-negotiation complete status
|
||||
* @phydev: target phy_device struct
|
||||
@@ -1192,6 +1305,10 @@ int genphy_c45_read_status(struct phy_device *phydev)
|
||||
ret = genphy_c45_baset1_read_status(phydev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
} else {
|
||||
ret = genphy_c45_read_master_slave(phydev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
phy_resolve_aneg_linkmode(phydev);
|
||||
@@ -1225,6 +1342,14 @@ int genphy_c45_config_aneg(struct phy_device *phydev)
|
||||
if (ret > 0)
|
||||
changed = true;
|
||||
|
||||
if (!genphy_c45_baset1_able(phydev)) {
|
||||
ret = genphy_c45_an_setup_master_slave(phydev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (ret > 0)
|
||||
changed = true;
|
||||
}
|
||||
|
||||
return genphy_c45_check_and_restart_aneg(phydev, changed);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(genphy_c45_config_aneg);
|
||||
|
||||
@@ -8,7 +8,9 @@
|
||||
* Copyright (c) 2004 Freescale Semiconductor, Inc.
|
||||
*/
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/crc32.h>
|
||||
#include <linux/ethtool_netlink.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/phy.h>
|
||||
#include <linux/pm_wakeirq.h>
|
||||
@@ -141,6 +143,10 @@
|
||||
#define RTL8211F_PHYSICAL_ADDR_WORD1 17
|
||||
#define RTL8211F_PHYSICAL_ADDR_WORD2 18
|
||||
|
||||
#define RTL8261X_EXT_ADDR_REG 0xa436
|
||||
#define RTL8261X_EXT_DATA_REG 0xa438
|
||||
#define RTL_8261X_SUB_PHY_ID_ADDR 0x801d
|
||||
|
||||
#define RTL822X_VND1_SERDES_OPTION 0x697a
|
||||
#define RTL822X_VND1_SERDES_OPTION_MODE_MASK GENMASK(5, 0)
|
||||
#define RTL822X_VND1_SERDES_OPTION_MODE_2500BASEX_SGMII 0
|
||||
@@ -251,6 +257,70 @@
|
||||
#define RTL_8221B_VM_CG 0x001cc84a
|
||||
#define RTL_8251B 0x001cc862
|
||||
#define RTL_8261C 0x001cc890
|
||||
#define RTL_8261C_CG 0x001cc898
|
||||
|
||||
#define RTL8261C_CE_MODEL 0x00
|
||||
#define RTL8261D_MODEL 0x81
|
||||
#define RTL8261X_INT_AUTONEG_ERROR BIT(0)
|
||||
#define RTL8261X_INT_PAGE_RECV BIT(2)
|
||||
#define RTL8261X_INT_AUTONEG_DONE BIT(3)
|
||||
#define RTL8261X_INT_LINK_CHG BIT(4)
|
||||
#define RTL8261X_INT_PHY_REG_ACCESS BIT(5)
|
||||
#define RTL8261X_INT_PME BIT(7)
|
||||
#define RTL8261X_INT_ALDPS_CHG BIT(9)
|
||||
#define RTL8261X_INT_JABBER BIT(10)
|
||||
|
||||
#define RTL8261X_INT_MASK_DEFAULT (RTL8261X_INT_AUTONEG_DONE | \
|
||||
RTL8261X_INT_LINK_CHG | \
|
||||
RTL8261X_INT_AUTONEG_ERROR | \
|
||||
RTL8261X_INT_JABBER)
|
||||
|
||||
#define RTL8261X_INT_MASK_ALL (RTL8261X_INT_AUTONEG_ERROR | \
|
||||
RTL8261X_INT_PAGE_RECV | \
|
||||
RTL8261X_INT_AUTONEG_DONE | \
|
||||
RTL8261X_INT_LINK_CHG | \
|
||||
RTL8261X_INT_PHY_REG_ACCESS | \
|
||||
RTL8261X_INT_PME | \
|
||||
RTL8261X_INT_ALDPS_CHG | \
|
||||
RTL8261X_INT_JABBER)
|
||||
|
||||
#define FW_MAIN_MAGIC 0x52544C38
|
||||
#define FW_SUB_MAGIC_8261C 0x32363143
|
||||
#define RTL8261X_POLL_TIMEOUT_MS 100
|
||||
#define RTL8261X_MAX_MMD_DEV 31
|
||||
|
||||
#define RTL8261C_CE_FW_NAME "rtl_nic/rtl8261c.bin"
|
||||
MODULE_FIRMWARE(RTL8261C_CE_FW_NAME);
|
||||
|
||||
enum rtl8261x_fw_op {
|
||||
OP_WRITE = 0x00, /* Write */
|
||||
OP_POLL = 0x02, /* Polling */
|
||||
};
|
||||
|
||||
struct rtl8261x_fw_header {
|
||||
__le32 main_magic; /* Main magic number */
|
||||
__le32 sub_magic; /* Sub magic number */
|
||||
__le16 version_major; /* Major version */
|
||||
__le16 version_minor; /* Minor version */
|
||||
__le16 num_entries; /* Number of entries */
|
||||
__le16 reserved; /* Reserved */
|
||||
__le32 crc32; /* CRC32 checksum */
|
||||
};
|
||||
|
||||
struct rtl8261x_fw_entry {
|
||||
__u8 type; /* Operation type (OP_*) */
|
||||
__u8 dev; /* MMD device */
|
||||
__le16 addr; /* Register address */
|
||||
__u8 msb; /* MSB bit position */
|
||||
__u8 lsb; /* LSB bit position */
|
||||
__le16 value; /* Value to write/compare */
|
||||
__le16 timeout_ms; /* Poll timeout in milliseconds */
|
||||
__u8 poll_set; /* Poll until equal (1) or not equal (0) */
|
||||
__u8 reserved; /* Reserved */
|
||||
};
|
||||
|
||||
#define FW_HEADER_SIZE sizeof(struct rtl8261x_fw_header)
|
||||
#define FW_ENTRY_SIZE sizeof(struct rtl8261x_fw_entry)
|
||||
|
||||
/* RTL8211E and RTL8211F support up to three LEDs */
|
||||
#define RTL8211x_LED_COUNT 3
|
||||
@@ -270,6 +340,11 @@ struct rtl821x_priv {
|
||||
u16 iner;
|
||||
};
|
||||
|
||||
struct rtl8261x_priv {
|
||||
const char *fw_name;
|
||||
bool fw_loaded;
|
||||
};
|
||||
|
||||
static int rtl821x_read_page(struct phy_device *phydev)
|
||||
{
|
||||
return __phy_read(phydev, RTL821x_PAGE_SELECT);
|
||||
@@ -310,6 +385,329 @@ static int rtl821x_modify_ext_page(struct phy_device *phydev, u16 ext_page,
|
||||
return phy_restore_page(phydev, oldpage, ret);
|
||||
}
|
||||
|
||||
static int rtl8261x_probe(struct phy_device *phydev)
|
||||
{
|
||||
struct device *dev = &phydev->mdio.dev;
|
||||
struct rtl8261x_priv *priv;
|
||||
int sub_phy_id, ret;
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
phydev->priv = priv;
|
||||
|
||||
ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, RTL8261X_EXT_ADDR_REG,
|
||||
RTL_8261X_SUB_PHY_ID_ADDR);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, RTL8261X_EXT_DATA_REG);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
sub_phy_id = (ret >> 8) & 0xff;
|
||||
|
||||
switch (sub_phy_id) {
|
||||
case RTL8261C_CE_MODEL:
|
||||
priv->fw_name = RTL8261C_CE_FW_NAME;
|
||||
phydev_info(phydev, "RTL8261C detected (sub_id 0x%02x)\n", sub_phy_id);
|
||||
break;
|
||||
|
||||
case RTL8261D_MODEL:
|
||||
phydev_info(phydev, "RTL8261D detected (sub_id 0x%02x)\n", sub_phy_id);
|
||||
break;
|
||||
|
||||
default:
|
||||
phydev_warn(phydev, "Unknown sub_id 0x%02x, default behavior\n", sub_phy_id);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rtl8261x_get_features(struct phy_device *phydev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = genphy_c45_pma_read_abilities(phydev);
|
||||
if (ret)
|
||||
return ret;
|
||||
/*
|
||||
* Supplement Multi-Gig speeds that may not be automatically detected
|
||||
* RTL8261X supports 2.5G/5G in addition to standard 10G
|
||||
*/
|
||||
linkmode_set_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
|
||||
phydev->supported);
|
||||
linkmode_set_bit(ETHTOOL_LINK_MODE_5000baseT_Full_BIT,
|
||||
phydev->supported);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rtl8261x_read_status(struct phy_device *phydev)
|
||||
{
|
||||
int ret, val = 0;
|
||||
|
||||
if (phydev->autoneg == AUTONEG_ENABLE) {
|
||||
ret = genphy_c45_aneg_done(phydev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (ret) {
|
||||
val = phy_read_mmd(phydev, MDIO_MMD_VEND2,
|
||||
RTL822X_VND2_C22_REG(MII_STAT1000));
|
||||
if (val < 0)
|
||||
return val;
|
||||
}
|
||||
}
|
||||
|
||||
mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising, val);
|
||||
|
||||
ret = genphy_c45_read_status(phydev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rtl8261x_verify_firmware(struct phy_device *phydev, const struct firmware *fw)
|
||||
{
|
||||
const struct rtl8261x_fw_header *hdr;
|
||||
u32 main_magic, sub_magic;
|
||||
u32 calc_crc, file_crc;
|
||||
size_t data_len;
|
||||
u16 num_entries;
|
||||
|
||||
if (fw->size < FW_HEADER_SIZE) {
|
||||
phydev_err(phydev, "Firmware too small: %zu bytes\n", fw->size);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
hdr = (const struct rtl8261x_fw_header *)fw->data;
|
||||
|
||||
main_magic = le32_to_cpu(hdr->main_magic);
|
||||
if (main_magic != FW_MAIN_MAGIC) {
|
||||
phydev_err(phydev, "Invalid firmware magic: 0x%08x\n", main_magic);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
sub_magic = le32_to_cpu(hdr->sub_magic);
|
||||
if (sub_magic != FW_SUB_MAGIC_8261C) {
|
||||
phydev_err(phydev, "Invalid sub magic: 0x%08x\n", sub_magic);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
num_entries = le16_to_cpu(hdr->num_entries);
|
||||
data_len = num_entries * FW_ENTRY_SIZE;
|
||||
|
||||
if (fw->size != sizeof(*hdr) + data_len) {
|
||||
phydev_err(phydev, "Firmware size mismatch\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
calc_crc = crc32(~0, fw->data + FW_HEADER_SIZE, data_len) ^ ~0;
|
||||
file_crc = le32_to_cpu(hdr->crc32);
|
||||
|
||||
if (calc_crc != file_crc) {
|
||||
phydev_err(phydev, "CRC32 mismatch: calculated=0x%08x file=0x%08x\n",
|
||||
calc_crc, file_crc);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rtl8261x_fw_execute_entry(struct phy_device *phydev,
|
||||
const struct rtl8261x_fw_entry *entry)
|
||||
{
|
||||
u16 addr, value, timeout_ms;
|
||||
u8 dev, msb, lsb, poll_set;
|
||||
u32 bits, expect_val;
|
||||
int ret, val;
|
||||
|
||||
dev = entry->dev;
|
||||
addr = le16_to_cpu(entry->addr);
|
||||
msb = entry->msb;
|
||||
lsb = entry->lsb;
|
||||
value = le16_to_cpu(entry->value);
|
||||
timeout_ms = le16_to_cpu(entry->timeout_ms);
|
||||
poll_set = entry->poll_set;
|
||||
|
||||
if (timeout_ms == 0)
|
||||
timeout_ms = RTL8261X_POLL_TIMEOUT_MS;
|
||||
|
||||
if (dev > RTL8261X_MAX_MMD_DEV) {
|
||||
phydev_err(phydev, "invalid firmware MMD device: dev=%u\n", dev);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (msb > 15 || lsb > msb) {
|
||||
phydev_err(phydev, "invalid firmware bits: msb=%u, lsb=%u\n", msb, lsb);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (entry->type) {
|
||||
case OP_WRITE:
|
||||
ret = phy_modify_mmd(phydev, dev, addr,
|
||||
GENMASK(msb, lsb), (value << lsb) & GENMASK(msb, lsb));
|
||||
if (ret)
|
||||
return ret;
|
||||
break;
|
||||
|
||||
case OP_POLL:
|
||||
bits = GENMASK(msb, lsb);
|
||||
expect_val = (value << lsb) & bits;
|
||||
|
||||
if (poll_set)
|
||||
ret = phy_read_mmd_poll_timeout(phydev, dev, addr, val,
|
||||
(val & bits) == expect_val,
|
||||
1000, timeout_ms * 1000, false);
|
||||
else
|
||||
ret = phy_read_mmd_poll_timeout(phydev, dev, addr, val,
|
||||
(val & bits) != expect_val,
|
||||
1000, timeout_ms * 1000, false);
|
||||
if (ret)
|
||||
return ret;
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rtl8261x_fw_load(struct phy_device *phydev)
|
||||
{
|
||||
struct rtl8261x_priv *priv = phydev->priv;
|
||||
const struct rtl8261x_fw_entry *entry;
|
||||
const struct rtl8261x_fw_header *hdr;
|
||||
const struct firmware *fw;
|
||||
int ret, i;
|
||||
|
||||
if (!priv->fw_name)
|
||||
return 0;
|
||||
|
||||
ret = request_firmware(&fw, priv->fw_name, &phydev->mdio.dev);
|
||||
if (ret) {
|
||||
phydev_err(phydev, "Failed to load firmware %s: %d\n", priv->fw_name, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = rtl8261x_verify_firmware(phydev, fw);
|
||||
if (ret)
|
||||
goto release_fw;
|
||||
|
||||
hdr = (const struct rtl8261x_fw_header *)fw->data;
|
||||
|
||||
entry = (const struct rtl8261x_fw_entry *)(fw->data + FW_HEADER_SIZE);
|
||||
for (i = 0; i < le16_to_cpu(hdr->num_entries); i++, entry++) {
|
||||
ret = rtl8261x_fw_execute_entry(phydev, entry);
|
||||
if (ret) {
|
||||
phydev_err(phydev, "Entry %d failed: %d\n", i, ret);
|
||||
goto release_fw;
|
||||
}
|
||||
}
|
||||
|
||||
priv->fw_loaded = true;
|
||||
|
||||
release_fw:
|
||||
release_firmware(fw);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rtl8261x_config_intr(struct phy_device *phydev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
|
||||
ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, RTL8221B_VND2_INSR);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, RTL8221B_VND2_INER,
|
||||
RTL8261X_INT_MASK_DEFAULT);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
} else {
|
||||
ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, RTL8221B_VND2_INER, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, RTL8221B_VND2_INSR);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t rtl8261x_handle_interrupt(struct phy_device *phydev)
|
||||
{
|
||||
int irq_status;
|
||||
|
||||
irq_status = phy_read_mmd(phydev, MDIO_MMD_VEND2, RTL8221B_VND2_INSR);
|
||||
if (irq_status < 0) {
|
||||
phy_error(phydev);
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
if (!(irq_status & RTL8261X_INT_MASK_ALL))
|
||||
return IRQ_NONE;
|
||||
|
||||
if (irq_status & (RTL8261X_INT_LINK_CHG | RTL8261X_INT_AUTONEG_DONE |
|
||||
RTL8261X_INT_AUTONEG_ERROR | RTL8261X_INT_JABBER))
|
||||
phy_trigger_machine(phydev);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int rtl8261x_config_aneg(struct phy_device *phydev)
|
||||
{
|
||||
u16 adv_1g = 0;
|
||||
int ret;
|
||||
|
||||
ret = genphy_c45_config_aneg(phydev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (phydev->autoneg == AUTONEG_DISABLE)
|
||||
return 0;
|
||||
|
||||
if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
|
||||
phydev->advertising))
|
||||
adv_1g = ADVERTISE_1000FULL;
|
||||
if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
|
||||
phydev->advertising))
|
||||
adv_1g |= ADVERTISE_1000HALF;
|
||||
|
||||
ret = phy_modify_mmd_changed(phydev, MDIO_MMD_VEND2,
|
||||
RTL822X_VND2_C22_REG(MII_CTRL1000),
|
||||
ADVERTISE_1000FULL | ADVERTISE_1000HALF,
|
||||
adv_1g);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (ret > 0)
|
||||
return genphy_c45_restart_aneg(phydev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rtl8261x_config_init(struct phy_device *phydev)
|
||||
{
|
||||
struct rtl8261x_priv *priv = phydev->priv;
|
||||
|
||||
/* The firmware parameters are preserved across IEEE soft resets and
|
||||
* suspend/resume cycles. Reloading is only necessary after a power
|
||||
* cycle or hard reset.
|
||||
*/
|
||||
if (priv->fw_name && !priv->fw_loaded)
|
||||
return rtl8261x_fw_load(phydev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rtl821x_probe(struct phy_device *phydev)
|
||||
{
|
||||
struct device *dev = &phydev->mdio.dev;
|
||||
@@ -3002,6 +3400,19 @@ static struct phy_driver realtek_drvs[] = {
|
||||
.resume = genphy_resume,
|
||||
.read_mmd = genphy_read_mmd_unsupported,
|
||||
.write_mmd = genphy_write_mmd_unsupported,
|
||||
}, {
|
||||
PHY_ID_MATCH_EXACT(RTL_8261C_CG),
|
||||
.name = "Realtek RTL8261C/D 10Gbps PHY",
|
||||
.probe = rtl8261x_probe,
|
||||
.config_init = rtl8261x_config_init,
|
||||
.get_features = rtl8261x_get_features,
|
||||
.config_aneg = rtl8261x_config_aneg,
|
||||
.read_status = rtl8261x_read_status,
|
||||
.config_intr = rtl8261x_config_intr,
|
||||
.handle_interrupt = rtl8261x_handle_interrupt,
|
||||
.soft_reset = genphy_c45_pma_soft_reset,
|
||||
.suspend = genphy_c45_pma_suspend,
|
||||
.resume = genphy_c45_pma_resume,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -2317,6 +2317,7 @@ int genphy_c37_read_status(struct phy_device *phydev, bool *changed);
|
||||
/* Clause 45 PHY */
|
||||
int genphy_c45_restart_aneg(struct phy_device *phydev);
|
||||
int genphy_c45_check_and_restart_aneg(struct phy_device *phydev, bool restart);
|
||||
int genphy_c45_pma_soft_reset(struct phy_device *phydev);
|
||||
int genphy_c45_aneg_done(struct phy_device *phydev);
|
||||
int genphy_c45_read_link(struct phy_device *phydev);
|
||||
int genphy_c45_read_lpa(struct phy_device *phydev);
|
||||
|
||||
@@ -332,8 +332,13 @@
|
||||
#define MDIO_AN_10GBT_CTRL_ADV2_5G 0x0080 /* Advertise 2.5GBASE-T */
|
||||
#define MDIO_AN_10GBT_CTRL_ADV5G 0x0100 /* Advertise 5GBASE-T */
|
||||
#define MDIO_AN_10GBT_CTRL_ADV10G 0x1000 /* Advertise 10GBASE-T */
|
||||
#define MDIO_AN_10GBT_CTRL_MS_ENABLE 0x8000 /* Master/slave manual config enable */
|
||||
#define MDIO_AN_10GBT_CTRL_MS_VALUE 0x4000 /* Master/slave config value (1=Master) */
|
||||
#define MDIO_AN_10GBT_CTRL_MS_PORT_TYPE 0x2000 /* Master Preferred Type */
|
||||
|
||||
/* AN 10GBASE-T status register. */
|
||||
#define MDIO_AN_10GBT_STAT_MS_FAULT 0x8000 /* Master/slave fault */
|
||||
#define MDIO_AN_10GBT_STAT_MS_RES 0x4000 /* Master/slave resolution (1=Master) */
|
||||
#define MDIO_AN_10GBT_STAT_LP2_5G 0x0020 /* LP is 2.5GBT capable */
|
||||
#define MDIO_AN_10GBT_STAT_LP5G 0x0040 /* LP is 5GBT capable */
|
||||
#define MDIO_AN_10GBT_STAT_LPTRR 0x0200 /* LP training reset req. */
|
||||
|
||||
Reference in New Issue
Block a user