soc: qcom: Add support for Cx iPeak limit driver
Implement common driver to limit Cx ipeak based on voting from various clients in multimedia. Change-Id: Ie0a57e49f7a8ba8a4fa3aa7f50dd0947f8e9d11b Signed-off-by: Rajesh Kemisetti <rajeshk@codeaurora.org> Signed-off-by: samit vats <svats@codeaurora.org>
This commit is contained in:
committed by
samit vats
parent
ad1e28d686
commit
d1c244f353
22
Documentation/devicetree/bindings/soc/qcom/qcom,cx_ipeak.txt
Normal file
22
Documentation/devicetree/bindings/soc/qcom/qcom,cx_ipeak.txt
Normal file
@@ -0,0 +1,22 @@
|
||||
* Cx iPeak driver to handle requests from various multimedia clients.
|
||||
|
||||
Cx ipeak HW module is used to limit the current drawn by various subsystem
|
||||
blocks on Cx power rail. Each client needs to set their
|
||||
bit in tcsr register if it is going to cross its own threshold. If all
|
||||
clients are going to cross their thresholds then Cx ipeak hw module will raise
|
||||
an interrupt to cDSP block to throttle cDSP's fmax.
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : name of the component used for driver matching, should be
|
||||
"qcom,cx-ipeak-sdm660"
|
||||
|
||||
- reg : physical base address and length of the register set(s), SRAM and XPU
|
||||
of the component.
|
||||
|
||||
Example:
|
||||
|
||||
cx_ipeak_lm: cx_ipeak@1fe5040 {
|
||||
compatible = "qcom,cx-ipeak-sdm660";
|
||||
reg = <0x01fe5040 0x28>;
|
||||
};
|
||||
@@ -787,4 +787,13 @@ config QCOM_CDSP_RM
|
||||
improving CDSP performance. Using this driver, CDSP can set appropriate
|
||||
CPU L3 clock for improving IO-Coherent throughput and opt for QoS mode
|
||||
to improve RPC latency.
|
||||
|
||||
config QCOM_CX_IPEAK
|
||||
bool "Common driver to handle Cx iPeak limitation"
|
||||
help
|
||||
Cx ipeak HW module is used to limit the current drawn by various subsystem
|
||||
blocks on Cx power rail. Each client needs to set their
|
||||
bit in tcsr register if it is going to cross its own threshold. If all
|
||||
clients are going to cross their thresholds then Cx ipeak hw module will raise
|
||||
an interrupt to cDSP block to throttle cDSP fmax.
|
||||
endmenu
|
||||
|
||||
@@ -92,3 +92,4 @@ endif
|
||||
obj-$(CONFIG_QCOM_SMP2P_SLEEPSTATE) += smp2p_sleepstate.o
|
||||
obj-$(CONFIG_QCOM_MEM_OFFLINE) += mem-offline.o
|
||||
obj-$(CONFIG_QCOM_CDSP_RM) += cdsprm.o
|
||||
obj-$(CONFIG_QCOM_CX_IPEAK) += cx_ipeak.o
|
||||
|
||||
202
drivers/soc/qcom/cx_ipeak.c
Normal file
202
drivers/soc/qcom/cx_ipeak.c
Normal file
@@ -0,0 +1,202 @@
|
||||
/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#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>
|
||||
#include <linux/err.h>
|
||||
|
||||
#include <soc/qcom/cx_ipeak.h>
|
||||
|
||||
#define TCSR_CXIP_LM_VOTE_BYPASS_OFFSET 0x4
|
||||
#define TCSR_CXIP_LM_VOTE_CLEAR_OFFSET 0x8
|
||||
#define TCSR_CXIP_LM_VOTE_SET_OFFSET 0xC
|
||||
#define TCSR_CXIP_LM_VOTE_FEATURE_ENABLE_OFFSET 0x10
|
||||
#define TCSR_CXIP_LM_TRS_OFFSET 0x24
|
||||
|
||||
#define CXIP_POLL_TIMEOUT_US (50 * 1000)
|
||||
|
||||
static struct cx_ipeak_device {
|
||||
spinlock_t vote_lock;
|
||||
void __iomem *tcsr_vptr;
|
||||
} device_ipeak;
|
||||
|
||||
struct cx_ipeak_client {
|
||||
int vote_count;
|
||||
unsigned int vote_mask;
|
||||
struct cx_ipeak_device *dev;
|
||||
};
|
||||
|
||||
/**
|
||||
* cx_ipeak_register() - allocate client structure and fill device private and
|
||||
* bit details.
|
||||
* @dev_node: device node of the client
|
||||
* @client_name: property name of the client
|
||||
*
|
||||
* Allocate client memory and fill the structure with device private and bit
|
||||
*
|
||||
*/
|
||||
struct cx_ipeak_client *cx_ipeak_register(struct device_node *dev_node,
|
||||
const char *client_name)
|
||||
{
|
||||
struct of_phandle_args cx_spec;
|
||||
struct cx_ipeak_client *client;
|
||||
unsigned int reg_enable, reg_bypass;
|
||||
int ret;
|
||||
|
||||
ret = of_parse_phandle_with_fixed_args(dev_node, client_name,
|
||||
1, 0, &cx_spec);
|
||||
if (ret)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
if (!of_device_is_available(cx_spec.np))
|
||||
return NULL;
|
||||
|
||||
if (device_ipeak.tcsr_vptr == NULL)
|
||||
return ERR_PTR(-EPROBE_DEFER);
|
||||
|
||||
if (cx_spec.args[0] > 31)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
reg_enable = readl_relaxed(device_ipeak.tcsr_vptr +
|
||||
TCSR_CXIP_LM_VOTE_FEATURE_ENABLE_OFFSET);
|
||||
reg_bypass = readl_relaxed(device_ipeak.tcsr_vptr +
|
||||
TCSR_CXIP_LM_VOTE_BYPASS_OFFSET) &
|
||||
BIT(cx_spec.args[0]);
|
||||
|
||||
if (!reg_enable || reg_bypass)
|
||||
return NULL;
|
||||
|
||||
client = kzalloc(sizeof(struct cx_ipeak_client), GFP_KERNEL);
|
||||
if (!client)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
client->vote_mask = BIT(cx_spec.args[0]);
|
||||
client->dev = &device_ipeak;
|
||||
|
||||
return client;
|
||||
}
|
||||
EXPORT_SYMBOL(cx_ipeak_register);
|
||||
|
||||
/**
|
||||
* cx_ipeak_unregister() - unregister client
|
||||
* @client: client address to free
|
||||
*
|
||||
* Free the client memory
|
||||
*/
|
||||
void cx_ipeak_unregister(struct cx_ipeak_client *client)
|
||||
{
|
||||
kfree(client);
|
||||
}
|
||||
EXPORT_SYMBOL(cx_ipeak_unregister);
|
||||
|
||||
/*
|
||||
* cx_ipeak_update() - Set/Clear client vote for Cx iPeak limit
|
||||
* manager to throttle cDSP.
|
||||
* @client: client handle.
|
||||
* @vote: True to set the vote and False for reset.
|
||||
*
|
||||
* Receives vote from each client and decides whether to throttle cDSP or not.
|
||||
* This function is NOP for the targets which does not support TCSR Cx iPeak.
|
||||
*/
|
||||
int cx_ipeak_update(struct cx_ipeak_client *client, bool vote)
|
||||
{
|
||||
unsigned int reg_val;
|
||||
int ret = 0;
|
||||
|
||||
/* Check for client and device availability and proceed */
|
||||
if (client == NULL || client->dev->tcsr_vptr == NULL)
|
||||
return ret;
|
||||
|
||||
spin_lock(&client->dev->vote_lock);
|
||||
|
||||
if (vote) {
|
||||
if (client->vote_count == 0) {
|
||||
writel_relaxed(client->vote_mask,
|
||||
client->dev->tcsr_vptr +
|
||||
TCSR_CXIP_LM_VOTE_SET_OFFSET);
|
||||
|
||||
/*
|
||||
* Do a dummy read to give enough time for TRS register
|
||||
* to become 1 when the last client votes.
|
||||
*/
|
||||
readl_relaxed(client->dev->tcsr_vptr +
|
||||
TCSR_CXIP_LM_TRS_OFFSET);
|
||||
|
||||
ret = readl_poll_timeout(client->dev->tcsr_vptr +
|
||||
TCSR_CXIP_LM_TRS_OFFSET, reg_val, !reg_val,
|
||||
0, CXIP_POLL_TIMEOUT_US);
|
||||
if (ret) {
|
||||
writel_relaxed(client->vote_mask,
|
||||
client->dev->tcsr_vptr +
|
||||
TCSR_CXIP_LM_VOTE_CLEAR_OFFSET);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
client->vote_count++;
|
||||
} else {
|
||||
if (client->vote_count > 0) {
|
||||
client->vote_count--;
|
||||
if (client->vote_count == 0) {
|
||||
writel_relaxed(client->vote_mask,
|
||||
client->dev->tcsr_vptr +
|
||||
TCSR_CXIP_LM_VOTE_CLEAR_OFFSET);
|
||||
}
|
||||
} else
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
done:
|
||||
spin_unlock(&client->dev->vote_lock);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(cx_ipeak_update);
|
||||
|
||||
static int cx_ipeak_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *res;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
device_ipeak.tcsr_vptr = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(device_ipeak.tcsr_vptr))
|
||||
return PTR_ERR(device_ipeak.tcsr_vptr);
|
||||
|
||||
spin_lock_init(&device_ipeak.vote_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id cx_ipeak_match_table[] = {
|
||||
{ .compatible = "qcom,cx-ipeak-sdm660"},
|
||||
{}
|
||||
};
|
||||
|
||||
static struct platform_driver cx_ipeak_platform_driver = {
|
||||
.probe = cx_ipeak_probe,
|
||||
.driver = {
|
||||
.name = "cx_ipeak",
|
||||
.of_match_table = cx_ipeak_match_table,
|
||||
.suppress_bind_attrs = true,
|
||||
}
|
||||
};
|
||||
|
||||
static int __init cx_ipeak_init(void)
|
||||
{
|
||||
return platform_driver_register(&cx_ipeak_platform_driver);
|
||||
}
|
||||
|
||||
arch_initcall(cx_ipeak_init);
|
||||
46
include/soc/qcom/cx_ipeak.h
Normal file
46
include/soc/qcom/cx_ipeak.h
Normal file
@@ -0,0 +1,46 @@
|
||||
/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef __SOC_COM_CX_IPEAK_H
|
||||
#define __SOC_COM_CX_IPEAK_H
|
||||
|
||||
struct device_node;
|
||||
struct cx_ipeak_client;
|
||||
|
||||
#ifndef CONFIG_QCOM_CX_IPEAK
|
||||
|
||||
static inline struct cx_ipeak_client *cx_ipeak_register(
|
||||
struct device_node *dev_node,
|
||||
const char *client_name)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline void cx_ipeak_unregister(struct cx_ipeak_client *client)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int cx_ipeak_update(struct cx_ipeak_client *ipeak_client,
|
||||
bool vote)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#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);
|
||||
|
||||
#endif
|
||||
|
||||
#endif /*__SOC_COM_CX_IPEAK_H*/
|
||||
Reference in New Issue
Block a user