mirror of
https://github.com/torvalds/linux.git
synced 2026-03-08 03:24:45 +01:00
phy common properties
Vladimir Oltean <vladimir.oltean@nxp.com> wrote: Introduce "rx-polarity" and "tx-polarity" device tree properties with Kunit tests -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEE+vs47OPLdNbVcHzyfBQHDyUjg0cFAmlnl3IACgkQfBQHDyUj g0dz1Q/+L0d0rin2AtgetIIn5zv/OhMAcDo55eJ1fktBFDMBW/M1SIj+fCJ+kxsc t4MWBxoiwRFxnZCV3SrxuOMsGfGRINXlTicC9j9PDY+Z+IsCrZhL3LOyJQuV3FgQ +Dht+OUhDr5y4jNVh5af/oD92dBYDwlyUP46Bem1cSVnDdXJ2kOEpbxOnKfW6oNK leif7ufahVZMHNbMUq7mu7ro95FpUV848fTbEBW4VhelLjg8qxZWYFsdiEMwshYw vr7pJShph9o86DBAmvu43eR6dSgQl1fD2PEQXVYfrjQ1hyEPEZugLmLskd1gdvj2 rEClo1VNuruAPvgMuwifVDt8RjdABfu15GY6x/W+l3Nqx6SQCNZVJdLGRYXs+Qq4 e5GCWhG1xtzeGDVNr12Ynk4/VF+nwjrJ+57SEV/vjfnZKlzg4nZD31+qAH5uDtrK Gc1TCCqsUTg806XGKB9h+6r8fAspgmEe+cur2TrEeFZJ3GQrIJTBfgmXZ5hJZsWn TW02+JfjLk/I9nvBLfyNbiZgpOFppnQCYk5bGPxfQxJfibhIO0SpVFigGRX8Pgb+ 0g7IAyl9AEatterhtOB7QWiTPNOxIlQPFr5IM+8wCRUC+tdr7YCwARZexXUyokOz CKNUCSsBofGTK9sGlaWfxWS1i6mYGTbPklrXiYoPv/K8wDcIQyo= =aAVU -----END PGP SIGNATURE----- Merge tag 'phy_common_properties' into next phy common properties Vladimir Oltean <vladimir.oltean@nxp.com> wrote: Introduce "rx-polarity" and "tx-polarity" device tree properties with Kunit tests
This commit is contained in:
commit
a699808928
9 changed files with 858 additions and 103 deletions
157
Documentation/devicetree/bindings/phy/phy-common-props.yaml
Normal file
157
Documentation/devicetree/bindings/phy/phy-common-props.yaml
Normal file
|
|
@ -0,0 +1,157 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/phy/phy-common-props.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Common PHY and network PCS properties
|
||||
|
||||
description:
|
||||
Common PHY and network PCS properties, such as peak-to-peak transmit
|
||||
amplitude.
|
||||
|
||||
maintainers:
|
||||
- Marek Behún <kabel@kernel.org>
|
||||
|
||||
$defs:
|
||||
protocol-names:
|
||||
description:
|
||||
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
|
||||
uniqueItems: true
|
||||
items:
|
||||
enum:
|
||||
- default
|
||||
|
||||
# ethernet modes
|
||||
- sgmii
|
||||
- qsgmii
|
||||
- xgmii
|
||||
- 1000base-x
|
||||
- 2500base-x
|
||||
- 5gbase-r
|
||||
- rxaui
|
||||
- xaui
|
||||
- 10gbase-kr
|
||||
- usxgmii
|
||||
- 10gbase-r
|
||||
- 25gbase-r
|
||||
|
||||
# PCIe modes
|
||||
- pcie
|
||||
- pcie1
|
||||
- pcie2
|
||||
- pcie3
|
||||
- pcie4
|
||||
- pcie5
|
||||
- pcie6
|
||||
|
||||
# USB modes
|
||||
- usb
|
||||
- usb-ls
|
||||
- usb-fs
|
||||
- usb-hs
|
||||
- usb-ss
|
||||
- usb-ss+
|
||||
- usb-4
|
||||
|
||||
# storage modes
|
||||
- sata
|
||||
- ufs-hs
|
||||
- ufs-hs-a
|
||||
- ufs-hs-b
|
||||
|
||||
# display modes
|
||||
- lvds
|
||||
- dp
|
||||
- dp-rbr
|
||||
- dp-hbr
|
||||
- dp-hbr2
|
||||
- dp-hbr3
|
||||
- dp-uhbr-10
|
||||
- dp-uhbr-13.5
|
||||
- dp-uhbr-20
|
||||
|
||||
# camera modes
|
||||
- mipi-dphy
|
||||
- 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"
|
||||
|
||||
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 input, and the positive signal from the RXP input;
|
||||
PHY_POL_INVERT (1) which means the negative signal is decoded from the
|
||||
RXP input, and the positive signal from the RXN input;
|
||||
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 <dt-bindings/phy/phy.h>. If the property is
|
||||
absent, the default value is undefined.
|
||||
|
||||
Note that the RXP and RXN inputs refer to the block that this property is
|
||||
under, and do not necessarily directly translate to external pins.
|
||||
|
||||
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 <dt-bindings/phy/phy.h>
|
||||
|
||||
phy: phy {
|
||||
#phy-cells = <1>;
|
||||
tx-p2p-microvolt = <915000>, <1100000>, <1200000>;
|
||||
tx-p2p-microvolt-names = "2500base-x", "usb-hs", "usb-ss";
|
||||
rx-polarity = <PHY_POL_AUTO>, <PHY_POL_NORMAL>;
|
||||
rx-polarity-names = "usb-ss", "default";
|
||||
tx-polarity = <PHY_POL_INVERT>;
|
||||
};
|
||||
|
|
@ -1,103 +0,0 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/phy/transmit-amplitude.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Common PHY and network PCS transmit amplitude property
|
||||
|
||||
description:
|
||||
Binding describing the peak-to-peak transmit amplitude for common PHYs
|
||||
and network PCSes.
|
||||
|
||||
maintainers:
|
||||
- Marek Behún <kabel@kernel.org>
|
||||
|
||||
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.
|
||||
|
||||
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:
|
||||
enum:
|
||||
- default
|
||||
|
||||
# ethernet modes
|
||||
- sgmii
|
||||
- qsgmii
|
||||
- xgmii
|
||||
- 1000base-x
|
||||
- 2500base-x
|
||||
- 5gbase-r
|
||||
- rxaui
|
||||
- xaui
|
||||
- 10gbase-kr
|
||||
- usxgmii
|
||||
- 10gbase-r
|
||||
- 25gbase-r
|
||||
|
||||
# PCIe modes
|
||||
- pcie
|
||||
- pcie1
|
||||
- pcie2
|
||||
- pcie3
|
||||
- pcie4
|
||||
- pcie5
|
||||
- pcie6
|
||||
|
||||
# USB modes
|
||||
- usb
|
||||
- usb-ls
|
||||
- usb-fs
|
||||
- usb-hs
|
||||
- usb-ss
|
||||
- usb-ss+
|
||||
- usb-4
|
||||
|
||||
# storage modes
|
||||
- sata
|
||||
- ufs-hs
|
||||
- ufs-hs-a
|
||||
- ufs-hs-b
|
||||
|
||||
# display modes
|
||||
- lvds
|
||||
- dp
|
||||
- dp-rbr
|
||||
- dp-hbr
|
||||
- dp-hbr2
|
||||
- dp-hbr3
|
||||
- dp-uhbr-10
|
||||
- dp-uhbr-13.5
|
||||
- dp-uhbr-20
|
||||
|
||||
# camera modes
|
||||
- mipi-dphy
|
||||
- mipi-dphy-univ
|
||||
- mipi-dphy-v2.5-univ
|
||||
|
||||
dependencies:
|
||||
tx-p2p-microvolt-names: [ tx-p2p-microvolt ]
|
||||
|
||||
additionalProperties: true
|
||||
|
||||
examples:
|
||||
- |
|
||||
phy: phy {
|
||||
#phy-cells = <1>;
|
||||
tx-p2p-microvolt = <915000>, <1100000>, <1200000>;
|
||||
tx-p2p-microvolt-names = "2500base-x", "usb-hs", "usb-ss";
|
||||
};
|
||||
10
MAINTAINERS
10
MAINTAINERS
|
|
@ -20517,6 +20517,16 @@ L: linux-mtd@lists.infradead.org
|
|||
S: Maintained
|
||||
F: drivers/mtd/devices/phram.c
|
||||
|
||||
PHY COMMON PROPERTIES
|
||||
M: Vladimir Oltean <vladimir.oltean@nxp.com>
|
||||
L: netdev@vger.kernel.org
|
||||
S: Maintained
|
||||
Q: https://patchwork.kernel.org/project/netdevbpf/list/
|
||||
F: Documentation/devicetree/bindings/phy/phy-common-props.yaml
|
||||
F: drivers/phy/phy-common-props-test.c
|
||||
F: drivers/phy/phy-common-props.c
|
||||
F: include/linux/phy/phy-common-props.h
|
||||
|
||||
PICOLCD HID DRIVER
|
||||
M: Bruno Prémont <bonbons@linux-vserver.org>
|
||||
L: linux-input@vger.kernel.org
|
||||
|
|
|
|||
|
|
@ -5,6 +5,28 @@
|
|||
|
||||
menu "PHY Subsystem"
|
||||
|
||||
config PHY_COMMON_PROPS
|
||||
bool
|
||||
help
|
||||
This parses properties common between generic PHYs and Ethernet PHYs.
|
||||
|
||||
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 PHY_COMMON_PROPS_TEST
|
||||
tristate "KUnit tests for PHY common props" if !KUNIT_ALL_TESTS
|
||||
select PHY_COMMON_PROPS
|
||||
depends on KUNIT
|
||||
default KUNIT_ALL_TESTS
|
||||
help
|
||||
This builds KUnit tests for the PHY common property API.
|
||||
|
||||
For more information on KUnit and unit tests in general,
|
||||
please refer to the KUnit documentation in Documentation/dev-tools/kunit/.
|
||||
|
||||
When in doubt, say N.
|
||||
|
||||
config GENERIC_PHY
|
||||
bool "PHY Core"
|
||||
help
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@
|
|||
# Makefile for the phy drivers.
|
||||
#
|
||||
|
||||
obj-$(CONFIG_PHY_COMMON_PROPS) += phy-common-props.o
|
||||
obj-$(CONFIG_PHY_COMMON_PROPS_TEST) += phy-common-props-test.o
|
||||
obj-$(CONFIG_GENERIC_PHY) += phy-core.o
|
||||
obj-$(CONFIG_GENERIC_PHY_MIPI_DPHY) += phy-core-mipi-dphy.o
|
||||
obj-$(CONFIG_PHY_CAN_TRANSCEIVER) += phy-can-transceiver.o
|
||||
|
|
|
|||
422
drivers/phy/phy-common-props-test.c
Normal file
422
drivers/phy/phy-common-props-test.c
Normal file
|
|
@ -0,0 +1,422 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* phy-common-props-test.c -- Unit tests for PHY common properties API
|
||||
*
|
||||
* Copyright 2025-2026 NXP
|
||||
*/
|
||||
#include <kunit/test.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/phy/phy-common-props.h>
|
||||
#include <dt-bindings/phy/phy.h>
|
||||
|
||||
/* Test: rx-polarity property is missing */
|
||||
static void phy_test_rx_polarity_is_missing(struct kunit *test)
|
||||
{
|
||||
static const struct property_entry entries[] = {
|
||||
{}
|
||||
};
|
||||
struct fwnode_handle *node;
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
node = fwnode_create_software_node(entries, NULL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, node);
|
||||
|
||||
ret = phy_get_manual_rx_polarity(node, "sgmii", &val);
|
||||
KUNIT_EXPECT_EQ(test, ret, 0);
|
||||
KUNIT_EXPECT_EQ(test, val, PHY_POL_NORMAL);
|
||||
|
||||
fwnode_remove_software_node(node);
|
||||
}
|
||||
|
||||
/* Test: rx-polarity has more values than rx-polarity-names */
|
||||
static void phy_test_rx_polarity_more_values_than_names(struct kunit *test)
|
||||
{
|
||||
static const u32 rx_pol[] = { PHY_POL_NORMAL, PHY_POL_INVERT, PHY_POL_NORMAL };
|
||||
static const char * const rx_pol_names[] = { "sgmii", "2500base-x" };
|
||||
static const struct property_entry entries[] = {
|
||||
PROPERTY_ENTRY_U32_ARRAY("rx-polarity", rx_pol),
|
||||
PROPERTY_ENTRY_STRING_ARRAY("rx-polarity-names", rx_pol_names),
|
||||
{}
|
||||
};
|
||||
struct fwnode_handle *node;
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
node = fwnode_create_software_node(entries, NULL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, node);
|
||||
|
||||
ret = phy_get_manual_rx_polarity(node, "sgmii", &val);
|
||||
KUNIT_EXPECT_EQ(test, ret, -EINVAL);
|
||||
|
||||
fwnode_remove_software_node(node);
|
||||
}
|
||||
|
||||
/* Test: rx-polarity has 1 value and rx-polarity-names does not exist */
|
||||
static void phy_test_rx_polarity_single_value_no_names(struct kunit *test)
|
||||
{
|
||||
static const u32 rx_pol[] = { PHY_POL_INVERT };
|
||||
static const struct property_entry entries[] = {
|
||||
PROPERTY_ENTRY_U32_ARRAY("rx-polarity", rx_pol),
|
||||
{}
|
||||
};
|
||||
struct fwnode_handle *node;
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
node = fwnode_create_software_node(entries, NULL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, node);
|
||||
|
||||
ret = phy_get_manual_rx_polarity(node, "sgmii", &val);
|
||||
KUNIT_EXPECT_EQ(test, ret, 0);
|
||||
KUNIT_EXPECT_EQ(test, val, PHY_POL_INVERT);
|
||||
|
||||
fwnode_remove_software_node(node);
|
||||
}
|
||||
|
||||
/* Test: rx-polarity-names has more values than rx-polarity */
|
||||
static void phy_test_rx_polarity_more_names_than_values(struct kunit *test)
|
||||
{
|
||||
static const u32 rx_pol[] = { PHY_POL_NORMAL, PHY_POL_INVERT };
|
||||
static const char * const rx_pol_names[] = { "sgmii", "2500base-x", "1000base-x" };
|
||||
static const struct property_entry entries[] = {
|
||||
PROPERTY_ENTRY_U32_ARRAY("rx-polarity", rx_pol),
|
||||
PROPERTY_ENTRY_STRING_ARRAY("rx-polarity-names", rx_pol_names),
|
||||
{}
|
||||
};
|
||||
struct fwnode_handle *node;
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
node = fwnode_create_software_node(entries, NULL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, node);
|
||||
|
||||
ret = phy_get_manual_rx_polarity(node, "sgmii", &val);
|
||||
KUNIT_EXPECT_EQ(test, ret, -EINVAL);
|
||||
|
||||
fwnode_remove_software_node(node);
|
||||
}
|
||||
|
||||
/* Test: rx-polarity and rx-polarity-names have same length, find the name */
|
||||
static void phy_test_rx_polarity_find_by_name(struct kunit *test)
|
||||
{
|
||||
static const u32 rx_pol[] = { PHY_POL_NORMAL, PHY_POL_INVERT, PHY_POL_AUTO };
|
||||
static const char * const rx_pol_names[] = { "sgmii", "2500base-x", "usb-ss" };
|
||||
static const struct property_entry entries[] = {
|
||||
PROPERTY_ENTRY_U32_ARRAY("rx-polarity", rx_pol),
|
||||
PROPERTY_ENTRY_STRING_ARRAY("rx-polarity-names", rx_pol_names),
|
||||
{}
|
||||
};
|
||||
struct fwnode_handle *node;
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
node = fwnode_create_software_node(entries, NULL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, node);
|
||||
|
||||
ret = phy_get_manual_rx_polarity(node, "sgmii", &val);
|
||||
KUNIT_EXPECT_EQ(test, ret, 0);
|
||||
KUNIT_EXPECT_EQ(test, val, PHY_POL_NORMAL);
|
||||
|
||||
ret = phy_get_manual_rx_polarity(node, "2500base-x", &val);
|
||||
KUNIT_EXPECT_EQ(test, ret, 0);
|
||||
KUNIT_EXPECT_EQ(test, val, PHY_POL_INVERT);
|
||||
|
||||
ret = phy_get_rx_polarity(node, "usb-ss", BIT(PHY_POL_AUTO),
|
||||
PHY_POL_AUTO, &val);
|
||||
KUNIT_EXPECT_EQ(test, ret, 0);
|
||||
KUNIT_EXPECT_EQ(test, val, PHY_POL_AUTO);
|
||||
|
||||
fwnode_remove_software_node(node);
|
||||
}
|
||||
|
||||
/* Test: same length, name not found, no "default" - error */
|
||||
static void phy_test_rx_polarity_name_not_found_no_default(struct kunit *test)
|
||||
{
|
||||
static const u32 rx_pol[] = { PHY_POL_NORMAL, PHY_POL_INVERT };
|
||||
static const char * const rx_pol_names[] = { "2500base-x", "1000base-x" };
|
||||
static const struct property_entry entries[] = {
|
||||
PROPERTY_ENTRY_U32_ARRAY("rx-polarity", rx_pol),
|
||||
PROPERTY_ENTRY_STRING_ARRAY("rx-polarity-names", rx_pol_names),
|
||||
{}
|
||||
};
|
||||
struct fwnode_handle *node;
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
node = fwnode_create_software_node(entries, NULL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, node);
|
||||
|
||||
ret = phy_get_manual_rx_polarity(node, "sgmii", &val);
|
||||
KUNIT_EXPECT_EQ(test, ret, -EINVAL);
|
||||
|
||||
fwnode_remove_software_node(node);
|
||||
}
|
||||
|
||||
/* Test: same length, name not found, but "default" exists */
|
||||
static void phy_test_rx_polarity_name_not_found_with_default(struct kunit *test)
|
||||
{
|
||||
static const u32 rx_pol[] = { PHY_POL_NORMAL, PHY_POL_INVERT };
|
||||
static const char * const rx_pol_names[] = { "2500base-x", "default" };
|
||||
static const struct property_entry entries[] = {
|
||||
PROPERTY_ENTRY_U32_ARRAY("rx-polarity", rx_pol),
|
||||
PROPERTY_ENTRY_STRING_ARRAY("rx-polarity-names", rx_pol_names),
|
||||
{}
|
||||
};
|
||||
struct fwnode_handle *node;
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
node = fwnode_create_software_node(entries, NULL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, node);
|
||||
|
||||
ret = phy_get_manual_rx_polarity(node, "sgmii", &val);
|
||||
KUNIT_EXPECT_EQ(test, ret, 0);
|
||||
KUNIT_EXPECT_EQ(test, val, PHY_POL_INVERT);
|
||||
|
||||
fwnode_remove_software_node(node);
|
||||
}
|
||||
|
||||
/* Test: polarity found but value is unsupported */
|
||||
static void phy_test_rx_polarity_unsupported_value(struct kunit *test)
|
||||
{
|
||||
static const u32 rx_pol[] = { PHY_POL_AUTO };
|
||||
static const char * const rx_pol_names[] = { "sgmii" };
|
||||
static const struct property_entry entries[] = {
|
||||
PROPERTY_ENTRY_U32_ARRAY("rx-polarity", rx_pol),
|
||||
PROPERTY_ENTRY_STRING_ARRAY("rx-polarity-names", rx_pol_names),
|
||||
{}
|
||||
};
|
||||
struct fwnode_handle *node;
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
node = fwnode_create_software_node(entries, NULL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, node);
|
||||
|
||||
ret = phy_get_manual_rx_polarity(node, "sgmii", &val);
|
||||
KUNIT_EXPECT_EQ(test, ret, -EOPNOTSUPP);
|
||||
|
||||
fwnode_remove_software_node(node);
|
||||
}
|
||||
|
||||
/* Test: tx-polarity property is missing */
|
||||
static void phy_test_tx_polarity_is_missing(struct kunit *test)
|
||||
{
|
||||
static const struct property_entry entries[] = {
|
||||
{}
|
||||
};
|
||||
struct fwnode_handle *node;
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
node = fwnode_create_software_node(entries, NULL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, node);
|
||||
|
||||
ret = phy_get_manual_tx_polarity(node, "sgmii", &val);
|
||||
KUNIT_EXPECT_EQ(test, ret, 0);
|
||||
KUNIT_EXPECT_EQ(test, val, PHY_POL_NORMAL);
|
||||
|
||||
fwnode_remove_software_node(node);
|
||||
}
|
||||
|
||||
/* Test: tx-polarity has more values than tx-polarity-names */
|
||||
static void phy_test_tx_polarity_more_values_than_names(struct kunit *test)
|
||||
{
|
||||
static const u32 tx_pol[] = { PHY_POL_NORMAL, PHY_POL_INVERT, PHY_POL_NORMAL };
|
||||
static const char * const tx_pol_names[] = { "sgmii", "2500base-x" };
|
||||
static const struct property_entry entries[] = {
|
||||
PROPERTY_ENTRY_U32_ARRAY("tx-polarity", tx_pol),
|
||||
PROPERTY_ENTRY_STRING_ARRAY("tx-polarity-names", tx_pol_names),
|
||||
{}
|
||||
};
|
||||
struct fwnode_handle *node;
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
node = fwnode_create_software_node(entries, NULL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, node);
|
||||
|
||||
ret = phy_get_manual_tx_polarity(node, "sgmii", &val);
|
||||
KUNIT_EXPECT_EQ(test, ret, -EINVAL);
|
||||
|
||||
fwnode_remove_software_node(node);
|
||||
}
|
||||
|
||||
/* Test: tx-polarity has 1 value and tx-polarity-names does not exist */
|
||||
static void phy_test_tx_polarity_single_value_no_names(struct kunit *test)
|
||||
{
|
||||
static const u32 tx_pol[] = { PHY_POL_INVERT };
|
||||
static const struct property_entry entries[] = {
|
||||
PROPERTY_ENTRY_U32_ARRAY("tx-polarity", tx_pol),
|
||||
{}
|
||||
};
|
||||
struct fwnode_handle *node;
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
node = fwnode_create_software_node(entries, NULL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, node);
|
||||
|
||||
ret = phy_get_manual_tx_polarity(node, "sgmii", &val);
|
||||
KUNIT_EXPECT_EQ(test, ret, 0);
|
||||
KUNIT_EXPECT_EQ(test, val, PHY_POL_INVERT);
|
||||
|
||||
fwnode_remove_software_node(node);
|
||||
}
|
||||
|
||||
/* Test: tx-polarity-names has more values than tx-polarity */
|
||||
static void phy_test_tx_polarity_more_names_than_values(struct kunit *test)
|
||||
{
|
||||
static const u32 tx_pol[] = { PHY_POL_NORMAL, PHY_POL_INVERT };
|
||||
static const char * const tx_pol_names[] = { "sgmii", "2500base-x", "1000base-x" };
|
||||
static const struct property_entry entries[] = {
|
||||
PROPERTY_ENTRY_U32_ARRAY("tx-polarity", tx_pol),
|
||||
PROPERTY_ENTRY_STRING_ARRAY("tx-polarity-names", tx_pol_names),
|
||||
{}
|
||||
};
|
||||
struct fwnode_handle *node;
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
node = fwnode_create_software_node(entries, NULL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, node);
|
||||
|
||||
ret = phy_get_manual_tx_polarity(node, "sgmii", &val);
|
||||
KUNIT_EXPECT_EQ(test, ret, -EINVAL);
|
||||
|
||||
fwnode_remove_software_node(node);
|
||||
}
|
||||
|
||||
/* Test: tx-polarity and tx-polarity-names have same length, find the name */
|
||||
static void phy_test_tx_polarity_find_by_name(struct kunit *test)
|
||||
{
|
||||
static const u32 tx_pol[] = { PHY_POL_NORMAL, PHY_POL_INVERT, PHY_POL_NORMAL };
|
||||
static const char * const tx_pol_names[] = { "sgmii", "2500base-x", "1000base-x" };
|
||||
static const struct property_entry entries[] = {
|
||||
PROPERTY_ENTRY_U32_ARRAY("tx-polarity", tx_pol),
|
||||
PROPERTY_ENTRY_STRING_ARRAY("tx-polarity-names", tx_pol_names),
|
||||
{}
|
||||
};
|
||||
struct fwnode_handle *node;
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
node = fwnode_create_software_node(entries, NULL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, node);
|
||||
|
||||
ret = phy_get_manual_tx_polarity(node, "sgmii", &val);
|
||||
KUNIT_EXPECT_EQ(test, ret, 0);
|
||||
KUNIT_EXPECT_EQ(test, val, PHY_POL_NORMAL);
|
||||
|
||||
ret = phy_get_manual_tx_polarity(node, "2500base-x", &val);
|
||||
KUNIT_EXPECT_EQ(test, ret, 0);
|
||||
KUNIT_EXPECT_EQ(test, val, PHY_POL_INVERT);
|
||||
|
||||
ret = phy_get_manual_tx_polarity(node, "1000base-x", &val);
|
||||
KUNIT_EXPECT_EQ(test, ret, 0);
|
||||
KUNIT_EXPECT_EQ(test, val, PHY_POL_NORMAL);
|
||||
|
||||
fwnode_remove_software_node(node);
|
||||
}
|
||||
|
||||
/* Test: same length, name not found, no "default" - error */
|
||||
static void phy_test_tx_polarity_name_not_found_no_default(struct kunit *test)
|
||||
{
|
||||
static const u32 tx_pol[] = { PHY_POL_NORMAL, PHY_POL_INVERT };
|
||||
static const char * const tx_pol_names[] = { "2500base-x", "1000base-x" };
|
||||
static const struct property_entry entries[] = {
|
||||
PROPERTY_ENTRY_U32_ARRAY("tx-polarity", tx_pol),
|
||||
PROPERTY_ENTRY_STRING_ARRAY("tx-polarity-names", tx_pol_names),
|
||||
{}
|
||||
};
|
||||
struct fwnode_handle *node;
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
node = fwnode_create_software_node(entries, NULL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, node);
|
||||
|
||||
ret = phy_get_manual_tx_polarity(node, "sgmii", &val);
|
||||
KUNIT_EXPECT_EQ(test, ret, -EINVAL);
|
||||
|
||||
fwnode_remove_software_node(node);
|
||||
}
|
||||
|
||||
/* Test: same length, name not found, but "default" exists */
|
||||
static void phy_test_tx_polarity_name_not_found_with_default(struct kunit *test)
|
||||
{
|
||||
static const u32 tx_pol[] = { PHY_POL_NORMAL, PHY_POL_INVERT };
|
||||
static const char * const tx_pol_names[] = { "2500base-x", "default" };
|
||||
static const struct property_entry entries[] = {
|
||||
PROPERTY_ENTRY_U32_ARRAY("tx-polarity", tx_pol),
|
||||
PROPERTY_ENTRY_STRING_ARRAY("tx-polarity-names", tx_pol_names),
|
||||
{}
|
||||
};
|
||||
struct fwnode_handle *node;
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
node = fwnode_create_software_node(entries, NULL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, node);
|
||||
|
||||
ret = phy_get_manual_tx_polarity(node, "sgmii", &val);
|
||||
KUNIT_EXPECT_EQ(test, ret, 0);
|
||||
KUNIT_EXPECT_EQ(test, val, PHY_POL_INVERT);
|
||||
|
||||
fwnode_remove_software_node(node);
|
||||
}
|
||||
|
||||
/* Test: polarity found but value is unsupported (AUTO for TX) */
|
||||
static void phy_test_tx_polarity_unsupported_value(struct kunit *test)
|
||||
{
|
||||
static const u32 tx_pol[] = { PHY_POL_AUTO };
|
||||
static const char * const tx_pol_names[] = { "sgmii" };
|
||||
static const struct property_entry entries[] = {
|
||||
PROPERTY_ENTRY_U32_ARRAY("tx-polarity", tx_pol),
|
||||
PROPERTY_ENTRY_STRING_ARRAY("tx-polarity-names", tx_pol_names),
|
||||
{}
|
||||
};
|
||||
struct fwnode_handle *node;
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
node = fwnode_create_software_node(entries, NULL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, node);
|
||||
|
||||
ret = phy_get_manual_tx_polarity(node, "sgmii", &val);
|
||||
KUNIT_EXPECT_EQ(test, ret, -EOPNOTSUPP);
|
||||
|
||||
fwnode_remove_software_node(node);
|
||||
}
|
||||
|
||||
static struct kunit_case phy_common_props_test_cases[] = {
|
||||
KUNIT_CASE(phy_test_rx_polarity_is_missing),
|
||||
KUNIT_CASE(phy_test_rx_polarity_more_values_than_names),
|
||||
KUNIT_CASE(phy_test_rx_polarity_single_value_no_names),
|
||||
KUNIT_CASE(phy_test_rx_polarity_more_names_than_values),
|
||||
KUNIT_CASE(phy_test_rx_polarity_find_by_name),
|
||||
KUNIT_CASE(phy_test_rx_polarity_name_not_found_no_default),
|
||||
KUNIT_CASE(phy_test_rx_polarity_name_not_found_with_default),
|
||||
KUNIT_CASE(phy_test_rx_polarity_unsupported_value),
|
||||
KUNIT_CASE(phy_test_tx_polarity_is_missing),
|
||||
KUNIT_CASE(phy_test_tx_polarity_more_values_than_names),
|
||||
KUNIT_CASE(phy_test_tx_polarity_single_value_no_names),
|
||||
KUNIT_CASE(phy_test_tx_polarity_more_names_than_values),
|
||||
KUNIT_CASE(phy_test_tx_polarity_find_by_name),
|
||||
KUNIT_CASE(phy_test_tx_polarity_name_not_found_no_default),
|
||||
KUNIT_CASE(phy_test_tx_polarity_name_not_found_with_default),
|
||||
KUNIT_CASE(phy_test_tx_polarity_unsupported_value),
|
||||
{}
|
||||
};
|
||||
|
||||
static struct kunit_suite phy_common_props_test_suite = {
|
||||
.name = "phy-common-props",
|
||||
.test_cases = phy_common_props_test_cases,
|
||||
};
|
||||
|
||||
kunit_test_suite(phy_common_props_test_suite);
|
||||
|
||||
MODULE_DESCRIPTION("Test module for PHY common properties API");
|
||||
MODULE_AUTHOR("Vladimir Oltean <vladimir.oltean@nxp.com>");
|
||||
MODULE_LICENSE("GPL");
|
||||
209
drivers/phy/phy-common-props.c
Normal file
209
drivers/phy/phy-common-props.c
Normal file
|
|
@ -0,0 +1,209 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* phy-common-props.c -- Common PHY properties
|
||||
*
|
||||
* Copyright 2025-2026 NXP
|
||||
*/
|
||||
#include <linux/export.h>
|
||||
#include <linux/fwnode.h>
|
||||
#include <linux/phy/phy-common-props.h>
|
||||
#include <linux/printk.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
/**
|
||||
* fwnode_get_u32_prop_for_name - Find u32 property by name, or default value
|
||||
* @fwnode: Pointer to firmware node, or NULL to use @default_val
|
||||
* @name: Property name used as lookup key in @names_title (must not be NULL)
|
||||
* @props_title: Name of u32 array property holding values
|
||||
* @names_title: Name of string array property holding lookup keys
|
||||
* @default_val: Default value if @fwnode is NULL or @props_title is empty
|
||||
* @val: Pointer to store the returned value
|
||||
*
|
||||
* This function retrieves a u32 value from @props_title based on a name lookup
|
||||
* in @names_title. The value stored in @val is determined as follows:
|
||||
*
|
||||
* - If @fwnode is NULL or @props_title is empty: @default_val is used
|
||||
* - If @props_title has exactly one element and @names_title is empty:
|
||||
* that element is used
|
||||
* - Otherwise: @val is set to the element at the same index where @name is
|
||||
* found in @names_title.
|
||||
* - If @name is not found, the function looks for a "default" entry in
|
||||
* @names_title and uses the corresponding value from @props_title
|
||||
*
|
||||
* When both @props_title and @names_title are present, they must have the
|
||||
* same number of elements (except when @props_title has exactly one element).
|
||||
*
|
||||
* Return: zero on success, negative error on failure.
|
||||
*/
|
||||
static int fwnode_get_u32_prop_for_name(struct fwnode_handle *fwnode,
|
||||
const char *name,
|
||||
const char *props_title,
|
||||
const char *names_title,
|
||||
unsigned int default_val,
|
||||
unsigned int *val)
|
||||
{
|
||||
int err, n_props, n_names, idx;
|
||||
u32 *props;
|
||||
|
||||
if (!name) {
|
||||
pr_err("Lookup key inside \"%s\" is mandatory\n", names_title);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
n_props = fwnode_property_count_u32(fwnode, props_title);
|
||||
if (n_props <= 0) {
|
||||
/* fwnode is NULL, or is missing requested property */
|
||||
*val = default_val;
|
||||
return 0;
|
||||
}
|
||||
|
||||
n_names = fwnode_property_string_array_count(fwnode, names_title);
|
||||
if (n_names >= 0 && n_props != n_names) {
|
||||
pr_err("%pfw mismatch between \"%s\" and \"%s\" property count (%d vs %d)\n",
|
||||
fwnode, props_title, names_title, n_props, n_names);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
idx = fwnode_property_match_string(fwnode, names_title, name);
|
||||
if (idx < 0)
|
||||
idx = fwnode_property_match_string(fwnode, names_title, "default");
|
||||
/*
|
||||
* If the mode name is missing, it can only mean the specified property
|
||||
* is the default one for all modes, so reject any other property count
|
||||
* than 1.
|
||||
*/
|
||||
if (idx < 0 && n_props != 1) {
|
||||
pr_err("%pfw \"%s \" property has %d elements, but cannot find \"%s\" in \"%s\" and there is no default value\n",
|
||||
fwnode, props_title, n_props, name, names_title);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (n_props == 1) {
|
||||
err = fwnode_property_read_u32(fwnode, props_title, val);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* We implicitly know idx >= 0 here */
|
||||
props = kcalloc(n_props, sizeof(*props), GFP_KERNEL);
|
||||
if (!props)
|
||||
return -ENOMEM;
|
||||
|
||||
err = fwnode_property_read_u32_array(fwnode, props_title, props, n_props);
|
||||
if (err >= 0)
|
||||
*val = props[idx];
|
||||
|
||||
kfree(props);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
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,
|
||||
unsigned int *val)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = fwnode_get_u32_prop_for_name(fwnode, mode_name, polarity_prop,
|
||||
names_prop, default_val, val);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (!(supported & BIT(*val))) {
|
||||
pr_err("%d is not a supported value for %pfw '%s' element '%s'\n",
|
||||
*val, fwnode, polarity_prop, mode_name);
|
||||
err = -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @val: Pointer to returned polarity.
|
||||
*
|
||||
* Return: zero on success, negative error on failure.
|
||||
*/
|
||||
int __must_check phy_get_rx_polarity(struct fwnode_handle *fwnode,
|
||||
const char *mode_name,
|
||||
unsigned int supported,
|
||||
unsigned int default_val,
|
||||
unsigned int *val)
|
||||
{
|
||||
return phy_get_polarity_for_mode(fwnode, mode_name, supported,
|
||||
default_val, "rx-polarity",
|
||||
"rx-polarity-names", val);
|
||||
}
|
||||
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
|
||||
* @val: Pointer to returned polarity.
|
||||
*
|
||||
* Return: zero on success, negative error on failure.
|
||||
*/
|
||||
int __must_check phy_get_tx_polarity(struct fwnode_handle *fwnode,
|
||||
const char *mode_name, unsigned int supported,
|
||||
unsigned int default_val, unsigned int *val)
|
||||
{
|
||||
return phy_get_polarity_for_mode(fwnode, mode_name, supported,
|
||||
default_val, "tx-polarity",
|
||||
"tx-polarity-names", val);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(phy_get_tx_polarity);
|
||||
|
||||
/**
|
||||
* phy_get_manual_rx_polarity - Get manual 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.
|
||||
* @val: Pointer to returned polarity.
|
||||
*
|
||||
* Helper for PHYs which do not support protocols with automatic RX polarity
|
||||
* detection and correction.
|
||||
*
|
||||
* Return: zero on success, negative error on failure.
|
||||
*/
|
||||
int __must_check phy_get_manual_rx_polarity(struct fwnode_handle *fwnode,
|
||||
const char *mode_name,
|
||||
unsigned int *val)
|
||||
{
|
||||
return phy_get_rx_polarity(fwnode, mode_name,
|
||||
BIT(PHY_POL_NORMAL) | BIT(PHY_POL_INVERT),
|
||||
PHY_POL_NORMAL, val);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(phy_get_manual_rx_polarity);
|
||||
|
||||
/**
|
||||
* phy_get_manual_tx_polarity - Get manual 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.
|
||||
* @val: Pointer to returned polarity.
|
||||
*
|
||||
* Helper for PHYs without any custom default value for the TX polarity.
|
||||
*
|
||||
* Return: zero on success, negative error on failure.
|
||||
*/
|
||||
int __must_check phy_get_manual_tx_polarity(struct fwnode_handle *fwnode,
|
||||
const char *mode_name,
|
||||
unsigned int *val)
|
||||
{
|
||||
return phy_get_tx_polarity(fwnode, mode_name,
|
||||
BIT(PHY_POL_NORMAL) | BIT(PHY_POL_INVERT),
|
||||
PHY_POL_NORMAL, val);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(phy_get_manual_tx_polarity);
|
||||
|
|
@ -25,4 +25,8 @@
|
|||
#define PHY_TYPE_USXGMII 12
|
||||
#define PHY_TYPE_XAUI 13
|
||||
|
||||
#define PHY_POL_NORMAL 0
|
||||
#define PHY_POL_INVERT 1
|
||||
#define PHY_POL_AUTO 2
|
||||
|
||||
#endif /* _DT_BINDINGS_PHY */
|
||||
|
|
|
|||
32
include/linux/phy/phy-common-props.h
Normal file
32
include/linux/phy/phy-common-props.h
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
/* 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 <dt-bindings/phy/phy.h>
|
||||
|
||||
struct fwnode_handle;
|
||||
|
||||
int __must_check phy_get_rx_polarity(struct fwnode_handle *fwnode,
|
||||
const char *mode_name,
|
||||
unsigned int supported,
|
||||
unsigned int default_val,
|
||||
unsigned int *val);
|
||||
int __must_check phy_get_tx_polarity(struct fwnode_handle *fwnode,
|
||||
const char *mode_name,
|
||||
unsigned int supported,
|
||||
unsigned int default_val,
|
||||
unsigned int *val);
|
||||
int __must_check phy_get_manual_rx_polarity(struct fwnode_handle *fwnode,
|
||||
const char *mode_name,
|
||||
unsigned int *val);
|
||||
int __must_check phy_get_manual_tx_polarity(struct fwnode_handle *fwnode,
|
||||
const char *mode_name,
|
||||
unsigned int *val);
|
||||
|
||||
#endif /* __PHY_COMMON_PROPS_H */
|
||||
Loading…
Add table
Add a link
Reference in a new issue