Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 06bda73e authored by Yu Wang's avatar Yu Wang Committed by Gerrit - the friendly Code Review server
Browse files

cnss2: add support for converged dt node



Converge dt nodes for wlan chips those share the same
PCI slot into one node. The items in this node are
split into 2 parts: common and chip specific.

The common part will be parsed during cnss probe, such as
basic power related configuration and bus type, to power
on the device and trigger pci probe;
Chip specific part will be parsed during pci probe, such as
chip specific power and feature related configurations.

With this change, cnss2 can support different chips on
one pci slot with the same image.

CRs-Fixed: 2384088
Change-Id: I08c64b6dc3cc27a479ee6fd07e3c8705cc3ac43d
Signed-off-by: default avatarYu Wang <yyuwang@codeaurora.org>
parent f45aa3d8
Loading
Loading
Loading
Loading
+26 −6
Original line number Diff line number Diff line
/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
/* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -28,17 +28,37 @@ enum cnss_dev_bus_type cnss_get_dev_bus_type(struct device *dev)
		return CNSS_BUS_NONE;
}

enum cnss_dev_bus_type cnss_get_bus_type(unsigned long device_id)
enum cnss_dev_bus_type cnss_get_bus_type(struct cnss_plat_data *plat_priv)
{
	switch (device_id) {
	int ret;
	struct device *dev;
	u32 bus_type = CNSS_BUS_NONE;

	if (plat_priv->is_converged_dt) {
		dev = &plat_priv->plat_dev->dev;
		ret = of_property_read_u32(dev->of_node, "qcom,bus-type",
					   &bus_type);
		if (!ret)
			cnss_pr_dbg("Got bus type[%u] from dt\n", bus_type);
		else
			cnss_pr_err("No bus type for converged dt\n");

		return bus_type;
	}

	/* Get bus type according to device id if it's not converged DT */
	switch (plat_priv->device_id) {
	case QCA6174_DEVICE_ID:
	case QCA6290_DEVICE_ID:
	case QCA6390_DEVICE_ID:
		return CNSS_BUS_PCI;
		bus_type = CNSS_BUS_PCI;
		break;
	default:
		cnss_pr_err("Unknown device_id: 0x%lx\n", device_id);
		return CNSS_BUS_NONE;
		cnss_pr_err("Unknown device: 0x%lx\n", plat_priv->device_id);
		break;
	}

	return bus_type;
}

void *cnss_bus_dev_to_bus_priv(struct device *dev)
+2 −2
Original line number Diff line number Diff line
/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
/* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -26,7 +26,7 @@
#define QCA6390_DEVICE_ID		0x1101

enum cnss_dev_bus_type cnss_get_dev_bus_type(struct device *dev);
enum cnss_dev_bus_type cnss_get_bus_type(unsigned long device_id);
enum cnss_dev_bus_type cnss_get_bus_type(struct cnss_plat_data *plat_priv);
void *cnss_bus_dev_to_bus_priv(struct device *dev);
struct cnss_plat_data *cnss_bus_dev_to_plat_priv(struct device *dev);
int cnss_bus_init(struct cnss_plat_data *plat_priv);
+61 −6
Original line number Diff line number Diff line
@@ -642,6 +642,8 @@ static int cnss_get_resources(struct cnss_plat_data *plat_priv)

static void cnss_put_resources(struct cnss_plat_data *plat_priv)
{
	cnss_put_pinctrl(plat_priv);
	cnss_put_vreg(plat_priv);
}

static int cnss_modem_notifier_nb(struct notifier_block *nb,
@@ -759,7 +761,7 @@ static int cnss_subsys_powerup(const struct subsys_desc *subsys_desc)
	}

	if (!plat_priv->driver_state) {
		cnss_pr_dbg("Powerup is ignored\n");
		cnss_pr_dbg("subsys powerup is ignored\n");
		return 0;
	}

@@ -783,7 +785,7 @@ static int cnss_subsys_shutdown(const struct subsys_desc *subsys_desc,
	}

	if (!plat_priv->driver_state) {
		cnss_pr_dbg("shutdown is ignored\n");
		cnss_pr_dbg("subsys shutdown is ignored\n");
		return 0;
	}

@@ -1293,7 +1295,8 @@ static int cnss_register_ramdump_v1(struct cnss_plat_data *plat_priv)
	subsys_info = &plat_priv->subsys_info;
	ramdump_info = &plat_priv->ramdump_info;

	if (of_property_read_u32(dev->of_node, "qcom,wlan-ramdump-dynamic",
	if (of_property_read_u32(plat_priv->dev_node,
				 "qcom,wlan-ramdump-dynamic",
				 &ramdump_size) == 0) {
		ramdump_info->ramdump_va =
			dma_alloc_coherent(dev, ramdump_size,
@@ -1358,14 +1361,14 @@ static int cnss_register_ramdump_v2(struct cnss_plat_data *plat_priv)
	struct cnss_ramdump_info_v2 *info_v2;
	struct cnss_dump_data *dump_data;
	struct msm_dump_entry dump_entry;
	struct device *dev = &plat_priv->plat_dev->dev;
	u32 ramdump_size = 0;

	subsys_info = &plat_priv->subsys_info;
	info_v2 = &plat_priv->ramdump_info_v2;
	dump_data = &info_v2->dump_data;

	if (of_property_read_u32(dev->of_node, "qcom,wlan-ramdump-dynamic",
	if (of_property_read_u32(plat_priv->dev_node,
				 "qcom,wlan-ramdump-dynamic",
				 &ramdump_size) == 0)
		info_v2->ramdump_size = ramdump_size;

@@ -1625,6 +1628,7 @@ static const struct platform_device_id cnss_platform_id_table[] = {
	{ .name = "qca6174", .driver_data = QCA6174_DEVICE_ID, },
	{ .name = "qca6290", .driver_data = QCA6290_DEVICE_ID, },
	{ .name = "qca6390", .driver_data = QCA6390_DEVICE_ID, },
	{ .name = "qcaconv", .driver_data = 0},
};

static const struct of_device_id cnss_of_match_table[] = {
@@ -1637,10 +1641,55 @@ static const struct of_device_id cnss_of_match_table[] = {
	{
		.compatible = "qcom,cnss-qca6390",
		.data = (void *)&cnss_platform_id_table[2]},
	{
		.compatible = "qcom,cnss-qca-converged",
		.data = (void *)&cnss_platform_id_table[3]},
	{ },
};
MODULE_DEVICE_TABLE(of, cnss_of_match_table);

struct cnss_fw_path {
	unsigned long device_id;
	const char path[CNSS_FW_PATH_MAX_LEN];
};

static const struct cnss_fw_path cnss_fw_path_table[] = {
	{ QCA6174_DEVICE_ID, "qca6174/" },
	{ QCA6290_DEVICE_ID, "qca6290/" },
	{ QCA6390_DEVICE_ID, "qca6390/" },
	{ 0, "" }
};

const char *cnss_get_fw_path(struct cnss_plat_data *plat_priv)
{
	const struct cnss_fw_path *fw_path;
	const char *path;
	int size = ARRAY_SIZE(cnss_fw_path_table);

	if (!plat_priv->is_converged_dt) {
		path = cnss_fw_path_table[size - 1].path;
	} else {
		fw_path = cnss_fw_path_table;
		while (fw_path->device_id &&
		       fw_path->device_id != plat_priv->device_id) {
			fw_path++;
		}

		path = fw_path->path;
	}

	cnss_pr_dbg("get firmware path[%s] for device[0x%lx]\n",
		    path, plat_priv->device_id);
	return path;
}

static inline bool
cnss_is_converged_dt(struct cnss_plat_data *plat_priv)
{
	return of_property_read_bool(plat_priv->plat_dev->dev.of_node,
		"qcom,converged-dt");
}

static int cnss_probe(struct platform_device *plat_dev)
{
	int ret = 0;
@@ -1671,10 +1720,16 @@ static int cnss_probe(struct platform_device *plat_dev)
	}

	plat_priv->plat_dev = plat_dev;
	plat_priv->dev_node = NULL;
	plat_priv->device_id = device_id->driver_data;
	plat_priv->bus_type = cnss_get_bus_type(plat_priv->device_id);
	plat_priv->is_converged_dt = cnss_is_converged_dt(plat_priv);
	cnss_pr_dbg("Probing platform driver from %s DT\n",
		    plat_priv->is_converged_dt ? "converged" : "single");

	plat_priv->bus_type = cnss_get_bus_type(plat_priv);
	cnss_set_plat_priv(plat_dev, plat_priv);
	platform_set_drvdata(plat_dev, plat_priv);
	INIT_LIST_HEAD(&plat_priv->vreg_list);

	cnss_init_control_params(plat_priv);

+20 −5
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
#include <linux/etherdevice.h>
#include <linux/msm-bus.h>
#include <linux/pm_qos.h>
#include <linux/of.h>
#include <net/cnss2.h>
#include <soc/qcom/memory_dump.h>
#include <soc/qcom/subsystem_restart.h>
@@ -33,13 +34,14 @@
#define CNSS_EVENT_SYNC_UNINTERRUPTIBLE (CNSS_EVENT_SYNC | \
				CNSS_EVENT_UNINTERRUPTIBLE)

#define CNSS_FW_PATH_MAX_LEN 32

enum cnss_dev_bus_type {
	CNSS_BUS_NONE = -1,
	CNSS_BUS_PCI,
};

struct cnss_vreg_info {
	struct regulator *reg;
struct cnss_vreg_cfg {
	const char *name;
	u32 min_uv;
	u32 max_uv;
@@ -47,11 +49,19 @@ struct cnss_vreg_info {
	u32 delay_us;
};

struct cnss_vreg_info {
	struct list_head list;
	struct regulator *reg;
	struct cnss_vreg_cfg cfg;
	u32 enabled;
};

struct cnss_pinctrl_info {
	struct pinctrl *pinctrl;
	struct pinctrl_state *bootstrap_active;
	struct pinctrl_state *wlan_en_active;
	struct pinctrl_state *wlan_en_sleep;
	u32 activated;
};

struct cnss_subsys_info {
@@ -229,7 +239,7 @@ struct cnss_plat_data {
	struct platform_device *plat_dev;
	void *bus_priv;
	enum cnss_dev_bus_type bus_type;
	struct cnss_vreg_info *vreg_info;
	struct list_head vreg_list;
	struct cnss_pinctrl_info pinctrl_info;
	struct cnss_subsys_info subsys_info;
	struct cnss_ramdump_info ramdump_info;
@@ -267,10 +277,11 @@ struct cnss_plat_data {
	u32 diag_reg_read_len;
	u8 *diag_reg_read_buf;
	bool cal_done;
	bool powered_on;
	char firmware_name[13];
	char firmware_name[CNSS_FW_PATH_MAX_LEN];
	struct completion rddm_complete;
	struct cnss_control_params ctrl_params;
	u32 is_converged_dt;
	struct device_node *dev_node;
};

struct cnss_plat_data *cnss_get_plat_priv(struct platform_device *plat_dev);
@@ -279,6 +290,8 @@ int cnss_driver_event_post(struct cnss_plat_data *plat_priv,
			   u32 flags, void *data);
int cnss_get_vreg(struct cnss_plat_data *plat_priv);
int cnss_get_pinctrl(struct cnss_plat_data *plat_priv);
void cnss_put_vreg(struct cnss_plat_data *plat_priv);
void cnss_put_pinctrl(struct cnss_plat_data *plat_priv);
int cnss_power_on_device(struct cnss_plat_data *plat_priv);
void cnss_power_off_device(struct cnss_plat_data *plat_priv);
int cnss_register_subsys(struct cnss_plat_data *plat_priv);
@@ -286,5 +299,7 @@ void cnss_unregister_subsys(struct cnss_plat_data *plat_priv);
int cnss_register_ramdump(struct cnss_plat_data *plat_priv);
void cnss_unregister_ramdump(struct cnss_plat_data *plat_priv);
void cnss_set_pin_connect_status(struct cnss_plat_data *plat_priv);
const char *cnss_get_fw_path(struct cnss_plat_data *plat_priv);
int cnss_dev_specific_power_on(struct cnss_plat_data *plat_priv);

#endif /* _CNSS_MAIN_H */
+160 −31
Original line number Diff line number Diff line
@@ -14,7 +14,7 @@
#include <linux/irq.h>
#include <linux/module.h>
#include <linux/msi.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/pm_runtime.h>
#include <linux/memblock.h>
#include <linux/completion.h>
@@ -43,7 +43,6 @@
#define MHI_NODE_NAME			"qcom,mhi"
#define MHI_MSI_NAME			"MHI"

#define MAX_M3_FILE_NAME_LENGTH		13
#define DEFAULT_M3_FILE_NAME		"m3.bin"
#define DEFAULT_FW_FILE_NAME		"amss.bin"

@@ -1495,12 +1494,14 @@ int cnss_pci_load_m3(struct cnss_pci_data *pci_priv)
{
	struct cnss_plat_data *plat_priv = pci_priv->plat_priv;
	struct cnss_fw_mem *m3_mem = &plat_priv->m3_mem;
	char filename[MAX_M3_FILE_NAME_LENGTH];
	char filename[CNSS_FW_PATH_MAX_LEN];
	const struct firmware *fw_entry;
	int ret = 0;

	if (!m3_mem->va && !m3_mem->size) {
		snprintf(filename, sizeof(filename), DEFAULT_M3_FILE_NAME);
		snprintf(filename, sizeof(filename),
			 "%s" DEFAULT_M3_FILE_NAME,
			 cnss_get_fw_path(plat_priv));

		ret = request_firmware(&fw_entry, filename,
				       &pci_priv->pci_dev->dev);
@@ -2165,7 +2166,7 @@ static int cnss_pci_register_mhi(struct cnss_pci_data *pci_priv)

	mhi_ctrl->priv_data = pci_priv;
	mhi_ctrl->dev = &pci_dev->dev;
	mhi_ctrl->of_node = (&plat_priv->plat_dev->dev)->of_node;
	mhi_ctrl->of_node = plat_priv->dev_node;
	mhi_ctrl->dev_id = pci_priv->device_id;
	mhi_ctrl->domain = pci_domain_nr(pci_dev->bus);
	mhi_ctrl->bus = pci_dev->bus->number;
@@ -2436,13 +2437,143 @@ void cnss_pci_stop_mhi(struct cnss_pci_data *pci_priv)
	cnss_pci_set_mhi_state(pci_priv, CNSS_MHI_DEINIT);
}

static int cnss_pci_get_dev_cfg_node(struct cnss_plat_data *plat_priv)
{
	struct device_node *child;
	u32 id, i;
	int id_n, ret;

	if (!plat_priv->is_converged_dt) {
		plat_priv->dev_node = plat_priv->plat_dev->dev.of_node;
		return 0;
	}

	if (!plat_priv->device_id) {
		cnss_pr_err("Invalid device id\n");
		return -EINVAL;
	}

	for_each_available_child_of_node(plat_priv->plat_dev->dev.of_node,
					 child) {
		if (strcmp(child->name, "chip_cfg"))
			continue;

		id_n = of_property_count_u32_elems(child, "supported-ids");
		if (id_n <= 0) {
			cnss_pr_err("Device id is NOT set\n");
			return -EINVAL;
		}

		for (i = 0; i < id_n; i++) {
			ret = of_property_read_u32_index(child,
							 "supported-ids",
							 i, &id);
			if (ret) {
				cnss_pr_err("Failed to read supported ids\n");
				return -EINVAL;
			}

			if (id == plat_priv->device_id) {
				plat_priv->dev_node = child;
				cnss_pr_dbg("got node[%s@%d] for device[0x%x]\n",
					    child->name, i, id);
				return 0;
			}
		}
	}

	return -EINVAL;
}

/* For converged dt, property 'reg' is declared in sub node,
 * won't be parsed during probe.
 */
static int cnss_pci_get_smmu_cfg(struct cnss_plat_data *plat_priv)
{
	struct resource res_tmp;
	struct cnss_pci_data *pci_priv;
	struct resource *res;
	int index;
	int ret;
	struct device_node *dev_node;

	dev_node = (plat_priv->dev_node ?
		    plat_priv->dev_node : plat_priv->plat_dev->dev.of_node);

	if (plat_priv->is_converged_dt) {
		index = of_property_match_string(dev_node, "reg-names",
						 "smmu_iova_base");
		if (index < 0) {
			ret = -ENODATA;
			goto out;
		}
		ret = of_address_to_resource(dev_node, index, &res_tmp);
		if (ret)
			goto out;

		res = &res_tmp;
	} else {
		res = platform_get_resource_byname(plat_priv->plat_dev,
						   IORESOURCE_MEM,
						   "smmu_iova_base");
		if (!res) {
			ret = -ENODATA;
			goto out;
		}
	}

	pci_priv = plat_priv->bus_priv;
	if (of_property_read_bool(dev_node, "qcom,smmu-s1-enable"))
		pci_priv->smmu_s1_enable = true;

	pci_priv->smmu_iova_start = res->start;
	pci_priv->smmu_iova_len = resource_size(res);
	cnss_pr_dbg("smmu_iova_start: %pa, smmu_iova_len: %zu\n",
		    &pci_priv->smmu_iova_start,
		    pci_priv->smmu_iova_len);

	if (plat_priv->is_converged_dt) {
		index = of_property_match_string(dev_node, "reg-names",
						 "smmu_iova_ipa");
		if (index < 0) {
			ret = -ENODATA;
			goto out;
		}

		ret = of_address_to_resource(dev_node, index, &res_tmp);
		if (ret)
			goto out;

		res = &res_tmp;
	} else {
		res = platform_get_resource_byname(plat_priv->plat_dev,
						   IORESOURCE_MEM,
						   "smmu_iova_ipa");
		if (!res) {
			ret = -ENODATA;
			goto out;
		}
	}

	pci_priv->smmu_iova_ipa_start = res->start;
	pci_priv->smmu_iova_ipa_len = resource_size(res);
	cnss_pr_dbg("%s - smmu_iova_ipa_start: %pa, smmu_iova_ipa_len: %zu\n",
		    (plat_priv->is_converged_dt ?
		    "converged dt" : "single dt"),
		    &pci_priv->smmu_iova_ipa_start,
		    pci_priv->smmu_iova_ipa_len);
	return 0;

out:
	return ret;
}

static int cnss_pci_probe(struct pci_dev *pci_dev,
			  const struct pci_device_id *id)
{
	int ret = 0;
	struct cnss_pci_data *pci_priv;
	struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(NULL);
	struct resource *res;

	cnss_pr_dbg("PCI is probing, vendor ID: 0x%x, device ID: 0x%x\n",
		    id->vendor, pci_dev->device);
@@ -2462,8 +2593,19 @@ static int cnss_pci_probe(struct pci_dev *pci_dev,
	cnss_set_pci_priv(pci_dev, pci_priv);
	plat_priv->device_id = pci_dev->device;
	plat_priv->bus_priv = pci_priv;

	snprintf(plat_priv->firmware_name, sizeof(plat_priv->firmware_name),
		 DEFAULT_FW_FILE_NAME);
		 "%s" DEFAULT_FW_FILE_NAME, cnss_get_fw_path(plat_priv));

	ret = cnss_pci_get_dev_cfg_node(plat_priv);
	if (ret) {
		cnss_pr_err("Failed to get device cfg node, err = %d\n", ret);
		goto reset_ctx;
	}

	ret = cnss_dev_specific_power_on(plat_priv);
	if (ret)
		goto reset_ctx;

	ret = cnss_register_subsys(plat_priv);
	if (ret)
@@ -2473,30 +2615,8 @@ static int cnss_pci_probe(struct pci_dev *pci_dev,
	if (ret)
		goto unregister_subsys;

	res = platform_get_resource_byname(plat_priv->plat_dev, IORESOURCE_MEM,
					   "smmu_iova_base");
	if (res) {
		if (of_property_read_bool(plat_priv->plat_dev->dev.of_node,
					  "qcom,smmu-s1-enable"))
			pci_priv->smmu_s1_enable = true;

		pci_priv->smmu_iova_start = res->start;
		pci_priv->smmu_iova_len = resource_size(res);
		cnss_pr_dbg("smmu_iova_start: %pa, smmu_iova_len: %zu\n",
			    &pci_priv->smmu_iova_start,
			    pci_priv->smmu_iova_len);

		res = platform_get_resource_byname(plat_priv->plat_dev,
						   IORESOURCE_MEM,
						   "smmu_iova_ipa");
		if (res) {
			pci_priv->smmu_iova_ipa_start = res->start;
			pci_priv->smmu_iova_ipa_len = resource_size(res);
			cnss_pr_dbg("smmu_iova_ipa_start: %pa, smmu_iova_ipa_len: %zu\n",
				    &pci_priv->smmu_iova_ipa_start,
				    pci_priv->smmu_iova_ipa_len);
		}

	ret = cnss_pci_get_smmu_cfg(plat_priv);
	if (!ret) {
		ret = cnss_pci_init_smmu(pci_priv);
		if (ret) {
			cnss_pr_err("Failed to init SMMU, err = %d\n", ret);
@@ -2658,7 +2778,16 @@ int cnss_pci_init(struct cnss_plat_data *plat_priv)
		goto out;
	}

	if (!plat_priv->bus_priv) {
		cnss_pr_err("Failed to probe pci driver\n");
		ret = -ENODEV;
		goto deinit;
	}

	return 0;

deinit:
	pci_unregister_driver(&cnss_pci_driver);
out:
	return ret;
}
Loading