Merge branch 'txgbe-link-modes'
Jiawen Wu says: ==================== support more link mode for TXGBE There are three new interface mode support for Wangxun 10Gb NICs: 1000BASE-X, SGMII and XAUI. Specific configurations are added to XPCS. And external PHY attaching is added for copper NICs. v2 -> v3: - add device identifier read - restrict pcs soft reset - add firmware version warning v1 -> v2: - use the string "txgbe_pcs_mdio_bus" directly - use dev_err() instead of pr_err() - add device quirk flag - add more macro definitions to explain PMA registers - move txgbe_enable_sec_tx_path() to mac_finish() - implement phylink for copper NICs ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
8e8fc62d7c
@ -22925,6 +22925,7 @@ S: Maintained
|
||||
W: https://www.net-swift.com
|
||||
F: Documentation/networking/device_drivers/ethernet/wangxun/*
|
||||
F: drivers/net/ethernet/wangxun/
|
||||
F: drivers/net/pcs/pcs-xpcs-wx.c
|
||||
|
||||
WATCHDOG DEVICE DRIVERS
|
||||
M: Wim Van Sebroeck <wim@linux-watchdog.org>
|
||||
|
@ -41,6 +41,7 @@ config TXGBE
|
||||
tristate "Wangxun(R) 10GbE PCI Express adapters support"
|
||||
depends on PCI
|
||||
depends on COMMON_CLK
|
||||
select MARVELL_10G_PHY
|
||||
select REGMAP
|
||||
select I2C
|
||||
select I2C_DESIGNWARE_PLATFORM
|
||||
|
@ -205,6 +205,8 @@
|
||||
#define WX_TSC_CTL 0x1D000
|
||||
#define WX_TSC_CTL_TX_DIS BIT(1)
|
||||
#define WX_TSC_CTL_TSEC_DIS BIT(0)
|
||||
#define WX_TSC_ST 0x1D004
|
||||
#define WX_TSC_ST_SECTX_RDY BIT(0)
|
||||
#define WX_TSC_BUF_AE 0x1D00C
|
||||
#define WX_TSC_BUF_AE_THR GENMASK(9, 0)
|
||||
|
||||
@ -231,6 +233,24 @@
|
||||
#define WX_MAC_WDG_TIMEOUT 0x1100C
|
||||
#define WX_MAC_RX_FLOW_CTRL 0x11090
|
||||
#define WX_MAC_RX_FLOW_CTRL_RFE BIT(0) /* receive fc enable */
|
||||
/* MDIO Registers */
|
||||
#define WX_MSCA 0x11200
|
||||
#define WX_MSCA_RA(v) FIELD_PREP(U16_MAX, v)
|
||||
#define WX_MSCA_PA(v) FIELD_PREP(GENMASK(20, 16), v)
|
||||
#define WX_MSCA_DA(v) FIELD_PREP(GENMASK(25, 21), v)
|
||||
#define WX_MSCC 0x11204
|
||||
#define WX_MSCC_CMD(v) FIELD_PREP(GENMASK(17, 16), v)
|
||||
|
||||
enum WX_MSCA_CMD_value {
|
||||
WX_MSCA_CMD_RSV = 0,
|
||||
WX_MSCA_CMD_WRITE,
|
||||
WX_MSCA_CMD_POST_READ,
|
||||
WX_MSCA_CMD_READ,
|
||||
};
|
||||
|
||||
#define WX_MSCC_SADDR BIT(18)
|
||||
#define WX_MSCC_BUSY BIT(22)
|
||||
#define WX_MDIO_CLK(v) FIELD_PREP(GENMASK(21, 19), v)
|
||||
#define WX_MMC_CONTROL 0x11800
|
||||
#define WX_MMC_CONTROL_RSTONRD BIT(2) /* reset on read */
|
||||
|
||||
@ -580,6 +600,13 @@ enum wx_mac_type {
|
||||
wx_mac_em
|
||||
};
|
||||
|
||||
enum sp_media_type {
|
||||
sp_media_unknown = 0,
|
||||
sp_media_fiber,
|
||||
sp_media_copper,
|
||||
sp_media_backplane
|
||||
};
|
||||
|
||||
enum em_mac_type {
|
||||
em_mac_type_unknown = 0,
|
||||
em_mac_type_mdi,
|
||||
@ -827,6 +854,7 @@ struct wx {
|
||||
struct wx_bus_info bus;
|
||||
struct wx_mac_info mac;
|
||||
enum em_mac_type mac_type;
|
||||
enum sp_media_type media_type;
|
||||
struct wx_eeprom_info eeprom;
|
||||
struct wx_addr_filter_info addr_ctrl;
|
||||
struct wx_mac_addr *mac_table;
|
||||
|
@ -37,24 +37,24 @@ static int ngbe_phy_read_reg_mdi_c22(struct mii_bus *bus, int phy_addr, int regn
|
||||
|
||||
wr32(wx, NGBE_MDIO_CLAUSE_SELECT, 0xF);
|
||||
/* setup and write the address cycle command */
|
||||
command = NGBE_MSCA_RA(regnum) |
|
||||
NGBE_MSCA_PA(phy_addr) |
|
||||
NGBE_MSCA_DA(device_type);
|
||||
wr32(wx, NGBE_MSCA, command);
|
||||
command = NGBE_MSCC_CMD(NGBE_MSCA_CMD_READ) |
|
||||
NGBE_MSCC_BUSY |
|
||||
NGBE_MDIO_CLK(6);
|
||||
wr32(wx, NGBE_MSCC, command);
|
||||
command = WX_MSCA_RA(regnum) |
|
||||
WX_MSCA_PA(phy_addr) |
|
||||
WX_MSCA_DA(device_type);
|
||||
wr32(wx, WX_MSCA, command);
|
||||
command = WX_MSCC_CMD(WX_MSCA_CMD_READ) |
|
||||
WX_MSCC_BUSY |
|
||||
WX_MDIO_CLK(6);
|
||||
wr32(wx, WX_MSCC, command);
|
||||
|
||||
/* wait to complete */
|
||||
ret = read_poll_timeout(rd32, val, !(val & NGBE_MSCC_BUSY), 1000,
|
||||
100000, false, wx, NGBE_MSCC);
|
||||
ret = read_poll_timeout(rd32, val, !(val & WX_MSCC_BUSY), 1000,
|
||||
100000, false, wx, WX_MSCC);
|
||||
if (ret) {
|
||||
wx_err(wx, "Mdio read c22 command did not complete.\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return (u16)rd32(wx, NGBE_MSCC);
|
||||
return (u16)rd32(wx, WX_MSCC);
|
||||
}
|
||||
|
||||
static int ngbe_phy_write_reg_mdi_c22(struct mii_bus *bus, int phy_addr, int regnum, u16 value)
|
||||
@ -65,19 +65,19 @@ static int ngbe_phy_write_reg_mdi_c22(struct mii_bus *bus, int phy_addr, int reg
|
||||
|
||||
wr32(wx, NGBE_MDIO_CLAUSE_SELECT, 0xF);
|
||||
/* setup and write the address cycle command */
|
||||
command = NGBE_MSCA_RA(regnum) |
|
||||
NGBE_MSCA_PA(phy_addr) |
|
||||
NGBE_MSCA_DA(device_type);
|
||||
wr32(wx, NGBE_MSCA, command);
|
||||
command = WX_MSCA_RA(regnum) |
|
||||
WX_MSCA_PA(phy_addr) |
|
||||
WX_MSCA_DA(device_type);
|
||||
wr32(wx, WX_MSCA, command);
|
||||
command = value |
|
||||
NGBE_MSCC_CMD(NGBE_MSCA_CMD_WRITE) |
|
||||
NGBE_MSCC_BUSY |
|
||||
NGBE_MDIO_CLK(6);
|
||||
wr32(wx, NGBE_MSCC, command);
|
||||
WX_MSCC_CMD(WX_MSCA_CMD_WRITE) |
|
||||
WX_MSCC_BUSY |
|
||||
WX_MDIO_CLK(6);
|
||||
wr32(wx, WX_MSCC, command);
|
||||
|
||||
/* wait to complete */
|
||||
ret = read_poll_timeout(rd32, val, !(val & NGBE_MSCC_BUSY), 1000,
|
||||
100000, false, wx, NGBE_MSCC);
|
||||
ret = read_poll_timeout(rd32, val, !(val & WX_MSCC_BUSY), 1000,
|
||||
100000, false, wx, WX_MSCC);
|
||||
if (ret)
|
||||
wx_err(wx, "Mdio write c22 command did not complete.\n");
|
||||
|
||||
@ -92,24 +92,24 @@ static int ngbe_phy_read_reg_mdi_c45(struct mii_bus *bus, int phy_addr, int devn
|
||||
|
||||
wr32(wx, NGBE_MDIO_CLAUSE_SELECT, 0x0);
|
||||
/* setup and write the address cycle command */
|
||||
command = NGBE_MSCA_RA(regnum) |
|
||||
NGBE_MSCA_PA(phy_addr) |
|
||||
NGBE_MSCA_DA(devnum);
|
||||
wr32(wx, NGBE_MSCA, command);
|
||||
command = NGBE_MSCC_CMD(NGBE_MSCA_CMD_READ) |
|
||||
NGBE_MSCC_BUSY |
|
||||
NGBE_MDIO_CLK(6);
|
||||
wr32(wx, NGBE_MSCC, command);
|
||||
command = WX_MSCA_RA(regnum) |
|
||||
WX_MSCA_PA(phy_addr) |
|
||||
WX_MSCA_DA(devnum);
|
||||
wr32(wx, WX_MSCA, command);
|
||||
command = WX_MSCC_CMD(WX_MSCA_CMD_READ) |
|
||||
WX_MSCC_BUSY |
|
||||
WX_MDIO_CLK(6);
|
||||
wr32(wx, WX_MSCC, command);
|
||||
|
||||
/* wait to complete */
|
||||
ret = read_poll_timeout(rd32, val, !(val & NGBE_MSCC_BUSY), 1000,
|
||||
100000, false, wx, NGBE_MSCC);
|
||||
ret = read_poll_timeout(rd32, val, !(val & WX_MSCC_BUSY), 1000,
|
||||
100000, false, wx, WX_MSCC);
|
||||
if (ret) {
|
||||
wx_err(wx, "Mdio read c45 command did not complete.\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return (u16)rd32(wx, NGBE_MSCC);
|
||||
return (u16)rd32(wx, WX_MSCC);
|
||||
}
|
||||
|
||||
static int ngbe_phy_write_reg_mdi_c45(struct mii_bus *bus, int phy_addr,
|
||||
@ -121,19 +121,19 @@ static int ngbe_phy_write_reg_mdi_c45(struct mii_bus *bus, int phy_addr,
|
||||
|
||||
wr32(wx, NGBE_MDIO_CLAUSE_SELECT, 0x0);
|
||||
/* setup and write the address cycle command */
|
||||
command = NGBE_MSCA_RA(regnum) |
|
||||
NGBE_MSCA_PA(phy_addr) |
|
||||
NGBE_MSCA_DA(devnum);
|
||||
wr32(wx, NGBE_MSCA, command);
|
||||
command = WX_MSCA_RA(regnum) |
|
||||
WX_MSCA_PA(phy_addr) |
|
||||
WX_MSCA_DA(devnum);
|
||||
wr32(wx, WX_MSCA, command);
|
||||
command = value |
|
||||
NGBE_MSCC_CMD(NGBE_MSCA_CMD_WRITE) |
|
||||
NGBE_MSCC_BUSY |
|
||||
NGBE_MDIO_CLK(6);
|
||||
wr32(wx, NGBE_MSCC, command);
|
||||
WX_MSCC_CMD(WX_MSCA_CMD_WRITE) |
|
||||
WX_MSCC_BUSY |
|
||||
WX_MDIO_CLK(6);
|
||||
wr32(wx, WX_MSCC, command);
|
||||
|
||||
/* wait to complete */
|
||||
ret = read_poll_timeout(rd32, val, !(val & NGBE_MSCC_BUSY), 1000,
|
||||
100000, false, wx, NGBE_MSCC);
|
||||
ret = read_poll_timeout(rd32, val, !(val & WX_MSCC_BUSY), 1000,
|
||||
100000, false, wx, WX_MSCC);
|
||||
if (ret)
|
||||
wx_err(wx, "Mdio write c45 command did not complete.\n");
|
||||
|
||||
|
@ -59,25 +59,6 @@
|
||||
#define NGBE_EEPROM_VERSION_L 0x1D
|
||||
#define NGBE_EEPROM_VERSION_H 0x1E
|
||||
|
||||
/* mdio access */
|
||||
#define NGBE_MSCA 0x11200
|
||||
#define NGBE_MSCA_RA(v) FIELD_PREP(U16_MAX, v)
|
||||
#define NGBE_MSCA_PA(v) FIELD_PREP(GENMASK(20, 16), v)
|
||||
#define NGBE_MSCA_DA(v) FIELD_PREP(GENMASK(25, 21), v)
|
||||
#define NGBE_MSCC 0x11204
|
||||
#define NGBE_MSCC_CMD(v) FIELD_PREP(GENMASK(17, 16), v)
|
||||
|
||||
enum NGBE_MSCA_CMD_value {
|
||||
NGBE_MSCA_CMD_RSV = 0,
|
||||
NGBE_MSCA_CMD_WRITE,
|
||||
NGBE_MSCA_CMD_POST_READ,
|
||||
NGBE_MSCA_CMD_READ,
|
||||
};
|
||||
|
||||
#define NGBE_MSCC_SADDR BIT(18)
|
||||
#define NGBE_MSCC_BUSY BIT(22)
|
||||
#define NGBE_MDIO_CLK(v) FIELD_PREP(GENMASK(21, 19), v)
|
||||
|
||||
/* Media-dependent registers. */
|
||||
#define NGBE_MDIO_CLAUSE_SELECT 0x11220
|
||||
|
||||
|
@ -13,6 +13,34 @@
|
||||
#include "txgbe_type.h"
|
||||
#include "txgbe_hw.h"
|
||||
|
||||
/**
|
||||
* txgbe_disable_sec_tx_path - Stops the transmit data path
|
||||
* @wx: pointer to hardware structure
|
||||
*
|
||||
* Stops the transmit data path and waits for the HW to internally empty
|
||||
* the tx security block
|
||||
**/
|
||||
int txgbe_disable_sec_tx_path(struct wx *wx)
|
||||
{
|
||||
int val;
|
||||
|
||||
wr32m(wx, WX_TSC_CTL, WX_TSC_CTL_TX_DIS, WX_TSC_CTL_TX_DIS);
|
||||
return read_poll_timeout(rd32, val, val & WX_TSC_ST_SECTX_RDY,
|
||||
1000, 20000, false, wx, WX_TSC_ST);
|
||||
}
|
||||
|
||||
/**
|
||||
* txgbe_enable_sec_tx_path - Enables the transmit data path
|
||||
* @wx: pointer to hardware structure
|
||||
*
|
||||
* Enables the transmit data path.
|
||||
**/
|
||||
void txgbe_enable_sec_tx_path(struct wx *wx)
|
||||
{
|
||||
wr32m(wx, WX_TSC_CTL, WX_TSC_CTL_TX_DIS, 0);
|
||||
WX_WRITE_FLUSH(wx);
|
||||
}
|
||||
|
||||
/**
|
||||
* txgbe_init_thermal_sensor_thresh - Inits thermal sensor thresholds
|
||||
* @wx: pointer to hardware structure
|
||||
@ -257,17 +285,20 @@ static void txgbe_reset_misc(struct wx *wx)
|
||||
int txgbe_reset_hw(struct wx *wx)
|
||||
{
|
||||
int status;
|
||||
u32 val;
|
||||
|
||||
/* Call adapter stop to disable tx/rx and clear interrupts */
|
||||
status = wx_stop_adapter(wx);
|
||||
if (status != 0)
|
||||
return status;
|
||||
|
||||
val = WX_MIS_RST_LAN_RST(wx->bus.func);
|
||||
wr32(wx, WX_MIS_RST, val | rd32(wx, WX_MIS_RST));
|
||||
WX_WRITE_FLUSH(wx);
|
||||
usleep_range(10, 100);
|
||||
if (wx->media_type != sp_media_copper) {
|
||||
u32 val;
|
||||
|
||||
val = WX_MIS_RST_LAN_RST(wx->bus.func);
|
||||
wr32(wx, WX_MIS_RST, val | rd32(wx, WX_MIS_RST));
|
||||
WX_WRITE_FLUSH(wx);
|
||||
usleep_range(10, 100);
|
||||
}
|
||||
|
||||
status = wx_check_flash_load(wx, TXGBE_SPI_ILDR_STATUS_LAN_SW_RST(wx->bus.func));
|
||||
if (status != 0)
|
||||
|
@ -4,6 +4,8 @@
|
||||
#ifndef _TXGBE_HW_H_
|
||||
#define _TXGBE_HW_H_
|
||||
|
||||
int txgbe_disable_sec_tx_path(struct wx *wx);
|
||||
void txgbe_enable_sec_tx_path(struct wx *wx);
|
||||
int txgbe_read_pba_string(struct wx *wx, u8 *pba_num, u32 pba_num_size);
|
||||
int txgbe_validate_eeprom_checksum(struct wx *wx, u16 *checksum_val);
|
||||
int txgbe_reset_hw(struct wx *wx);
|
||||
|
@ -300,6 +300,49 @@ static void txgbe_down(struct wx *wx)
|
||||
wx_clean_all_rx_rings(wx);
|
||||
}
|
||||
|
||||
/**
|
||||
* txgbe_init_type_code - Initialize the shared code
|
||||
* @wx: pointer to hardware structure
|
||||
**/
|
||||
static void txgbe_init_type_code(struct wx *wx)
|
||||
{
|
||||
u8 device_type = wx->subsystem_device_id & 0xF0;
|
||||
|
||||
switch (wx->device_id) {
|
||||
case TXGBE_DEV_ID_SP1000:
|
||||
case TXGBE_DEV_ID_WX1820:
|
||||
wx->mac.type = wx_mac_sp;
|
||||
break;
|
||||
default:
|
||||
wx->mac.type = wx_mac_unknown;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (device_type) {
|
||||
case TXGBE_ID_SFP:
|
||||
wx->media_type = sp_media_fiber;
|
||||
break;
|
||||
case TXGBE_ID_XAUI:
|
||||
case TXGBE_ID_SGMII:
|
||||
wx->media_type = sp_media_copper;
|
||||
break;
|
||||
case TXGBE_ID_KR_KX_KX4:
|
||||
case TXGBE_ID_MAC_XAUI:
|
||||
case TXGBE_ID_MAC_SGMII:
|
||||
wx->media_type = sp_media_backplane;
|
||||
break;
|
||||
case TXGBE_ID_SFI_XAUI:
|
||||
if (wx->bus.func == 0)
|
||||
wx->media_type = sp_media_fiber;
|
||||
else
|
||||
wx->media_type = sp_media_copper;
|
||||
break;
|
||||
default:
|
||||
wx->media_type = sp_media_unknown;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* txgbe_sw_init - Initialize general software structures (struct wx)
|
||||
* @wx: board private structure to initialize
|
||||
@ -324,15 +367,7 @@ static int txgbe_sw_init(struct wx *wx)
|
||||
return err;
|
||||
}
|
||||
|
||||
switch (wx->device_id) {
|
||||
case TXGBE_DEV_ID_SP1000:
|
||||
case TXGBE_DEV_ID_WX1820:
|
||||
wx->mac.type = wx_mac_sp;
|
||||
break;
|
||||
default:
|
||||
wx->mac.type = wx_mac_unknown;
|
||||
break;
|
||||
}
|
||||
txgbe_init_type_code(wx);
|
||||
|
||||
/* Set common capability flags and settings */
|
||||
wx->max_q_vectors = TXGBE_MAX_MSIX_VECTORS;
|
||||
@ -663,6 +698,9 @@ static int txgbe_probe(struct pci_dev *pdev,
|
||||
"0x%08x", etrack_id);
|
||||
}
|
||||
|
||||
if (etrack_id < 0x20010)
|
||||
dev_warn(&pdev->dev, "Please upgrade the firmware to 0x20010 or above.\n");
|
||||
|
||||
txgbe = devm_kzalloc(&pdev->dev, sizeof(*txgbe), GFP_KERNEL);
|
||||
if (!txgbe) {
|
||||
err = -ENOMEM;
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include "../libwx/wx_hw.h"
|
||||
#include "txgbe_type.h"
|
||||
#include "txgbe_phy.h"
|
||||
#include "txgbe_hw.h"
|
||||
|
||||
static int txgbe_swnodes_register(struct txgbe *txgbe)
|
||||
{
|
||||
@ -160,7 +161,10 @@ static struct phylink_pcs *txgbe_phylink_mac_select(struct phylink_config *confi
|
||||
{
|
||||
struct txgbe *txgbe = netdev_to_txgbe(to_net_dev(config->dev));
|
||||
|
||||
return &txgbe->xpcs->pcs;
|
||||
if (interface == PHY_INTERFACE_MODE_10GBASER)
|
||||
return &txgbe->xpcs->pcs;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void txgbe_mac_config(struct phylink_config *config, unsigned int mode,
|
||||
@ -210,8 +214,32 @@ static void txgbe_mac_link_up(struct phylink_config *config,
|
||||
wr32(wx, WX_MAC_WDG_TIMEOUT, wdg);
|
||||
}
|
||||
|
||||
static int txgbe_mac_prepare(struct phylink_config *config, unsigned int mode,
|
||||
phy_interface_t interface)
|
||||
{
|
||||
struct wx *wx = netdev_priv(to_net_dev(config->dev));
|
||||
|
||||
wr32m(wx, WX_MAC_TX_CFG, WX_MAC_TX_CFG_TE, 0);
|
||||
wr32m(wx, WX_MAC_RX_CFG, WX_MAC_RX_CFG_RE, 0);
|
||||
|
||||
return txgbe_disable_sec_tx_path(wx);
|
||||
}
|
||||
|
||||
static int txgbe_mac_finish(struct phylink_config *config, unsigned int mode,
|
||||
phy_interface_t interface)
|
||||
{
|
||||
struct wx *wx = netdev_priv(to_net_dev(config->dev));
|
||||
|
||||
txgbe_enable_sec_tx_path(wx);
|
||||
wr32m(wx, WX_MAC_RX_CFG, WX_MAC_RX_CFG_RE, WX_MAC_RX_CFG_RE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct phylink_mac_ops txgbe_mac_ops = {
|
||||
.mac_select_pcs = txgbe_phylink_mac_select,
|
||||
.mac_prepare = txgbe_mac_prepare,
|
||||
.mac_finish = txgbe_mac_finish,
|
||||
.mac_config = txgbe_mac_config,
|
||||
.mac_link_down = txgbe_mac_link_down,
|
||||
.mac_link_up = txgbe_mac_link_up,
|
||||
@ -219,8 +247,8 @@ static const struct phylink_mac_ops txgbe_mac_ops = {
|
||||
|
||||
static int txgbe_phylink_init(struct txgbe *txgbe)
|
||||
{
|
||||
struct fwnode_handle *fwnode = NULL;
|
||||
struct phylink_config *config;
|
||||
struct fwnode_handle *fwnode;
|
||||
struct wx *wx = txgbe->wx;
|
||||
phy_interface_t phy_mode;
|
||||
struct phylink *phylink;
|
||||
@ -231,14 +259,34 @@ static int txgbe_phylink_init(struct txgbe *txgbe)
|
||||
|
||||
config->dev = &wx->netdev->dev;
|
||||
config->type = PHYLINK_NETDEV;
|
||||
config->mac_capabilities = MAC_10000FD | MAC_1000FD | MAC_SYM_PAUSE | MAC_ASYM_PAUSE;
|
||||
phy_mode = PHY_INTERFACE_MODE_10GBASER;
|
||||
__set_bit(PHY_INTERFACE_MODE_10GBASER, config->supported_interfaces);
|
||||
fwnode = software_node_fwnode(txgbe->nodes.group[SWNODE_PHYLINK]);
|
||||
config->mac_capabilities = MAC_10000FD | MAC_1000FD | MAC_100FD |
|
||||
MAC_SYM_PAUSE | MAC_ASYM_PAUSE;
|
||||
|
||||
if (wx->media_type == sp_media_copper) {
|
||||
phy_mode = PHY_INTERFACE_MODE_XAUI;
|
||||
__set_bit(PHY_INTERFACE_MODE_XAUI, config->supported_interfaces);
|
||||
} else {
|
||||
phy_mode = PHY_INTERFACE_MODE_10GBASER;
|
||||
fwnode = software_node_fwnode(txgbe->nodes.group[SWNODE_PHYLINK]);
|
||||
__set_bit(PHY_INTERFACE_MODE_10GBASER, config->supported_interfaces);
|
||||
__set_bit(PHY_INTERFACE_MODE_1000BASEX, config->supported_interfaces);
|
||||
__set_bit(PHY_INTERFACE_MODE_SGMII, config->supported_interfaces);
|
||||
}
|
||||
|
||||
phylink = phylink_create(config, fwnode, phy_mode, &txgbe_mac_ops);
|
||||
if (IS_ERR(phylink))
|
||||
return PTR_ERR(phylink);
|
||||
|
||||
if (wx->phydev) {
|
||||
int ret;
|
||||
|
||||
ret = phylink_connect_phy(phylink, wx->phydev);
|
||||
if (ret) {
|
||||
phylink_destroy(phylink);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
txgbe->phylink = phylink;
|
||||
|
||||
return 0;
|
||||
@ -431,7 +479,8 @@ static void txgbe_irq_handler(struct irq_desc *desc)
|
||||
|
||||
chained_irq_exit(chip, desc);
|
||||
|
||||
if (eicr & (TXGBE_PX_MISC_ETH_LK | TXGBE_PX_MISC_ETH_LKDN)) {
|
||||
if (eicr & (TXGBE_PX_MISC_ETH_LK | TXGBE_PX_MISC_ETH_LKDN |
|
||||
TXGBE_PX_MISC_ETH_AN)) {
|
||||
u32 reg = rd32(wx, TXGBE_CFG_PORT_ST);
|
||||
|
||||
phylink_mac_change(txgbe->phylink, !!(reg & TXGBE_CFG_PORT_ST_LINK_UP));
|
||||
@ -598,10 +647,117 @@ static int txgbe_sfp_register(struct txgbe *txgbe)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int txgbe_phy_read(struct mii_bus *bus, int phy_addr,
|
||||
int devnum, int regnum)
|
||||
{
|
||||
struct wx *wx = bus->priv;
|
||||
u32 val, command;
|
||||
int ret;
|
||||
|
||||
/* setup and write the address cycle command */
|
||||
command = WX_MSCA_RA(regnum) |
|
||||
WX_MSCA_PA(phy_addr) |
|
||||
WX_MSCA_DA(devnum);
|
||||
wr32(wx, WX_MSCA, command);
|
||||
|
||||
command = WX_MSCC_CMD(WX_MSCA_CMD_READ) | WX_MSCC_BUSY;
|
||||
wr32(wx, WX_MSCC, command);
|
||||
|
||||
/* wait to complete */
|
||||
ret = read_poll_timeout(rd32, val, !(val & WX_MSCC_BUSY), 1000,
|
||||
100000, false, wx, WX_MSCC);
|
||||
if (ret) {
|
||||
wx_err(wx, "Mdio read c45 command did not complete.\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return (u16)rd32(wx, WX_MSCC);
|
||||
}
|
||||
|
||||
static int txgbe_phy_write(struct mii_bus *bus, int phy_addr,
|
||||
int devnum, int regnum, u16 value)
|
||||
{
|
||||
struct wx *wx = bus->priv;
|
||||
int ret, command;
|
||||
u16 val;
|
||||
|
||||
/* setup and write the address cycle command */
|
||||
command = WX_MSCA_RA(regnum) |
|
||||
WX_MSCA_PA(phy_addr) |
|
||||
WX_MSCA_DA(devnum);
|
||||
wr32(wx, WX_MSCA, command);
|
||||
|
||||
command = value | WX_MSCC_CMD(WX_MSCA_CMD_WRITE) | WX_MSCC_BUSY;
|
||||
wr32(wx, WX_MSCC, command);
|
||||
|
||||
/* wait to complete */
|
||||
ret = read_poll_timeout(rd32, val, !(val & WX_MSCC_BUSY), 1000,
|
||||
100000, false, wx, WX_MSCC);
|
||||
if (ret)
|
||||
wx_err(wx, "Mdio write c45 command did not complete.\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int txgbe_ext_phy_init(struct txgbe *txgbe)
|
||||
{
|
||||
struct phy_device *phydev;
|
||||
struct mii_bus *mii_bus;
|
||||
struct pci_dev *pdev;
|
||||
struct wx *wx;
|
||||
int ret = 0;
|
||||
|
||||
wx = txgbe->wx;
|
||||
pdev = wx->pdev;
|
||||
|
||||
mii_bus = devm_mdiobus_alloc(&pdev->dev);
|
||||
if (!mii_bus)
|
||||
return -ENOMEM;
|
||||
|
||||
mii_bus->name = "txgbe_mii_bus";
|
||||
mii_bus->read_c45 = &txgbe_phy_read;
|
||||
mii_bus->write_c45 = &txgbe_phy_write;
|
||||
mii_bus->parent = &pdev->dev;
|
||||
mii_bus->phy_mask = GENMASK(31, 1);
|
||||
mii_bus->priv = wx;
|
||||
snprintf(mii_bus->id, MII_BUS_ID_SIZE, "txgbe-%x",
|
||||
(pdev->bus->number << 8) | pdev->devfn);
|
||||
|
||||
ret = devm_mdiobus_register(&pdev->dev, mii_bus);
|
||||
if (ret) {
|
||||
wx_err(wx, "failed to register MDIO bus: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
phydev = phy_find_first(mii_bus);
|
||||
if (!phydev) {
|
||||
wx_err(wx, "no PHY found\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
phy_attached_info(phydev);
|
||||
|
||||
wx->link = 0;
|
||||
wx->speed = 0;
|
||||
wx->duplex = 0;
|
||||
wx->phydev = phydev;
|
||||
|
||||
ret = txgbe_phylink_init(txgbe);
|
||||
if (ret) {
|
||||
wx_err(wx, "failed to init phylink: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int txgbe_init_phy(struct txgbe *txgbe)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (txgbe->wx->media_type == sp_media_copper)
|
||||
return txgbe_ext_phy_init(txgbe);
|
||||
|
||||
ret = txgbe_swnodes_register(txgbe);
|
||||
if (ret) {
|
||||
wx_err(txgbe->wx, "failed to register software nodes\n");
|
||||
@ -663,6 +819,12 @@ err_unregister_swnode:
|
||||
|
||||
void txgbe_remove_phy(struct txgbe *txgbe)
|
||||
{
|
||||
if (txgbe->wx->media_type == sp_media_copper) {
|
||||
phylink_disconnect_phy(txgbe->phylink);
|
||||
phylink_destroy(txgbe->phylink);
|
||||
return;
|
||||
}
|
||||
|
||||
platform_device_unregister(txgbe->sfp_dev);
|
||||
platform_device_unregister(txgbe->i2c_dev);
|
||||
clkdev_drop(txgbe->clock);
|
||||
|
@ -1,7 +1,7 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
# Makefile for Linux PCS drivers
|
||||
|
||||
pcs_xpcs-$(CONFIG_PCS_XPCS) := pcs-xpcs.o pcs-xpcs-nxp.o
|
||||
pcs_xpcs-$(CONFIG_PCS_XPCS) := pcs-xpcs.o pcs-xpcs-nxp.o pcs-xpcs-wx.o
|
||||
|
||||
obj-$(CONFIG_PCS_XPCS) += pcs_xpcs.o
|
||||
obj-$(CONFIG_PCS_LYNX) += pcs-lynx.o
|
||||
|
209
drivers/net/pcs/pcs-xpcs-wx.c
Normal file
209
drivers/net/pcs/pcs-xpcs-wx.c
Normal file
@ -0,0 +1,209 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (c) 2015 - 2023 Beijing WangXun Technology Co., Ltd. */
|
||||
|
||||
#include <linux/pcs/pcs-xpcs.h>
|
||||
#include <linux/mdio.h>
|
||||
#include "pcs-xpcs.h"
|
||||
|
||||
/* VR_XS_PMA_MMD */
|
||||
#define TXGBE_PMA_MMD 0x8020
|
||||
#define TXGBE_TX_GENCTL1 0x11
|
||||
#define TXGBE_TX_GENCTL1_VBOOST_LVL GENMASK(10, 8)
|
||||
#define TXGBE_TX_GENCTL1_VBOOST_EN0 BIT(4)
|
||||
#define TXGBE_TX_GEN_CTL2 0x12
|
||||
#define TXGBE_TX_GEN_CTL2_TX0_WIDTH(v) FIELD_PREP(GENMASK(9, 8), v)
|
||||
#define TXGBE_TX_RATE_CTL 0x14
|
||||
#define TXGBE_TX_RATE_CTL_TX0_RATE(v) FIELD_PREP(GENMASK(2, 0), v)
|
||||
#define TXGBE_RX_GEN_CTL2 0x32
|
||||
#define TXGBE_RX_GEN_CTL2_RX0_WIDTH(v) FIELD_PREP(GENMASK(9, 8), v)
|
||||
#define TXGBE_RX_GEN_CTL3 0x33
|
||||
#define TXGBE_RX_GEN_CTL3_LOS_TRSHLD0 GENMASK(2, 0)
|
||||
#define TXGBE_RX_RATE_CTL 0x34
|
||||
#define TXGBE_RX_RATE_CTL_RX0_RATE(v) FIELD_PREP(GENMASK(1, 0), v)
|
||||
#define TXGBE_RX_EQ_ATTN_CTL 0x37
|
||||
#define TXGBE_RX_EQ_ATTN_LVL0 GENMASK(2, 0)
|
||||
#define TXGBE_RX_EQ_CTL0 0x38
|
||||
#define TXGBE_RX_EQ_CTL0_VGA1_GAIN(v) FIELD_PREP(GENMASK(15, 12), v)
|
||||
#define TXGBE_RX_EQ_CTL0_VGA2_GAIN(v) FIELD_PREP(GENMASK(11, 8), v)
|
||||
#define TXGBE_RX_EQ_CTL0_CTLE_POLE(v) FIELD_PREP(GENMASK(7, 5), v)
|
||||
#define TXGBE_RX_EQ_CTL0_CTLE_BOOST(v) FIELD_PREP(GENMASK(4, 0), v)
|
||||
#define TXGBE_RX_EQ_CTL4 0x3C
|
||||
#define TXGBE_RX_EQ_CTL4_CONT_OFF_CAN0 BIT(4)
|
||||
#define TXGBE_RX_EQ_CTL4_CONT_ADAPT0 BIT(0)
|
||||
#define TXGBE_AFE_DFE_ENABLE 0x3D
|
||||
#define TXGBE_DFE_EN_0 BIT(4)
|
||||
#define TXGBE_AFE_EN_0 BIT(0)
|
||||
#define TXGBE_DFE_TAP_CTL0 0x3E
|
||||
#define TXGBE_MPLLA_CTL0 0x51
|
||||
#define TXGBE_MPLLA_CTL2 0x53
|
||||
#define TXGBE_MPLLA_CTL2_DIV16P5_CLK_EN BIT(10)
|
||||
#define TXGBE_MPLLA_CTL2_DIV10_CLK_EN BIT(9)
|
||||
#define TXGBE_MPLLA_CTL3 0x57
|
||||
#define TXGBE_MISC_CTL0 0x70
|
||||
#define TXGBE_MISC_CTL0_PLL BIT(15)
|
||||
#define TXGBE_MISC_CTL0_CR_PARA_SEL BIT(14)
|
||||
#define TXGBE_MISC_CTL0_RX_VREF(v) FIELD_PREP(GENMASK(12, 8), v)
|
||||
#define TXGBE_VCO_CAL_LD0 0x72
|
||||
#define TXGBE_VCO_CAL_REF0 0x76
|
||||
|
||||
static int txgbe_read_pma(struct dw_xpcs *xpcs, int reg)
|
||||
{
|
||||
return xpcs_read(xpcs, MDIO_MMD_PMAPMD, TXGBE_PMA_MMD + reg);
|
||||
}
|
||||
|
||||
static int txgbe_write_pma(struct dw_xpcs *xpcs, int reg, u16 val)
|
||||
{
|
||||
return xpcs_write(xpcs, MDIO_MMD_PMAPMD, TXGBE_PMA_MMD + reg, val);
|
||||
}
|
||||
|
||||
static void txgbe_pma_config_10gbaser(struct dw_xpcs *xpcs)
|
||||
{
|
||||
int val;
|
||||
|
||||
txgbe_write_pma(xpcs, TXGBE_MPLLA_CTL0, 0x21);
|
||||
txgbe_write_pma(xpcs, TXGBE_MPLLA_CTL3, 0);
|
||||
val = txgbe_read_pma(xpcs, TXGBE_TX_GENCTL1);
|
||||
val = u16_replace_bits(val, 0x5, TXGBE_TX_GENCTL1_VBOOST_LVL);
|
||||
txgbe_write_pma(xpcs, TXGBE_TX_GENCTL1, val);
|
||||
txgbe_write_pma(xpcs, TXGBE_MISC_CTL0, TXGBE_MISC_CTL0_PLL |
|
||||
TXGBE_MISC_CTL0_CR_PARA_SEL | TXGBE_MISC_CTL0_RX_VREF(0xF));
|
||||
txgbe_write_pma(xpcs, TXGBE_VCO_CAL_LD0, 0x549);
|
||||
txgbe_write_pma(xpcs, TXGBE_VCO_CAL_REF0, 0x29);
|
||||
txgbe_write_pma(xpcs, TXGBE_TX_RATE_CTL, 0);
|
||||
txgbe_write_pma(xpcs, TXGBE_RX_RATE_CTL, 0);
|
||||
txgbe_write_pma(xpcs, TXGBE_TX_GEN_CTL2, TXGBE_TX_GEN_CTL2_TX0_WIDTH(3));
|
||||
txgbe_write_pma(xpcs, TXGBE_RX_GEN_CTL2, TXGBE_RX_GEN_CTL2_RX0_WIDTH(3));
|
||||
txgbe_write_pma(xpcs, TXGBE_MPLLA_CTL2, TXGBE_MPLLA_CTL2_DIV16P5_CLK_EN |
|
||||
TXGBE_MPLLA_CTL2_DIV10_CLK_EN);
|
||||
|
||||
txgbe_write_pma(xpcs, TXGBE_RX_EQ_CTL0, TXGBE_RX_EQ_CTL0_CTLE_POLE(2) |
|
||||
TXGBE_RX_EQ_CTL0_CTLE_BOOST(5));
|
||||
val = txgbe_read_pma(xpcs, TXGBE_RX_EQ_ATTN_CTL);
|
||||
val &= ~TXGBE_RX_EQ_ATTN_LVL0;
|
||||
txgbe_write_pma(xpcs, TXGBE_RX_EQ_ATTN_CTL, val);
|
||||
txgbe_write_pma(xpcs, TXGBE_DFE_TAP_CTL0, 0xBE);
|
||||
val = txgbe_read_pma(xpcs, TXGBE_AFE_DFE_ENABLE);
|
||||
val &= ~(TXGBE_DFE_EN_0 | TXGBE_AFE_EN_0);
|
||||
txgbe_write_pma(xpcs, TXGBE_AFE_DFE_ENABLE, val);
|
||||
val = txgbe_read_pma(xpcs, TXGBE_RX_EQ_CTL4);
|
||||
val &= ~TXGBE_RX_EQ_CTL4_CONT_ADAPT0;
|
||||
txgbe_write_pma(xpcs, TXGBE_RX_EQ_CTL4, val);
|
||||
}
|
||||
|
||||
static void txgbe_pma_config_1g(struct dw_xpcs *xpcs)
|
||||
{
|
||||
int val;
|
||||
|
||||
val = txgbe_read_pma(xpcs, TXGBE_TX_GENCTL1);
|
||||
val = u16_replace_bits(val, 0x5, TXGBE_TX_GENCTL1_VBOOST_LVL);
|
||||
val &= ~TXGBE_TX_GENCTL1_VBOOST_EN0;
|
||||
txgbe_write_pma(xpcs, TXGBE_TX_GENCTL1, val);
|
||||
txgbe_write_pma(xpcs, TXGBE_MISC_CTL0, TXGBE_MISC_CTL0_PLL |
|
||||
TXGBE_MISC_CTL0_CR_PARA_SEL | TXGBE_MISC_CTL0_RX_VREF(0xF));
|
||||
|
||||
txgbe_write_pma(xpcs, TXGBE_RX_EQ_CTL0, TXGBE_RX_EQ_CTL0_VGA1_GAIN(7) |
|
||||
TXGBE_RX_EQ_CTL0_VGA2_GAIN(7) | TXGBE_RX_EQ_CTL0_CTLE_BOOST(6));
|
||||
val = txgbe_read_pma(xpcs, TXGBE_RX_EQ_ATTN_CTL);
|
||||
val &= ~TXGBE_RX_EQ_ATTN_LVL0;
|
||||
txgbe_write_pma(xpcs, TXGBE_RX_EQ_ATTN_CTL, val);
|
||||
txgbe_write_pma(xpcs, TXGBE_DFE_TAP_CTL0, 0);
|
||||
val = txgbe_read_pma(xpcs, TXGBE_RX_GEN_CTL3);
|
||||
val = u16_replace_bits(val, 0x4, TXGBE_RX_GEN_CTL3_LOS_TRSHLD0);
|
||||
txgbe_write_pma(xpcs, TXGBE_RX_EQ_ATTN_CTL, val);
|
||||
|
||||
txgbe_write_pma(xpcs, TXGBE_MPLLA_CTL0, 0x20);
|
||||
txgbe_write_pma(xpcs, TXGBE_MPLLA_CTL3, 0x46);
|
||||
txgbe_write_pma(xpcs, TXGBE_VCO_CAL_LD0, 0x540);
|
||||
txgbe_write_pma(xpcs, TXGBE_VCO_CAL_REF0, 0x2A);
|
||||
txgbe_write_pma(xpcs, TXGBE_AFE_DFE_ENABLE, 0);
|
||||
txgbe_write_pma(xpcs, TXGBE_RX_EQ_CTL4, TXGBE_RX_EQ_CTL4_CONT_OFF_CAN0);
|
||||
txgbe_write_pma(xpcs, TXGBE_TX_RATE_CTL, TXGBE_TX_RATE_CTL_TX0_RATE(3));
|
||||
txgbe_write_pma(xpcs, TXGBE_RX_RATE_CTL, TXGBE_RX_RATE_CTL_RX0_RATE(3));
|
||||
txgbe_write_pma(xpcs, TXGBE_TX_GEN_CTL2, TXGBE_TX_GEN_CTL2_TX0_WIDTH(1));
|
||||
txgbe_write_pma(xpcs, TXGBE_RX_GEN_CTL2, TXGBE_RX_GEN_CTL2_RX0_WIDTH(1));
|
||||
txgbe_write_pma(xpcs, TXGBE_MPLLA_CTL2, TXGBE_MPLLA_CTL2_DIV10_CLK_EN);
|
||||
}
|
||||
|
||||
static int txgbe_pcs_poll_power_up(struct dw_xpcs *xpcs)
|
||||
{
|
||||
int val, ret;
|
||||
|
||||
/* Wait xpcs power-up good */
|
||||
ret = read_poll_timeout(xpcs_read_vpcs, val,
|
||||
(val & DW_PSEQ_ST) == DW_PSEQ_ST_GOOD,
|
||||
10000, 1000000, false,
|
||||
xpcs, DW_VR_XS_PCS_DIG_STS);
|
||||
if (ret < 0)
|
||||
dev_err(&xpcs->mdiodev->dev, "xpcs power-up timeout\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int txgbe_pma_init_done(struct dw_xpcs *xpcs)
|
||||
{
|
||||
int val, ret;
|
||||
|
||||
xpcs_write_vpcs(xpcs, DW_VR_XS_PCS_DIG_CTRL1, DW_VR_RST | DW_EN_VSMMD1);
|
||||
|
||||
/* wait pma initialization done */
|
||||
ret = read_poll_timeout(xpcs_read_vpcs, val, !(val & DW_VR_RST),
|
||||
100000, 10000000, false,
|
||||
xpcs, DW_VR_XS_PCS_DIG_CTRL1);
|
||||
if (ret < 0)
|
||||
dev_err(&xpcs->mdiodev->dev, "xpcs pma initialization timeout\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool txgbe_xpcs_mode_quirk(struct dw_xpcs *xpcs)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* When txgbe do LAN reset, PCS will change to default 10GBASE-R mode */
|
||||
ret = xpcs_read(xpcs, MDIO_MMD_PCS, MDIO_CTRL2);
|
||||
ret &= MDIO_PCS_CTRL2_TYPE;
|
||||
if ((ret == MDIO_PCS_CTRL2_10GBR &&
|
||||
xpcs->interface != PHY_INTERFACE_MODE_10GBASER) ||
|
||||
xpcs->interface == PHY_INTERFACE_MODE_SGMII)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int txgbe_xpcs_switch_mode(struct dw_xpcs *xpcs, phy_interface_t interface)
|
||||
{
|
||||
int val, ret;
|
||||
|
||||
switch (interface) {
|
||||
case PHY_INTERFACE_MODE_10GBASER:
|
||||
case PHY_INTERFACE_MODE_SGMII:
|
||||
case PHY_INTERFACE_MODE_1000BASEX:
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (xpcs->interface == interface && !txgbe_xpcs_mode_quirk(xpcs))
|
||||
return 0;
|
||||
|
||||
xpcs->interface = interface;
|
||||
|
||||
ret = txgbe_pcs_poll_power_up(xpcs);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (interface == PHY_INTERFACE_MODE_10GBASER) {
|
||||
xpcs_write(xpcs, MDIO_MMD_PCS, MDIO_CTRL2, MDIO_PCS_CTRL2_10GBR);
|
||||
val = xpcs_read(xpcs, MDIO_MMD_PMAPMD, MDIO_CTRL1);
|
||||
val |= MDIO_CTRL1_SPEED10G;
|
||||
xpcs_write(xpcs, MDIO_MMD_PMAPMD, MDIO_CTRL1, val);
|
||||
txgbe_pma_config_10gbaser(xpcs);
|
||||
} else {
|
||||
xpcs_write(xpcs, MDIO_MMD_PCS, MDIO_CTRL2, MDIO_PCS_CTRL2_10GBX);
|
||||
xpcs_write(xpcs, MDIO_MMD_PMAPMD, MDIO_CTRL1, 0);
|
||||
xpcs_write(xpcs, MDIO_MMD_PCS, MDIO_CTRL1, 0);
|
||||
txgbe_pma_config_1g(xpcs);
|
||||
}
|
||||
|
||||
return txgbe_pma_init_done(xpcs);
|
||||
}
|
@ -228,16 +228,39 @@ static int xpcs_write_vendor(struct dw_xpcs *xpcs, int dev, int reg,
|
||||
return xpcs_write(xpcs, dev, DW_VENDOR | reg, val);
|
||||
}
|
||||
|
||||
static int xpcs_read_vpcs(struct dw_xpcs *xpcs, int reg)
|
||||
int xpcs_read_vpcs(struct dw_xpcs *xpcs, int reg)
|
||||
{
|
||||
return xpcs_read_vendor(xpcs, MDIO_MMD_PCS, reg);
|
||||
}
|
||||
|
||||
static int xpcs_write_vpcs(struct dw_xpcs *xpcs, int reg, u16 val)
|
||||
int xpcs_write_vpcs(struct dw_xpcs *xpcs, int reg, u16 val)
|
||||
{
|
||||
return xpcs_write_vendor(xpcs, MDIO_MMD_PCS, reg, val);
|
||||
}
|
||||
|
||||
static int xpcs_dev_flag(struct dw_xpcs *xpcs)
|
||||
{
|
||||
int ret, oui;
|
||||
|
||||
ret = xpcs_read(xpcs, MDIO_MMD_PMAPMD, MDIO_DEVID1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
oui = ret;
|
||||
|
||||
ret = xpcs_read(xpcs, MDIO_MMD_PMAPMD, MDIO_DEVID2);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = (ret >> 10) & 0x3F;
|
||||
oui |= ret << 16;
|
||||
|
||||
if (oui == DW_OUI_WX)
|
||||
xpcs->dev_flag = DW_DEV_TXGBE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int xpcs_poll_reset(struct dw_xpcs *xpcs, int dev)
|
||||
{
|
||||
/* Poll until the reset bit clears (50ms per retry == 0.6 sec) */
|
||||
@ -660,7 +683,10 @@ EXPORT_SYMBOL_GPL(xpcs_config_eee);
|
||||
static int xpcs_config_aneg_c37_sgmii(struct dw_xpcs *xpcs,
|
||||
unsigned int neg_mode)
|
||||
{
|
||||
int ret, mdio_ctrl;
|
||||
int ret, mdio_ctrl, tx_conf;
|
||||
|
||||
if (xpcs->dev_flag == DW_DEV_TXGBE)
|
||||
xpcs_write_vpcs(xpcs, DW_VR_XS_PCS_DIG_CTRL1, DW_CL37_BP | DW_EN_VSMMD1);
|
||||
|
||||
/* For AN for C37 SGMII mode, the settings are :-
|
||||
* 1) VR_MII_MMD_CTRL Bit(12) [AN_ENABLE] = 0b (Disable SGMII AN in case
|
||||
@ -697,9 +723,15 @@ static int xpcs_config_aneg_c37_sgmii(struct dw_xpcs *xpcs,
|
||||
ret |= (DW_VR_MII_PCS_MODE_C37_SGMII <<
|
||||
DW_VR_MII_AN_CTRL_PCS_MODE_SHIFT &
|
||||
DW_VR_MII_PCS_MODE_MASK);
|
||||
ret |= (DW_VR_MII_TX_CONFIG_MAC_SIDE_SGMII <<
|
||||
DW_VR_MII_AN_CTRL_TX_CONFIG_SHIFT &
|
||||
DW_VR_MII_TX_CONFIG_MASK);
|
||||
if (xpcs->dev_flag == DW_DEV_TXGBE) {
|
||||
ret |= DW_VR_MII_AN_CTRL_8BIT;
|
||||
/* Hardware requires it to be PHY side SGMII */
|
||||
tx_conf = DW_VR_MII_TX_CONFIG_PHY_SIDE_SGMII;
|
||||
} else {
|
||||
tx_conf = DW_VR_MII_TX_CONFIG_MAC_SIDE_SGMII;
|
||||
}
|
||||
ret |= tx_conf << DW_VR_MII_AN_CTRL_TX_CONFIG_SHIFT &
|
||||
DW_VR_MII_TX_CONFIG_MASK;
|
||||
ret = xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_AN_CTRL, ret);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
@ -713,6 +745,9 @@ static int xpcs_config_aneg_c37_sgmii(struct dw_xpcs *xpcs,
|
||||
else
|
||||
ret &= ~DW_VR_MII_DIG_CTRL1_MAC_AUTO_SW;
|
||||
|
||||
if (xpcs->dev_flag == DW_DEV_TXGBE)
|
||||
ret |= DW_VR_MII_DIG_CTRL1_PHY_MODE_CTRL;
|
||||
|
||||
ret = xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_DIG_CTRL1, ret);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
@ -732,6 +767,9 @@ static int xpcs_config_aneg_c37_1000basex(struct dw_xpcs *xpcs,
|
||||
int ret, mdio_ctrl, adv;
|
||||
bool changed = 0;
|
||||
|
||||
if (xpcs->dev_flag == DW_DEV_TXGBE)
|
||||
xpcs_write_vpcs(xpcs, DW_VR_XS_PCS_DIG_CTRL1, DW_CL37_BP | DW_EN_VSMMD1);
|
||||
|
||||
/* According to Chap 7.12, to set 1000BASE-X C37 AN, AN must
|
||||
* be disabled first:-
|
||||
* 1) VR_MII_MMD_CTRL Bit(12)[AN_ENABLE] = 0b
|
||||
@ -753,6 +791,8 @@ static int xpcs_config_aneg_c37_1000basex(struct dw_xpcs *xpcs,
|
||||
return ret;
|
||||
|
||||
ret &= ~DW_VR_MII_PCS_MODE_MASK;
|
||||
if (!xpcs->pcs.poll)
|
||||
ret |= DW_VR_MII_AN_INTR_EN;
|
||||
ret = xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_AN_CTRL, ret);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
@ -818,6 +858,12 @@ int xpcs_do_config(struct dw_xpcs *xpcs, phy_interface_t interface,
|
||||
if (!compat)
|
||||
return -ENODEV;
|
||||
|
||||
if (xpcs->dev_flag == DW_DEV_TXGBE) {
|
||||
ret = txgbe_xpcs_switch_mode(xpcs, interface);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
switch (compat->an_mode) {
|
||||
case DW_10GBASER:
|
||||
break;
|
||||
@ -977,6 +1023,33 @@ static int xpcs_get_state_c37_sgmii(struct dw_xpcs *xpcs,
|
||||
state->duplex = DUPLEX_FULL;
|
||||
else
|
||||
state->duplex = DUPLEX_HALF;
|
||||
} else if (ret == DW_VR_MII_AN_STS_C37_ANCMPLT_INTR) {
|
||||
int speed, duplex;
|
||||
|
||||
state->link = true;
|
||||
|
||||
speed = xpcs_read(xpcs, MDIO_MMD_VEND2, MDIO_CTRL1);
|
||||
if (speed < 0)
|
||||
return speed;
|
||||
|
||||
speed &= SGMII_SPEED_SS13 | SGMII_SPEED_SS6;
|
||||
if (speed == SGMII_SPEED_SS6)
|
||||
state->speed = SPEED_1000;
|
||||
else if (speed == SGMII_SPEED_SS13)
|
||||
state->speed = SPEED_100;
|
||||
else if (speed == 0)
|
||||
state->speed = SPEED_10;
|
||||
|
||||
duplex = xpcs_read(xpcs, MDIO_MMD_VEND2, MII_ADVERTISE);
|
||||
if (duplex < 0)
|
||||
return duplex;
|
||||
|
||||
if (duplex & DW_FULL_DUPLEX)
|
||||
state->duplex = DUPLEX_FULL;
|
||||
else if (duplex & DW_HALF_DUPLEX)
|
||||
state->duplex = DUPLEX_HALF;
|
||||
|
||||
xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_AN_INTR_STS, 0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -1000,6 +1073,17 @@ static int xpcs_get_state_c37_1000basex(struct dw_xpcs *xpcs,
|
||||
if (bmsr < 0)
|
||||
return bmsr;
|
||||
|
||||
/* Clear AN complete interrupt */
|
||||
if (!xpcs->pcs.poll) {
|
||||
int an_intr;
|
||||
|
||||
an_intr = xpcs_read(xpcs, MDIO_MMD_VEND2, DW_VR_MII_AN_INTR_STS);
|
||||
if (an_intr & DW_VR_MII_AN_STS_C37_ANCMPLT_INTR) {
|
||||
an_intr &= ~DW_VR_MII_AN_STS_C37_ANCMPLT_INTR;
|
||||
xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_AN_INTR_STS, an_intr);
|
||||
}
|
||||
}
|
||||
|
||||
phylink_mii_c22_pcs_decode_state(state, bmsr, lpa);
|
||||
}
|
||||
|
||||
@ -1284,17 +1368,21 @@ static struct dw_xpcs *xpcs_create(struct mdio_device *mdiodev,
|
||||
goto out;
|
||||
}
|
||||
|
||||
xpcs->pcs.ops = &xpcs_phylink_ops;
|
||||
xpcs->pcs.neg_mode = true;
|
||||
if (compat->an_mode == DW_10GBASER)
|
||||
return xpcs;
|
||||
|
||||
xpcs->pcs.poll = true;
|
||||
|
||||
ret = xpcs_soft_reset(xpcs, compat);
|
||||
ret = xpcs_dev_flag(xpcs);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
xpcs->pcs.ops = &xpcs_phylink_ops;
|
||||
xpcs->pcs.neg_mode = true;
|
||||
|
||||
if (xpcs->dev_flag != DW_DEV_TXGBE) {
|
||||
xpcs->pcs.poll = true;
|
||||
|
||||
ret = xpcs_soft_reset(xpcs, compat);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
|
||||
return xpcs;
|
||||
}
|
||||
|
||||
|
@ -15,8 +15,14 @@
|
||||
/* VR_XS_PCS */
|
||||
#define DW_USXGMII_RST BIT(10)
|
||||
#define DW_USXGMII_EN BIT(9)
|
||||
#define DW_VR_XS_PCS_DIG_CTRL1 0x0000
|
||||
#define DW_VR_RST BIT(15)
|
||||
#define DW_EN_VSMMD1 BIT(13)
|
||||
#define DW_CL37_BP BIT(12)
|
||||
#define DW_VR_XS_PCS_DIG_STS 0x0010
|
||||
#define DW_RXFIFO_ERR GENMASK(6, 5)
|
||||
#define DW_PSEQ_ST GENMASK(4, 2)
|
||||
#define DW_PSEQ_ST_GOOD FIELD_PREP(GENMASK(4, 2), 0x4)
|
||||
|
||||
/* SR_MII */
|
||||
#define DW_USXGMII_FULL BIT(8)
|
||||
@ -61,12 +67,14 @@
|
||||
|
||||
/* VR_MII_DIG_CTRL1 */
|
||||
#define DW_VR_MII_DIG_CTRL1_MAC_AUTO_SW BIT(9)
|
||||
#define DW_VR_MII_DIG_CTRL1_PHY_MODE_CTRL BIT(0)
|
||||
|
||||
/* VR_MII_DIG_CTRL2 */
|
||||
#define DW_VR_MII_DIG_CTRL2_TX_POL_INV BIT(4)
|
||||
#define DW_VR_MII_DIG_CTRL2_RX_POL_INV BIT(0)
|
||||
|
||||
/* VR_MII_AN_CTRL */
|
||||
#define DW_VR_MII_AN_CTRL_8BIT BIT(8)
|
||||
#define DW_VR_MII_AN_CTRL_TX_CONFIG_SHIFT 3
|
||||
#define DW_VR_MII_TX_CONFIG_MASK BIT(3)
|
||||
#define DW_VR_MII_TX_CONFIG_PHY_SIDE_SGMII 0x1
|
||||
@ -75,8 +83,10 @@
|
||||
#define DW_VR_MII_PCS_MODE_MASK GENMASK(2, 1)
|
||||
#define DW_VR_MII_PCS_MODE_C37_1000BASEX 0x0
|
||||
#define DW_VR_MII_PCS_MODE_C37_SGMII 0x2
|
||||
#define DW_VR_MII_AN_INTR_EN BIT(0)
|
||||
|
||||
/* VR_MII_AN_INTR_STS */
|
||||
#define DW_VR_MII_AN_STS_C37_ANCMPLT_INTR BIT(0)
|
||||
#define DW_VR_MII_AN_STS_C37_ANSGM_FD BIT(1)
|
||||
#define DW_VR_MII_AN_STS_C37_ANSGM_SP_SHIFT 2
|
||||
#define DW_VR_MII_AN_STS_C37_ANSGM_SP GENMASK(3, 2)
|
||||
@ -90,6 +100,10 @@
|
||||
#define SGMII_SPEED_SS13 BIT(13) /* SGMII speed along with SS6 */
|
||||
#define SGMII_SPEED_SS6 BIT(6) /* SGMII speed along with SS13 */
|
||||
|
||||
/* SR MII MMD AN Advertisement defines */
|
||||
#define DW_HALF_DUPLEX BIT(6)
|
||||
#define DW_FULL_DUPLEX BIT(5)
|
||||
|
||||
/* VR MII EEE Control 0 defines */
|
||||
#define DW_VR_MII_EEE_LTX_EN BIT(0) /* LPI Tx Enable */
|
||||
#define DW_VR_MII_EEE_LRX_EN BIT(1) /* LPI Rx Enable */
|
||||
@ -106,6 +120,9 @@
|
||||
|
||||
int xpcs_read(struct dw_xpcs *xpcs, int dev, u32 reg);
|
||||
int xpcs_write(struct dw_xpcs *xpcs, int dev, u32 reg, u16 val);
|
||||
int xpcs_read_vpcs(struct dw_xpcs *xpcs, int reg);
|
||||
int xpcs_write_vpcs(struct dw_xpcs *xpcs, int reg, u16 val);
|
||||
int nxp_sja1105_sgmii_pma_config(struct dw_xpcs *xpcs);
|
||||
int nxp_sja1110_sgmii_pma_config(struct dw_xpcs *xpcs);
|
||||
int nxp_sja1110_2500basex_pma_config(struct dw_xpcs *xpcs);
|
||||
int txgbe_xpcs_switch_mode(struct dw_xpcs *xpcs, phy_interface_t interface);
|
||||
|
@ -20,12 +20,20 @@
|
||||
#define DW_AN_C37_1000BASEX 4
|
||||
#define DW_10GBASER 5
|
||||
|
||||
/* device vendor OUI */
|
||||
#define DW_OUI_WX 0x0018fc80
|
||||
|
||||
/* dev_flag */
|
||||
#define DW_DEV_TXGBE BIT(0)
|
||||
|
||||
struct xpcs_id;
|
||||
|
||||
struct dw_xpcs {
|
||||
struct mdio_device *mdiodev;
|
||||
const struct xpcs_id *id;
|
||||
struct phylink_pcs pcs;
|
||||
phy_interface_t interface;
|
||||
int dev_flag;
|
||||
};
|
||||
|
||||
int xpcs_get_an_mode(struct dw_xpcs *xpcs, phy_interface_t interface);
|
||||
|
Loading…
x
Reference in New Issue
Block a user