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

Commit 192eb3b2 authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "soc: qcom: glink: Add power vote database for QoS Support"

parents b9c92d0d ac20ef95
Loading
Loading
Loading
Loading
+22 −0
Original line number Diff line number Diff line
Qualcomm Technologies, Inc. G-link QoS Configuration

Required properties:
-compatible : should be "qcom,glink-qos-config"
-qcom,flow-info : A table of MTU transmission time specific for a power state.
		The total number of entries in the table should be equal to the
		number of supported flows.
-qcom,mtu-size : The MTU size for which the qos elements are configured.
-qcom,tput-stats-cycle: The number of allowable cycles for a packet to be
			transmitted without its priority being re-evaluated.

Example:

	qcom,glink-qos-config-adsp {
		compatible = "qcom,glink-qos-config";
		qcom,flow-info = <0x80 0x0>,
				 <0x70 0x1>,
				 <0x60 0x2>,
				 <0x50 0x3>;
		qcom,mtu-size = <0x800>;
		qcom,tput-stats-cycle = <0xa>;
	};
+31 −0
Original line number Diff line number Diff line
@@ -10,6 +10,12 @@ Required properties:
-interrupts : the receiving interrupt line
-label : the name of the subsystem this link connects to

Optional properties:
-qcom,qos-config: Reference to the qos configuration elements.It depends on
		ramp-time.
-qcom,ramp-time: Worst case time in microseconds to transition to this power
		state. Power states are numbered by array index position.

Example:

	qcom,glink-smem-native-xprt-modem@fa00000 {
@@ -21,3 +27,28 @@ Example:
		interrupts = <0 25 1>;
		label = "mpss";
	};

	qcom,glink-smem-native-xprt-adsp@fa00000 {
		compatible = "qcom,glink-smem-native-xprt";
		reg = <0xfa00000 0x200000>,
			<0xfa006008 0x4>;
		reg-names = "smem", "irq-reg-base";
		qcom,irq-mask = <0x1000>;
		interrupts = <0 25 1>;
		label = "lpass";
		qcom,qos-config = <&glink_qos_adsp>;
		qcom,ramp-time = <0x10>,
				     <0x20>,
				     <0x30>,
				     <0x40>;
	};

	glink_qos_adsp: qcom,glink-qos-config-adsp {
		compatible = "qcom,glink-qos-config";
		qcom,flow-info = <0x80 0x0>,
				 <0x70 0x1>,
				 <0x60 0x2>,
				 <0x50 0x3>;
		qcom,mtu-size = <0x800>;
		qcom,tput-stats-cycle = <0xa>;
	};
+91 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@
#include <linux/spinlock.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/types.h>
@@ -3331,6 +3332,96 @@ int glink_xprt_name_to_id(const char *name, uint16_t *id)
}
EXPORT_SYMBOL(glink_xprt_name_to_id);

/**
 * of_get_glink_core_qos_cfg() - Parse the qos related dt entries
 * @phandle:	The handle to the qos related node in DT.
 * @cfg:	The transport configuration to be filled.
 *
 * Return: 0 on Success, standard Linux error otherwise.
 */
int of_get_glink_core_qos_cfg(struct device_node *phandle,
				struct glink_core_transport_cfg *cfg)
{
	int rc, i;
	char *key;
	uint32_t num_flows;
	uint32_t *arr32;

	if (!phandle) {
		GLINK_ERR("%s: phandle is NULL\n", __func__);
		return -EINVAL;
	}

	key = "qcom,mtu-size";
	rc = of_property_read_u32(phandle, key, (uint32_t *)&cfg->mtu);
	if (rc) {
		GLINK_ERR("%s: missing key %s\n", __func__, key);
		return -ENODEV;
	}

	key = "qcom,tput-stats-cycle";
	rc = of_property_read_u32(phandle, key, &cfg->token_count);
	if (rc) {
		GLINK_ERR("%s: missing key %s\n", __func__, key);
		rc = -ENODEV;
		goto error;
	}

	key = "qcom,flow-info";
	if (!of_find_property(phandle, key, &num_flows)) {
		GLINK_ERR("%s: missing key %s\n", __func__, key);
		rc = -ENODEV;
		goto error;
	}

	num_flows /= sizeof(uint32_t);
	if (num_flows % 2) {
		GLINK_ERR("%s: Invalid flow info length\n", __func__);
		rc = -EINVAL;
		goto error;
	}

	num_flows /= 2;
	cfg->num_flows = num_flows;

	cfg->flow_info = kmalloc_array(num_flows, sizeof(cfg->flow_info),
					GFP_KERNEL);
	if (!cfg->flow_info) {
		GLINK_ERR("%s: Memory allocation for flow info failed\n",
				__func__);
		rc = -ENOMEM;
		goto error;
	}
	arr32 = kmalloc_array(num_flows, sizeof(uint32_t), GFP_KERNEL);
	if (!arr32) {
		GLINK_ERR("%s: Memory allocation for temporary array failed\n",
				__func__);
		rc = -ENOMEM;
		goto temp_mem_alloc_fail;
	}

	of_property_read_u32_array(phandle, key, arr32, num_flows * 2);

	for (i = 0; i < num_flows; i++) {
		cfg->flow_info[i].mtu_tx_time_us = arr32[2 * i];
		cfg->flow_info[i].power_state = arr32[2 * i + 1];
	}

	kfree(arr32);
	of_node_put(phandle);
	return 0;

temp_mem_alloc_fail:
	kfree(cfg->flow_info);
error:
	cfg->mtu = 0;
	cfg->token_count = 0;
	cfg->num_flows = 0;
	cfg->flow_info = NULL;
	return rc;
}
EXPORT_SYMBOL(of_get_glink_core_qos_cfg);

/**
 * glink_core_init_xprt_qos_cfg() - Initialize a transport's QoS configuration
 * @xprt_ptr:	Transport to be initialized with QoS configuration.
+11 −0
Original line number Diff line number Diff line
@@ -12,6 +12,7 @@
#ifndef _SOC_QCOM_GLINK_CORE_IF_H_
#define _SOC_QCOM_GLINK_CORE_IF_H_

#include <linux/of.h>
#include <linux/types.h>
#include "glink_private.h"

@@ -168,6 +169,16 @@ int glink_core_register_transport(struct glink_transport_if *if_ptr,

void glink_core_unregister_transport(struct glink_transport_if *if_ptr);

/**
 * of_get_glink_core_qos_cfg() - Parse the qos related dt entries
 * @phandle:	The handle to the qos related node in DT.
 * @cfg:	The transport configuration to be filled.
 *
 * Return: 0 on Success, standard Linux error otherwise.
 */
int of_get_glink_core_qos_cfg(struct device_node *phandle,
				struct glink_core_transport_cfg *cfg);

/**
 * rx_linear_vbuf_provider() - Virtual Buffer Provider for linear buffers
 * iovec:	Pointer to the beginning of the linear buffer.
+113 −0
Original line number Diff line number Diff line
@@ -156,6 +156,9 @@ struct channel_desc {
 *				processing.
 * @deferred_cmds:		List of deferred commands that need to be
 *				processed in process context.
 * @num_pw_states:		Size of @ramp_time_us.
 * @ramp_time_us:		Array of ramp times in microseconds where array
 *				index position represents a power state.
 */
struct edge_info {
	struct glink_transport_if xprt_if;
@@ -187,6 +190,8 @@ struct edge_info {
	bool in_ssr;
	spinlock_t rx_lock;
	struct list_head deferred_cmds;
	uint32_t num_pw_states;
	unsigned long *ramp_time_us;
};

/**
@@ -1924,6 +1929,51 @@ static int tx_cmd_tracer_pkt(struct glink_transport_if *if_ptr, uint32_t lcid,
	return tx_data(if_ptr, TRACER_PKT_CMD, lcid, pctx);
}

/**
 * get_power_vote_ramp_time() - Get the ramp time required for the power
 *				votes to be applied
 * @if_ptr:	The transport interface on which power voting is requested.
 * @state:	The power state for which ramp time is required.
 *
 * Return: The ramp time specific to the power state, standard error otherwise.
 */
static unsigned long get_power_vote_ramp_time(
				struct glink_transport_if *if_ptr,
				uint32_t state)
{
	struct edge_info *einfo;

	einfo = container_of(if_ptr, struct edge_info, xprt_if);

	if (state >= einfo->num_pw_states || !(einfo->ramp_time_us))
		return (unsigned long)ERR_PTR(-EINVAL);

	return einfo->ramp_time_us[state];
}

/**
 * power_vote() - Update the power votes to meet qos requirement
 * @if_ptr:	The transport interface on which power voting is requested.
 * @state:	The power state for which the voting should be done.
 *
 * Return: 0 on Success, standard error otherwise.
 */
static int power_vote(struct glink_transport_if *if_ptr, uint32_t state)
{
	return 0;
}

/**
 * power_unvote() - Remove the all the power votes
 * @if_ptr:	The transport interface on which power voting is requested.
 *
 * Return: 0 on Success, standard error otherwise.
 */
static int power_unvote(struct glink_transport_if *if_ptr)
{
	return 0;
}

/**
 * negotiate_features_v1() - determine what features of a version can be used
 * @if_ptr:	The transport for which features are negotiated for.
@@ -1966,6 +2016,9 @@ static void init_xprt_if(struct edge_info *einfo)
	einfo->xprt_if.mask_rx_irq = mask_rx_irq;
	einfo->xprt_if.wait_link_down = wait_link_down;
	einfo->xprt_if.tx_cmd_tracer_pkt = tx_cmd_tracer_pkt;
	einfo->xprt_if.get_power_vote_ramp_time = get_power_vote_ramp_time;
	einfo->xprt_if.power_vote = power_vote;
	einfo->xprt_if.power_unvote = power_unvote;
}

/**
@@ -1983,6 +2036,59 @@ static void init_xprt_cfg(struct edge_info *einfo, const char *name)
	einfo->xprt_cfg.max_iid = SZ_2G;
}

/**
 * parse_qos_dt_params() - Parse the power states from DT
 * @dev:	Reference to the platform device for a specific edge.
 * @einfo:	Edge information for the edge probe function is called.
 *
 * Return: 0 on success, standard error code otherwise.
 */
static int parse_qos_dt_params(struct device_node *node,
				struct edge_info *einfo)
{
	int rc;
	int i;
	char *key;
	uint32_t *arr32;
	uint32_t num_states;

	key = "qcom,ramp-time";
	if (!of_find_property(node, key, &num_states))
		return -ENODEV;

	num_states /= sizeof(uint32_t);

	einfo->num_pw_states = num_states;

	arr32 = kmalloc_array(num_states, sizeof(uint32_t), GFP_KERNEL);
	if (!arr32)
		return -ENOMEM;

	einfo->ramp_time_us = kmalloc_array(num_states, sizeof(unsigned long),
					GFP_KERNEL);
	if (!einfo->ramp_time_us) {
		rc = -ENOMEM;
		goto mem_alloc_fail;
	}

	rc = of_property_read_u32_array(node, key, arr32, num_states);
	if (rc) {
		rc = -ENODEV;
		goto invalid_key;
	}
	for (i = 0; i < num_states; i++)
		einfo->ramp_time_us[i] = arr32[i];

	rc = 0;
	return rc;

invalid_key:
	kfree(einfo->ramp_time_us);
mem_alloc_fail:
	kfree(arr32);
	return rc;
}

/**
 * subsys_name_to_id() - translate a subsystem name to a processor id
 * @name:	The subsystem name to look up.
@@ -2012,6 +2118,7 @@ static int subsys_name_to_id(const char *name)
static int glink_smem_native_probe(struct platform_device *pdev)
{
	struct device_node *node;
	struct device_node *phandle_node;
	struct edge_info *einfo;
	int rc;
	char *key;
@@ -2133,6 +2240,12 @@ static int glink_smem_native_probe(struct platform_device *pdev)
		goto smem_alloc_fail;
	}

	key = "qcom,qos-config";
	phandle_node = of_parse_phandle(node, key, 0);
	if (phandle_node && !(of_get_glink_core_qos_cfg(phandle_node,
							&einfo->xprt_cfg)))
		parse_qos_dt_params(node, einfo);

	rc = glink_core_register_transport(&einfo->xprt_if, &einfo->xprt_cfg);
	if (rc == -EPROBE_DEFER)
		goto reg_xprt_fail;