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

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

Merge "soc: qcom: Update cx_ipeak victim handling"

parents 3c3b8712 d4d7b2c6
Loading
Loading
Loading
Loading
+116 −79
Original line number Diff line number Diff line
@@ -7,7 +7,6 @@
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/printk.h>
#include <linux/spinlock.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/slab.h>
@@ -44,30 +43,34 @@ struct cx_ipeak_core_ops {
	struct cx_ipeak_client* (*register_client)(int client_id);
};

static struct cx_ipeak_victims {
	u32 client_id;
	u32 victim_id;
	u32 freq_limit;
	void *data;
	cx_ipeak_victim_fn victim_cb;
	struct cx_ipeak_client *client;
} victim_list[CXIP_VICTIMS];

static struct cx_ipeak_device {
	spinlock_t vote_lock;
	struct platform_device *pdev;
	struct mutex vote_lock;
	struct mutex throttle_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;
	int danger_intr_num;
	int safe_intr_num;
} device_ipeak;

struct cx_ipeak_client {
	int vote_count;
	u32 vote_count;
	unsigned int offset;
	int client_id;
	u32 client_id;
	bool danger_assert;
	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
@@ -181,17 +184,16 @@ int cx_ipeak_victim_register(struct cx_ipeak_client *client,
	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++;
	}
	for (i = 0; i < device_ipeak.victims_count; i++)
		if (client->client_id == victim_list[i].client_id) {
			victim_list[i].victim_cb = victim_cb;
			victim_list[i].data = data;
			victim_list[i].client = client;
			return 0;
		}

	return -ENOENT;
}
EXPORT_SYMBOL(cx_ipeak_victim_register);

/**
@@ -204,14 +206,11 @@ 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++;
	for (i = 0; i < device_ipeak.victims_count; i++)
		if (client->client_id == victim_list[i].client_id) {
			victim_list[i].victim_cb = NULL;
			victim_list[i].data = NULL;
			victim_list[i].client = NULL;
		}
}
EXPORT_SYMBOL(cx_ipeak_victim_unregister);
@@ -243,7 +242,7 @@ static int cx_ipeak_update_v1(struct cx_ipeak_client *client, bool vote)
	unsigned int reg_val;
	int ret = 0;

	spin_lock(&client->dev->vote_lock);
	mutex_lock(&client->dev->vote_lock);

	if (vote) {
		if (client->vote_count == 0) {
@@ -282,16 +281,16 @@ static int cx_ipeak_update_v1(struct cx_ipeak_client *client, bool vote)
	}

done:
	spin_unlock(&client->dev->vote_lock);
	mutex_unlock(&client->dev->vote_lock);
	return ret;
}

static int cx_ipeak_update_v2(struct cx_ipeak_client *client, bool vote)
{
	unsigned int reg_val;
	u32 reg_val;
	int ret = 0;

	spin_lock(&client->dev->vote_lock);
	mutex_lock(&client->dev->vote_lock);

	if (vote) {
		if (client->vote_count == 0) {
@@ -299,10 +298,19 @@ static int cx_ipeak_update_v2(struct cx_ipeak_client *client, bool vote)
					client->dev->tcsr_vptr +
					client->offset);

			ret = readl_poll_timeout(client->dev->tcsr_vptr +
			ret = readl_poll_timeout(
					client->dev->tcsr_vptr +
					TCSR_CXIP_LM_DANGER_OFFSET,
						 reg_val, !reg_val, 0,
						 CXIP_POLL_TIMEOUT_US);
					reg_val, !reg_val ||
					client->danger_assert,
					0, CXIP_POLL_TIMEOUT_US);
			/*
			 * If poll exits due to danger assert condition return
			 * error to client to avoid voting.
			 */
			if (client->danger_assert)
				ret = -ETIMEDOUT;

			if (ret) {
				writel_relaxed(0,
					       client->dev->tcsr_vptr +
@@ -325,57 +333,86 @@ static int cx_ipeak_update_v2(struct cx_ipeak_client *client, bool vote)
	}

done:
	spin_unlock(&client->dev->vote_lock);
	mutex_unlock(&client->dev->vote_lock);
	return ret;
}

static irqreturn_t cx_ipeak_irq_handler(int irq, void *data)
static irqreturn_t cx_ipeak_irq_soft_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;
	mutex_lock(&device_ipeak.throttle_lock);

	for (i = 0; i < device_ipeak.victims_count; i++) {
		cx_ipeak_victim_fn victim_cb = victim_list[i].victim_cb;
		struct cx_ipeak_client *victim_client = victim_list[i].client;

		if (!victim_cb || !victim_client)
			continue;

		if (irq == device_ipeak.danger_intr_num) {

			victim_client->danger_assert = true;

			/*
			 * To set frequency limit at victim client
			 * side in danger interrupt case
			 */
			victim_cb(victims_ipeak[i].data,
				victims_ipeak[i].freq_limit);

			ret = victim_cb(victim_list[i].data,
					victim_list[i].freq_limit);

			if (ret) {
				dev_err(&device_ipeak.pdev->dev,
					"Unable to throttle client:%d freq:%d\n",
					victim_list[i].client_id,
					victim_list[i].freq_limit);
				victim_client->danger_assert = false;
				ret = IRQ_HANDLED;
				goto done;
			}

			writel_relaxed(1, (device_ipeak.tcsr_vptr +
						CXIP_VICTIM_OFFSET +
						((victims_ipeak[i].victim_id)*
						((victim_list[i].victim_id)*
						 CXIP_CLIENT_OFFSET)));

			ret = IRQ_HANDLED;
		} else if (irq == device_ipeak.safe_intr_num) {
			victim_client->danger_assert = false;
			/*
			 * To remove frequency limit at victim client
			 * side in safe interrupt case
			 */
			victim_cb(victims_ipeak[i].data, 0);
			ret = victim_cb(victim_list[i].data, 0);

			if (ret)
				dev_err(&device_ipeak.pdev->dev, "Unable to remove freq limit client:%d\n",
						victim_list[i].client_id);

			writel_relaxed(0, (device_ipeak.tcsr_vptr +
						CXIP_VICTIM_OFFSET +
						((victims_ipeak[i].victim_id)*
						((victim_list[i].victim_id)*
						 CXIP_CLIENT_OFFSET)));
			ret = IRQ_HANDLED;
		}
	}

done:
	mutex_unlock(&device_ipeak.throttle_lock);
	return ret;
}

int cx_ipeak_request_irq(struct platform_device *pdev, const  char *name,
		irq_handler_t handler, void *data)
		irq_handler_t handler, irq_handler_t thread_fn, 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);
	ret = devm_request_threaded_irq(&pdev->dev, num, handler, thread_fn,
			IRQF_ONESHOT | IRQF_TRIGGER_RISING, name, data);

	if (ret)
		dev_err(&pdev->dev, "Unable to get interrupt %s: %d\n",
@@ -409,7 +446,6 @@ 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;

@@ -440,21 +476,21 @@ static int cx_ipeak_probe(struct platform_device *pdev)
		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);
					&victim_list[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);
					&victim_list[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);
					&victim_list[i].freq_limit);

			if (ret)
				return ret;
@@ -462,24 +498,25 @@ static int cx_ipeak_probe(struct platform_device *pdev)
			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 = cx_ipeak_request_irq(pdev,
				"cx_ipeak_danger", NULL,
				cx_ipeak_irq_soft_handler, NULL);

		device_ipeak.danger_intr_num = status;
		if (device_ipeak.danger_intr_num < 0)
			return device_ipeak.danger_intr_num;

		status = cx_ipeak_request_irq(pdev, "cx_ipeak_safe",
				cx_ipeak_irq_handler, NULL);
		device_ipeak.safe_intr_num = cx_ipeak_request_irq(pdev,
				"cx_ipeak_safe", NULL,
				cx_ipeak_irq_soft_handler, NULL);

		if (status < 0)
			return status;
		if (device_ipeak.safe_intr_num < 0)
			return device_ipeak.safe_intr_num;

		device_ipeak.safe_intr_num = status;
	}

	spin_lock_init(&device_ipeak.vote_lock);
	device_ipeak.pdev = pdev;
	mutex_init(&device_ipeak.vote_lock);
	mutex_init(&device_ipeak.throttle_lock);
	return 0;
}

+1 −1
Original line number Diff line number Diff line
@@ -6,7 +6,7 @@
#ifndef __SOC_COM_CX_IPEAK_H
#define __SOC_COM_CX_IPEAK_H

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

struct device_node;
struct cx_ipeak_client;