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

Commit cb65dc24 authored by qctecmdr's avatar qctecmdr Committed by Gerrit - the friendly Code Review server
Browse files

Merge "interconnect: qcom: Add interconnect provider driver for Holi"

parents 0335c137 0320785f
Loading
Loading
Loading
Loading
+14 −0
Original line number Diff line number Diff line
@@ -5,6 +5,17 @@ config INTERCONNECT_QCOM
	help
	  Support for Qualcomm's Network-on-Chip interconnect hardware.

config INTERCONNECT_QCOM_HOLI
	tristate "Qualcomm HOLI interconnect driver"
	depends on INTERCONNECT_QCOM
	depends on MSM_RPM_SMD
	select INTERCONNECT_QCOM_RPM
	help
	  This is a driver for the Qualcomm Technologies, Inc. Network-on-Chip
	  on holi-based platforms. The interconnect provider collects and
	  aggreagates the cosumer bandwidth requests to satisfy constraints
	  placed on Network-on-Chip performance states.

config INTERCONNECT_QCOM_QCS404
	tristate "Qualcomm QCS404 interconnect driver"
	depends on INTERCONNECT_QCOM
@@ -70,3 +81,6 @@ config INTERCONNECT_QCOM_SDXLEMUR
	help
	  This is a driver for the Qualcomm Technologies, Inc. Network-on-Chip
	  on sdxlemur-based platforms.

config INTERCONNECT_QCOM_RPM
	tristate
+4 −0
Original line number Diff line number Diff line
# SPDX-License-Identifier: GPL-2.0

qnoc-holi-objs			:= holi.o
qnoc-qcs404-objs			:= qcs404.o
qnoc-sdm845-objs			:= sdm845.o
qnoc-lahaina-objs			:= lahaina.o qnoc-qos.o
@@ -8,12 +9,15 @@ qnoc-shima-objs := shima.o
icc-smd-rpm-objs			:= smd-rpm.o
icc-bcm-voter-objs			:= bcm-voter.o
icc-rpmh-obj				:= icc-rpmh.o
icc-rpm-objs			:= icc-rpm.o

obj-$(CONFIG_INTERCONNECT_QCOM_HOLI) += qnoc-holi.o
obj-$(CONFIG_INTERCONNECT_QCOM_QCS404) += qnoc-qcs404.o
obj-$(CONFIG_INTERCONNECT_QCOM_SDM845) += qnoc-sdm845.o
obj-$(CONFIG_INTERCONNECT_QCOM_LAHAINA) += qnoc-lahaina.o
obj-$(CONFIG_INTERCONNECT_QCOM_SHIMA) += qnoc-shima.o
obj-$(CONFIG_INTERCONNECT_QCOM_SDXLEMUR) += qnoc-sdxlemur.o
obj-$(CONFIG_INTERCONNECT_QCOM_RPM) += icc-rpm.o
obj-$(CONFIG_INTERCONNECT_QCOM_SMD_RPM) += icc-smd-rpm.o
obj-$(CONFIG_INTERCONNECT_QCOM_BCM_VOTER) += icc-bcm-voter.o
obj-$(CONFIG_INTERCONNECT_QCOM_RPMH) += icc-rpmh.o
+1327 −0

File added.

Preview size limit exceeded, changes collapsed.

+199 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (c) 2020, The Linux Foundation. All rights reserved.
 *
 */

#include <asm/div64.h>
#include <linux/clk.h>
#include <linux/interconnect.h>
#include <linux/interconnect-provider.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/soc/qcom/smd-rpm.h>

#include "icc-rpm.h"

static int qcom_icc_rpm_smd_send_msg(int ctx, int rsc_type, int rpm_id, u64 val)
{
	int ret;
	struct msm_rpm_kvp rpm_kvp;

	rpm_kvp.length = sizeof(uint64_t);
	rpm_kvp.key = RPM_MASTER_FIELD_BW;
	rpm_kvp.data = (uint8_t *)&val;

	ret = msm_rpm_send_message(ctx, rsc_type, rpm_id, &rpm_kvp, 1);

	return ret;
}

/**
 * qcom_icc_rpm_pre_aggregate - cleans up stale values from prior icc_set
 * @node: icc node to operate on
 */
void qcom_icc_rpm_pre_aggregate(struct icc_node *node)
{
	size_t i;
	struct qcom_icc_node *qn;

	qn = node->data;

	for (i = 0; i < RPM_NUM_CXT; i++) {
		qn->sum_avg[i] = 0;
		qn->max_peak[i] = 0;
	}
}
EXPORT_SYMBOL_GPL(qcom_icc_rpm_pre_aggregate);

/**
 * qcom_icc_rpm_aggregate - aggregate bw for buckets indicated by tag
 * @node: node to aggregate
 * @tag: tag to indicate which buckets to aggregate
 * @avg_bw: new bw to sum aggregate
 * @peak_bw: new bw to max aggregate
 * @agg_avg: existing aggregate avg bw val
 * @agg_peak: existing aggregate peak bw val
 */
int qcom_icc_rpm_aggregate(struct icc_node *node, u32 tag, u32 avg_bw,
		       u32 peak_bw, u32 *agg_avg, u32 *agg_peak)
{
	size_t i;
	struct qcom_icc_node *qn;

	qn = node->data;

	if (!tag)
		tag = BIT(RPM_SLEEP_CXT) | BIT(RPM_ACTIVE_CXT);
	else
		tag = BIT(RPM_ACTIVE_CXT);

	for (i = 0; i < RPM_NUM_CXT; i++) {
		if (tag & BIT(i)) {
			qn->sum_avg[i] += avg_bw;
			qn->max_peak[i] = max_t(u32, qn->max_peak[i], peak_bw);
		}
	}

	qn->dirty = true;

	return 0;
}
EXPORT_SYMBOL_GPL(qcom_icc_rpm_aggregate);

/**
 * qcom_icc_rpm_set - set the constraints based on path
 * @src: source node for the path to set constraints on
 * @dst: destination node for the path to set constraints on
 *
 * Return: 0 on success, or an error code otherwise
 */
int qcom_icc_rpm_set(struct icc_node *src, struct icc_node *dst)
{
	struct qcom_icc_provider *qp;
	struct qcom_icc_node *qn;
	struct icc_node *n, *node;
	struct icc_provider *provider;
	int ret, i;
	int rpm_ctx;
	u64 clk_rate, sum_avg, max_peak;
	u64 bus_clk_rate[RPM_NUM_CXT] = {0, 0};

	if (!src)
		node = dst;
	else
		node = src;

	qp = to_qcom_provider(node->provider);
	qn = node->data;

	if (!qn->dirty)
		return 0;

	provider = node->provider;

	list_for_each_entry(n, &provider->nodes, node_list) {
		qn = n->data;
		for (i = 0; i < RPM_NUM_CXT; i++) {
			sum_avg = icc_units_to_bps(qn->sum_avg[i]);
			do_div(sum_avg, qn->channels);
			max_peak = icc_units_to_bps(qn->max_peak[i]);

			clk_rate = max(sum_avg, max_peak);
			do_div(clk_rate, qn->buswidth);

			bus_clk_rate[i] = max(bus_clk_rate[i], clk_rate);
		}
	}

	for (i = 0; i < RPM_NUM_CXT; i++) {
		if (qp->bus_clk_cur_rate[i] != bus_clk_rate[i]) {
			ret = clk_set_rate(qp->bus_clks[i].clk,
					bus_clk_rate[i]);
			if (ret) {
				pr_err("%s clk_set_rate error: %d\n",
					qp->bus_clks[i].id, ret);
				return ret;
			}

			qp->bus_clk_cur_rate[i] = bus_clk_rate[i];
		}
	}

	list_for_each_entry(n, &provider->nodes, node_list) {
		qn = n->data;
		if (!qn->dirty)
			continue;

		qn->dirty = false;
		if ((qn->mas_rpm_id == -1) && (qn->slv_rpm_id == -1))
			continue;

		/* send bandwidth request message to the RPM processor */
		for (i = 0; i < RPM_NUM_CXT; i++) {
			if (qn->last_sum_avg[i] != qn->sum_avg[i]) {
				rpm_ctx = (i == RPM_SLEEP_CXT) ?
					RPM_SLEEP_SET : RPM_ACTIVE_SET;

				sum_avg = icc_units_to_bps(qn->sum_avg[i]);

				if (qn->mas_rpm_id != -1) {
					ret = qcom_icc_rpm_smd_send_msg(
						rpm_ctx,
						RPM_BUS_MASTER_REQ,
						qn->mas_rpm_id,
						sum_avg);

					if (ret) {
						pr_err("qcom_icc_rpm_smd_send_msg mas %d error %d\n",
							qn->mas_rpm_id, ret);
						return ret;
					}
				}

				if (qn->slv_rpm_id != -1) {
					ret = qcom_icc_rpm_smd_send_msg(
						rpm_ctx,
						RPM_BUS_SLAVE_REQ,
						qn->slv_rpm_id,
						sum_avg);

					if (ret) {
						pr_err("qcom_icc_rpm_smd_send_msg slv %s error %d\n",
							qn->slv_rpm_id, ret);
						return ret;
					}
				}

				qn->last_sum_avg[i] = qn->sum_avg[i];
			}
		}
	}

	return 0;
}
EXPORT_SYMBOL_GPL(qcom_icc_rpm_set);

MODULE_LICENSE("GPL v2");
+102 −0
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * Copyright (c) 2020, The Linux Foundation. All rights reserved.
 *
 */

#ifndef __DRIVERS_INTERCONNECT_QCOM_ICC_RPM_H__
#define __DRIVERS_INTERCONNECT_QCOM_ICC_RPM_H__

#include <linux/regmap.h>
#include <soc/qcom/rpm-smd.h>

#define MAX_LINKS		64

#define RPM_BUS_MASTER_REQ		0x73616d62
#define RPM_BUS_SLAVE_REQ		0x766c7362

#define RPM_SLEEP_SET		MSM_RPM_CTX_SLEEP_SET
#define RPM_ACTIVE_SET		MSM_RPM_CTX_ACTIVE_SET

#define to_qcom_provider(_provider) \
	container_of(_provider, struct qcom_icc_provider, provider)

enum qcom_icc_rpm_slave_field_type {
	RPM_SLAVE_FIELD_BW = 0x00007762,
};

enum qcom_icc_rpm_mas_field_type {
	RPM_MASTER_FIELD_BW = 0x00007762,
	RPM_MASTER_FIELD_BW_T0 = 0x30747762,
	RPM_MASTER_FIELD_BW_T1 = 0x31747762,
	RPM_MASTER_FIELD_BW_T2 = 0x32747762,
};

enum qcom_icc_rpm_context {
	RPM_SLEEP_CXT,
	RPM_ACTIVE_CXT,
	RPM_NUM_CXT
};

/**
 * struct qcom_icc_provider - QTI specific interconnect provider
 * @provider: generic interconnect provider
 * @dev: reference to the NoC device
 * @bus_clks: the clk_bulk_data table of bus clocks
 * @num_clks: the total number of clk_bulk_data entries
 * @bus_clk_cur_rate: current frequency of bus clock
 */
struct qcom_icc_provider {
	struct icc_provider provider;
	struct device *dev;
	struct regmap *regmap;
	struct clk_bulk_data *bus_clks;
	int num_clks;
	u64 bus_clk_cur_rate[RPM_NUM_CXT];
};

/**
 * struct qcom_icc_node - QTI specific interconnect nodes
 * @name: the node name used in debugfs
 * @id: a unique node identifier
 * @links: an array of nodes where we can go next while traversing
 * @num_links: the total number of @links
 * @channels: num of channels at this node
 * @buswidth: width of the interconnect between a node and the bus (bytes)
 * @last_sum_avg: aggregated average bandwidth from previous aggregation
 * @sum_avg: current sum aggregate value of all avg bw requests
 * @max_peak: current max aggregate value of all peak bw requests
 * @mas_rpm_id: RPM id for devices that are bus masters
 * @slv_rpm_id: RPM id for devices that are bus slaves
 * @dirty: flag used to indicate whether the node needs to be committed
 */
struct qcom_icc_node {
	const char *name;
	u16 id;
	u16 links[MAX_LINKS];
	u16 num_links;
	u16 channels;
	u16 buswidth;
	u64 last_sum_avg[RPM_NUM_CXT];
	u64 sum_avg[RPM_NUM_CXT];
	u64 max_peak[RPM_NUM_CXT];
	int mas_rpm_id;
	int slv_rpm_id;
	struct regmap *regmap;
	struct qcom_icc_qosbox *qosbox;
	const struct qcom_icc_noc_ops *noc_ops;
	bool dirty;
};

struct qcom_icc_desc {
	const struct regmap_config *config;
	struct qcom_icc_node **nodes;
	size_t num_nodes;
};

int qcom_icc_rpm_aggregate(struct icc_node *node, u32 tag, u32 avg_bw,
			      u32 peak_bw, u32 *agg_avg, u32 *agg_peak);
int qcom_icc_rpm_set(struct icc_node *src, struct icc_node *dst);
void qcom_icc_rpm_pre_aggregate(struct icc_node *node);

#endif
Loading