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