net: txgbe: support copper NIC with external PHY
Wangxun SP chip supports to connect with external PHY (marvell 88x3310), which links to 10GBASE-T/1000BASE-T/100BASE-T. Add the identification of media types from subsystem device IDs. For sp_media_copper, register mdio bus for the external PHY. Signed-off-by: Jiawen Wu <jiawenwu@trustnetic.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
a4414dd13f
commit
02b2a6f91b
5 changed files with 221 additions and 22 deletions
|
@ -41,6 +41,7 @@ config TXGBE
|
||||||
tristate "Wangxun(R) 10GbE PCI Express adapters support"
|
tristate "Wangxun(R) 10GbE PCI Express adapters support"
|
||||||
depends on PCI
|
depends on PCI
|
||||||
depends on COMMON_CLK
|
depends on COMMON_CLK
|
||||||
|
select MARVELL_10G_PHY
|
||||||
select REGMAP
|
select REGMAP
|
||||||
select I2C
|
select I2C
|
||||||
select I2C_DESIGNWARE_PLATFORM
|
select I2C_DESIGNWARE_PLATFORM
|
||||||
|
|
|
@ -233,6 +233,24 @@
|
||||||
#define WX_MAC_WDG_TIMEOUT 0x1100C
|
#define WX_MAC_WDG_TIMEOUT 0x1100C
|
||||||
#define WX_MAC_RX_FLOW_CTRL 0x11090
|
#define WX_MAC_RX_FLOW_CTRL 0x11090
|
||||||
#define WX_MAC_RX_FLOW_CTRL_RFE BIT(0) /* receive fc enable */
|
#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 0x11800
|
||||||
#define WX_MMC_CONTROL_RSTONRD BIT(2) /* reset on read */
|
#define WX_MMC_CONTROL_RSTONRD BIT(2) /* reset on read */
|
||||||
|
|
||||||
|
@ -582,6 +600,13 @@ enum wx_mac_type {
|
||||||
wx_mac_em
|
wx_mac_em
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum sp_media_type {
|
||||||
|
sp_media_unknown = 0,
|
||||||
|
sp_media_fiber,
|
||||||
|
sp_media_copper,
|
||||||
|
sp_media_backplane
|
||||||
|
};
|
||||||
|
|
||||||
enum em_mac_type {
|
enum em_mac_type {
|
||||||
em_mac_type_unknown = 0,
|
em_mac_type_unknown = 0,
|
||||||
em_mac_type_mdi,
|
em_mac_type_mdi,
|
||||||
|
@ -829,6 +854,7 @@ struct wx {
|
||||||
struct wx_bus_info bus;
|
struct wx_bus_info bus;
|
||||||
struct wx_mac_info mac;
|
struct wx_mac_info mac;
|
||||||
enum em_mac_type mac_type;
|
enum em_mac_type mac_type;
|
||||||
|
enum sp_media_type media_type;
|
||||||
struct wx_eeprom_info eeprom;
|
struct wx_eeprom_info eeprom;
|
||||||
struct wx_addr_filter_info addr_ctrl;
|
struct wx_addr_filter_info addr_ctrl;
|
||||||
struct wx_mac_addr *mac_table;
|
struct wx_mac_addr *mac_table;
|
||||||
|
|
|
@ -285,17 +285,20 @@ static void txgbe_reset_misc(struct wx *wx)
|
||||||
int txgbe_reset_hw(struct wx *wx)
|
int txgbe_reset_hw(struct wx *wx)
|
||||||
{
|
{
|
||||||
int status;
|
int status;
|
||||||
u32 val;
|
|
||||||
|
|
||||||
/* Call adapter stop to disable tx/rx and clear interrupts */
|
/* Call adapter stop to disable tx/rx and clear interrupts */
|
||||||
status = wx_stop_adapter(wx);
|
status = wx_stop_adapter(wx);
|
||||||
if (status != 0)
|
if (status != 0)
|
||||||
return status;
|
return status;
|
||||||
|
|
||||||
|
if (wx->media_type != sp_media_copper) {
|
||||||
|
u32 val;
|
||||||
|
|
||||||
val = WX_MIS_RST_LAN_RST(wx->bus.func);
|
val = WX_MIS_RST_LAN_RST(wx->bus.func);
|
||||||
wr32(wx, WX_MIS_RST, val | rd32(wx, WX_MIS_RST));
|
wr32(wx, WX_MIS_RST, val | rd32(wx, WX_MIS_RST));
|
||||||
WX_WRITE_FLUSH(wx);
|
WX_WRITE_FLUSH(wx);
|
||||||
usleep_range(10, 100);
|
usleep_range(10, 100);
|
||||||
|
}
|
||||||
|
|
||||||
status = wx_check_flash_load(wx, TXGBE_SPI_ILDR_STATUS_LAN_SW_RST(wx->bus.func));
|
status = wx_check_flash_load(wx, TXGBE_SPI_ILDR_STATUS_LAN_SW_RST(wx->bus.func));
|
||||||
if (status != 0)
|
if (status != 0)
|
||||||
|
|
|
@ -300,6 +300,49 @@ static void txgbe_down(struct wx *wx)
|
||||||
wx_clean_all_rx_rings(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)
|
* txgbe_sw_init - Initialize general software structures (struct wx)
|
||||||
* @wx: board private structure to initialize
|
* @wx: board private structure to initialize
|
||||||
|
@ -324,15 +367,7 @@ static int txgbe_sw_init(struct wx *wx)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (wx->device_id) {
|
txgbe_init_type_code(wx);
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Set common capability flags and settings */
|
/* Set common capability flags and settings */
|
||||||
wx->max_q_vectors = TXGBE_MAX_MSIX_VECTORS;
|
wx->max_q_vectors = TXGBE_MAX_MSIX_VECTORS;
|
||||||
|
|
|
@ -161,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));
|
struct txgbe *txgbe = netdev_to_txgbe(to_net_dev(config->dev));
|
||||||
|
|
||||||
|
if (interface == PHY_INTERFACE_MODE_10GBASER)
|
||||||
return &txgbe->xpcs->pcs;
|
return &txgbe->xpcs->pcs;
|
||||||
|
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void txgbe_mac_config(struct phylink_config *config, unsigned int mode,
|
static void txgbe_mac_config(struct phylink_config *config, unsigned int mode,
|
||||||
|
@ -244,8 +247,8 @@ static const struct phylink_mac_ops txgbe_mac_ops = {
|
||||||
|
|
||||||
static int txgbe_phylink_init(struct txgbe *txgbe)
|
static int txgbe_phylink_init(struct txgbe *txgbe)
|
||||||
{
|
{
|
||||||
|
struct fwnode_handle *fwnode = NULL;
|
||||||
struct phylink_config *config;
|
struct phylink_config *config;
|
||||||
struct fwnode_handle *fwnode;
|
|
||||||
struct wx *wx = txgbe->wx;
|
struct wx *wx = txgbe->wx;
|
||||||
phy_interface_t phy_mode;
|
phy_interface_t phy_mode;
|
||||||
struct phylink *phylink;
|
struct phylink *phylink;
|
||||||
|
@ -256,16 +259,34 @@ static int txgbe_phylink_init(struct txgbe *txgbe)
|
||||||
|
|
||||||
config->dev = &wx->netdev->dev;
|
config->dev = &wx->netdev->dev;
|
||||||
config->type = PHYLINK_NETDEV;
|
config->type = PHYLINK_NETDEV;
|
||||||
config->mac_capabilities = MAC_10000FD | MAC_1000FD | MAC_SYM_PAUSE | MAC_ASYM_PAUSE;
|
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;
|
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_10GBASER, config->supported_interfaces);
|
||||||
__set_bit(PHY_INTERFACE_MODE_1000BASEX, config->supported_interfaces);
|
__set_bit(PHY_INTERFACE_MODE_1000BASEX, config->supported_interfaces);
|
||||||
__set_bit(PHY_INTERFACE_MODE_SGMII, config->supported_interfaces);
|
__set_bit(PHY_INTERFACE_MODE_SGMII, config->supported_interfaces);
|
||||||
fwnode = software_node_fwnode(txgbe->nodes.group[SWNODE_PHYLINK]);
|
}
|
||||||
|
|
||||||
phylink = phylink_create(config, fwnode, phy_mode, &txgbe_mac_ops);
|
phylink = phylink_create(config, fwnode, phy_mode, &txgbe_mac_ops);
|
||||||
if (IS_ERR(phylink))
|
if (IS_ERR(phylink))
|
||||||
return PTR_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;
|
txgbe->phylink = phylink;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -626,10 +647,117 @@ static int txgbe_sfp_register(struct txgbe *txgbe)
|
||||||
return 0;
|
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 txgbe_init_phy(struct txgbe *txgbe)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
if (txgbe->wx->media_type == sp_media_copper)
|
||||||
|
return txgbe_ext_phy_init(txgbe);
|
||||||
|
|
||||||
ret = txgbe_swnodes_register(txgbe);
|
ret = txgbe_swnodes_register(txgbe);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
wx_err(txgbe->wx, "failed to register software nodes\n");
|
wx_err(txgbe->wx, "failed to register software nodes\n");
|
||||||
|
@ -691,6 +819,12 @@ err_unregister_swnode:
|
||||||
|
|
||||||
void txgbe_remove_phy(struct txgbe *txgbe)
|
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->sfp_dev);
|
||||||
platform_device_unregister(txgbe->i2c_dev);
|
platform_device_unregister(txgbe->i2c_dev);
|
||||||
clkdev_drop(txgbe->clock);
|
clkdev_drop(txgbe->clock);
|
||||||
|
|
Loading…
Add table
Reference in a new issue