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