1 // SPDX-License-Identifier: GPL-2.0-only 2 3 #include <linux/array_size.h> 4 #include <linux/dev_printk.h> 5 #include <linux/err.h> 6 #include <linux/errno.h> 7 #include <linux/fwnode.h> 8 #include <linux/property.h> 9 #include <linux/slab.h> 10 #include <linux/string.h> 11 12 #include "core.h" 13 #include "prop.h" 14 15 /** 16 * zl3073x_pin_check_freq - verify frequency for given pin 17 * @zldev: pointer to zl3073x device 18 * @dir: pin direction 19 * @id: pin index 20 * @freq: frequency to check 21 * 22 * The function checks the given frequency is valid for the device. For input 23 * pins it checks that the frequency can be factorized using supported base 24 * frequencies. For output pins it checks that the frequency divides connected 25 * synth frequency without remainder. 26 * 27 * Return: true if the frequency is valid, false if not. 28 */ 29 static bool 30 zl3073x_pin_check_freq(struct zl3073x_dev *zldev, enum dpll_pin_direction dir, 31 u8 id, u64 freq) 32 { 33 if (freq > U32_MAX) 34 goto err_inv_freq; 35 36 if (dir == DPLL_PIN_DIRECTION_INPUT) { 37 int rc; 38 39 /* Check if the frequency can be factorized */ 40 rc = zl3073x_ref_freq_factorize(freq, NULL, NULL); 41 if (rc) 42 goto err_inv_freq; 43 } else { 44 u32 synth_freq; 45 u8 out, synth; 46 47 /* Get output pin synthesizer */ 48 out = zl3073x_output_pin_out_get(id); 49 synth = zl3073x_out_synth_get(zldev, out); 50 51 /* Get synth frequency */ 52 synth_freq = zl3073x_synth_freq_get(zldev, synth); 53 54 /* Check the frequency divides synth frequency */ 55 if (synth_freq % (u32)freq) 56 goto err_inv_freq; 57 } 58 59 return true; 60 61 err_inv_freq: 62 dev_warn(zldev->dev, 63 "Unsupported frequency %llu Hz in firmware node\n", freq); 64 65 return false; 66 } 67 68 /** 69 * zl3073x_prop_pin_package_label_set - get package label for the pin 70 * @zldev: pointer to zl3073x device 71 * @props: pointer to pin properties 72 * @dir: pin direction 73 * @id: pin index 74 * 75 * Generates package label string and stores it into pin properties structure. 76 * 77 * Possible formats: 78 * REF<n> - differential input reference 79 * REF<n>P & REF<n>N - single-ended input reference (P or N pin) 80 * OUT<n> - differential output 81 * OUT<n>P & OUT<n>N - single-ended output (P or N pin) 82 */ 83 static void 84 zl3073x_prop_pin_package_label_set(struct zl3073x_dev *zldev, 85 struct zl3073x_pin_props *props, 86 enum dpll_pin_direction dir, u8 id) 87 { 88 const char *prefix, *suffix; 89 bool is_diff; 90 91 if (dir == DPLL_PIN_DIRECTION_INPUT) { 92 u8 ref; 93 94 prefix = "REF"; 95 ref = zl3073x_input_pin_ref_get(id); 96 is_diff = zl3073x_ref_is_diff(zldev, ref); 97 } else { 98 u8 out; 99 100 prefix = "OUT"; 101 out = zl3073x_output_pin_out_get(id); 102 is_diff = zl3073x_out_is_diff(zldev, out); 103 } 104 105 if (!is_diff) 106 suffix = zl3073x_is_p_pin(id) ? "P" : "N"; 107 else 108 suffix = ""; /* No suffix for differential one */ 109 110 snprintf(props->package_label, sizeof(props->package_label), "%s%u%s", 111 prefix, id / 2, suffix); 112 113 /* Set package_label pointer in DPLL core properties to generated 114 * string. 115 */ 116 props->dpll_props.package_label = props->package_label; 117 } 118 119 /** 120 * zl3073x_prop_pin_fwnode_get - get fwnode for given pin 121 * @zldev: pointer to zl3073x device 122 * @props: pointer to pin properties 123 * @dir: pin direction 124 * @id: pin index 125 * 126 * Return: 0 on success, -ENOENT if the firmware node does not exist 127 */ 128 static int 129 zl3073x_prop_pin_fwnode_get(struct zl3073x_dev *zldev, 130 struct zl3073x_pin_props *props, 131 enum dpll_pin_direction dir, u8 id) 132 { 133 struct fwnode_handle *pins_node, *pin_node; 134 const char *node_name; 135 136 if (dir == DPLL_PIN_DIRECTION_INPUT) 137 node_name = "input-pins"; 138 else 139 node_name = "output-pins"; 140 141 /* Get node containing input or output pins */ 142 pins_node = device_get_named_child_node(zldev->dev, node_name); 143 if (!pins_node) { 144 dev_dbg(zldev->dev, "'%s' sub-node is missing\n", node_name); 145 return -ENOENT; 146 } 147 148 /* Enumerate child pin nodes and find the requested one */ 149 fwnode_for_each_child_node(pins_node, pin_node) { 150 u32 reg; 151 152 if (fwnode_property_read_u32(pin_node, "reg", ®)) 153 continue; 154 155 if (id == reg) 156 break; 157 } 158 159 /* Release pin parent node */ 160 fwnode_handle_put(pins_node); 161 162 /* Save found node */ 163 props->fwnode = pin_node; 164 165 dev_dbg(zldev->dev, "Firmware node for %s %sfound\n", 166 props->package_label, pin_node ? "" : "NOT "); 167 168 return pin_node ? 0 : -ENOENT; 169 } 170 171 /** 172 * zl3073x_pin_props_get - get pin properties 173 * @zldev: pointer to zl3073x device 174 * @dir: pin direction 175 * @index: pin index 176 * 177 * The function looks for firmware node for the given pin if it is provided 178 * by the system firmware (DT or ACPI), allocates pin properties structure, 179 * generates package label string according pin type and optionally fetches 180 * board label, connection type, supported frequencies and esync capability 181 * from the firmware node if it does exist. 182 * 183 * Pointer that is returned by this function should be freed using 184 * @zl3073x_pin_props_put(). 185 * 186 * Return: 187 * * pointer to allocated pin properties structure on success 188 * * error pointer in case of error 189 */ 190 struct zl3073x_pin_props *zl3073x_pin_props_get(struct zl3073x_dev *zldev, 191 enum dpll_pin_direction dir, 192 u8 index) 193 { 194 struct dpll_pin_frequency *ranges; 195 struct zl3073x_pin_props *props; 196 int i, j, num_freqs, rc; 197 const char *type; 198 u64 *freqs; 199 200 props = kzalloc(sizeof(*props), GFP_KERNEL); 201 if (!props) 202 return ERR_PTR(-ENOMEM); 203 204 /* Set default pin type and capabilities */ 205 if (dir == DPLL_PIN_DIRECTION_INPUT) { 206 props->dpll_props.type = DPLL_PIN_TYPE_EXT; 207 props->dpll_props.capabilities = 208 DPLL_PIN_CAPABILITIES_PRIORITY_CAN_CHANGE | 209 DPLL_PIN_CAPABILITIES_STATE_CAN_CHANGE; 210 } else { 211 props->dpll_props.type = DPLL_PIN_TYPE_GNSS; 212 } 213 214 props->dpll_props.phase_range.min = S32_MIN; 215 props->dpll_props.phase_range.max = S32_MAX; 216 217 zl3073x_prop_pin_package_label_set(zldev, props, dir, index); 218 219 /* Get firmware node for the given pin */ 220 rc = zl3073x_prop_pin_fwnode_get(zldev, props, dir, index); 221 if (rc) 222 return props; /* Return if it does not exist */ 223 224 /* Look for label property and store the value as board label */ 225 fwnode_property_read_string(props->fwnode, "label", 226 &props->dpll_props.board_label); 227 228 /* Look for pin type property and translate its value to DPLL 229 * pin type enum if it is present. 230 */ 231 if (!fwnode_property_read_string(props->fwnode, "connection-type", 232 &type)) { 233 if (!strcmp(type, "ext")) 234 props->dpll_props.type = DPLL_PIN_TYPE_EXT; 235 else if (!strcmp(type, "gnss")) 236 props->dpll_props.type = DPLL_PIN_TYPE_GNSS; 237 else if (!strcmp(type, "int")) 238 props->dpll_props.type = DPLL_PIN_TYPE_INT_OSCILLATOR; 239 else if (!strcmp(type, "synce")) 240 props->dpll_props.type = DPLL_PIN_TYPE_SYNCE_ETH_PORT; 241 else 242 dev_warn(zldev->dev, 243 "Unknown or unsupported pin type '%s'\n", 244 type); 245 } 246 247 /* Check if the pin supports embedded sync control */ 248 props->esync_control = fwnode_property_read_bool(props->fwnode, 249 "esync-control"); 250 251 /* Read supported frequencies property if it is specified */ 252 num_freqs = fwnode_property_count_u64(props->fwnode, 253 "supported-frequencies-hz"); 254 if (num_freqs <= 0) 255 /* Return if the property does not exist or number is 0 */ 256 return props; 257 258 /* The firmware node specifies list of supported frequencies while 259 * DPLL core pin properties requires list of frequency ranges. 260 * So read the frequency list into temporary array. 261 */ 262 freqs = kcalloc(num_freqs, sizeof(*freqs), GFP_KERNEL); 263 if (!freqs) { 264 rc = -ENOMEM; 265 goto err_alloc_freqs; 266 } 267 268 /* Read frequencies list from firmware node */ 269 fwnode_property_read_u64_array(props->fwnode, 270 "supported-frequencies-hz", freqs, 271 num_freqs); 272 273 /* Allocate frequency ranges list and fill it */ 274 ranges = kcalloc(num_freqs, sizeof(*ranges), GFP_KERNEL); 275 if (!ranges) { 276 rc = -ENOMEM; 277 goto err_alloc_ranges; 278 } 279 280 /* Convert list of frequencies to list of frequency ranges but 281 * filter-out frequencies that are not representable by device 282 */ 283 for (i = 0, j = 0; i < num_freqs; i++) { 284 struct dpll_pin_frequency freq = DPLL_PIN_FREQUENCY(freqs[i]); 285 286 if (zl3073x_pin_check_freq(zldev, dir, index, freqs[i])) { 287 ranges[j] = freq; 288 j++; 289 } 290 } 291 292 /* Save number of freq ranges and pointer to them into pin properties */ 293 props->dpll_props.freq_supported = ranges; 294 props->dpll_props.freq_supported_num = j; 295 296 /* Free temporary array */ 297 kfree(freqs); 298 299 return props; 300 301 err_alloc_ranges: 302 kfree(freqs); 303 err_alloc_freqs: 304 fwnode_handle_put(props->fwnode); 305 kfree(props); 306 307 return ERR_PTR(rc); 308 } 309 310 /** 311 * zl3073x_pin_props_put - release pin properties 312 * @props: pin properties to free 313 * 314 * The function deallocates given pin properties structure. 315 */ 316 void zl3073x_pin_props_put(struct zl3073x_pin_props *props) 317 { 318 /* Free supported frequency ranges list if it is present */ 319 kfree(props->dpll_props.freq_supported); 320 321 /* Put firmware handle if it is present */ 322 if (props->fwnode) 323 fwnode_handle_put(props->fwnode); 324 325 kfree(props); 326 } 327 328 /** 329 * zl3073x_prop_dpll_type_get - get DPLL channel type 330 * @zldev: pointer to zl3073x device 331 * @index: DPLL channel index 332 * 333 * Return: DPLL type for given DPLL channel 334 */ 335 enum dpll_type 336 zl3073x_prop_dpll_type_get(struct zl3073x_dev *zldev, u8 index) 337 { 338 const char *types[ZL3073X_MAX_CHANNELS]; 339 int count; 340 341 /* Read dpll types property from firmware */ 342 count = device_property_read_string_array(zldev->dev, "dpll-types", 343 types, ARRAY_SIZE(types)); 344 345 /* Return default if property or entry for given channel is missing */ 346 if (index >= count) 347 return DPLL_TYPE_PPS; 348 349 if (!strcmp(types[index], "pps")) 350 return DPLL_TYPE_PPS; 351 else if (!strcmp(types[index], "eec")) 352 return DPLL_TYPE_EEC; 353 354 dev_info(zldev->dev, "Unknown DPLL type '%s', using default\n", 355 types[index]); 356 357 return DPLL_TYPE_PPS; /* Default */ 358 } 359