@@ -9,6 +9,7 @@
#include <linux/remoteproc.h>
#include <linux/firmware.h>
#include <linux/of.h>
+#include <linux/of_graph.h>
#include "core.h"
#include "dp_tx.h"
#include "dp_rx.h"
@@ -1403,6 +1404,7 @@ ath12k_core_hw_group_alloc(u8 id, u8 max_devices)
ag->num_devices = max_devices;
list_add(&ag->list, &ath12k_hw_group_list);
mutex_init(&ag->mutex_lock);
+ ag->mlo_capable = false;
return ag;
}
@@ -1417,34 +1419,156 @@ static void ath12k_core_hw_group_free(struct ath12k_hw_group *ag)
mutex_unlock(&ath12k_ag_list_lock);
}
+/* This function needs to be used only when dt has multi chip grouping information */
+static struct ath12k_hw_group *ath12k_core_hw_group_find_by_id(u8 group_id)
+{
+ struct ath12k_hw_group *ag;
+
+ /* group ids will be unique only for multi chip group */
+ list_for_each_entry(ag, &ath12k_hw_group_list, list) {
+ if (group_id == ag->id && ag->num_devices > 1)
+ return ag;
+ }
+
+ return NULL;
+}
+
+static int ath12k_core_get_wsi_info(struct ath12k_base *ab,
+ struct ath12k_wsi_info *wsi_info)
+{
+ struct device_node *wsi_dev, *next_wsi_dev;
+ struct device_node *tx_endpoint, *next_rx_endpoint;
+ int device_count = 0, wsi_master_index = -1;
+
+ wsi_dev = of_get_child_by_name(ab->dev->of_node, "wsi");
+ if (!wsi_dev)
+ return -ENODEV;
+
+ if (of_property_read_u32(wsi_dev, "qcom,wsi-group-id",
+ &wsi_info->group_id)) {
+ of_node_put(wsi_dev);
+ return -EINVAL;
+ }
+
+ next_wsi_dev = wsi_dev;
+
+ do {
+ if (of_property_read_bool(next_wsi_dev, "qcom,wsi-master"))
+ wsi_master_index = device_count;
+
+ tx_endpoint = of_graph_get_endpoint_by_regs(next_wsi_dev, 0, -1);
+ if (!tx_endpoint) {
+ of_node_put(next_wsi_dev);
+ return -ENODEV;
+ }
+
+ next_rx_endpoint = of_graph_get_remote_endpoint(tx_endpoint);
+ if (!next_rx_endpoint) {
+ of_node_put(next_wsi_dev);
+ of_node_put(tx_endpoint);
+ return -ENODEV;
+ }
+
+ of_node_put(tx_endpoint);
+ of_node_put(next_wsi_dev);
+ next_wsi_dev = of_graph_get_port_parent(next_rx_endpoint);
+ if (!next_wsi_dev) {
+ of_node_put(next_rx_endpoint);
+ return -ENODEV;
+ }
+ of_node_put(next_rx_endpoint);
+
+ device_count++;
+ if (device_count > ATH12K_MAX_SOCS) {
+ ath12k_warn(ab, "device count in DT %d is more than limit %d\n",
+ device_count, ATH12K_MAX_SOCS);
+ of_node_put(next_wsi_dev);
+ return -EINVAL;
+ }
+ } while (wsi_dev != next_wsi_dev);
+
+ of_node_put(next_wsi_dev);
+ if (wsi_master_index == -1) {
+ ath12k_dbg(ab, ATH12K_DBG_BOOT, "no WSI master defined in DT");
+ return -EINVAL;
+ }
+
+ wsi_info->num_devices = device_count;
+ wsi_info->index = (device_count - wsi_master_index) % device_count;
+ return 0;
+}
+
static struct ath12k_hw_group *ath12k_core_assign_hw_group(struct ath12k_base *ab)
{
struct ath12k_hw_group *ag;
- u32 group_id = ATH12K_INVALID_GROUP_ID;
+ struct ath12k_wsi_info *wsi = &ab->wsi_info;
+ int ret;
lockdep_assert_held(&ath12k_ag_list_lock);
- /* The grouping of multiple devices will be done based on device tree file.
- * TODO: device tree file parsing to know about the devices involved in group.
- *
- * The platforms that do not have any valid group information would have each
- * device to be part of its own invalid group.
+ /* The grouping of multiple devices will be done based on device
+ * tree file.
*
- * Currently, we are not parsing any device tree information and hence, grouping
- * of multiple devices is not involved. Thus, single device is added to device
- * group.
+ * The platforms that do not have any valid group information would
+ * have each device to be part of its own invalid group.
+ */
+ ret = ath12k_core_get_wsi_info(ab, wsi);
+ if (ret) {
+ ath12k_dbg(ab, ATH12K_DBG_BOOT,
+ "unable to get complete WSI Info from DT, err = %d", ret);
+ wsi->group_id = ATH12K_INVALID_GROUP_ID;
+ wsi->num_devices = 1;
+ wsi->index = 0;
+ goto invalid_group;
+ }
+
+ ath12k_dbg(ab, ATH12K_DBG_BOOT,
+ "WSI info: group-id: %d, num-devices: %d, index: %d",
+ wsi->group_id, wsi->num_devices, wsi->index);
+
+ /* Currently only one group of multiple devices are supported,
+ * since we use group id ATH12K_INVALID_GROUP_ID for single
+ * device group which didn't have dt entry, there could be many
+ * groups with same group id, i.e ATH12K_INVALID_GROUP_ID. So
+ * default group id of ATH12K_INVALID_GROUP_ID combined with
+ * num devices in ath12k_hw_group determines if the group is
+ * multi device or single device group
*/
- ag = ath12k_core_hw_group_alloc(group_id, 1);
+ ag = ath12k_core_hw_group_find_by_id(wsi->group_id);
+ if (!ag) {
+ ag = ath12k_core_hw_group_alloc(wsi->group_id, wsi->num_devices);
+ if (!ag) {
+ ath12k_warn(ab, "unable to create new hw group\n");
+ return NULL;
+ }
+ goto exit;
+ } else if (test_bit(ATH12K_GROUP_FLAG_UNREGISTER, &ag->flags)) {
+ ath12k_dbg(ab, ATH12K_DBG_BOOT, "group id %d in unregister state\n",
+ ag->id);
+ wsi->group_id = ATH12K_INVALID_GROUP_ID;
+ goto invalid_group;
+ } else {
+ goto exit;
+ }
+
+invalid_group:
+ ag = ath12k_core_hw_group_alloc(wsi->group_id, 1);
if (!ag) {
ath12k_warn(ab, "unable to create new hw group\n");
return NULL;
}
ath12k_dbg(ab, ATH12K_DBG_BOOT, "Single device is added to hardware group\n");
+exit:
+ if (ag->num_probed >= ag->num_devices) {
+ ath12k_warn(ab, "unable to add new device to group, max limit reached\n");
+ wsi->group_id = ATH12K_INVALID_GROUP_ID;
+ goto invalid_group;
+ }
+
ab->device_id = ag->num_probed++;
ag->ab[ab->device_id] = ab;
ab->ag = ag;
- ag->mlo_capable = false;
return ag;
}
@@ -1511,6 +1635,14 @@ static void ath12k_core_hw_group_cleanup(struct ath12k_hw_group *ag)
return;
mutex_lock(&ag->mutex_lock);
+
+ if (test_bit(ATH12K_GROUP_FLAG_UNREGISTER, &ag->flags)) {
+ mutex_unlock(&ag->mutex_lock);
+ return;
+ }
+
+ set_bit(ATH12K_GROUP_FLAG_UNREGISTER, &ag->flags);
+
ath12k_core_hw_group_stop(ag);
for (i = 0; i < ag->num_devices; i++) {
@@ -211,6 +211,7 @@ enum ath12k_scan_state {
enum ath12k_hw_group_flags {
ATH12K_GROUP_FLAG_REGISTERED,
+ ATH12K_GROUP_FLAG_UNREGISTER,
};
enum ath12k_dev_flags {
@@ -845,6 +846,12 @@ struct ath12k_hw_group {
bool mlo_capable;
};
+struct ath12k_wsi_info {
+ u32 group_id;
+ u32 num_devices;
+ u32 index;
+};
+
/* Master structure to hold the hw data which may be used in core module */
struct ath12k_base {
enum ath12k_hw_rev hw_rev;
@@ -1026,6 +1033,7 @@ struct ath12k_base {
struct notifier_block panic_nb;
struct ath12k_hw_group *ag;
+ struct ath12k_wsi_info wsi_info;
/* must be last */
u8 drv_priv[] __aligned(sizeof(void *));