net: pcs: xpcs: add support for NXP SJA1105
The NXP SJA1105 DSA switch integrates a Synopsys SGMII XPCS on port 4. The generic code works fine, except there is an integration issue which needs to be dealt with: in this switch, the XPCS is integrated with a PMA that has the TX lane polarity inverted by default (PLUS is MINUS, MINUS is PLUS). To obtain normal non-inverted behavior, the TX lane polarity must be inverted in the PCS, via the DIGITAL_CONTROL_2 register. We introduce a pma_config() method in xpcs_compat which is called by the phylink_pcs_config() implementation. Also, the NXP SJA1105 returns all zeroes in the PHY ID registers 2 and 3. We need to hack up an ad-hoc PHY ID (OUI is zero, device ID is 1) in order for the XPCS driver to recognize it. This PHY ID is added to the public include/linux/pcs/pcs-xpcs.h for that reason (for the sja1105 driver to be able to use it in a later patch). Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Родитель
36641b045c
Коммит
dd0721ea4c
|
@ -13203,6 +13203,7 @@ M: Vladimir Oltean <olteanv@gmail.com>
|
||||||
L: linux-kernel@vger.kernel.org
|
L: linux-kernel@vger.kernel.org
|
||||||
S: Maintained
|
S: Maintained
|
||||||
F: drivers/net/dsa/sja1105
|
F: drivers/net/dsa/sja1105
|
||||||
|
F: drivers/net/pcs/pcs-xpcs-nxp.c
|
||||||
|
|
||||||
NXP TDA998X DRM DRIVER
|
NXP TDA998X DRM DRIVER
|
||||||
M: Russell King <linux@armlinux.org.uk>
|
M: Russell King <linux@armlinux.org.uk>
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
# SPDX-License-Identifier: GPL-2.0
|
# SPDX-License-Identifier: GPL-2.0
|
||||||
# Makefile for Linux PCS drivers
|
# Makefile for Linux PCS drivers
|
||||||
|
|
||||||
obj-$(CONFIG_PCS_XPCS) += pcs-xpcs.o
|
pcs_xpcs-$(CONFIG_PCS_XPCS) := pcs-xpcs.o pcs-xpcs-nxp.o
|
||||||
|
|
||||||
|
obj-$(CONFIG_PCS_XPCS) += pcs_xpcs.o
|
||||||
obj-$(CONFIG_PCS_LYNX) += pcs-lynx.o
|
obj-$(CONFIG_PCS_LYNX) += pcs-lynx.o
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
/* Copyright 2021 NXP Semiconductors
|
||||||
|
*/
|
||||||
|
#include <linux/pcs/pcs-xpcs.h>
|
||||||
|
#include "pcs-xpcs.h"
|
||||||
|
|
||||||
|
/* In NXP SJA1105, the PCS is integrated with a PMA that has the TX lane
|
||||||
|
* polarity inverted by default (PLUS is MINUS, MINUS is PLUS). To obtain
|
||||||
|
* normal non-inverted behavior, the TX lane polarity must be inverted in the
|
||||||
|
* PCS, via the DIGITAL_CONTROL_2 register.
|
||||||
|
*/
|
||||||
|
int nxp_sja1105_sgmii_pma_config(struct dw_xpcs *xpcs)
|
||||||
|
{
|
||||||
|
return xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_DIG_CTRL2,
|
||||||
|
DW_VR_MII_DIG_CTRL2_TX_POL_INV);
|
||||||
|
}
|
|
@ -117,6 +117,7 @@ struct xpcs_compat {
|
||||||
const phy_interface_t *interface;
|
const phy_interface_t *interface;
|
||||||
int num_interfaces;
|
int num_interfaces;
|
||||||
int an_mode;
|
int an_mode;
|
||||||
|
int (*pma_config)(struct dw_xpcs *xpcs);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct xpcs_id {
|
struct xpcs_id {
|
||||||
|
@ -168,7 +169,7 @@ static bool __xpcs_linkmode_supported(const struct xpcs_compat *compat,
|
||||||
#define xpcs_linkmode_supported(compat, mode) \
|
#define xpcs_linkmode_supported(compat, mode) \
|
||||||
__xpcs_linkmode_supported(compat, ETHTOOL_LINK_MODE_ ## mode ## _BIT)
|
__xpcs_linkmode_supported(compat, ETHTOOL_LINK_MODE_ ## mode ## _BIT)
|
||||||
|
|
||||||
static int xpcs_read(struct dw_xpcs *xpcs, int dev, u32 reg)
|
int xpcs_read(struct dw_xpcs *xpcs, int dev, u32 reg)
|
||||||
{
|
{
|
||||||
u32 reg_addr = mdiobus_c45_addr(dev, reg);
|
u32 reg_addr = mdiobus_c45_addr(dev, reg);
|
||||||
struct mii_bus *bus = xpcs->mdiodev->bus;
|
struct mii_bus *bus = xpcs->mdiodev->bus;
|
||||||
|
@ -177,7 +178,7 @@ static int xpcs_read(struct dw_xpcs *xpcs, int dev, u32 reg)
|
||||||
return mdiobus_read(bus, addr, reg_addr);
|
return mdiobus_read(bus, addr, reg_addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int xpcs_write(struct dw_xpcs *xpcs, int dev, u32 reg, u16 val)
|
int xpcs_write(struct dw_xpcs *xpcs, int dev, u32 reg, u16 val)
|
||||||
{
|
{
|
||||||
u32 reg_addr = mdiobus_c45_addr(dev, reg);
|
u32 reg_addr = mdiobus_c45_addr(dev, reg);
|
||||||
struct mii_bus *bus = xpcs->mdiodev->bus;
|
struct mii_bus *bus = xpcs->mdiodev->bus;
|
||||||
|
@ -788,6 +789,12 @@ static int xpcs_do_config(struct dw_xpcs *xpcs, phy_interface_t interface,
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (compat->pma_config) {
|
||||||
|
ret = compat->pma_config(xpcs);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1022,11 +1029,25 @@ static const struct xpcs_compat synopsys_xpcs_compat[DW_XPCS_INTERFACE_MAX] = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct xpcs_compat nxp_sja1105_xpcs_compat[DW_XPCS_INTERFACE_MAX] = {
|
||||||
|
[DW_XPCS_SGMII] = {
|
||||||
|
.supported = xpcs_sgmii_features,
|
||||||
|
.interface = xpcs_sgmii_interfaces,
|
||||||
|
.num_interfaces = ARRAY_SIZE(xpcs_sgmii_interfaces),
|
||||||
|
.an_mode = DW_AN_C37_SGMII,
|
||||||
|
.pma_config = nxp_sja1105_sgmii_pma_config,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
static const struct xpcs_id xpcs_id_list[] = {
|
static const struct xpcs_id xpcs_id_list[] = {
|
||||||
{
|
{
|
||||||
.id = SYNOPSYS_XPCS_ID,
|
.id = SYNOPSYS_XPCS_ID,
|
||||||
.mask = SYNOPSYS_XPCS_MASK,
|
.mask = SYNOPSYS_XPCS_MASK,
|
||||||
.compat = synopsys_xpcs_compat,
|
.compat = synopsys_xpcs_compat,
|
||||||
|
}, {
|
||||||
|
.id = NXP_SJA1105_XPCS_ID,
|
||||||
|
.mask = SYNOPSYS_XPCS_MASK,
|
||||||
|
.compat = nxp_sja1105_xpcs_compat,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -60,10 +60,15 @@
|
||||||
/* EEE Mode Control Register */
|
/* EEE Mode Control Register */
|
||||||
#define DW_VR_MII_EEE_MCTRL0 0x8006
|
#define DW_VR_MII_EEE_MCTRL0 0x8006
|
||||||
#define DW_VR_MII_EEE_MCTRL1 0x800b
|
#define DW_VR_MII_EEE_MCTRL1 0x800b
|
||||||
|
#define DW_VR_MII_DIG_CTRL2 0x80e1
|
||||||
|
|
||||||
/* VR_MII_DIG_CTRL1 */
|
/* VR_MII_DIG_CTRL1 */
|
||||||
#define DW_VR_MII_DIG_CTRL1_MAC_AUTO_SW BIT(9)
|
#define DW_VR_MII_DIG_CTRL1_MAC_AUTO_SW BIT(9)
|
||||||
|
|
||||||
|
/* 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 */
|
/* VR_MII_AN_CTRL */
|
||||||
#define DW_VR_MII_AN_CTRL_TX_CONFIG_SHIFT 3
|
#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_MASK BIT(3)
|
||||||
|
@ -101,3 +106,8 @@
|
||||||
|
|
||||||
/* VR MII EEE Control 1 defines */
|
/* VR MII EEE Control 1 defines */
|
||||||
#define DW_VR_MII_EEE_TRN_LPI BIT(0) /* Transparent Mode Enable */
|
#define DW_VR_MII_EEE_TRN_LPI BIT(0) /* Transparent Mode Enable */
|
||||||
|
|
||||||
|
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 nxp_sja1105_sgmii_pma_config(struct dw_xpcs *xpcs);
|
||||||
|
|
|
@ -10,6 +10,8 @@
|
||||||
#include <linux/phy.h>
|
#include <linux/phy.h>
|
||||||
#include <linux/phylink.h>
|
#include <linux/phylink.h>
|
||||||
|
|
||||||
|
#define NXP_SJA1105_XPCS_ID 0x00000010
|
||||||
|
|
||||||
/* AN mode */
|
/* AN mode */
|
||||||
#define DW_AN_C73 1
|
#define DW_AN_C73 1
|
||||||
#define DW_AN_C37_SGMII 2
|
#define DW_AN_C37_SGMII 2
|
||||||
|
|
Загрузка…
Ссылка в новой задаче