| 1 | |
| 2 | |
| 3 | * phy-common-props.c -- Common PHY properties
|
| 4 | *
|
| 5 | * Copyright 2025-2026 NXP
|
| 6 |
|
| 7 | #include <linux/export.h>
|
| 8 | #include <linux/fwnode.h>
|
| 9 | #include <linux/phy/phy-common-props.h>
|
| 10 | #include <linux/printk.h>
|
| 11 | #include <linux/property.h>
|
| 12 | #include <linux/slab.h>
|
| 13 |
|
| 14 | |
| 15 | * fwnode_get_u32_prop_for_name - Find u32 property by name, or default value
|
| 16 | * @fwnode: Pointer to firmware node, or NULL to use @default_val
|
| 17 | * @name: Property name used as lookup key in @names_title (must not be NULL)
|
| 18 | * @props_title: Name of u32 array property holding values
|
| 19 | * @names_title: Name of string array property holding lookup keys
|
| 20 | * @default_val: Default value if @fwnode is NULL or @props_title is empty
|
| 21 | * @val: Pointer to store the returned value
|
| 22 | *
|
| 23 | * This function retrieves a u32 value from @props_title based on a name lookup
|
| 24 | * in @names_title. The value stored in @val is determined as follows:
|
| 25 | *
|
| 26 | * - If @fwnode is NULL or @props_title is empty: @default_val is used
|
| 27 | * - If @props_title has exactly one element and @names_title is empty:
|
| 28 | * that element is used
|
| 29 | * - Otherwise: @val is set to the element at the same index where @name is
|
| 30 | * found in @names_title.
|
| 31 | * - If @name is not found, the function looks for a "default" entry in
|
| 32 | * @names_title and uses the corresponding value from @props_title
|
| 33 | *
|
| 34 | * When both @props_title and @names_title are present, they must have the
|
| 35 | * same number of elements (except when @props_title has exactly one element).
|
| 36 | *
|
| 37 | * Return: zero on success, negative error on failure.
|
| 38 |
|
| 39 | static int fwnode_get_u32_prop_for_name(struct fwnode_handle *fwnode,
|
| 40 | const char *name,
|
| 41 | const char *props_title,
|
| 42 | const char *names_title,
|
| 43 | unsigned int default_val,
|
| 44 | unsigned int *val)
|
| 45 | {
|
| 46 | int err, n_props, n_names, idx;
|
| 47 | u32 *props;
|
| 48 |
|
| 49 | if (!name) {
|
| 50 | pr_err("Lookup key inside \"%s\" is mandatory\n", names_title);
|
| 51 | return -EINVAL;
|
| 52 | }
|
| 53 |
|
| 54 | n_props = fwnode_property_count_u32(fwnode, props_title);
|
| 55 | if (n_props <= 0) {
|
| 56 |
|
| 57 | *val = default_val;
|
| 58 | return 0;
|
| 59 | }
|
| 60 |
|
| 61 | n_names = fwnode_property_string_array_count(fwnode, names_title);
|
| 62 | if (n_names >= 0 && n_props != n_names) {
|
| 63 | pr_err("%pfw mismatch between \"%s\" and \"%s\" property count (%d vs %d)\n",
|
| 64 | fwnode, props_title, names_title, n_props, n_names);
|
| 65 | return -EINVAL;
|
| 66 | }
|
| 67 |
|
| 68 | idx = fwnode_property_match_string(fwnode, names_title, name);
|
| 69 | if (idx < 0)
|
| 70 | idx = fwnode_property_match_string(fwnode, names_title, "default");
|
| 71 | |
| 72 | * If the mode name is missing, it can only mean the specified property
|
| 73 | * is the default one for all modes, so reject any other property count
|
| 74 | * than 1.
|
| 75 |
|
| 76 | if (idx < 0 && n_props != 1) {
|
| 77 | pr_err("%pfw \"%s \" property has %d elements, but cannot find \"%s\" in \"%s\" and there is no default value\n",
|
| 78 | fwnode, props_title, n_props, name, names_title);
|
| 79 | return -EINVAL;
|
| 80 | }
|
| 81 |
|
| 82 | if (n_props == 1) {
|
| 83 | err = fwnode_property_read_u32(fwnode, props_title, val);
|
| 84 | if (err)
|
| 85 | return err;
|
| 86 |
|
| 87 | return 0;
|
| 88 | }
|
| 89 |
|
| 90 |
|
| 91 | props = kcalloc(n_props, sizeof(*props), GFP_KERNEL);
|
| 92 | if (!props)
|
| 93 | return -ENOMEM;
|
| 94 |
|
| 95 | err = fwnode_property_read_u32_array(fwnode, props_title, props, n_props);
|
| 96 | if (err >= 0)
|
| 97 | *val = props[idx];
|
| 98 |
|
| 99 | kfree(props);
|
| 100 |
|
| 101 | return err;
|
| 102 | }
|
| 103 |
|
| 104 | static int phy_get_polarity_for_mode(struct fwnode_handle *fwnode,
|
| 105 | const char *mode_name,
|
| 106 | unsigned int supported,
|
| 107 | unsigned int default_val,
|
| 108 | const char *polarity_prop,
|
| 109 | const char *names_prop,
|
| 110 | unsigned int *val)
|
| 111 | {
|
| 112 | int err;
|
| 113 |
|
| 114 | err = fwnode_get_u32_prop_for_name(fwnode, mode_name, polarity_prop,
|
| 115 | names_prop, default_val, val);
|
| 116 | if (err)
|
| 117 | return err;
|
| 118 |
|
| 119 | if (!(supported & BIT(*val))) {
|
| 120 | pr_err("%d is not a supported value for %pfw '%s' element '%s'\n",
|
| 121 | *val, fwnode, polarity_prop, mode_name);
|
| 122 | err = -EOPNOTSUPP;
|
| 123 | }
|
| 124 |
|
| 125 | return err;
|
| 126 | }
|
| 127 |
|
| 128 | |
| 129 | * phy_get_rx_polarity - Get RX polarity for PHY differential lane
|
| 130 | * @fwnode: Pointer to the PHY's firmware node.
|
| 131 | * @mode_name: The name of the PHY mode to look up.
|
| 132 | * @supported: Bit mask of PHY_POL_NORMAL, PHY_POL_INVERT and PHY_POL_AUTO
|
| 133 | * @default_val: Default polarity value if property is missing
|
| 134 | * @val: Pointer to returned polarity.
|
| 135 | *
|
| 136 | * Return: zero on success, negative error on failure.
|
| 137 |
|
| 138 | int __must_check phy_get_rx_polarity(struct fwnode_handle *fwnode,
|
| 139 | const char *mode_name,
|
| 140 | unsigned int supported,
|
| 141 | unsigned int default_val,
|
| 142 | unsigned int *val)
|
| 143 | {
|
| 144 | return phy_get_polarity_for_mode(fwnode, mode_name, supported,
|
| 145 | default_val, "rx-polarity",
|
| 146 | "rx-polarity-names", val);
|
| 147 | }
|
| 148 | EXPORT_SYMBOL_GPL(phy_get_rx_polarity);
|
| 149 |
|
| 150 | |
| 151 | * phy_get_tx_polarity - Get TX polarity for PHY differential lane
|
| 152 | * @fwnode: Pointer to the PHY's firmware node.
|
| 153 | * @mode_name: The name of the PHY mode to look up.
|
| 154 | * @supported: Bit mask of PHY_POL_NORMAL and PHY_POL_INVERT
|
| 155 | * @default_val: Default polarity value if property is missing
|
| 156 | * @val: Pointer to returned polarity.
|
| 157 | *
|
| 158 | * Return: zero on success, negative error on failure.
|
| 159 |
|
| 160 | int __must_check phy_get_tx_polarity(struct fwnode_handle *fwnode,
|
| 161 | const char *mode_name, unsigned int supported,
|
| 162 | unsigned int default_val, unsigned int *val)
|
| 163 | {
|
| 164 | return phy_get_polarity_for_mode(fwnode, mode_name, supported,
|
| 165 | default_val, "tx-polarity",
|
| 166 | "tx-polarity-names", val);
|
| 167 | }
|
| 168 | EXPORT_SYMBOL_GPL(phy_get_tx_polarity);
|
| 169 |
|
| 170 | |
| 171 | * phy_get_manual_rx_polarity - Get manual RX polarity for PHY differential lane
|
| 172 | * @fwnode: Pointer to the PHY's firmware node.
|
| 173 | * @mode_name: The name of the PHY mode to look up.
|
| 174 | * @val: Pointer to returned polarity.
|
| 175 | *
|
| 176 | * Helper for PHYs which do not support protocols with automatic RX polarity
|
| 177 | * detection and correction.
|
| 178 | *
|
| 179 | * Return: zero on success, negative error on failure.
|
| 180 |
|
| 181 | int __must_check phy_get_manual_rx_polarity(struct fwnode_handle *fwnode,
|
| 182 | const char *mode_name,
|
| 183 | unsigned int *val)
|
| 184 | {
|
| 185 | return phy_get_rx_polarity(fwnode, mode_name,
|
| 186 | BIT(PHY_POL_NORMAL) | BIT(PHY_POL_INVERT),
|
| 187 | PHY_POL_NORMAL, val);
|
| 188 | }
|
| 189 | EXPORT_SYMBOL_GPL(phy_get_manual_rx_polarity);
|
| 190 |
|
| 191 | |
| 192 | * phy_get_manual_tx_polarity - Get manual TX polarity for PHY differential lane
|
| 193 | * @fwnode: Pointer to the PHY's firmware node.
|
| 194 | * @mode_name: The name of the PHY mode to look up.
|
| 195 | * @val: Pointer to returned polarity.
|
| 196 | *
|
| 197 | * Helper for PHYs without any custom default value for the TX polarity.
|
| 198 | *
|
| 199 | * Return: zero on success, negative error on failure.
|
| 200 |
|
| 201 | int __must_check phy_get_manual_tx_polarity(struct fwnode_handle *fwnode,
|
| 202 | const char *mode_name,
|
| 203 | unsigned int *val)
|
| 204 | {
|
| 205 | return phy_get_tx_polarity(fwnode, mode_name,
|
| 206 | BIT(PHY_POL_NORMAL) | BIT(PHY_POL_INVERT),
|
| 207 | PHY_POL_NORMAL, val);
|
| 208 | }
|
| 209 | EXPORT_SYMBOL_GPL(phy_get_manual_tx_polarity);
|