From d1c244f353bdcaebcfa26fad5a6fcbb45dd148b8 Mon Sep 17 00:00:00 2001 From: Rajesh Kemisetti Date: Tue, 20 Dec 2016 10:47:38 +0530 Subject: [PATCH] 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 Signed-off-by: samit vats --- .../bindings/soc/qcom/qcom,cx_ipeak.txt | 22 ++ drivers/soc/qcom/Kconfig | 9 + drivers/soc/qcom/Makefile | 1 + drivers/soc/qcom/cx_ipeak.c | 202 ++++++++++++++++++ include/soc/qcom/cx_ipeak.h | 46 ++++ 5 files changed, 280 insertions(+) create mode 100644 Documentation/devicetree/bindings/soc/qcom/qcom,cx_ipeak.txt create mode 100644 drivers/soc/qcom/cx_ipeak.c create mode 100644 include/soc/qcom/cx_ipeak.h diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,cx_ipeak.txt b/Documentation/devicetree/bindings/soc/qcom/qcom,cx_ipeak.txt new file mode 100644 index 000000000000..d5fb03cc9318 --- /dev/null +++ b/Documentation/devicetree/bindings/soc/qcom/qcom,cx_ipeak.txt @@ -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>; + }; diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index d514c7a5b3a5..99c034b7393b 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -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 diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index 7feed2df6715..8534f1d036a1 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -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 diff --git a/drivers/soc/qcom/cx_ipeak.c b/drivers/soc/qcom/cx_ipeak.c new file mode 100644 index 000000000000..a74806446445 --- /dev/null +++ b/drivers/soc/qcom/cx_ipeak.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#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); diff --git a/include/soc/qcom/cx_ipeak.h b/include/soc/qcom/cx_ipeak.h new file mode 100644 index 000000000000..1205d8aed3cf --- /dev/null +++ b/include/soc/qcom/cx_ipeak.h @@ -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*/