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

Commit f0aabc60 authored by Pankaj Gupta's avatar Pankaj Gupta Committed by Gerrit - the friendly Code Review server
Browse files

soc: qcom: Add support for victim registration to throttle freq



Add support for victim registration API to handle the cx ipeak hw
interrupts (danger/safe) to throttle freq. Clients who has freq
limitation should use these API for victim registration and handle
these interrupts callbacks to limit client freq.

Change-Id: I8cfe747ece7ca1cf624a15d771b08850830fb5e3
Signed-off-by: default avatarPankaj Gupta <gpankaj@codeaurora.org>
parent 3423e083
Loading
Loading
Loading
Loading
+184 −2
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
 * Copyright (c) 2018-2020, The Linux Foundation. All rights reserved.
 */

#include <linux/module.h>
@@ -12,6 +12,7 @@
#include <linux/of_device.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/interrupt.h>

#include <soc/qcom/cx_ipeak.h>

@@ -29,9 +30,13 @@

#define CXIP_CLIENT_OFFSET				0x1000
#define CXIP_CLIENT10_OFFSET				0x3000
#define CXIP_VICTIM_OFFSET				0xB000

#define CXIP_POLL_TIMEOUT_US (50 * 1000)

#define CXIP_VICTIMS    3
#define VICTIM_ENTRIES    3

struct cx_ipeak_client;

struct cx_ipeak_core_ops {
@@ -43,14 +48,27 @@ static struct cx_ipeak_device {
	spinlock_t vote_lock;
	void __iomem *tcsr_vptr;
	struct cx_ipeak_core_ops *core_ops;
	u32 victims_count;
	u32 victims_reg_count;
	u32 danger_intr_num;
	u32 safe_intr_num;
} device_ipeak;

struct cx_ipeak_client {
	int vote_count;
	unsigned int offset;
	int client_id;
	struct cx_ipeak_device *dev;
};

static struct cx_ipeak_victims {
	int client_id;
	int victim_id;
	u32 freq_limit;
	void *data;
	cx_ipeak_victim_fn victim_cb;
} victims_ipeak[CXIP_VICTIMS];

/**
 * cx_ipeak_register() - allocate client structure and fill device private and
 *			offset details.
@@ -84,6 +102,9 @@ struct cx_ipeak_client *cx_ipeak_register(struct device_node *dev_node,
	if (device_ipeak.core_ops)
		client =  device_ipeak.core_ops->register_client
						(cx_spec.args[0]);

	client->client_id = cx_spec.args[0];

	return client;
}
EXPORT_SYMBOL(cx_ipeak_register);
@@ -145,7 +166,57 @@ static struct cx_ipeak_client *cx_ipeak_register_v2(int client_id)
	return client;
}

/*
/**
 * cx_ipeak_victim_register - victim registration API to handle
 * the cx ipeak hw interrupts (danger/safe) to throttle freq.
 * @client: cx ipeak client
 * @victim_cb: callback function of victim
 * @data: data to be passed to victim while handling irq
 */
int cx_ipeak_victim_register(struct cx_ipeak_client *client,
		cx_ipeak_victim_fn victim_cb, void *data)
{
	int i = 0;

	if (!victim_cb)
		return -EINVAL;

	while (i < device_ipeak.victims_count) {
		if (client->client_id == victims_ipeak[i].client_id) {
			victims_ipeak[i].victim_cb = victim_cb;
			victims_ipeak[i].data = data;
			device_ipeak.victims_reg_count++;
			break;
		}
		i++;
	}
	return 0;
}
EXPORT_SYMBOL(cx_ipeak_victim_register);

/**
 * cx_ipeak_victim_unregister - unregister victim client from
 * cx_ipeak driver.
 * @client: cx ipeak client
 */

void cx_ipeak_victim_unregister(struct cx_ipeak_client *client)
{
	int i = 0;

	while (i < device_ipeak.victims_count) {
		if (client->client_id == victims_ipeak[i].client_id) {
			victims_ipeak[i].victim_cb = NULL;
			victims_ipeak[i].data = NULL;
			device_ipeak.victims_reg_count--;
			break;
		}
		i++;
	}
}
EXPORT_SYMBOL(cx_ipeak_victim_unregister);

/**
 * cx_ipeak_update() - Set/Clear client vote for Cx iPeak limit
 * manager to throttle cDSP.
 * @client: client handle.
@@ -258,6 +329,61 @@ static int cx_ipeak_update_v2(struct cx_ipeak_client *client, bool vote)
	return ret;
}

static irqreturn_t cx_ipeak_irq_handler(int irq, void *data)
{
	int i;
	irqreturn_t ret = IRQ_NONE;

	for (i = 0; i < device_ipeak.victims_reg_count; i++) {
		cx_ipeak_victim_fn victim_cb = victims_ipeak[i].victim_cb;

		if (irq == device_ipeak.danger_intr_num) {
		/*
		 * To set frequency limit at victim client
		 * side in danger interrupt case
		 */
			victim_cb(victims_ipeak[i].data,
				victims_ipeak[i].freq_limit);
			writel_relaxed(1, (device_ipeak.tcsr_vptr +
						CXIP_VICTIM_OFFSET +
						((victims_ipeak[i].victim_id)*
							 CXIP_CLIENT_OFFSET)));
			ret = IRQ_HANDLED;
		} else if (irq == device_ipeak.safe_intr_num) {
		/*
		 * To remove frequency limit at victim client
		 * side in safe interrupt case
		 */
			victim_cb(victims_ipeak[i].data, 0);
			writel_relaxed(0, (device_ipeak.tcsr_vptr +
						CXIP_VICTIM_OFFSET +
						((victims_ipeak[i].victim_id)*
						 CXIP_CLIENT_OFFSET)));
			ret = IRQ_HANDLED;
		}
	}

	return ret;
}

int cx_ipeak_request_irq(struct platform_device *pdev, const  char *name,
		irq_handler_t handler, void *data)
{
	int ret, num = platform_get_irq_byname(pdev, name);

	if (num < 0)
		return num;

	ret = devm_request_irq(&pdev->dev, num, handler, IRQF_TRIGGER_RISING,
				name, data);

	if (ret)
		dev_err(&pdev->dev, "Unable to get interrupt %s: %d\n",
				name, ret);

	return ret ? ret : num;
}

/**
 * cx_ipeak_unregister() - unregister client
 * @client: client address to free
@@ -283,6 +409,9 @@ struct cx_ipeak_core_ops core_ops_v2 = {
static int cx_ipeak_probe(struct platform_device *pdev)
{
	struct resource *res;
	int status = -EINVAL;
	int i, ret, count;
	u32 victim_en;

	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	device_ipeak.tcsr_vptr = devm_ioremap_resource(&pdev->dev, res);
@@ -297,6 +426,59 @@ static int cx_ipeak_probe(struct platform_device *pdev)
	else
		device_ipeak.core_ops = NULL;

	victim_en = of_property_read_bool(pdev->dev.of_node,
			"victims_table");

	if (victim_en) {
		count = of_property_count_u32_elems(pdev->dev.of_node,
						"victims_table");

		if (((count%VICTIM_ENTRIES) != 0) ||
				((count/VICTIM_ENTRIES) > CXIP_VICTIMS))
			return -EINVAL;

		for (i = 0; i < (count/VICTIM_ENTRIES); i++) {
			ret = of_property_read_u32_index(pdev->dev.of_node,
					"victims_table", i*VICTIM_ENTRIES,
					&victims_ipeak[i].client_id);

			if (ret)
				return ret;

			ret = of_property_read_u32_index(pdev->dev.of_node,
					"victims_table", (i*VICTIM_ENTRIES) + 1,
					&victims_ipeak[i].victim_id);

			if (ret)
				return ret;

			ret = of_property_read_u32_index(pdev->dev.of_node,
					"victims_table", (i*VICTIM_ENTRIES) + 2,
					&victims_ipeak[i].freq_limit);

			if (ret)
				return ret;

			device_ipeak.victims_count++;
		}

		status = cx_ipeak_request_irq(pdev, "cx_ipeak_danger",
				cx_ipeak_irq_handler, NULL);

		if (status < 0)
			return status;

		device_ipeak.danger_intr_num = status;

		status = cx_ipeak_request_irq(pdev, "cx_ipeak_safe",
				cx_ipeak_irq_handler, NULL);

		if (status < 0)
			return status;

		device_ipeak.safe_intr_num = status;
	}

	spin_lock_init(&device_ipeak.vote_lock);
	return 0;
}
+16 −1
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * Copyright (c) 2018, The Linux Foundation. All rights reserved.
 * Copyright (c) 2018, 2020 The Linux Foundation. All rights reserved.
 */

#ifndef __SOC_COM_CX_IPEAK_H
#define __SOC_COM_CX_IPEAK_H

typedef void (*cx_ipeak_victim_fn)(void *data, u32 freq_limit);

struct device_node;
struct cx_ipeak_client;

@@ -27,12 +29,25 @@ static inline int cx_ipeak_update(struct cx_ipeak_client *ipeak_client,
{
	return 0;
}

static inline int cx_ipeak_victim_register(struct cx_ipeak_client *client,
		cx_ipeak_victim_fn victim_cb, void *data)
{
	return 0;
}

static inline void cx_ipeak_victim_unregister(struct cx_ipeak_client *client)
{
}
#else

struct cx_ipeak_client *cx_ipeak_register(struct device_node *dev_node,
		const char *client_name);
void cx_ipeak_unregister(struct cx_ipeak_client *client);
int cx_ipeak_update(struct cx_ipeak_client *ipeak_client, bool vote);
int cx_ipeak_victim_register(struct cx_ipeak_client *client,
		cx_ipeak_victim_fn victim_cb, void *data);
void cx_ipeak_victim_unregister(struct cx_ipeak_client *client);

#endif