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_dev_out_synth_get(zldev, out); 50 51 /* Get synth frequency */ 52 synth_freq = zl3073x_dev_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_dev_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_dev_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 u8 out, synth; 212 u32 f; 213 214 props->dpll_props.type = DPLL_PIN_TYPE_GNSS; 215 216 /* The output pin phase adjustment granularity equals half of 217 * the synth frequency count. 218 */ 219 out = zl3073x_output_pin_out_get(index); 220 synth = zl3073x_dev_out_synth_get(zldev, out); 221 f = 2 * zl3073x_dev_synth_freq_get(zldev, synth); 222 props->dpll_props.phase_gran = f ? div_u64(PSEC_PER_SEC, f) : 1; 223 } 224 225 props->dpll_props.phase_range.min = S32_MIN; 226 props->dpll_props.phase_range.max = S32_MAX; 227 228 zl3073x_prop_pin_package_label_set(zldev, props, dir, index); 229 230 /* Get firmware node for the given pin */ 231 rc = zl3073x_prop_pin_fwnode_get(zldev, props, dir, index); 232 if (rc) 233 return props; /* Return if it does not exist */ 234 235 /* Look for label property and store the value as board label */ 236 fwnode_property_read_string(props->fwnode, "label", 237 &props->dpll_props.board_label); 238 239 /* Look for pin type property and translate its value to DPLL 240 * pin type enum if it is present. 241 */ 242 if (!fwnode_property_read_string(props->fwnode, "connection-type", 243 &type)) { 244 if (!strcmp(type, "ext")) 245 props->dpll_props.type = DPLL_PIN_TYPE_EXT; 246 else if (!strcmp(type, "gnss")) 247 props->dpll_props.type = DPLL_PIN_TYPE_GNSS; 248 else if (!strcmp(type, "int")) 249 props->dpll_props.type = DPLL_PIN_TYPE_INT_OSCILLATOR; 250 else if (!strcmp(type, "synce")) 251 props->dpll_props.type = DPLL_PIN_TYPE_SYNCE_ETH_PORT; 252 else 253 dev_warn(zldev->dev, 254 "Unknown or unsupported pin type '%s'\n", 255 type); 256 } 257 258 /* Check if the pin supports embedded sync control */ 259 props->esync_control = fwnode_property_read_bool(props->fwnode, 260 "esync-control"); 261 262 /* Read supported frequencies property if it is specified */ 263 num_freqs = fwnode_property_count_u64(props->fwnode, 264 "supported-frequencies-hz"); 265 if (num_freqs <= 0) 266 /* Return if the property does not exist or number is 0 */ 267 return props; 268 269 /* The firmware node specifies list of supported frequencies while 270 * DPLL core pin properties requires list of frequency ranges. 271 * So read the frequency list into temporary array. 272 */ 273 freqs = kcalloc(num_freqs, sizeof(*freqs), GFP_KERNEL); 274 if (!freqs) { 275 rc = -ENOMEM; 276 goto err_alloc_freqs; 277 } 278 279 /* Read frequencies list from firmware node */ 280 fwnode_property_read_u64_array(props->fwnode, 281 "supported-frequencies-hz", freqs, 282 num_freqs); 283 284 /* Allocate frequency ranges list and fill it */ 285 ranges = kcalloc(num_freqs, sizeof(*ranges), GFP_KERNEL); 286 if (!ranges) { 287 rc = -ENOMEM; 288 goto err_alloc_ranges; 289 } 290 291 /* Convert list of frequencies to list of frequency ranges but 292 * filter-out frequencies that are not representable by device 293 */ 294 for (i = 0, j = 0; i < num_freqs; i++) { 295 struct dpll_pin_frequency freq = DPLL_PIN_FREQUENCY(freqs[i]); 296 297 if (zl3073x_pin_check_freq(zldev, dir, index, freqs[i])) { 298 ranges[j] = freq; 299 j++; 300 } 301 } 302 303 /* Save number of freq ranges and pointer to them into pin properties */ 304 props->dpll_props.freq_supported = ranges; 305 props->dpll_props.freq_supported_num = j; 306 307 /* Free temporary array */ 308 kfree(freqs); 309 310 return props; 311 312 err_alloc_ranges: 313 kfree(freqs); 314 err_alloc_freqs: 315 fwnode_handle_put(props->fwnode); 316 kfree(props); 317 318 return ERR_PTR(rc); 319 } 320 321 /** 322 * zl3073x_pin_props_put - release pin properties 323 * @props: pin properties to free 324 * 325 * The function deallocates given pin properties structure. 326 */ 327 void zl3073x_pin_props_put(struct zl3073x_pin_props *props) 328 { 329 /* Free supported frequency ranges list if it is present */ 330 kfree(props->dpll_props.freq_supported); 331 332 /* Put firmware handle if it is present */ 333 if (props->fwnode) 334 fwnode_handle_put(props->fwnode); 335 336 kfree(props); 337 } 338 339 /** 340 * zl3073x_prop_dpll_type_get - get DPLL channel type 341 * @zldev: pointer to zl3073x device 342 * @index: DPLL channel index 343 * 344 * Return: DPLL type for given DPLL channel 345 */ 346 enum dpll_type 347 zl3073x_prop_dpll_type_get(struct zl3073x_dev *zldev, u8 index) 348 { 349 const char *types[ZL3073X_MAX_CHANNELS]; 350 int count; 351 352 /* Read dpll types property from firmware */ 353 count = device_property_read_string_array(zldev->dev, "dpll-types", 354 types, ARRAY_SIZE(types)); 355 356 /* Return default if property or entry for given channel is missing */ 357 if (index >= count) 358 return DPLL_TYPE_PPS; 359 360 if (!strcmp(types[index], "pps")) 361 return DPLL_TYPE_PPS; 362 else if (!strcmp(types[index], "eec")) 363 return DPLL_TYPE_EEC; 364 365 dev_info(zldev->dev, "Unknown DPLL type '%s', using default\n", 366 types[index]); 367 368 return DPLL_TYPE_PPS; /* Default */ 369 } 370