I would like to add more properties similar to tx-p2p-microvolt, and I don't think it makes sense to create one schema for each such property (transmit-amplitude.yaml, lane-polarity.yaml, transmit-equalization.yaml etc). Instead, let's rename to phy-common-props.yaml, which makes it a more adequate host schema for all the above properties. Signed-off-by: Vladimir Oltean --- .../{transmit-amplitude.yaml => phy-common-props.yaml} | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) rename Documentation/devicetree/bindings/phy/{transmit-amplitude.yaml => phy-common-props.yaml} (90%) diff --git a/Documentation/devicetree/bindings/phy/transmit-amplitude.yaml b/Documentation/devicetree/bindings/phy/phy-common-props.yaml similarity index 90% rename from Documentation/devicetree/bindings/phy/transmit-amplitude.yaml rename to Documentation/devicetree/bindings/phy/phy-common-props.yaml index 617f3c0b3dfb..255205ac09cd 100644 --- a/Documentation/devicetree/bindings/phy/transmit-amplitude.yaml +++ b/Documentation/devicetree/bindings/phy/phy-common-props.yaml @@ -1,14 +1,14 @@ # SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) %YAML 1.2 --- -$id: http://devicetree.org/schemas/phy/transmit-amplitude.yaml# +$id: http://devicetree.org/schemas/phy/phy-common-props.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: Common PHY and network PCS transmit amplitude property +title: Common PHY and network PCS properties description: - Binding describing the peak-to-peak transmit amplitude for common PHYs - and network PCSes. + Common PHY and network PCS properties, such as peak-to-peak transmit + amplitude. maintainers: - Marek BehĂșn -- 2.34.1 Other properties also need to be defined per protocol than just tx-p2p-microvolt-names. Create a common definition to avoid copying a 55 line property. Signed-off-by: Vladimir Oltean --- .../bindings/phy/phy-common-props.yaml | 34 +++++++++++-------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/Documentation/devicetree/bindings/phy/phy-common-props.yaml b/Documentation/devicetree/bindings/phy/phy-common-props.yaml index 255205ac09cd..775f4dfe3cc3 100644 --- a/Documentation/devicetree/bindings/phy/phy-common-props.yaml +++ b/Documentation/devicetree/bindings/phy/phy-common-props.yaml @@ -13,22 +13,12 @@ description: maintainers: - Marek BehĂșn -properties: - tx-p2p-microvolt: +$defs: + protocol-names: description: - Transmit amplitude voltages in microvolts, peak-to-peak. If this property - contains multiple values for various PHY modes, the - 'tx-p2p-microvolt-names' property must be provided and contain - corresponding mode names. - - tx-p2p-microvolt-names: - description: | - Names of the modes corresponding to voltages in the 'tx-p2p-microvolt' - property. Required only if multiple voltages are provided. - - If a value of 'default' is provided, the system should use it for any PHY - mode that is otherwise not defined here. If 'default' is not provided, the - system should use manufacturer default value. + Names of the PHY modes. If a value of 'default' is provided, the system + should use it for any PHY mode that is otherwise not defined here. If + 'default' is not provided, the system should use manufacturer default value. minItems: 1 maxItems: 16 items: @@ -89,6 +79,20 @@ properties: - mipi-dphy-univ - mipi-dphy-v2.5-univ +properties: + tx-p2p-microvolt: + description: + Transmit amplitude voltages in microvolts, peak-to-peak. If this property + contains multiple values for various PHY modes, the + 'tx-p2p-microvolt-names' property must be provided and contain + corresponding mode names. + + tx-p2p-microvolt-names: + description: + Names of the modes corresponding to voltages in the 'tx-p2p-microvolt' + property. Required only if multiple voltages are provided. + $ref: "#/$defs/protocol-names" + dependencies: tx-p2p-microvolt-names: [ tx-p2p-microvolt ] -- 2.34.1 Differential signaling is a technique for high-speed protocols to be more resilient to noise. At the transmit side we have a positive and a negative signal which are mirror images of each other. At the receiver, if we subtract the negative signal (say of amplitude -A) from the positive signal (say +A), we recover the original single-ended signal at twice its original amplitude. But any noise, like one coming from EMI from outside sources, is supposed to have an almost equal impact upon the positive (A + E, E being for "error") and negative signal (-A + E). So (A + E) - (-A + E) eliminates this noise, and this is what makes differential signaling useful. Except that in order to work, there must be strict requirements observed during PCB design and layout, like the signal traces needing to have the same length and be physically close to each other, and many others. Sometimes it is not easy to fulfill all these requirements, a simple case to understand is when on chip A's pins, the positive pin is on the left and the negative is on the right, but on the chip B's pins (with which A tries to communicate), positive is on the right and negative on the left. The signals would need to cross, using vias and other ugly stuff that affects signal integrity (introduces impedance discontinuities which cause reflections, etc). So sometimes, board designers intentionally connect differential lanes the wrong way, and expect somebody else to invert that signal to recover useful data. This is where RX and TX polarity inversion comes in as a generic concept that applies to any high-speed serial protocol as long as it uses differential signaling. I've stopped two attempts to introduce more vendor-specific descriptions of this only in the past month: https://lore.kernel.org/linux-phy/20251110110536.2596490-1-horatiu.vultur@microchip.com/ https://lore.kernel.org/netdev/20251028000959.3kiac5kwo5pcl4ft@skbuf/ and in the kernel we already have merged: - "st,px_rx_pol_inv" - "st,pcie-tx-pol-inv" - "st,sata-tx-pol-inv" - "mediatek,pnswap" - "airoha,pnswap-rx" - "airoha,pnswap-tx" and maybe more. So it is pretty general. One additional element of complexity is introduced by the fact that for some protocols, receivers can automatically detect and correct for an inverted lane polarity (example: the PCIe LTSSM does this in the Polling.Configuration state; the USB 3.1 Link Layer Test Specification says that the detection and correction of the lane polarity inversion in SuperSpeed operation shall be enabled in Polling.RxEQ.). Whereas for other protocols (SGMII, SATA, 10GBase-R, etc etc), the polarity is all manual and there is no detection mechanism mandated by their respective standards. So why would one even describe rx-polarity and tx-polarity for protocols like PCIe, if it had to always be PHY_POL_AUTO? Related question: why would we define the polarity as an array per protocol? Isn't the physical PCB layout protocol-agnostic, and aren't we describing the same physical reality from the lens of different protocols? The answer to both questions is because multi-protocol PHYs exist (supporting e.g. USB2 and USB3, or SATA and PCIe, or PCIe and Ethernet over the same lane), one would need to manually set the polarity for SATA/Ethernet, while leaving it at auto for PCIe/USB 3.0+. I also investigated from another angle: what if polarity inversion in the PHY is one layer, and then the PCIe/USB3 LTSSM polarity detection is another layer on top? Then rx-polarity = doesn't make sense, it can still be rx-polarity = or , and the link training state machine figures things out on top of that. This would radically simplify the design, as the elimination of PHY_POL_AUTO inherently means that the need for a property array per protocol also goes away. I don't know how things are in the general case, but at least in the 10G and 28G Lynx SerDes blocks from NXP Layerscape devices, this isn't the case, and there's only a single level of RX polarity inversion: in the SerDes lane. In the case of PCIe, the controller is in charge of driving the RDAT_INV bit autonomously, and it is read-only to software. So the existence of this kind of SerDes lane proves the need for PHY_POL_AUTO to be a third state. Signed-off-by: Vladimir Oltean --- .../bindings/phy/phy-common-props.yaml | 45 +++++++++++++++++++ include/dt-bindings/phy/phy.h | 4 ++ 2 files changed, 49 insertions(+) diff --git a/Documentation/devicetree/bindings/phy/phy-common-props.yaml b/Documentation/devicetree/bindings/phy/phy-common-props.yaml index 775f4dfe3cc3..538b85559113 100644 --- a/Documentation/devicetree/bindings/phy/phy-common-props.yaml +++ b/Documentation/devicetree/bindings/phy/phy-common-props.yaml @@ -93,15 +93,60 @@ properties: property. Required only if multiple voltages are provided. $ref: "#/$defs/protocol-names" + rx-polarity: + description: + An array of values indicating whether the differential receiver's + polarity is inverted. Each value can be one of + PHY_POL_NORMAL (0) which means the negative signal is decoded from the + RXN pin, and the positive signal from the the RXP pin; + PHY_POL_INVERT (1) which means the negative signal is decoded from the + RXP pin, and the positive signal from the RXN pin; + PHY_POL_AUTO (2) which means the receiver performs automatic polarity + detection and correction, which is a mandatory part of link training for + some protocols (PCIe, USB SS). + + The values are defined in . + + If this property contains multiple values for various protocols, the + 'rx-polarity-names' property must be provided. + $ref: /schemas/types.yaml#/definitions/uint32-array + minItems: 1 + maxItems: 16 + items: + enum: [0, 1, 2] + + rx-polarity-names: + $ref: '#/$defs/protocol-names' + + tx-polarity: + description: + Like 'rx-polarity', except it applies to differential transmitters, + and only the values of PHY_POL_NORMAL and PHY_POL_INVERT are possible. + $ref: /schemas/types.yaml#/definitions/uint32-array + minItems: 1 + maxItems: 16 + items: + enum: [0, 1] + + tx-polarity-names: + $ref: '#/$defs/protocol-names' + dependencies: tx-p2p-microvolt-names: [ tx-p2p-microvolt ] + rx-polarity-names: [ rx-polarity ] + tx-polarity-names: [ tx-polarity ] additionalProperties: true examples: - | + #include + phy: phy { #phy-cells = <1>; tx-p2p-microvolt = <915000>, <1100000>, <1200000>; tx-p2p-microvolt-names = "2500base-x", "usb-hs", "usb-ss"; + rx-polarity = , ; + rx-polarity-names = "usb-ss", "default"; + tx-polarity = ; }; diff --git a/include/dt-bindings/phy/phy.h b/include/dt-bindings/phy/phy.h index 6b901b342348..f8d4094f0880 100644 --- a/include/dt-bindings/phy/phy.h +++ b/include/dt-bindings/phy/phy.h @@ -24,4 +24,8 @@ #define PHY_TYPE_CPHY 11 #define PHY_TYPE_USXGMII 12 +#define PHY_POL_NORMAL 0 +#define PHY_POL_INVERT 1 +#define PHY_POL_AUTO 2 + #endif /* _DT_BINDINGS_PHY */ -- 2.34.1 Now XPCS device tree nodes can specify properties to configure transmit amplitude, receiver polarity inversion, and transmitter polarity inversion for different PHY protocols. Signed-off-by: Vladimir Oltean --- Documentation/devicetree/bindings/net/pcs/snps,dw-xpcs.yaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/net/pcs/snps,dw-xpcs.yaml b/Documentation/devicetree/bindings/net/pcs/snps,dw-xpcs.yaml index e77eec9ac9ee..9977a3153f41 100644 --- a/Documentation/devicetree/bindings/net/pcs/snps,dw-xpcs.yaml +++ b/Documentation/devicetree/bindings/net/pcs/snps,dw-xpcs.yaml @@ -22,6 +22,9 @@ description: by means of the APB3/MCI interfaces. In the later case the XPCS can be mapped right to the system IO memory space. +allOf: + - $ref: /schemas/phy/phy-common-props.yaml# + properties: compatible: oneOf: @@ -102,7 +105,7 @@ required: - compatible - reg -additionalProperties: false +unevaluatedProperties: false examples: - | -- 2.34.1 Add helpers in the generic PHY folder which can be used using 'select GENERIC_PHY_COMMON_PROPS' from Kconfig, without otherwise needing to enable GENERIC_PHY. These helpers need to deal with the slight messiness of the fact that the polarity properties are arrays per protocol, and with the fact that there is no default value mandated by the standard properties, all default values depend on driver and protocol (PHY_POL_NORMAL may be a good default for SGMII, whereas PHY_POL_AUTO may be a good default for PCIe). Push the supported mask of polarities to these helpers, to simplify drivers such that they don't need to validate what's in the device tree (or other firmware description). The proposed maintainership model is joint custody between netdev and linux-phy, because of the fact that these properties can be applied to Ethernet PCS blocks just as well as Generic PHY devices. I've added as maintainers those from "ETHERNET PHY LIBRARY", "NETWORKING DRIVERS" and "GENERIC PHY FRAMEWORK". Signed-off-by: Vladimir Oltean --- MAINTAINERS | 21 +++++ drivers/phy/Kconfig | 9 +++ drivers/phy/Makefile | 1 + drivers/phy/phy-common-props.c | 117 +++++++++++++++++++++++++++ include/linux/phy/phy-common-props.h | 20 +++++ 5 files changed, 168 insertions(+) create mode 100644 drivers/phy/phy-common-props.c create mode 100644 include/linux/phy/phy-common-props.h diff --git a/MAINTAINERS b/MAINTAINERS index e9a8d945632b..658feb06cc29 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -10445,6 +10445,27 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/arnd/asm-generic.git F: include/asm-generic/ F: include/uapi/asm-generic/ +GENERIC PHY COMMON PROPERTIES +M: Andrew Lunn +M: "David S. Miller" +M: Eric Dumazet +M: Heiner Kallweit +M: Jakub Kicinski +M: Kishon Vijay Abraham I +M: Paolo Abeni +R: Russell King +M: Vinod Koul +L: linux-phy@lists.infradead.org +L: netdev@vger.kernel.org +S: Maintained +Q: https://patchwork.kernel.org/project/linux-phy/list/ +Q: https://patchwork.kernel.org/project/netdevbpf/list/ +T: git git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net.git +T: git git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next.git +T: git git://git.kernel.org/pub/scm/linux/kernel/git/phy/linux-phy.git +F: Documentation/devicetree/bindings/phy/phy-common-props.yaml +F: drivers/phy/phy-common-props.c + GENERIC PHY FRAMEWORK M: Vinod Koul M: Kishon Vijay Abraham I diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index 678dd0452f0a..479986434086 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -16,6 +16,15 @@ config GENERIC_PHY phy users can obtain reference to the PHY. All the users of this framework should select this config. +config GENERIC_PHY_COMMON_PROPS + bool + help + Generic PHY common property parsing. + + Select this from consumer drivers to gain access to helpers for + parsing properties from the + Documentation/devicetree/bindings/phy/phy-common-props.yaml schema. + config GENERIC_PHY_MIPI_DPHY bool select GENERIC_PHY diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index bfb27fb5a494..d07accc15086 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -4,6 +4,7 @@ # obj-$(CONFIG_GENERIC_PHY) += phy-core.o +obj-$(CONFIG_GENERIC_PHY_COMMON_PROPS) += phy-common-props.o obj-$(CONFIG_GENERIC_PHY_MIPI_DPHY) += phy-core-mipi-dphy.o obj-$(CONFIG_PHY_CAN_TRANSCEIVER) += phy-can-transceiver.o obj-$(CONFIG_PHY_LPC18XX_USB_OTG) += phy-lpc18xx-usb-otg.o diff --git a/drivers/phy/phy-common-props.c b/drivers/phy/phy-common-props.c new file mode 100644 index 000000000000..4c9dca98d23f --- /dev/null +++ b/drivers/phy/phy-common-props.c @@ -0,0 +1,117 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * phy-common-props.c -- Common PHY properties + * + * Copyright 2025 NXP + */ +#include +#include +#include +#include +#include +#include + +static int phy_get_polarity_for_mode(struct fwnode_handle *fwnode, + const char *mode_name, + unsigned int supported, + unsigned int default_val, + const char *polarity_prop, + const char *names_prop) +{ + int err, n_pols, n_names, idx = -1; + u32 val, *pols; + + if (!fwnode) + return default_val; + + n_pols = fwnode_property_count_u32(fwnode, polarity_prop); + if (n_pols <= 0) + return default_val; + + n_names = fwnode_property_string_array_count(fwnode, names_prop); + if (n_names >= 0 && n_pols != n_names) { + pr_err("%pfw mismatch between \"%s\" and \"%s\" property count (%d vs %d)\n", + fwnode, polarity_prop, names_prop, n_pols, n_names); + return -EINVAL; + } + + if (mode_name) + idx = fwnode_property_match_string(fwnode, names_prop, mode_name); + if (idx < 0) + idx = fwnode_property_match_string(fwnode, names_prop, "default"); + /* + * If the mode name is missing, it can only mean the specified polarity + * is the default one for all modes, so reject any other polarity count + * than 1. + */ + if (idx < 0 && n_pols != 1) { + pr_err("%pfw \"%s \" property has %d elements, but cannot find \"%s\" in \"%s\" and there is no default value\n", + fwnode, polarity_prop, n_pols, mode_name, names_prop); + return -EINVAL; + } + + if (n_pols == 1) { + err = fwnode_property_read_u32(fwnode, polarity_prop, &val); + if (err) + return err; + + return val; + } + + /* We implicitly know idx >= 0 here */ + pols = kcalloc(n_pols, sizeof(*pols), GFP_KERNEL); + if (!pols) + return -ENOMEM; + + err = fwnode_property_read_u32_array(fwnode, polarity_prop, pols, n_pols); + if (err == 0) { + val = pols[idx]; + if (!(supported & BIT(val))) { + pr_err("%pfw mismatch between '%s' and '%s' property count (%d vs %d)\n", + fwnode, polarity_prop, names_prop, n_pols, n_names); + err = -EOPNOTSUPP; + } + } + + kfree(pols); + + return (err < 0) ? err : val; +} + +/** + * phy_get_rx_polarity - Get RX polarity for PHY differential lane + * @fwnode: Pointer to the PHY's firmware node. + * @mode_name: The name of the PHY mode to look up. + * @supported: Bit mask of PHY_POL_NORMAL, PHY_POL_INVERT and PHY_POL_AUTO + * @default_val: Default polarity value if property is missing + * + * Return: One of PHY_POL_NORMAL, PHY_POL_INVERT or PHY_POL_AUTO on success, or + * negative error on failure. + */ +int phy_get_rx_polarity(struct fwnode_handle *fwnode, const char *mode_name, + unsigned int supported, unsigned int default_val) +{ + return phy_get_polarity_for_mode(fwnode, mode_name, supported, + default_val, "rx-polarity", + "rx-polarity-names"); +} +EXPORT_SYMBOL_GPL(phy_get_rx_polarity); + +/** + * phy_get_tx_polarity - Get TX polarity for PHY differential lane + * @fwnode: Pointer to the PHY's firmware node. + * @mode_name: The name of the PHY mode to look up. + * @supported: Bit mask of PHY_POL_NORMAL and PHY_POL_INVERT + * @default_val: Default polarity value if property is missing + * + * Return: One of PHY_POL_NORMAL or PHY_POL_INVERT on success, or negative + * error on failure. + */ +int phy_get_tx_polarity(struct fwnode_handle *fwnode, const char *mode_name, + unsigned int supported, unsigned int default_val) +{ + return phy_get_polarity_for_mode(fwnode, mode_name, supported, + default_val, "tx-polarity", + "tx-polarity-names"); +} +EXPORT_SYMBOL_GPL(phy_get_tx_polarity); diff --git a/include/linux/phy/phy-common-props.h b/include/linux/phy/phy-common-props.h new file mode 100644 index 000000000000..0b8ba76e2a15 --- /dev/null +++ b/include/linux/phy/phy-common-props.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * phy-common-props.h -- Common properties for generic PHYs + * + * Copyright 2025 NXP + */ + +#ifndef __PHY_COMMON_PROPS_H +#define __PHY_COMMON_PROPS_H + +#include + +struct fwnode_handle; + +int phy_get_rx_polarity(struct fwnode_handle *fwnode, const char *mode_name, + unsigned int supported, unsigned int default_val); +int phy_get_tx_polarity(struct fwnode_handle *fwnode, const char *mode_name, + unsigned int supported, unsigned int default_val); + +#endif /* __PHY_COMMON_PROPS_H */ -- 2.34.1 The SJA1105 'PMA' code is actually PCS code to adapt to a custom PMA as present in NXP SJA1105, that wants opposite differential lane polarity in the TX direction, to account for an internal quirk. We should write to the DW_VR_MII_DIG_CTRL2 PCS register from PCS code, especially since the XPCS is about to gain more freeform support to alter the lane polarity in the RX and TX directions. The compat->pma_config() interface is kept for SJA1110, but is now wrapped around a xpcs_pma_config() that handles SJA1105 as a quirk implemented in common code. Signed-off-by: Vladimir Oltean --- drivers/net/pcs/pcs-xpcs-nxp.c | 11 ---------- drivers/net/pcs/pcs-xpcs.c | 37 ++++++++++++++++++++++++++-------- drivers/net/pcs/pcs-xpcs.h | 2 +- 3 files changed, 30 insertions(+), 20 deletions(-) diff --git a/drivers/net/pcs/pcs-xpcs-nxp.c b/drivers/net/pcs/pcs-xpcs-nxp.c index e8efe94cf4ec..37708b28a7aa 100644 --- a/drivers/net/pcs/pcs-xpcs-nxp.c +++ b/drivers/net/pcs/pcs-xpcs-nxp.c @@ -64,17 +64,6 @@ /* RX_CDR_CTLE register */ #define SJA1110_RX_CDR_CTLE 0x8042 -/* 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); -} - static int nxp_sja1110_pma_config(struct dw_xpcs *xpcs, u16 txpll_fbdiv, u16 txpll_refdiv, u16 rxpll_fbdiv, u16 rxpll_refdiv, diff --git a/drivers/net/pcs/pcs-xpcs.c b/drivers/net/pcs/pcs-xpcs.c index 3d1bd5aac093..670441186cc6 100644 --- a/drivers/net/pcs/pcs-xpcs.c +++ b/drivers/net/pcs/pcs-xpcs.c @@ -808,6 +808,26 @@ static int xpcs_config_2500basex(struct dw_xpcs *xpcs) BMCR_SPEED1000); } +static int xpcs_pma_config(struct dw_xpcs *xpcs, const struct dw_xpcs_compat *compat) +{ + int ret; + + if (xpcs->need_opposite_tx_polarity) { + ret = xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_DIG_CTRL2, + DW_VR_MII_DIG_CTRL2_TX_POL_INV); + if (ret) + return ret; + } + + if (compat->pma_config) { + ret = compat->pma_config(xpcs); + if (ret) + return ret; + } + + return 0; +} + static int xpcs_do_config(struct dw_xpcs *xpcs, phy_interface_t interface, const unsigned long *advertising, unsigned int neg_mode) @@ -859,13 +879,7 @@ static int xpcs_do_config(struct dw_xpcs *xpcs, phy_interface_t interface, return -EINVAL; } - if (compat->pma_config) { - ret = compat->pma_config(xpcs); - if (ret) - return ret; - } - - return 0; + return xpcs_pma_config(xpcs, compat); } static int xpcs_config(struct phylink_pcs *pcs, unsigned int neg_mode, @@ -1341,7 +1355,6 @@ static const struct dw_xpcs_compat nxp_sja1105_xpcs_compat[] = { .interface = PHY_INTERFACE_MODE_SGMII, .supported = xpcs_sgmii_features, .an_mode = DW_AN_C37_SGMII, - .pma_config = nxp_sja1105_sgmii_pma_config, }, { } }; @@ -1500,6 +1513,14 @@ static struct dw_xpcs *xpcs_create(struct mdio_device *mdiodev) else xpcs->need_reset = true; + /* 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. + */ + if (xpcs->desc->compat == nxp_sja1105_xpcs_compat) + xpcs->need_opposite_tx_polarity = true; + return xpcs; out_clear_clks: diff --git a/drivers/net/pcs/pcs-xpcs.h b/drivers/net/pcs/pcs-xpcs.h index 929fa238445e..2a92e101da1b 100644 --- a/drivers/net/pcs/pcs-xpcs.h +++ b/drivers/net/pcs/pcs-xpcs.h @@ -113,6 +113,7 @@ struct dw_xpcs { struct phylink_pcs pcs; phy_interface_t interface; bool need_reset; + bool need_opposite_tx_polarity; u8 eee_mult_fact; }; @@ -121,7 +122,6 @@ int xpcs_write(struct dw_xpcs *xpcs, int dev, u32 reg, u16 val); int xpcs_modify(struct dw_xpcs *xpcs, int dev, u32 reg, u16 mask, u16 set); 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); -- 2.34.1 Using the linux/phy/phy-common-props.h helpers, get the 'rx-polarity' and 'tx-polarity' device tree properties, and apply them to hardware in the newly introduced xpcs_pma_config(), called from phylink_pcs_ops :: pcs_config(). This is the right place to do it, as the generic PHY helpers require knowing the phy_interface_t for which we want the polarity known, and that comes from phylink. Default to PHY_POL_NORMAL, and support normal and inverted polarities in the RX and TX directions. Note that for SJA1105, 'normal' in the TX direction is inverted in the PCS, and 'inverted' is 'normal' in the PCS. This is because the device tree property refers to the effect as visible at the device's pinout. Signed-off-by: Vladimir Oltean --- drivers/net/pcs/Kconfig | 1 + drivers/net/pcs/pcs-xpcs.c | 33 +++++++++++++++++++++++++++------ 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/drivers/net/pcs/Kconfig b/drivers/net/pcs/Kconfig index ecbc3530e780..3598747d6c53 100644 --- a/drivers/net/pcs/Kconfig +++ b/drivers/net/pcs/Kconfig @@ -8,6 +8,7 @@ menu "PCS device drivers" config PCS_XPCS tristate "Synopsys DesignWare Ethernet XPCS" select PHYLINK + select GENERIC_PHY_COMMON_PROPS help This module provides a driver and helper functions for Synopsys DesignWare XPCS controllers. diff --git a/drivers/net/pcs/pcs-xpcs.c b/drivers/net/pcs/pcs-xpcs.c index 670441186cc6..7625dc29d2ee 100644 --- a/drivers/net/pcs/pcs-xpcs.c +++ b/drivers/net/pcs/pcs-xpcs.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -810,14 +811,34 @@ static int xpcs_config_2500basex(struct dw_xpcs *xpcs) static int xpcs_pma_config(struct dw_xpcs *xpcs, const struct dw_xpcs_compat *compat) { + struct fwnode_handle *fwnode = dev_fwnode(&xpcs->mdiodev->dev); + u32 val = 0, mask; + int pol; int ret; - if (xpcs->need_opposite_tx_polarity) { - ret = xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_DIG_CTRL2, - DW_VR_MII_DIG_CTRL2_TX_POL_INV); - if (ret) - return ret; - } + mask = DW_VR_MII_DIG_CTRL2_TX_POL_INV | DW_VR_MII_DIG_CTRL2_RX_POL_INV; + + pol = phy_get_rx_polarity(fwnode, phy_modes(compat->interface), + PHY_POL_NORMAL | PHY_POL_INVERT, + PHY_POL_NORMAL); + if (pol < 0) + return pol; + if (pol == PHY_POL_INVERT) + val |= DW_VR_MII_DIG_CTRL2_RX_POL_INV; + + pol = phy_get_tx_polarity(fwnode, phy_modes(compat->interface), + PHY_POL_NORMAL | PHY_POL_INVERT, + PHY_POL_NORMAL); + if (pol < 0) + return pol; + if (xpcs->need_opposite_tx_polarity) + pol = !pol; + if (pol == PHY_POL_INVERT) + val |= DW_VR_MII_DIG_CTRL2_TX_POL_INV; + + ret = xpcs_modify(xpcs, MDIO_MMD_VEND2, DW_VR_MII_DIG_CTRL2, mask, val); + if (ret < 0) + return ret; if (compat->pma_config) { ret = compat->pma_config(xpcs); -- 2.34.1 Prefer the new "rx-polarity" and "tx-polarity" properties, and use the vendor specific ones as fallback if the standard description doesn't exist. Signed-off-by: Vladimir Oltean --- drivers/net/phy/Kconfig | 1 + drivers/net/phy/air_en8811h.c | 50 ++++++++++++++++++++++++----------- 2 files changed, 36 insertions(+), 15 deletions(-) diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index a7ade7b95a2e..7b73332a13d9 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -98,6 +98,7 @@ config AS21XXX_PHY config AIR_EN8811H_PHY tristate "Airoha EN8811H 2.5 Gigabit PHY" + select PHY_COMMON_PROPS help Currently supports the Airoha EN8811H PHY. diff --git a/drivers/net/phy/air_en8811h.c b/drivers/net/phy/air_en8811h.c index badd65f0ccee..4171fecb1def 100644 --- a/drivers/net/phy/air_en8811h.c +++ b/drivers/net/phy/air_en8811h.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -966,11 +967,42 @@ static int en8811h_probe(struct phy_device *phydev) return 0; } +static int en8811h_config_serdes_polarity(struct phy_device *phydev) +{ + struct device *dev = &phydev->mdio.dev; + int pol, default_pol; + u32 pbus_value = 0; + + default_pol = PHY_POL_NORMAL; + if (device_property_read_bool(dev, "airoha,pnswap-rx")) + default_pol = PHY_POL_INVERT; + + pol = phy_get_rx_polarity(dev_fwnode(dev), phy_modes(phydev->interface), + PHY_POL_NORMAL | PHY_POL_INVERT, default_pol); + if (pol < 0) + return pol; + if (pol == PHY_POL_INVERT) + pbus_value |= EN8811H_POLARITY_RX_REVERSE; + + default_pol = PHY_POL_NORMAL; + if (device_property_read_bool(dev, "airoha,pnswap-tx")) + default_pol = PHY_POL_INVERT; + + pol = phy_get_tx_polarity(dev_fwnode(dev), phy_modes(phydev->interface), + PHY_POL_NORMAL | PHY_POL_INVERT, default_pol); + if (pol < 0) + return pol; + if (pol == PHY_POL_NORMAL) + pbus_value |= EN8811H_POLARITY_TX_NORMAL; + + return air_buckpbus_reg_modify(phydev, EN8811H_POLARITY, + EN8811H_POLARITY_RX_REVERSE | + EN8811H_POLARITY_TX_NORMAL, pbus_value); +} + static int en8811h_config_init(struct phy_device *phydev) { struct en8811h_priv *priv = phydev->priv; - struct device *dev = &phydev->mdio.dev; - u32 pbus_value; int ret; /* If restart happened in .probe(), no need to restart now */ @@ -1003,19 +1035,7 @@ static int en8811h_config_init(struct phy_device *phydev) if (ret < 0) return ret; - /* Serdes polarity */ - pbus_value = 0; - if (device_property_read_bool(dev, "airoha,pnswap-rx")) - pbus_value |= EN8811H_POLARITY_RX_REVERSE; - else - pbus_value &= ~EN8811H_POLARITY_RX_REVERSE; - if (device_property_read_bool(dev, "airoha,pnswap-tx")) - pbus_value &= ~EN8811H_POLARITY_TX_NORMAL; - else - pbus_value |= EN8811H_POLARITY_TX_NORMAL; - ret = air_buckpbus_reg_modify(phydev, EN8811H_POLARITY, - EN8811H_POLARITY_RX_REVERSE | - EN8811H_POLARITY_TX_NORMAL, pbus_value); + ret = en8811h_config_serdes_polarity(phydev); if (ret < 0) return ret; -- 2.34.1 Reference the common PHY properties, and update the example to use them. Signed-off-by: Vladimir Oltean --- .../devicetree/bindings/net/airoha,en8811h.yaml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/net/airoha,en8811h.yaml b/Documentation/devicetree/bindings/net/airoha,en8811h.yaml index ecb5149ec6b0..0de6e9284fbc 100644 --- a/Documentation/devicetree/bindings/net/airoha,en8811h.yaml +++ b/Documentation/devicetree/bindings/net/airoha,en8811h.yaml @@ -16,6 +16,7 @@ description: allOf: - $ref: ethernet-phy.yaml# + - $ref: /schemas/phy/phy-common-props.yaml# properties: compatible: @@ -30,12 +31,18 @@ properties: description: Reverse rx polarity of the SERDES. This is the receiving side of the lines from the MAC towards the EN881H. + This property is deprecated, for details please refer to + Documentation/devicetree/bindings/phy/phy-common-props.yaml + deprecated: true airoha,pnswap-tx: type: boolean description: Reverse tx polarity of SERDES. This is the transmitting side of the lines from EN8811H towards the MAC. + This property is deprecated, for details please refer to + Documentation/devicetree/bindings/phy/phy-common-props.yaml + deprecated: true required: - reg @@ -44,6 +51,8 @@ unevaluatedProperties: false examples: - | + #include + mdio { #address-cells = <1>; #size-cells = <0>; @@ -51,6 +60,6 @@ examples: ethernet-phy@1 { compatible = "ethernet-phy-id03a2.a411"; reg = <1>; - airoha,pnswap-rx; + rx-polarity = ; }; }; -- 2.34.1