diff --git a/drivers/jesd204/jesd204-core.c b/drivers/jesd204/jesd204-core.c index 73cb051d22c3d6..b57d1326e4f5fa 100644 --- a/drivers/jesd204/jesd204-core.c +++ b/drivers/jesd204/jesd204-core.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -140,6 +141,11 @@ bool jesd204_link_get_paused(const struct jesd204_link *lnk) } EXPORT_SYMBOL_GPL(jesd204_link_get_paused); +/** + * jesd204_device_count_get() - Get the total number of registered JESD204 devices + * + * Return: The current count of registered JESD204 devices in the framework. + */ int jesd204_device_count_get(void) { return jesd204_device_count; @@ -432,20 +438,42 @@ int jesd204_link_get_lmfc_lemc_rate(struct jesd204_link *lnk, } EXPORT_SYMBOL_GPL(jesd204_link_get_lmfc_lemc_rate); +/** + * jesd204_dev_get_topology_top_dev() - Get the top-level device for a JESD204 topology + * @jdev: Pointer to a JESD204 device within the topology + * + * This function finds and returns the top-level device (typically ADC, DAC, + * or transceiver) for the JESD204 topology that contains the given device. + * If the device itself is a top-level device, it returns that directly. + * Otherwise, it searches through all topologies to find where this device + * has connections. + * + * Return: Pointer to the top-level jesd204_dev_top structure, or NULL if + * the device doesn't belong to any topology. + */ struct jesd204_dev_top *jesd204_dev_get_topology_top_dev(struct jesd204_dev *jdev) { struct jesd204_dev_top *jdev_top = jesd204_dev_top_dev(jdev); + struct jesd204_dev_top *found = NULL; if (jdev_top) return jdev_top; - /* FIXME: enforce that one jdev object can only be in one topology */ list_for_each_entry(jdev_top, &jesd204_topologies, entry) { if (!jesd204_dev_has_con_in_topology(jdev, jdev_top)) continue; - return jdev_top; + if (found) { + jesd204_warn(jdev, + "Device belongs to multiple topologies (%d, %d); using first\n", + found->topo_id, jdev_top->topo_id); + break; + } + found = jdev_top; } + if (found) + return found; + jesd204_warn(jdev, "Device isn't a top-device, nor does it belong to topology with top-device\n"); return NULL; @@ -617,6 +645,17 @@ static void __jesd204_printk(const char *level, const struct jesd204_dev *jdev, vaf); } +/** + * jesd204_printk() - Print a kernel message for a JESD204 device + * @level: Kernel log level string (e.g., KERN_ERR, KERN_INFO) + * @jdev: Pointer to the JESD204 device structure (may be NULL) + * @fmt: printf-style format string + * @...: Arguments for the format string + * + * This function prints a kernel message with JESD204 device context. + * It handles NULL device pointers gracefully and includes device tree + * node information when available. + */ void jesd204_printk(const char *level, const struct jesd204_dev *jdev, const char *fmt, ...) { @@ -663,6 +702,30 @@ static int jesd204_dev_alloc_links(struct jesd204_dev_top *jdev_top) return 0; } +static void jesd204_dev_free_links(struct jesd204_dev_top *jdev_top) +{ + unsigned int i; + + if (jdev_top->active_links) { + for (i = 0; i < jdev_top->num_links; i++) { + /* Only free if not from init_links (static allocation) */ + if (!jdev_top->init_links || + !jdev_top->init_links[i].lane_ids) + kfree(jdev_top->active_links[i].link.lane_ids); + } + kfree(jdev_top->active_links); + } + + if (jdev_top->staged_links) { + for (i = 0; i < jdev_top->num_links; i++) { + if (!jdev_top->init_links || + !jdev_top->init_links[i].lane_ids) + kfree(jdev_top->staged_links[i].link.lane_ids); + } + kfree(jdev_top->staged_links); + } +} + static int jesd204_dev_init_stop_states(struct jesd204_dev *jdev, struct device_node *np) { @@ -725,6 +788,7 @@ static struct jesd204_dev *jesd204_dev_alloc(struct device_node *np) jdev = &jdev_top->jdev; + mutex_init(&jdev_top->fsm_lock); jdev_top->topo_id = topo_id; jdev_top->num_links = ret; for (i = 0; i < jdev_top->num_links; i++) @@ -923,8 +987,10 @@ static int jesd204_of_create_devices(void) for_each_node_with_property(np, "jesd204-device") { jdev = jesd204_dev_alloc(np); - if (IS_ERR(jdev)) + if (IS_ERR(jdev)) { + of_node_put(np); return PTR_ERR(jdev); + } } list_for_each_entry(jdev, &jesd204_device_list, entry) { @@ -1177,6 +1243,8 @@ static void jesd204_of_unregister_devices(void) } jdev_top = jesd204_dev_top_dev(jdev); list_del(&jdev_top->entry); + jesd204_dev_free_links(jdev_top); + mutex_destroy(&jdev_top->fsm_lock); kfree(jdev_top); jesd204_topologies_count--; } diff --git a/drivers/jesd204/jesd204-fsm.c b/drivers/jesd204/jesd204-fsm.c index 4f0c657bfc8981..bf2518e320f74c 100644 --- a/drivers/jesd204/jesd204-fsm.c +++ b/drivers/jesd204/jesd204-fsm.c @@ -14,6 +14,13 @@ #define JESD204_FSM_BUSY BIT(0) +/* + * Internal error code used to signal that the current FSM state doesn't match + * the expected state during validation. This is distinct from standard errno + * values to allow special handling (e.g., skipping states during resume). + * The value 9000 is chosen to be well outside the range of standard errno + * values (typically 1-4095) to avoid conflicts. + */ #define EINVALID_STATE 9000 typedef int (*jesd204_fsm_cb)(struct jesd204_dev *jdev, @@ -318,7 +325,7 @@ static int jesd204_fsm_propagate_rollback_cb_outputs(struct jesd204_dev *jdev_it static int jesd204_fsm_propagate_cb_top_level(struct jesd204_dev *jdev_it, struct jesd204_fsm_data *fsm_data) { - int i, ret; + int i, ret = 0; if (fsm_data->link_idx != JESD204_LINKS_ALL) return jesd204_fsm_handle_con_cb(jdev_it, NULL, @@ -330,7 +337,6 @@ static int jesd204_fsm_propagate_cb_top_level(struct jesd204_dev *jdev_it, if (ret) break; } - /* FIXME: error message here? */ return ret; } @@ -371,6 +377,10 @@ static int __jesd204_fsm_propagate_cb(struct jesd204_dev *jdev, static int __jesd204_fsm_propagate_rollback_cb(struct jesd204_dev *jdev, struct jesd204_fsm_data *data) { + jesd204_dbg(jdev, "Rolling back from state %s to %s\n", + jesd204_state_str(data->cur_state), + jesd204_state_str(data->nxt_state)); + jesd204_fsm_propagate_rollback_cb_top_level(jdev, data); jesd204_fsm_propagate_rollback_cb_outputs(jdev, data); jesd204_fsm_propagate_rollback_cb_inputs(jdev, data); @@ -1180,6 +1190,9 @@ static int jesd204_fsm_table_entry_cb(struct jesd204_dev *jdev, jesd204_fsm_handle_stop_state(jdev, link_idx, fsm_data); + if (!jdev->dev_data) + return JESD204_STATE_CHANGE_DONE; + state_op = &jdev->dev_data->state_ops[it->table[0].op]; if (fsm_data->rollback) @@ -1300,10 +1313,6 @@ static int jesd204_fsm_table_single(struct jesd204_dev *jdev, ret1 = 0; ret = 0; - /** - * FIXME: the handle_busy_flags logic needs re-visit, we should lock - * here and unlock after the loop is done - */ while (!jesd204_fsm_table_end(&it->table[0], rollback, jdev->fsm_rb_to_init)) { it->table = table; @@ -1414,6 +1423,8 @@ static int jesd204_fsm_table(struct jesd204_dev *jdev, if (!jdev_top) return -EFAULT; + mutex_lock(&jdev_top->fsm_lock); + memset(&data, 0, sizeof(data)); data.fsm_change_cb = jesd204_fsm_table_entry_cb; data.fsm_complete_cb = jesd204_fsm_table_entry_done; @@ -1443,6 +1454,8 @@ static int jesd204_fsm_table(struct jesd204_dev *jdev, jesd204_fsm_run_finished_cb(jdev, jdev_top, link_idx, handle_busy_flags); + mutex_unlock(&jdev_top->fsm_lock); + return ret; } diff --git a/drivers/jesd204/jesd204-priv.h b/drivers/jesd204/jesd204-priv.h index 62ede2aaf76ca6..d6d526be36d4d9 100644 --- a/drivers/jesd204/jesd204-priv.h +++ b/drivers/jesd204/jesd204-priv.h @@ -191,6 +191,7 @@ struct jesd204_link_opaque { * cb_ref on the jesd204_link_opaque struct, but each link * increments/decrements it, to group transitions of multiple * JESD204 links + * @fsm_lock mutex to serialize FSM state transitions * @topo_id topology ID for this device (and top-level device) * (connections should match against this) * @link_ids JESD204 link IDs for this top-level device @@ -211,6 +212,7 @@ struct jesd204_dev_top { struct jesd204_fsm_data *fsm_data; struct kref cb_ref; + struct mutex fsm_lock; int topo_id; unsigned int link_ids[JESD204_MAX_LINKS]; diff --git a/drivers/jesd204/jesd204-sysfs.c b/drivers/jesd204/jesd204-sysfs.c index a4e90a1935ffa6..912ac9f24e5d64 100644 --- a/drivers/jesd204/jesd204-sysfs.c +++ b/drivers/jesd204/jesd204-sysfs.c @@ -92,6 +92,7 @@ enum { JESD204_LNK_ATTR_fsm_paused, JESD204_LNK_ATTR_fsm_ignore_errors, JESD204_LNK_ATTR_sample_rate, + JESD204_LNK_ATTR_sample_rate_div, JESD204_LNK_ATTR_is_transmit, JESD204_LNK_ATTR_num_lanes, JESD204_LNK_ATTR_num_converters, @@ -122,6 +123,7 @@ static const struct jesd204_attr jesd204_lnk_attrs[] = { JESD204_LNK_ATTR_BOOL_PRIV(fsm_paused), JESD204_LNK_ATTR_BOOL_PRIV(fsm_ignore_errors), JESD204_LNK_ATTR_UINT(sample_rate), + JESD204_LNK_ATTR_UINT(sample_rate_div), JESD204_LNK_ATTR_BOOL(is_transmit), JESD204_LNK_ATTR_UINT(num_lanes), JESD204_LNK_ATTR_UINT(num_converters), @@ -212,7 +214,7 @@ static ssize_t jesd204_con_printf(struct jesd204_dev *jdev, static char *str_cut_from_chr(char *s, char c) { - char *ptr = strchr(s, '_'); + char *ptr = strchr(s, c); if (!ptr) return NULL; @@ -308,6 +310,7 @@ static ssize_t jesd204_con_show(struct device *dev, rc = jesd204_con_printf(e->jdev, ptr1, con, buf); break; } + iter_idx++; } out: @@ -320,7 +323,8 @@ static ssize_t jesd204_show_store_int(u64 *val, size_t usize, size_t count, bool store, bool is_signed) { u64 val1 = 0; - int ret, max; + u64 max; + int ret; if (!store) { memcpy(&val1, val, usize); @@ -330,7 +334,7 @@ static ssize_t jesd204_show_store_int(u64 *val, size_t usize, } if (is_signed) - ret = kstrtoll(rbuf, 0, &val1); + ret = kstrtoll(rbuf, 0, (s64 *)&val1); else ret = kstrtoull(rbuf, 0, &val1); if (ret) @@ -338,13 +342,13 @@ static ssize_t jesd204_show_store_int(u64 *val, size_t usize, switch (usize) { case 1: - max = 0xff; + max = U8_MAX; break; case 2: - max = 0xffff; + max = U16_MAX; break; case 4: - max = 0xffffffff; + max = U32_MAX; break; case 8: max = 0; diff --git a/include/linux/jesd204/jesd204.h b/include/linux/jesd204/jesd204.h index 1b6cc58fa85026..19250ff2ad3f2f 100644 --- a/include/linux/jesd204/jesd204.h +++ b/include/linux/jesd204/jesd204.h @@ -438,5 +438,7 @@ void jesd204_printk(const char *level, const struct jesd204_dev *jdev, jesd204_printk(KERN_NOTICE, dev, fmt, ##__VA_ARGS__) #define jesd204_info(dev, fmt, ...) \ jesd204_printk(KERN_INFO, dev, fmt, ##__VA_ARGS__) +#define jesd204_dbg(dev, fmt, ...) \ + jesd204_printk(KERN_DEBUG, dev, fmt, ##__VA_ARGS__) #endif