xref: /linux/drivers/phy/phy-common-props.c (revision c17ee635fd3a482b2ad2bf5e269755c2eae5f25e)
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