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

Commit 3e798bda authored by Prudhvi Yarlagadda's avatar Prudhvi Yarlagadda Committed by Gerrit - the friendly Code Review server
Browse files

qcom-geni-se: Geni common driver ICB vote logic changes



Use separate DDR and Corex/2x paths to put ICB vote.
Geni common driver aggregates the avg bandwidth put by individual
drivers to vote for DDR path bus bandwidth and for corex/core2x
separately. Common driver takes Instantaneous bandwidth put by
individual drivers also into consideration to put vote to ICB
driver for required bandwidth.

Change-Id: I0c729ea1590a8759b3985cfd0d81ecd57a795a85
Signed-off-by: default avatarPrudhvi Yarlagadda <pyarlaga@codeaurora.org>
parent 5f62bf9b
Loading
Loading
Loading
Loading
+301 −33
Original line number Diff line number Diff line
@@ -36,6 +36,11 @@
static unsigned long default_bus_bw_set[] = {0, 19200000, 50000000,
				100000000, 150000000, 200000000, 236000000};

struct bus_vectors {
	int src;
	int dst;
};

/**
 * @struct geni_se_device - Data structure to represent the QUPv3 Core
 * @dev:		Device pointer of the QUPv3 core.
@@ -45,18 +50,32 @@ static unsigned long default_bus_bw_set[] = {0, 19200000, 50000000,
 * @iommu_s1_bypass:	Bypass IOMMU stage 1 translation.
 * @base:		Base address of this instance of QUPv3 core.
 * @bus_bw:		Client handle to the bus bandwidth request.
 * @bus_bw_noc:		Client handle to the QUP clock and DDR path bus
			bandwidth request.
 * @bus_mas_id:		Master Endpoint ID for bus BW request.
 * @bus_slv_id:		Slave Endpoint ID for bus BW request.
 * @geni_dev_lock:		Lock to protect the bus ab & ib values, list.
 * @ab_list_head:	Sorted resource list based on average bus BW.
 * @ib_list_head:	Sorted resource list based on instantaneous bus BW.
 * @ab_list_head_noc:	Sorted resource list based on average DDR path bus BW.
 * @ib_list_head_noc:	Sorted resource list based on instantaneous DDR path
			bus BW.
 * @cur_ab:		Current Bus Average BW request value.
 * @cur_ib:		Current Bus Instantaneous BW request value.
 * @cur_ab_noc:		Current DDR Bus Average BW request value.
 * @cur_ib_noc:		Current DDR Bus Instantaneous BW request value.
 * @bus_bw_set:		Clock plan for the bus driver.
 * @bus_bw_set_noc:	Clock plan for DDR path.
 * @cur_bus_bw_idx:	Current index within the bus clock plan.
 * @cur_bus_bw_idx_noc:	Current index within the DDR path clock plan.
 * @num_clk_levels:	Number of valid clock levels in clk_perf_tbl.
 * @clk_perf_tbl:	Table of clock frequency input to Serial Engine clock.
 * @log_ctx:		Logging context to hold the debug information
 * @log_ctx:		Logging context to hold the debug information.
 * @vectors:		Structure to store Master End and Slave End IDs for
			QUPv3 clock and DDR path bus BW request.
 * @num_paths:		Two paths. QUPv3 clock and DDR paths.
 * @num_usecases:	One usecase to vote for both QUPv3 clock and DDR paths.
 * @pdata:		To register our client handle with the ICB driver.
 */
struct geni_se_device {
	struct device *dev;
@@ -66,19 +85,31 @@ struct geni_se_device {
	bool iommu_s1_bypass;
	void __iomem *base;
	struct msm_bus_client_handle *bus_bw;
	uint32_t bus_bw_noc;
	u32 bus_mas_id;
	u32 bus_slv_id;
	struct mutex geni_dev_lock;
	struct list_head ab_list_head;
	struct list_head ib_list_head;
	struct list_head ab_list_head_noc;
	struct list_head ib_list_head_noc;
	unsigned long cur_ab;
	unsigned long cur_ib;
	unsigned long cur_ab_noc;
	unsigned long cur_ib_noc;
	int bus_bw_set_size;
	int bus_bw_set_size_noc;
	unsigned long *bus_bw_set;
	unsigned long *bus_bw_set_noc;
	int cur_bus_bw_idx;
	int cur_bus_bw_idx_noc;
	unsigned int num_clk_levels;
	unsigned long *clk_perf_tbl;
	void *log_ctx;
	struct bus_vectors *vectors;
	int num_paths;
	int num_usecases;
	struct msm_bus_scale_pdata *pdata;
};

/* Offset of QUPV3 Hardware Version Register */
@@ -641,11 +672,37 @@ static bool geni_se_check_bus_bw(struct geni_se_device *geni_se_dev)
	return bus_bw_update;
}

static bool geni_se_check_bus_bw_noc(struct geni_se_device *geni_se_dev)
{
	int i;
	int new_bus_bw_idx = geni_se_dev->bus_bw_set_size_noc - 1;
	unsigned long new_bus_bw;
	bool bus_bw_update = false;

	new_bus_bw = max(geni_se_dev->cur_ib_noc, geni_se_dev->cur_ab_noc) /
							DEFAULT_BUS_WIDTH;

	for (i = 0; i < geni_se_dev->bus_bw_set_size_noc; i++) {
		if (geni_se_dev->bus_bw_set_noc[i] >= new_bus_bw) {
			new_bus_bw_idx = i;
			break;
		}
	}

	if (geni_se_dev->cur_bus_bw_idx_noc != new_bus_bw_idx) {
		geni_se_dev->cur_bus_bw_idx_noc = new_bus_bw_idx;
		bus_bw_update = true;
	}

	return bus_bw_update;
}

static int geni_se_rmv_ab_ib(struct geni_se_device *geni_se_dev,
			     struct se_geni_rsc *rsc)
{
	struct se_geni_rsc *tmp;
	bool bus_bw_update = false;
	bool bus_bw_update_noc = false;
	int ret = 0;

	if (unlikely(list_empty(&rsc->ab_list) || list_empty(&rsc->ib_list)))
@@ -664,7 +721,15 @@ static int geni_se_rmv_ab_ib(struct geni_se_device *geni_se_dev,
		geni_se_dev->cur_ib = 0;

	bus_bw_update = geni_se_check_bus_bw(geni_se_dev);
	if (bus_bw_update)

	if (geni_se_dev->num_paths == 2) {
		geni_se_dev->pdata->usecase[1].vectors[0].ab  =
			geni_se_dev->cur_ab;
		geni_se_dev->pdata->usecase[1].vectors[0].ib  =
			geni_se_dev->cur_ib;
	}

	if (bus_bw_update && geni_se_dev->num_paths != 2) {
		ret = msm_bus_scale_update_bw(geni_se_dev->bus_bw,
						geni_se_dev->cur_ab,
						geni_se_dev->cur_ib);
@@ -672,6 +737,35 @@ static int geni_se_rmv_ab_ib(struct geni_se_device *geni_se_dev,
			"%s: %s: cur_ab_ib(%lu:%lu) req_ab_ib(%lu:%lu) %d\n",
			__func__, dev_name(rsc->ctrl_dev), geni_se_dev->cur_ab,
			geni_se_dev->cur_ib, rsc->ab, rsc->ib, bus_bw_update);
	}

	if (geni_se_dev->num_paths == 2) {
		if (unlikely(list_empty(&rsc->ab_list_noc) ||
					list_empty(&rsc->ib_list_noc)))
			return -EINVAL;

		list_del_init(&rsc->ab_list_noc);
		geni_se_dev->cur_ab_noc -= rsc->ab_noc;

		list_del_init(&rsc->ib_list_noc);
		tmp = list_first_entry_or_null(&geni_se_dev->ib_list_head_noc,
					struct se_geni_rsc, ib_list_noc);
		if (tmp && tmp->ib_noc != geni_se_dev->cur_ib_noc)
			geni_se_dev->cur_ib_noc = tmp->ib_noc;
		else if (!tmp && geni_se_dev->cur_ib_noc)
			geni_se_dev->cur_ib_noc = 0;

		bus_bw_update_noc = geni_se_check_bus_bw_noc(geni_se_dev);

			geni_se_dev->pdata->usecase[1].vectors[1].ab  =
				geni_se_dev->cur_ab_noc;
			geni_se_dev->pdata->usecase[1].vectors[1].ib  =
				geni_se_dev->cur_ib_noc;

		if (bus_bw_update_noc || bus_bw_update)
			ret = msm_bus_scale_client_update_request
						(geni_se_dev->bus_bw_noc, 1);
	}
	mutex_unlock(&geni_se_dev->geni_dev_lock);
	return ret;
}
@@ -692,7 +786,8 @@ int se_geni_clks_off(struct se_geni_rsc *rsc)
		return -EINVAL;

	geni_se_dev = dev_get_drvdata(rsc->wrapper_dev);
	if (unlikely(!geni_se_dev || !geni_se_dev->bus_bw))
	if (unlikely(!geni_se_dev || !(geni_se_dev->bus_bw ||
					geni_se_dev->bus_bw_noc)))
		return -ENODEV;

	clk_disable_unprepare(rsc->se_clk);
@@ -703,6 +798,7 @@ int se_geni_clks_off(struct se_geni_rsc *rsc)
	if (ret)
		GENI_SE_ERR(geni_se_dev->log_ctx, false, NULL,
			"%s: Error %d during bus_bw_update\n", __func__, ret);

	return ret;
}
EXPORT_SYMBOL(se_geni_clks_off);
@@ -723,7 +819,9 @@ int se_geni_resources_off(struct se_geni_rsc *rsc)
		return -EINVAL;

	geni_se_dev = dev_get_drvdata(rsc->wrapper_dev);
	if (unlikely(!geni_se_dev || !geni_se_dev->bus_bw))
	if (unlikely(!geni_se_dev ||
			!(geni_se_dev->bus_bw ||
					geni_se_dev->bus_bw_noc)))
		return -ENODEV;

	ret = se_geni_clks_off(rsc);
@@ -743,10 +841,13 @@ static int geni_se_add_ab_ib(struct geni_se_device *geni_se_dev,
{
	struct se_geni_rsc *tmp = NULL;
	struct list_head *ins_list_head;
	struct list_head *ins_list_head_noc;
	bool bus_bw_update = false;
	bool bus_bw_update_noc = false;
	int ret = 0;

	mutex_lock(&geni_se_dev->geni_dev_lock);

	list_add(&rsc->ab_list, &geni_se_dev->ab_list_head);
	geni_se_dev->cur_ab += rsc->ab;

@@ -762,14 +863,51 @@ static int geni_se_add_ab_ib(struct geni_se_device *geni_se_dev,
		geni_se_dev->cur_ib = rsc->ib;

	bus_bw_update = geni_se_check_bus_bw(geni_se_dev);
	if (bus_bw_update)

	if (geni_se_dev->num_paths == 2) {
		geni_se_dev->pdata->usecase[1].vectors[0].ab  =
			geni_se_dev->cur_ab;
		geni_se_dev->pdata->usecase[1].vectors[0].ib  =
			geni_se_dev->cur_ib;
	}

	if (bus_bw_update && geni_se_dev->num_paths != 2) {
		ret = msm_bus_scale_update_bw(geni_se_dev->bus_bw,
						geni_se_dev->cur_ab,
						geni_se_dev->cur_ib);
		GENI_SE_DBG(geni_se_dev->log_ctx, false, NULL,
			"%s: %s: cur_ab_ib(%lu:%lu) req_ab_ib(%lu:%lu) %d\n",
			__func__, dev_name(rsc->ctrl_dev), geni_se_dev->cur_ab,
			geni_se_dev->cur_ib, rsc->ab, rsc->ib, bus_bw_update);
			"%s: %lu:%lu (%lu:%lu) %d\n", __func__,
			geni_se_dev->cur_ab, geni_se_dev->cur_ib,
			rsc->ab, rsc->ib, bus_bw_update);
	}

	if (geni_se_dev->num_paths == 2) {

		list_add(&rsc->ab_list_noc, &geni_se_dev->ab_list_head_noc);
		geni_se_dev->cur_ab_noc += rsc->ab_noc;
		ins_list_head_noc = &geni_se_dev->ib_list_head_noc;

		list_for_each_entry(tmp, &geni_se_dev->ib_list_head_noc,
					ib_list_noc) {
			if (tmp->ib < rsc->ib)
				break;
			ins_list_head_noc = &tmp->ib_list_noc;
		}
		list_add(&rsc->ib_list_noc, ins_list_head_noc);

		if (ins_list_head_noc == &geni_se_dev->ib_list_head_noc)
			geni_se_dev->cur_ib_noc = rsc->ib_noc;

		bus_bw_update_noc = geni_se_check_bus_bw_noc(geni_se_dev);

			geni_se_dev->pdata->usecase[1].vectors[1].ab  =
				geni_se_dev->cur_ab_noc;
			geni_se_dev->pdata->usecase[1].vectors[1].ib  =
				geni_se_dev->cur_ib_noc;
		if (bus_bw_update_noc || bus_bw_update)
			ret = msm_bus_scale_client_update_request
						(geni_se_dev->bus_bw_noc, 1);
	}
	mutex_unlock(&geni_se_dev->geni_dev_lock);
	return ret;
}
@@ -882,6 +1020,25 @@ int geni_se_resources_init(struct se_geni_rsc *rsc,
	if (unlikely(!geni_se_dev))
		return -EPROBE_DEFER;

	if (geni_se_dev->num_paths == 2) {
		if (unlikely(!(geni_se_dev->bus_bw_noc))) {
			geni_se_dev->bus_bw_noc =
			msm_bus_scale_register_client(geni_se_dev->pdata);
			if (!(geni_se_dev->bus_bw_noc)) {
				GENI_SE_ERR(geni_se_dev->log_ctx,
					false, NULL,
				"%s: Error creating bus client\n",  __func__);
				return -EFAULT;
			}
		}

		rsc->ab = ab;
		rsc->ib = ab;
		rsc->ab_noc = 0;
		rsc->ib_noc = ib;
		INIT_LIST_HEAD(&rsc->ab_list_noc);
		INIT_LIST_HEAD(&rsc->ib_list_noc);
	} else {
		if (unlikely(IS_ERR_OR_NULL(geni_se_dev->bus_bw))) {
			geni_se_dev->bus_bw = msm_bus_scale_register(
						geni_se_dev->bus_mas_id,
@@ -889,14 +1046,16 @@ int geni_se_resources_init(struct se_geni_rsc *rsc,
					(char *)dev_name(geni_se_dev->dev),
						false);
			if (IS_ERR_OR_NULL(geni_se_dev->bus_bw)) {
			GENI_SE_ERR(geni_se_dev->log_ctx, false, NULL,
				GENI_SE_ERR(geni_se_dev->log_ctx,
					false, NULL,
				"%s: Error creating bus client\n", __func__);
				return (int)PTR_ERR(geni_se_dev->bus_bw);
			}
		}

		rsc->ab = ab;
		rsc->ib = ib;
	}

	INIT_LIST_HEAD(&rsc->ab_list);
	INIT_LIST_HEAD(&rsc->ib_list);

@@ -1429,6 +1588,87 @@ static const struct of_device_id geni_se_dt_match[] = {
	{}
};

static struct msm_bus_scale_pdata *ab_ib_register(struct platform_device *pdev,
				struct geni_se_device *host)
{
	int rc = 0;
	struct device *dev = &pdev->dev;
	int i = 0, j, len;
	bool mem_err = false;
	const uint32_t *vec_arr = NULL;
	struct msm_bus_scale_pdata *pdata = NULL;
	struct msm_bus_paths *usecase = NULL;

	vec_arr = of_get_property(dev->of_node,
			"qcom,msm-bus,vectors-bus-ids", &len);
	if (vec_arr == NULL) {
		pr_err("Error: Vector array not found\n");
		rc = 1;
		goto out;
	}

	if (len != host->num_paths * sizeof(uint32_t) * 2) {
		pr_err("Error: Length-error on getting vectors\n");
		rc = 1;
		goto out;
	}


	pdata = devm_kzalloc(dev, sizeof(struct msm_bus_scale_pdata),
							GFP_KERNEL);
	if (!pdata) {
		mem_err = true;
		goto out;
	}

	pdata->name = (char *)dev_name(host->dev);

	pdata->num_usecases = 2;

	pdata->active_only = 0;

	usecase = devm_kzalloc(dev, (sizeof(struct msm_bus_paths) *
		pdata->num_usecases), GFP_KERNEL);
	if (!usecase) {
		mem_err = true;
		goto out;
	}

	for (i = 0; i < pdata->num_usecases; i++) {
		usecase[i].num_paths = host->num_paths;
		usecase[i].vectors = devm_kzalloc(dev, host->num_paths *
			sizeof(struct msm_bus_vectors), GFP_KERNEL);
		if (!usecase[i].vectors) {
			mem_err = true;
			pr_err("Error: Mem alloc failure in vectors\n");
			goto out;
		}

		for (j = 0; j < host->num_paths; j++) {
			int index = (j * 2);

			usecase[i].vectors[j].src =
					be32_to_cpu(vec_arr[index]);
			usecase[i].vectors[j].dst =
					be32_to_cpu(vec_arr[index + 1]);
			usecase[i].vectors[j].ab = 0;
			usecase[i].vectors[j].ib = 0;
		}
	}

	pdata->usecase = usecase;

	return pdata;
out:
	if (mem_err) {
		for ( ; i > 0; i--)
			devm_kfree(dev, usecase[i-1].vectors);
		devm_kfree(dev, usecase);
		devm_kfree(dev, pdata);
	}
	return NULL;
}

static int geni_se_iommu_probe(struct device *dev)
{
	struct geni_se_device *geni_se_dev;
@@ -1489,29 +1729,57 @@ static int geni_se_probe(struct platform_device *pdev)
	}

	geni_se_dev->dev = dev;
	geni_se_dev->cb_dev = dev;

	ret = of_property_read_u32(dev->of_node, "qcom,msm-bus,num-paths",
					&geni_se_dev->num_paths);
	if (!ret) {
		geni_se_dev->pdata = ab_ib_register(pdev, geni_se_dev);
		if (geni_se_dev->pdata == NULL) {
			dev_err(dev,
			"%s: Error missing bus master and slave id\n",
								__func__);
			devm_iounmap(dev, geni_se_dev->base);
			devm_kfree(dev, geni_se_dev);
		}
	}

	else {
		geni_se_dev->num_paths = 1;
		ret = of_property_read_u32(dev->of_node, "qcom,bus-mas-id",
				   &geni_se_dev->bus_mas_id);
		if (ret) {
		dev_err(dev, "%s: Error missing bus master id\n", __func__);
			dev_err(dev, "%s: Error missing bus master id\n",
								__func__);
			devm_iounmap(dev, geni_se_dev->base);
			devm_kfree(dev, geni_se_dev);
		}
		ret = of_property_read_u32(dev->of_node, "qcom,bus-slv-id",
				   &geni_se_dev->bus_slv_id);
		if (ret) {
		dev_err(dev, "%s: Error missing bus slave id\n", __func__);
			dev_err(dev, "%s: Error missing bus slave id\n",
								 __func__);
			devm_iounmap(dev, geni_se_dev->base);
			devm_kfree(dev, geni_se_dev);
		}
	}

	geni_se_dev->iommu_s1_bypass = of_property_read_bool(dev->of_node,
							"qcom,iommu-s1-bypass");
	geni_se_dev->bus_bw_set = default_bus_bw_set;
	geni_se_dev->bus_bw_set_size = ARRAY_SIZE(default_bus_bw_set);
	geni_se_dev->bus_bw_set_size =
				ARRAY_SIZE(default_bus_bw_set);
	if (geni_se_dev->num_paths == 2) {
		geni_se_dev->bus_bw_set_noc = default_bus_bw_set;
		geni_se_dev->bus_bw_set_size_noc =
				ARRAY_SIZE(default_bus_bw_set);
	}
	mutex_init(&geni_se_dev->iommu_lock);
	INIT_LIST_HEAD(&geni_se_dev->ab_list_head);
	INIT_LIST_HEAD(&geni_se_dev->ib_list_head);
	if (geni_se_dev->num_paths == 2) {
		INIT_LIST_HEAD(&geni_se_dev->ab_list_head_noc);
		INIT_LIST_HEAD(&geni_se_dev->ib_list_head_noc);
	}
	mutex_init(&geni_se_dev->geni_dev_lock);
	geni_se_dev->log_ctx = ipc_log_context_create(NUM_LOG_PAGES,
						dev_name(geni_se_dev->dev), 0);
+12 −1
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */
/*
 * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
 * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved.
 */

#ifndef _LINUX_QCOM_GENI_SE
@@ -37,9 +37,16 @@ enum se_protocol_types {
 * @m_ahb_clk:		Handle to the primary AHB clock.
 * @s_ahb_clk:		Handle to the secondary AHB clock.
 * @ab_list:		List Head of Average bus banwidth list.
 * @ab_list_noc:	List Head of Average DDR path bus
			bandwidth list.
 * @ab:			Average bus bandwidth request value.
 * @ab_noc:		Average DDR path bus bandwidth request value.
 * @ib_list:		List Head of Instantaneous bus banwidth list.
 * @ib_list_noc:	List Head of Instantaneous DDR path bus
			bandwidth list.
 * @ib:			Instantaneous bus bandwidth request value.
 * @ib_noc:		Instantaneous DDR path bus bandwidth
			request value.
 * @geni_pinctrl:	Handle to the pinctrl configuration.
 * @geni_gpio_active:	Handle to the default/active pinctrl state.
 * @geni_gpi_sleep:	Handle to the sleep pinctrl state.
@@ -51,9 +58,13 @@ struct se_geni_rsc {
	struct clk *m_ahb_clk;
	struct clk *s_ahb_clk;
	struct list_head ab_list;
	struct list_head ab_list_noc;
	unsigned long ab;
	unsigned long ab_noc;
	struct list_head ib_list;
	struct list_head ib_list_noc;
	unsigned long ib;
	unsigned long ib_noc;
	struct pinctrl *geni_pinctrl;
	struct pinctrl_state *geni_gpio_active;
	struct pinctrl_state *geni_gpio_sleep;