Commit 976daac4 authored by David Dai's avatar David Dai Committed by Georgi Djakov

interconnect: qcom: Consolidate interconnect RPMh support

Add bcm voter driver and add support for RPMh specific interconnect
providers which implements the set and aggregate functionalities that
translates bandwidth requests into RPMh messages. These modules provide
a common set of functionalities for all Qualcomm RPMh based interconnect
providers and should help reduce code duplication when adding new
providers.
Signed-off-by: default avatarDavid Dai <daidavid1@codeaurora.org>
Signed-off-by: default avatarOdelu Kukatla <okukatla@codeaurora.org>
Reviewed-by: default avatarEvan Green <evgreen@chromium.org>
Signed-off-by: default avatarSibi Sankar <sibis@codeaurora.org>
Link: https://lore.kernel.org/r/20200228095951.15457-1-sibis@codeaurora.orgSigned-off-by: default avatarGeorgi Djakov <georgi.djakov@linaro.org>
parent 6f690e16
......@@ -5,6 +5,9 @@ config INTERCONNECT_QCOM
help
Support for Qualcomm's Network-on-Chip interconnect hardware.
config INTERCONNECT_QCOM_BCM_VOTER
tristate
config INTERCONNECT_QCOM_MSM8916
tristate "Qualcomm MSM8916 interconnect driver"
depends on INTERCONNECT_QCOM
......@@ -32,10 +35,15 @@ config INTERCONNECT_QCOM_QCS404
This is a driver for the Qualcomm Network-on-Chip on qcs404-based
platforms.
config INTERCONNECT_QCOM_RPMH
tristate
config INTERCONNECT_QCOM_SDM845
tristate "Qualcomm SDM845 interconnect driver"
depends on INTERCONNECT_QCOM
depends on (QCOM_RPMH && QCOM_COMMAND_DB && OF) || COMPILE_TEST
select INTERCONNECT_QCOM_RPMH
select INTERCONNECT_QCOM_BCM_VOTER
help
This is a driver for the Qualcomm Network-on-Chip on sdm845-based
platforms.
......
# SPDX-License-Identifier: GPL-2.0
icc-bcm-voter-objs := bcm-voter.o
qnoc-msm8916-objs := msm8916.o
qnoc-msm8974-objs := msm8974.o
qnoc-qcs404-objs := qcs404.o
icc-rpmh-obj := icc-rpmh.o
qnoc-sdm845-objs := sdm845.o
icc-smd-rpm-objs := smd-rpm.o
obj-$(CONFIG_INTERCONNECT_QCOM_BCM_VOTER) += icc-bcm-voter.o
obj-$(CONFIG_INTERCONNECT_QCOM_MSM8916) += qnoc-msm8916.o
obj-$(CONFIG_INTERCONNECT_QCOM_MSM8974) += qnoc-msm8974.o
obj-$(CONFIG_INTERCONNECT_QCOM_QCS404) += qnoc-qcs404.o
obj-$(CONFIG_INTERCONNECT_QCOM_RPMH) += icc-rpmh.o
obj-$(CONFIG_INTERCONNECT_QCOM_SDM845) += qnoc-sdm845.o
obj-$(CONFIG_INTERCONNECT_QCOM_SMD_RPM) += icc-smd-rpm.o
This diff is collapsed.
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2020, The Linux Foundation. All rights reserved.
*/
#ifndef __DRIVERS_INTERCONNECT_QCOM_BCM_VOTER_H__
#define __DRIVERS_INTERCONNECT_QCOM_BCM_VOTER_H__
#include <soc/qcom/cmd-db.h>
#include <soc/qcom/rpmh.h>
#include <soc/qcom/tcs.h>
#include "icc-rpmh.h"
#define DEFINE_QBCM(_name, _bcmname, _keepalive, ...) \
static struct qcom_icc_bcm _name = { \
.name = _bcmname, \
.keepalive = _keepalive, \
.num_nodes = ARRAY_SIZE(((struct qcom_icc_node *[]){ __VA_ARGS__ })), \
.nodes = { __VA_ARGS__ }, \
}
struct bcm_voter *of_bcm_voter_get(struct device *dev, const char *name);
void qcom_icc_bcm_voter_add(struct bcm_voter *voter, struct qcom_icc_bcm *bcm);
int qcom_icc_bcm_voter_commit(struct bcm_voter *voter);
#endif
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2020, The Linux Foundation. All rights reserved.
*/
#include <linux/interconnect.h>
#include <linux/interconnect-provider.h>
#include <linux/module.h>
#include "bcm-voter.h"
#include "icc-rpmh.h"
/**
* qcom_icc_pre_aggregate - cleans up stale values from prior icc_set
* @node: icc node to operate on
*/
void qcom_icc_pre_aggregate(struct icc_node *node)
{
size_t i;
struct qcom_icc_node *qn;
qn = node->data;
for (i = 0; i < QCOM_ICC_NUM_BUCKETS; i++) {
qn->sum_avg[i] = 0;
qn->max_peak[i] = 0;
}
}
EXPORT_SYMBOL_GPL(qcom_icc_pre_aggregate);
/**
* qcom_icc_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_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;
struct qcom_icc_provider *qp;
qn = node->data;
qp = to_qcom_provider(node->provider);
if (!tag)
tag = QCOM_ICC_TAG_ALWAYS;
for (i = 0; i < QCOM_ICC_NUM_BUCKETS; i++) {
if (tag & BIT(i)) {
qn->sum_avg[i] += avg_bw;
qn->max_peak[i] = max_t(u32, qn->max_peak[i], peak_bw);
}
}
*agg_avg += avg_bw;
*agg_peak = max_t(u32, *agg_peak, peak_bw);
for (i = 0; i < qn->num_bcms; i++)
qcom_icc_bcm_voter_add(qp->voter, qn->bcms[i]);
return 0;
}
EXPORT_SYMBOL_GPL(qcom_icc_aggregate);
/**
* qcom_icc_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_set(struct icc_node *src, struct icc_node *dst)
{
struct qcom_icc_provider *qp;
struct icc_node *node;
if (!src)
node = dst;
else
node = src;
qp = to_qcom_provider(node->provider);
qcom_icc_bcm_voter_commit(qp->voter);
return 0;
}
EXPORT_SYMBOL_GPL(qcom_icc_set);
/**
* qcom_icc_bcm_init - populates bcm aux data and connect qnodes
* @bcm: bcm to be initialized
* @dev: associated provider device
*
* Return: 0 on success, or an error code otherwise
*/
int qcom_icc_bcm_init(struct qcom_icc_bcm *bcm, struct device *dev)
{
struct qcom_icc_node *qn;
const struct bcm_db *data;
size_t data_count;
int i;
/* BCM is already initialised*/
if (bcm->addr)
return 0;
bcm->addr = cmd_db_read_addr(bcm->name);
if (!bcm->addr) {
dev_err(dev, "%s could not find RPMh address\n",
bcm->name);
return -EINVAL;
}
data = cmd_db_read_aux_data(bcm->name, &data_count);
if (IS_ERR(data)) {
dev_err(dev, "%s command db read error (%ld)\n",
bcm->name, PTR_ERR(data));
return PTR_ERR(data);
}
if (!data_count) {
dev_err(dev, "%s command db missing or partial aux data\n",
bcm->name);
return -EINVAL;
}
bcm->aux_data.unit = le32_to_cpu(data->unit);
bcm->aux_data.width = le16_to_cpu(data->width);
bcm->aux_data.vcd = data->vcd;
bcm->aux_data.reserved = data->reserved;
INIT_LIST_HEAD(&bcm->list);
INIT_LIST_HEAD(&bcm->ws_list);
/* Link Qnodes to their respective BCMs */
for (i = 0; i < bcm->num_nodes; i++) {
qn = bcm->nodes[i];
qn->bcms[qn->num_bcms] = bcm;
qn->num_bcms++;
}
return 0;
}
EXPORT_SYMBOL_GPL(qcom_icc_bcm_init);
MODULE_LICENSE("GPL v2");
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2020, The Linux Foundation. All rights reserved.
*/
#ifndef __DRIVERS_INTERCONNECT_QCOM_ICC_RPMH_H__
#define __DRIVERS_INTERCONNECT_QCOM_ICC_RPMH_H__
#define to_qcom_provider(_provider) \
container_of(_provider, struct qcom_icc_provider, provider)
/**
* struct qcom_icc_provider - Qualcomm specific interconnect provider
* @provider: generic interconnect provider
* @dev: reference to the NoC device
* @bcms: list of bcms that maps to the provider
* @num_bcms: number of @bcms
* @voter: bcm voter targeted by this provider
*/
struct qcom_icc_provider {
struct icc_provider provider;
struct device *dev;
struct qcom_icc_bcm **bcms;
size_t num_bcms;
struct bcm_voter *voter;
};
/**
* struct bcm_db - Auxiliary data pertaining to each Bus Clock Manager (BCM)
* @unit: divisor used to convert bytes/sec bw value to an RPMh msg
* @width: multiplier used to convert bytes/sec bw value to an RPMh msg
* @vcd: virtual clock domain that this bcm belongs to
* @reserved: reserved field
*/
struct bcm_db {
__le32 unit;
__le16 width;
u8 vcd;
u8 reserved;
};
#define MAX_LINKS 128
#define MAX_BCMS 64
#define MAX_BCM_PER_NODE 3
#define MAX_VCD 10
/*
* The AMC bucket denotes constraints that are applied to hardware when
* icc_set_bw() completes, whereas the WAKE and SLEEP constraints are applied
* when the execution environment transitions between active and low power mode.
*/
#define QCOM_ICC_BUCKET_AMC 0
#define QCOM_ICC_BUCKET_WAKE 1
#define QCOM_ICC_BUCKET_SLEEP 2
#define QCOM_ICC_NUM_BUCKETS 3
#define QCOM_ICC_TAG_AMC BIT(QCOM_ICC_BUCKET_AMC)
#define QCOM_ICC_TAG_WAKE BIT(QCOM_ICC_BUCKET_WAKE)
#define QCOM_ICC_TAG_SLEEP BIT(QCOM_ICC_BUCKET_SLEEP)
#define QCOM_ICC_TAG_ACTIVE_ONLY (QCOM_ICC_TAG_AMC | QCOM_ICC_TAG_WAKE)
#define QCOM_ICC_TAG_ALWAYS (QCOM_ICC_TAG_AMC | QCOM_ICC_TAG_WAKE |\
QCOM_ICC_TAG_SLEEP)
/**
* struct qcom_icc_node - Qualcomm specific interconnect nodes
* @name: the node name used in debugfs
* @links: an array of nodes where we can go next while traversing
* @id: a unique node identifier
* @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
* @sum_avg: current sum aggregate value of all avg bw requests
* @max_peak: current max aggregate value of all peak bw requests
* @bcms: list of bcms associated with this logical node
* @num_bcms: num of @bcms
*/
struct qcom_icc_node {
const char *name;
u16 links[MAX_LINKS];
u16 id;
u16 num_links;
u16 channels;
u16 buswidth;
u64 sum_avg[QCOM_ICC_NUM_BUCKETS];
u64 max_peak[QCOM_ICC_NUM_BUCKETS];
struct qcom_icc_bcm *bcms[MAX_BCM_PER_NODE];
size_t num_bcms;
};
/**
* struct qcom_icc_bcm - Qualcomm specific hardware accelerator nodes
* known as Bus Clock Manager (BCM)
* @name: the bcm node name used to fetch BCM data from command db
* @type: latency or bandwidth bcm
* @addr: address offsets used when voting to RPMH
* @vote_x: aggregated threshold values, represents sum_bw when @type is bw bcm
* @vote_y: aggregated threshold values, represents peak_bw when @type is bw bcm
* @dirty: flag used to indicate whether the bcm needs to be committed
* @keepalive: flag used to indicate whether a keepalive is required
* @aux_data: auxiliary data used when calculating threshold values and
* communicating with RPMh
* @list: used to link to other bcms when compiling lists for commit
* @ws_list: used to keep track of bcms that may transition between wake/sleep
* @num_nodes: total number of @num_nodes
* @nodes: list of qcom_icc_nodes that this BCM encapsulates
*/
struct qcom_icc_bcm {
const char *name;
u32 type;
u32 addr;
u64 vote_x[QCOM_ICC_NUM_BUCKETS];
u64 vote_y[QCOM_ICC_NUM_BUCKETS];
bool dirty;
bool keepalive;
struct bcm_db aux_data;
struct list_head list;
struct list_head ws_list;
size_t num_nodes;
struct qcom_icc_node *nodes[];
};
struct qcom_icc_fabric {
struct qcom_icc_node **nodes;
size_t num_nodes;
};
struct qcom_icc_desc {
struct qcom_icc_node **nodes;
size_t num_nodes;
struct qcom_icc_bcm **bcms;
size_t num_bcms;
};
#define DEFINE_QNODE(_name, _id, _channels, _buswidth, ...) \
static struct qcom_icc_node _name = { \
.id = _id, \
.name = #_name, \
.channels = _channels, \
.buswidth = _buswidth, \
.num_links = ARRAY_SIZE(((int[]){ __VA_ARGS__ })), \
.links = { __VA_ARGS__ }, \
}
int qcom_icc_aggregate(struct icc_node *node, u32 tag, u32 avg_bw,
u32 peak_bw, u32 *agg_avg, u32 *agg_peak);
int qcom_icc_set(struct icc_node *src, struct icc_node *dst);
int qcom_icc_bcm_init(struct qcom_icc_bcm *bcm, struct device *dev);
void qcom_icc_pre_aggregate(struct icc_node *node);
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment