From patchwork Fri May 25 10:01:16 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Rajendra Nayak X-Patchwork-Id: 920372 Return-Path: X-Original-To: incoming-dt@patchwork.ozlabs.org Delivered-To: patchwork-incoming-dt@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=devicetree-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=codeaurora.org Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=codeaurora.org header.i=@codeaurora.org header.b="SICUf+Ur"; dkim=fail reason="signature verification failed" (1024-bit key) header.d=codeaurora.org header.i=@codeaurora.org header.b="ZRZZCXT1"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 40shfG3Z9Wz9s0q for ; Fri, 25 May 2018 20:03:34 +1000 (AEST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S965259AbeEYKBj (ORCPT ); Fri, 25 May 2018 06:01:39 -0400 Received: from smtp.codeaurora.org ([198.145.29.96]:45410 "EHLO smtp.codeaurora.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S965570AbeEYKBh (ORCPT ); Fri, 25 May 2018 06:01:37 -0400 Received: by smtp.codeaurora.org (Postfix, from userid 1000) id 8B5C560FEC; Fri, 25 May 2018 10:01:36 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=codeaurora.org; s=default; t=1527242496; bh=Jdx11wuOJGqT3+FmiF/uJChpCmzOgXE0n4thwPcK/K8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=SICUf+UrG8xXugOFAXFO+ollNpeZpOqowQEjUqy8B+K9F12J8PtvOhfC9woumToB3 RL0kelq0MAus258btsJ5imAjIljkXxAbjsVA4wVMx+1Hhf/2vNzJ/tuawJFKaIW4q3 Uv/U/WTfimI6h2lWMBpY+LtT53JD7lGW6kYP7jDs= X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on pdx-caf-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-2.8 required=2.0 tests=ALL_TRUSTED,BAYES_00, DKIM_SIGNED, T_DKIM_INVALID autolearn=no autolearn_force=no version=3.4.0 Received: from blr-ubuntu-173.qualcomm.com (blr-bdr-fw-01_globalnat_allzones-outside.qualcomm.com [103.229.18.19]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-SHA256 (128/128 bits)) (No client certificate requested) (Authenticated sender: rnayak@smtp.codeaurora.org) by smtp.codeaurora.org (Postfix) with ESMTPSA id B534460D81; Fri, 25 May 2018 10:01:32 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=codeaurora.org; s=default; t=1527242495; bh=Jdx11wuOJGqT3+FmiF/uJChpCmzOgXE0n4thwPcK/K8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=ZRZZCXT1GxXFkczlF6FfPAGWVyTZ0oLPKcdCVdW1RCmcvbtnIluZrfcui+LP+QsGm Thf6TGAg+MzTfGjrbgFRPhCyNWQfeetL9H5jfiVi0EUBW0Qqza1ic25JFl18t1AsT3 sBXau3ZEeGuhyO+9Q2IhnpSjdeCSa8xes0H6ED80= DMARC-Filter: OpenDMARC Filter v1.3.2 smtp.codeaurora.org B534460D81 Authentication-Results: pdx-caf-mail.web.codeaurora.org; dmarc=none (p=none dis=none) header.from=codeaurora.org Authentication-Results: pdx-caf-mail.web.codeaurora.org; spf=none smtp.mailfrom=rnayak@codeaurora.org From: Rajendra Nayak To: viresh.kumar@linaro.org, sboyd@kernel.org, andy.gross@linaro.org, ulf.hansson@linaro.org Cc: devicetree@vger.kernel.org, linux-arm-msm@vger.kernel.org, linux-kernel@vger.kernel.org, collinsd@codeaurora.org, Rajendra Nayak Subject: [PATCH v2 1/6] soc: qcom: rpmpd: Add a powerdomain driver to model corners Date: Fri, 25 May 2018 15:31:16 +0530 Message-Id: <20180525100121.28214-2-rnayak@codeaurora.org> X-Mailer: git-send-email 2.17.0 In-Reply-To: <20180525100121.28214-1-rnayak@codeaurora.org> References: <20180525100121.28214-1-rnayak@codeaurora.org> Sender: devicetree-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: devicetree@vger.kernel.org The powerdomains for corners just pass the performance state set by the consumers to the RPM (Remote Power manager) which then takes care of setting the appropriate voltage on the corresponding rails to meet the performance needs. We add all powerdomain data needed on msm8996 here. This driver can easily be extended by adding data for other qualcomm SoCs as well. Signed-off-by: Rajendra Nayak Signed-off-by: Viresh Kumar --- .../devicetree/bindings/power/qcom,rpmpd.txt | 55 ++++ drivers/soc/qcom/Kconfig | 9 + drivers/soc/qcom/Makefile | 1 + drivers/soc/qcom/rpmpd.c | 299 ++++++++++++++++++ 4 files changed, 364 insertions(+) create mode 100644 Documentation/devicetree/bindings/power/qcom,rpmpd.txt create mode 100644 drivers/soc/qcom/rpmpd.c diff --git a/Documentation/devicetree/bindings/power/qcom,rpmpd.txt b/Documentation/devicetree/bindings/power/qcom,rpmpd.txt new file mode 100644 index 000000000000..68f620a2af0d --- /dev/null +++ b/Documentation/devicetree/bindings/power/qcom,rpmpd.txt @@ -0,0 +1,55 @@ +Qualcomm RPM Powerdomains + +* For RPM powerdomains, we communicate a performance state to RPM +which then translates it into a corresponding voltage on a rail + +Required Properties: + - compatible: Should be one of the following + * qcom,msm8996-rpmpd: RPM Powerdomain for the msm8996 family of SoC + - power-domain-cells: number of cells in power domain specifier + must be 1. + - operating-points-v2: Phandle to the OPP table for the power-domain. + Refer to Documentation/devicetree/bindings/power/power_domain.txt + and Documentation/devicetree/bindings/opp/qcom-opp.txt for more details + +Example: + + rpmpd: power-controller { + compatible = "qcom,msm8996-rpmpd"; + #power-domain-cells = <1>; + operating-points-v2 = <&rpmpd_opp_table>, + <&rpmpd_opp_table>, + <&rpmpd_opp_table>, + <&rpmpd_opp_table>, + <&rpmpd_opp_table>, + <&rpmpd_opp_table>, + <&rpmpd_opp_table>; + }; + + rpmpd_opp_table: opp-table { + compatible = "operating-points-v2-qcom-level", "operating-points-v2"; + + rpmpd_opp1: opp@1 { + qcom,level = <1>; + }; + + rpmpd_opp2: opp@2 { + qcom,level = <2>; + }; + + rpmpd_opp3: opp@3 { + qcom,level = <3>; + }; + + rpmpd_opp4: opp@4 { + qcom,level = <4>; + }; + + rpmpd_opp5: opp@5 { + qcom,level = <5>; + }; + + rpmpd_opp6: opp@6 { + qcom,level = <6>; + }; + }; diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index 9dc02f390ba3..a7a405178967 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -74,6 +74,15 @@ config QCOM_RMTFS_MEM Say y here if you intend to boot the modem remoteproc. +config QCOM_RPMPD + tristate "Qualcomm RPM Powerdomain driver" + depends on MFD_QCOM_RPM && QCOM_SMD_RPM + help + QCOM RPM powerdomain driver to support powerdomain with + performance states. The driver communicates a performance state + value to RPM which then translates it into corresponding voltage + for the voltage rail. + config QCOM_SMEM tristate "Qualcomm Shared Memory Manager (SMEM)" depends on ARCH_QCOM diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index 19dcf957cb3a..9550c170de93 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -15,3 +15,4 @@ obj-$(CONFIG_QCOM_SMP2P) += smp2p.o obj-$(CONFIG_QCOM_SMSM) += smsm.o obj-$(CONFIG_QCOM_WCNSS_CTRL) += wcnss_ctrl.o obj-$(CONFIG_QCOM_APR) += apr.o +obj-$(CONFIG_QCOM_RPMPD) += rpmpd.o diff --git a/drivers/soc/qcom/rpmpd.c b/drivers/soc/qcom/rpmpd.c new file mode 100644 index 000000000000..df6427d43414 --- /dev/null +++ b/drivers/soc/qcom/rpmpd.c @@ -0,0 +1,299 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define domain_to_rpmpd(domain) container_of(domain, struct rpmpd, pd) + +/* Resource types */ +#define RPMPD_SMPA 0x61706d73 +#define RPMPD_LDOA 0x616f646c + +/* Operation Keys */ +#define KEY_CORNER 0x6e726f63 /* corn */ +#define KEY_ENABLE 0x6e657773 /* swen */ +#define KEY_FLOOR_CORNER 0x636676 /* vfc */ + +#define DEFINE_RPMPD_CORN_SMPA(_platform, _name, _active, r_id) \ + static struct rpmpd _platform##_##_active; \ + static struct rpmpd _platform##_##_name = { \ + .pd = { .name = #_name, }, \ + .peer = &_platform##_##_active, \ + .res_type = RPMPD_SMPA, \ + .res_id = r_id, \ + .key = KEY_CORNER, \ + }; \ + static struct rpmpd _platform##_##_active = { \ + .pd = { .name = #_active, }, \ + .peer = &_platform##_##_name, \ + .active_only = true, \ + .res_type = RPMPD_SMPA, \ + .res_id = r_id, \ + .key = KEY_CORNER, \ + } + +#define DEFINE_RPMPD_CORN_LDOA(_platform, _name, r_id) \ + static struct rpmpd _platform##_##_name = { \ + .pd = { .name = #_name, }, \ + .res_type = RPMPD_LDOA, \ + .res_id = r_id, \ + .key = KEY_CORNER, \ + } + +#define DEFINE_RPMPD_VFC(_platform, _name, r_id, r_type) \ + static struct rpmpd _platform##_##_name = { \ + .pd = { .name = #_name, }, \ + .res_type = r_type, \ + .res_id = r_id, \ + .key = KEY_FLOOR_CORNER, \ + } + +#define DEFINE_RPMPD_VFC_SMPA(_platform, _name, r_id) \ + DEFINE_RPMPD_VFC(_platform, _name, r_id, RPMPD_SMPA) + +#define DEFINE_RPMPD_VFC_LDOA(_platform, _name, r_id) \ + DEFINE_RPMPD_VFC(_platform, _name, r_id, RPMPD_LDOA) + +struct rpmpd_req { + __le32 key; + __le32 nbytes; + __le32 value; +}; + +struct rpmpd { + struct generic_pm_domain pd; + struct rpmpd *peer; + const bool active_only; + unsigned long corner; + bool enabled; + const char *res_name; + const int res_type; + const int res_id; + struct qcom_smd_rpm *rpm; + __le32 key; +}; + +struct rpmpd_desc { + struct rpmpd **rpmpds; + size_t num_pds; +}; + +static DEFINE_MUTEX(rpmpd_lock); + +/* msm8996 RPM powerdomains */ +DEFINE_RPMPD_CORN_SMPA(msm8996, vddcx, vddcx_ao, 1); +DEFINE_RPMPD_CORN_SMPA(msm8996, vddmx, vddmx_ao, 2); +DEFINE_RPMPD_CORN_LDOA(msm8996, vddsscx, 26); + +DEFINE_RPMPD_VFC_SMPA(msm8996, vddcx_vfc, 1); +DEFINE_RPMPD_VFC_LDOA(msm8996, vddsscx_vfc, 26); + +static struct rpmpd *msm8996_rpmpds[] = { + [0] = &msm8996_vddcx, + [1] = &msm8996_vddcx_ao, + [2] = &msm8996_vddcx_vfc, + [3] = &msm8996_vddmx, + [4] = &msm8996_vddmx_ao, + [5] = &msm8996_vddsscx, + [6] = &msm8996_vddsscx_vfc, +}; + +static const struct rpmpd_desc msm8996_desc = { + .rpmpds = msm8996_rpmpds, + .num_pds = ARRAY_SIZE(msm8996_rpmpds), +}; + +static const struct of_device_id rpmpd_match_table[] = { + { .compatible = "qcom,msm8996-rpmpd", .data = &msm8996_desc }, + { } +}; +MODULE_DEVICE_TABLE(of, rpmpd_match_table); + +static int rpmpd_send_enable(struct rpmpd *pd, bool enable) +{ + struct rpmpd_req req = { + .key = KEY_ENABLE, + .nbytes = cpu_to_le32(sizeof(u32)), + .value = cpu_to_le32(enable), + }; + + return qcom_rpm_smd_write(pd->rpm, QCOM_RPM_ACTIVE_STATE, pd->res_type, + pd->res_id, &req, sizeof(req)); +} + +static int rpmpd_send_corner(struct rpmpd *pd, int state, unsigned int corner) +{ + struct rpmpd_req req = { + .key = pd->key, + .nbytes = cpu_to_le32(sizeof(u32)), + .value = cpu_to_le32(corner), + }; + + return qcom_rpm_smd_write(pd->rpm, state, pd->res_type, pd->res_id, + &req, sizeof(req)); +}; + +static void to_active_sleep(struct rpmpd *pd, unsigned long corner, + unsigned long *active, unsigned long *sleep) +{ + *active = corner; + + if (pd->active_only) + *sleep = 0; + else + *sleep = *active; +} + +static int rpmpd_aggregate_corner(struct rpmpd *pd) +{ + int ret; + struct rpmpd *peer = pd->peer; + unsigned long active_corner, sleep_corner; + unsigned long this_corner = 0, this_sleep_corner = 0; + unsigned long peer_corner = 0, peer_sleep_corner = 0; + + to_active_sleep(pd, pd->corner, &this_corner, &this_sleep_corner); + + if (peer && peer->enabled) + to_active_sleep(peer, peer->corner, &peer_corner, + &peer_sleep_corner); + + active_corner = max(this_corner, peer_corner); + + ret = rpmpd_send_corner(pd, QCOM_RPM_ACTIVE_STATE, active_corner); + if (ret) + return ret; + + sleep_corner = max(this_sleep_corner, peer_sleep_corner); + + return rpmpd_send_corner(pd, QCOM_RPM_SLEEP_STATE, sleep_corner); +} + +static int rpmpd_power_on(struct generic_pm_domain *domain) +{ + int ret; + struct rpmpd *pd = domain_to_rpmpd(domain); + + mutex_lock(&rpmpd_lock); + + ret = rpmpd_send_enable(pd, true); + if (ret) + goto out; + + pd->enabled = true; + + if (pd->corner) + ret = rpmpd_aggregate_corner(pd); + +out: + mutex_unlock(&rpmpd_lock); + + return ret; +} + +static int rpmpd_power_off(struct generic_pm_domain *domain) +{ + int ret; + struct rpmpd *pd = domain_to_rpmpd(domain); + + mutex_lock(&rpmpd_lock); + + ret = rpmpd_send_enable(pd, false); + if (!ret) + pd->enabled = false; + + mutex_unlock(&rpmpd_lock); + + return ret; +} + +static int rpmpd_probe(struct platform_device *pdev) +{ + int i; + size_t num; + struct genpd_onecell_data *data; + struct qcom_smd_rpm *rpm; + struct rpmpd **rpmpds; + const struct rpmpd_desc *desc; + + rpm = dev_get_drvdata(pdev->dev.parent); + if (!rpm) { + dev_err(&pdev->dev, "Unable to retrieve handle to RPM\n"); + return -ENODEV; + } + + desc = of_device_get_match_data(&pdev->dev); + if (!desc) + return -EINVAL; + + rpmpds = desc->rpmpds; + num = desc->num_pds; + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->domains = devm_kcalloc(&pdev->dev, num, sizeof(*data->domains), + GFP_KERNEL); + data->num_domains = num; + + for (i = 0; i < num; i++) { + if (!rpmpds[i]) + continue; + + rpmpds[i]->rpm = rpm; + rpmpds[i]->pd.power_off = rpmpd_power_off; + rpmpds[i]->pd.power_on = rpmpd_power_on; + pm_genpd_init(&rpmpds[i]->pd, NULL, true); + + data->domains[i] = &rpmpds[i]->pd; + } + + return of_genpd_add_provider_onecell(pdev->dev.of_node, data); +} + +static int rpmpd_remove(struct platform_device *pdev) +{ + of_genpd_del_provider(pdev->dev.of_node); + return 0; +} + +static struct platform_driver rpmpd_driver = { + .driver = { + .name = "qcom-rpmpd", + .of_match_table = rpmpd_match_table, + }, + .probe = rpmpd_probe, + .remove = rpmpd_remove, +}; + +static int __init rpmpd_init(void) +{ + return platform_driver_register(&rpmpd_driver); +} +core_initcall(rpmpd_init); + +static void __exit rpmpd_exit(void) +{ + platform_driver_unregister(&rpmpd_driver); +} +module_exit(rpmpd_exit); + +MODULE_DESCRIPTION("Qualcomm RPM Power Domain Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:qcom-rpmpd"); From patchwork Fri May 25 10:01:17 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Rajendra Nayak X-Patchwork-Id: 920371 Return-Path: X-Original-To: incoming-dt@patchwork.ozlabs.org Delivered-To: patchwork-incoming-dt@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=devicetree-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=codeaurora.org Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=codeaurora.org header.i=@codeaurora.org header.b="H0eAUqJ/"; dkim=fail reason="signature verification failed" (1024-bit key) header.d=codeaurora.org header.i=@codeaurora.org header.b="baIOfFR9"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 40shdq3GPKz9s0q for ; Fri, 25 May 2018 20:03:11 +1000 (AEST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S965682AbeEYKBo (ORCPT ); Fri, 25 May 2018 06:01:44 -0400 Received: from smtp.codeaurora.org ([198.145.29.96]:45702 "EHLO smtp.codeaurora.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S965657AbeEYKBk (ORCPT ); Fri, 25 May 2018 06:01:40 -0400 Received: by smtp.codeaurora.org (Postfix, from userid 1000) id 190E060290; Fri, 25 May 2018 10:01:39 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=codeaurora.org; s=default; t=1527242500; bh=R7UbEQrKv1U1FIdVslcjaAYCIBhOUFddZQiFlmKmqwI=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=H0eAUqJ/rti72XmouAW7PjFlkDfNGZLxx4DNelpcrzd1b0c16YIvnOGp1AVBOEXOE ZEP6Sibn36/pYSKOA3oMIYGVY2CjSH6N5Qjs/AAH+PfCy8N5w9pXd91fUzPv8/8B7U HkFgDYvPwgPjul2rOQkIgLIuDI/yGX28FjYJhosQ= X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on pdx-caf-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-2.8 required=2.0 tests=ALL_TRUSTED,BAYES_00, DKIM_SIGNED, T_DKIM_INVALID autolearn=no autolearn_force=no version=3.4.0 Received: from blr-ubuntu-173.qualcomm.com (blr-bdr-fw-01_globalnat_allzones-outside.qualcomm.com [103.229.18.19]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-SHA256 (128/128 bits)) (No client certificate requested) (Authenticated sender: rnayak@smtp.codeaurora.org) by smtp.codeaurora.org (Postfix) with ESMTPSA id 4ADFA60FEA; Fri, 25 May 2018 10:01:36 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=codeaurora.org; s=default; t=1527242499; bh=R7UbEQrKv1U1FIdVslcjaAYCIBhOUFddZQiFlmKmqwI=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=baIOfFR9gxygD/OXBntA+9XwY6sTQC57KqkNb6JVeBTNG8aBdgLUSm+MysX+/yMYY qRW3a3/ntVFML1GIY91z8lpGSjzBnWsWUZmaIkKZ4bHh31c4P4P/eXiIymd6qG+PM1 Fib+Z0VP6rsX3/YxOxgTqGg32m5rQLtCo/pZD0UA= DMARC-Filter: OpenDMARC Filter v1.3.2 smtp.codeaurora.org 4ADFA60FEA Authentication-Results: pdx-caf-mail.web.codeaurora.org; dmarc=none (p=none dis=none) header.from=codeaurora.org Authentication-Results: pdx-caf-mail.web.codeaurora.org; spf=none smtp.mailfrom=rnayak@codeaurora.org From: Rajendra Nayak To: viresh.kumar@linaro.org, sboyd@kernel.org, andy.gross@linaro.org, ulf.hansson@linaro.org Cc: devicetree@vger.kernel.org, linux-arm-msm@vger.kernel.org, linux-kernel@vger.kernel.org, collinsd@codeaurora.org, Rajendra Nayak Subject: [PATCH v2 2/6] dt-bindings: opp: Introduce qcom-opp bindings Date: Fri, 25 May 2018 15:31:17 +0530 Message-Id: <20180525100121.28214-3-rnayak@codeaurora.org> X-Mailer: git-send-email 2.17.0 In-Reply-To: <20180525100121.28214-1-rnayak@codeaurora.org> References: <20180525100121.28214-1-rnayak@codeaurora.org> Sender: devicetree-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: devicetree@vger.kernel.org On Qualcomm platforms, an OPP node needs to describe an additional level/corner value that is then communicated to a remote microprocessor by the CPU, which then takes some actions (like adjusting voltage values across various rails) based on the value passed. Describe these bindings in the qcom-opp bindings document. Signed-off-by: Rajendra Nayak Acked-by: Viresh Kumar Reviewed-by: Rob Herring --- .../devicetree/bindings/opp/qcom-opp.txt | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 Documentation/devicetree/bindings/opp/qcom-opp.txt diff --git a/Documentation/devicetree/bindings/opp/qcom-opp.txt b/Documentation/devicetree/bindings/opp/qcom-opp.txt new file mode 100644 index 000000000000..db4d970c7ec7 --- /dev/null +++ b/Documentation/devicetree/bindings/opp/qcom-opp.txt @@ -0,0 +1,25 @@ +Qualcomm OPP bindings to descibe OPP nodes with corner/level values + +OPP tables for devices on Qualcomm platforms require an additional +platform specific corner/level value to be specified. +This value is passed on to the RPM (Resource Power Manager) by +the CPU, which then takes the necessary actions to set a voltage +rail to an appropriate voltage based on the value passed. + +The bindings are based on top of the operating-points-v2 bindings +described in Documentation/devicetree/bindings/opp/opp.txt +Additional properties are described below. + +* OPP Table Node + +Required properties: +- compatible: Allow OPPs to express their compatibility. It should be: + "operating-points-v2-qcom-level" + +* OPP Node + +Required properties: +- qcom,level: On Qualcomm platforms an OPP node can describe a positive value +representing a corner/level that's communicated with a remote microprocessor +(usually called the RPM) which then translates it into a certain voltage on +a voltage rail. From patchwork Fri May 25 10:01:20 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Rajendra Nayak X-Patchwork-Id: 920370 Return-Path: X-Original-To: incoming-dt@patchwork.ozlabs.org Delivered-To: patchwork-incoming-dt@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=devicetree-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=codeaurora.org Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=codeaurora.org header.i=@codeaurora.org header.b="jRxo8G1S"; dkim=fail reason="signature verification failed" (1024-bit key) header.d=codeaurora.org header.i=@codeaurora.org header.b="WHwO7NcB"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 40shd759gZz9s0q for ; Fri, 25 May 2018 20:02:35 +1000 (AEST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S965545AbeEYKCV (ORCPT ); Fri, 25 May 2018 06:02:21 -0400 Received: from smtp.codeaurora.org ([198.145.29.96]:46498 "EHLO smtp.codeaurora.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S965657AbeEYKBw (ORCPT ); Fri, 25 May 2018 06:01:52 -0400 Received: by smtp.codeaurora.org (Postfix, from userid 1000) id 9F8DF60618; Fri, 25 May 2018 10:01:51 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=codeaurora.org; s=default; t=1527242511; bh=aFlAsvwTxZJznhkGiTwvhJiMIqfsgIGgDvgrKIsRMfc=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=jRxo8G1SB7bMfrJRsXw+FSRPcwtYVDsslJfljDWI28g8HYwZWRDMmg5j4LsXcNHn4 9CKA8jPnrq6Ro+XLXL3LMIRWy0uqdU3bz8KRn7bIFFbrtWAX7qLBEVtaujPudecJH2 vxCg2gyr8lu31oAvU4NdZaOO//EKqj4JlqiGpP4I= X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on pdx-caf-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-2.8 required=2.0 tests=ALL_TRUSTED,BAYES_00, DKIM_SIGNED, T_DKIM_INVALID autolearn=no autolearn_force=no version=3.4.0 Received: from blr-ubuntu-173.qualcomm.com (blr-bdr-fw-01_globalnat_allzones-outside.qualcomm.com [103.229.18.19]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-SHA256 (128/128 bits)) (No client certificate requested) (Authenticated sender: rnayak@smtp.codeaurora.org) by smtp.codeaurora.org (Postfix) with ESMTPSA id 560D360F8E; Fri, 25 May 2018 10:01:47 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=codeaurora.org; s=default; t=1527242510; bh=aFlAsvwTxZJznhkGiTwvhJiMIqfsgIGgDvgrKIsRMfc=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=WHwO7NcBW8qbpoAjmjFDQR/exsClSBY3nno8LhGCeCFJTItWHy37uxwSNgjQaN9ni YoKGBRk99Yt0+KQmp/C/gB7nDdXfnaeMaRzTN28hHYCeL3iyaGJPc3mDeAXZU+JocC xyloyefkUq9KUOqSQC2f/SmKVwS/P8C5noMTBhfs= DMARC-Filter: OpenDMARC Filter v1.3.2 smtp.codeaurora.org 560D360F8E Authentication-Results: pdx-caf-mail.web.codeaurora.org; dmarc=none (p=none dis=none) header.from=codeaurora.org Authentication-Results: pdx-caf-mail.web.codeaurora.org; spf=none smtp.mailfrom=rnayak@codeaurora.org From: Rajendra Nayak To: viresh.kumar@linaro.org, sboyd@kernel.org, andy.gross@linaro.org, ulf.hansson@linaro.org Cc: devicetree@vger.kernel.org, linux-arm-msm@vger.kernel.org, linux-kernel@vger.kernel.org, collinsd@codeaurora.org, Rajendra Nayak Subject: [PATCH v2 5/6] soc: qcom: rpmh powerdomain driver Date: Fri, 25 May 2018 15:31:20 +0530 Message-Id: <20180525100121.28214-6-rnayak@codeaurora.org> X-Mailer: git-send-email 2.17.0 In-Reply-To: <20180525100121.28214-1-rnayak@codeaurora.org> References: <20180525100121.28214-1-rnayak@codeaurora.org> Sender: devicetree-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: devicetree@vger.kernel.org The RPMh powerdomain driver aggregates the corner votes from various consumers for the ARC resources and communicates it to RPMh. We also add data for all powerdomains on sdm845 as part of the patch. The driver can be extended to support other SoCs which support RPMh Signed-off-by: Rajendra Nayak --- .../devicetree/bindings/power/qcom,rpmhpd.txt | 65 ++++ drivers/soc/qcom/Kconfig | 9 + drivers/soc/qcom/Makefile | 1 + drivers/soc/qcom/rpmhpd.c | 360 ++++++++++++++++++ 4 files changed, 435 insertions(+) create mode 100644 Documentation/devicetree/bindings/power/qcom,rpmhpd.txt create mode 100644 drivers/soc/qcom/rpmhpd.c diff --git a/Documentation/devicetree/bindings/power/qcom,rpmhpd.txt b/Documentation/devicetree/bindings/power/qcom,rpmhpd.txt new file mode 100644 index 000000000000..c1fa986c12ee --- /dev/null +++ b/Documentation/devicetree/bindings/power/qcom,rpmhpd.txt @@ -0,0 +1,65 @@ +Qualcomm RPMh Powerdomains + +* For RPMh powerdomains, we communicate a performance state to RPMh +which then translates it into a corresponding voltage on a rail + +Required Properties: + - compatible: Should be one of the following + * qcom,sdm845-rpmhpd: RPMh powerdomain for the sdm845 family of SoC + - power-domain-cells: number of cells in power domain specifier + must be 1 + - operating-points-v2: Phandle to the OPP table for the power-domain. + Refer to Documentation/devicetree/bindings/power/power_domain.txt + and Documentation/devicetree/bindings/opp/qcom-opp.txt for more details + +Example: + + rpmhpd: power-controller { + compatible = "qcom,sdm845-rpmhpd"; + #power-domain-cells = <1>; + operating-points-v2 = <&rpmhpd_opp_table>, + <&rpmhpd_opp_table>, + <&rpmhpd_opp_table>, + <&rpmhpd_opp_table>, + <&rpmhpd_opp_table>, + <&rpmhpd_opp_table>, + <&rpmhpd_opp_table>, + <&rpmhpd_opp_table>, + <&rpmhpd_opp_table>; + }; + + rpmhpd_opp_table: opp-table { + compatible = "operating-points-v2-qcom-level", "operating-points-v2"; + + rpmhpd_opp1: opp@1 { + qcom-corner = <16>; + }; + + rpmhpd_opp2: opp@2 { + qcom-corner = <48>; + }; + + rpmhpd_opp3: opp@3 { + qcom-corner = <64>; + }; + + rpmhpd_opp4: opp@4 { + qcom-corner = <128>; + }; + + rpmhpd_opp5: opp@5 { + qcom-corner = <192>; + }; + + rpmhpd_opp6: opp@6 { + qcom-corner = <256>; + }; + + rpmhpd_opp7: opp@7 { + qcom-corner = <320>; + }; + + rpmhpd_opp8: opp@8 { + qcom-corner = <416>; + }; + }; diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index a7a405178967..1faed239701d 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -74,6 +74,15 @@ config QCOM_RMTFS_MEM Say y here if you intend to boot the modem remoteproc. +config QCOM_RPMHPD + tristate "Qualcomm RPMh Powerdomain driver" + depends on QCOM_RPMH && QCOM_COMMAND_DB + help + QCOM RPMh powerdomain driver to support powerdomain with + performance states. The driver communicates a performance state + value to RPMh which then translates it into corresponding voltage + for the voltage rail. + config QCOM_RPMPD tristate "Qualcomm RPM Powerdomain driver" depends on MFD_QCOM_RPM && QCOM_SMD_RPM diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index 9550c170de93..499513f63bef 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -16,3 +16,4 @@ obj-$(CONFIG_QCOM_SMSM) += smsm.o obj-$(CONFIG_QCOM_WCNSS_CTRL) += wcnss_ctrl.o obj-$(CONFIG_QCOM_APR) += apr.o obj-$(CONFIG_QCOM_RPMPD) += rpmpd.o +obj-$(CONFIG_QCOM_RPMHPD) += rpmhpd.o diff --git a/drivers/soc/qcom/rpmhpd.c b/drivers/soc/qcom/rpmhpd.c new file mode 100644 index 000000000000..a79f094ad326 --- /dev/null +++ b/drivers/soc/qcom/rpmhpd.c @@ -0,0 +1,360 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2018, The Linux Foundation. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define domain_to_rpmhpd(domain) container_of(domain, struct rpmhpd, pd) + +#define DEFINE_RPMHPD_AO(_platform, _name, _active) \ + static struct rpmhpd _platform##_##_active; \ + static struct rpmhpd _platform##_##_name = { \ + .pd = { .name = #_name, }, \ + .peer = &_platform##_##_active, \ + .res_name = #_name".lvl", \ + }; \ + static struct rpmhpd _platform##_##_active = { \ + .pd = { .name = #_active, }, \ + .peer = &_platform##_##_name, \ + .active_only = true, \ + .res_name = #_name".lvl", \ + } + +#define DEFINE_RPMHPD(_platform, _name) \ + static struct rpmhpd _platform##_##_name = { \ + .pd = { .name = #_name, }, \ + .res_name = #_name".lvl", \ + } + +/* + * This is the number of bytes used for each command DB aux data entry of an + * ARC resource. + */ +#define RPMH_ARC_LEVEL_SIZE 2 +#define RPMH_ARC_MAX_LEVELS 16 + +struct rpmhpd { + struct device *dev; + struct generic_pm_domain pd; + struct rpmhpd *peer; + const bool active_only; + unsigned long corner; + u32 level[RPMH_ARC_MAX_LEVELS]; + int level_count; + bool enabled; + const char *res_name; + u32 addr; +}; + +struct rpmhpd_desc { + struct rpmhpd **rpmhpds; + size_t num_pds; +}; + +static DEFINE_MUTEX(rpmhpd_lock); + +/* sdm845 RPMh powerdomains */ +DEFINE_RPMHPD(sdm845, ebi); +DEFINE_RPMHPD_AO(sdm845, mx, mx_ao); +DEFINE_RPMHPD_AO(sdm845, cx, cx_ao); +DEFINE_RPMHPD(sdm845, lmx); +DEFINE_RPMHPD(sdm845, lcx); +DEFINE_RPMHPD(sdm845, gfx); +DEFINE_RPMHPD(sdm845, mss); + +static struct rpmhpd *sdm845_rpmhpds[] = { + [0] = &sdm845_ebi, + [1] = &sdm845_mx, + [2] = &sdm845_mx_ao, + [3] = &sdm845_cx, + [4] = &sdm845_cx_ao, + [5] = &sdm845_lmx, + [6] = &sdm845_lcx, + [7] = &sdm845_gfx, + [8] = &sdm845_mss, +}; + +static const struct rpmhpd_desc sdm845_desc = { + .rpmhpds = sdm845_rpmhpds, + .num_pds = ARRAY_SIZE(sdm845_rpmhpds), +}; + +static const struct of_device_id rpmhpd_match_table[] = { + { .compatible = "qcom,sdm845-rpmhpd", .data = &sdm845_desc }, + { } +}; +MODULE_DEVICE_TABLE(of, rpmhpd_match_table); + +static int rpmhpd_send_corner(struct rpmhpd *pd, int state, unsigned int corner) +{ + struct tcs_cmd cmd = { + .addr = pd->addr, + .data = corner, + }; + + return rpmh_write(pd->dev, state, &cmd, 1); +}; + +static void to_active_sleep(struct rpmhpd *pd, unsigned long corner, + unsigned long *active, unsigned long *sleep) +{ + *active = corner; + + if (pd->active_only) + *sleep = 0; + else + *sleep = *active; +} + +static int rpmhpd_aggregate_corner(struct rpmhpd *pd) +{ + int ret; + struct rpmhpd *peer = pd->peer; + unsigned long active_corner, sleep_corner; + unsigned long this_corner = 0, this_sleep_corner = 0; + unsigned long peer_corner = 0, peer_sleep_corner = 0; + + to_active_sleep(pd, pd->corner, &this_corner, &this_sleep_corner); + + if (peer && peer->enabled) + to_active_sleep(peer, peer->corner, &peer_corner, + &peer_sleep_corner); + + active_corner = max(this_corner, peer_corner); + + ret = rpmhpd_send_corner(pd, RPMH_ACTIVE_ONLY_STATE, active_corner); + if (ret) + return ret; + + sleep_corner = max(this_sleep_corner, peer_sleep_corner); + + return rpmhpd_send_corner(pd, RPMH_SLEEP_STATE, sleep_corner); +} + +static int rpmhpd_power_on(struct generic_pm_domain *domain) +{ + int ret = 0; + struct rpmhpd *pd = domain_to_rpmhpd(domain); + + mutex_lock(&rpmhpd_lock); + + pd->enabled = true; + + if (pd->corner) + ret = rpmhpd_aggregate_corner(pd); + + mutex_unlock(&rpmhpd_lock); + + return ret; +} + +static int rpmhpd_power_off(struct generic_pm_domain *domain) +{ + int ret; + struct rpmhpd *pd = domain_to_rpmhpd(domain); + + mutex_lock(&rpmhpd_lock); + + if (pd->level[0] == 0) { + ret = rpmhpd_send_corner(pd, RPMH_ACTIVE_ONLY_STATE, 0); + if (ret) + return ret; + + ret = rpmhpd_send_corner(pd, RPMH_SLEEP_STATE, 0); + if (ret) + return ret; + } + + pd->enabled = false; + + mutex_unlock(&rpmhpd_lock); + + return 0; +} + +static int rpmhpd_set_performance(struct generic_pm_domain *domain, + unsigned int state) +{ + int ret = 0; + struct rpmhpd *pd = domain_to_rpmhpd(domain); + + mutex_lock(&rpmhpd_lock); + + pd->corner = state; + + if (!pd->enabled) + goto out; + + ret = rpmhpd_aggregate_corner(pd); +out: + mutex_unlock(&rpmhpd_lock); + + return ret; +} + +static unsigned int rpmhpd_get_performance(struct generic_pm_domain *genpd, + struct dev_pm_opp *opp) +{ + struct device_node *np; + unsigned int corner = 0; + + np = dev_pm_opp_get_of_node(opp); + if (of_property_read_u32(np, "qcom,level", &corner)) { + pr_err("%s: missing 'qcom,level' property\n", __func__); + return 0; + } + + of_node_put(np); + + return corner; +} + +static int rpmhpd_update_level_mapping(struct rpmhpd *rpmhpd) +{ + u8 *buf; + int i, j, len, ret; + + len = cmd_db_read_aux_data_len(rpmhpd->res_name); + if (len <= 0) + return len; + + buf = kzalloc(len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + ret = cmd_db_read_aux_data(rpmhpd->res_name, buf, len); + if (ret < 0) + return ret; + + rpmhpd->level_count = len / RPMH_ARC_LEVEL_SIZE; + + for (i = 0; i < rpmhpd->level_count; i++) { + for (j = 0; j < RPMH_ARC_LEVEL_SIZE; j++) + rpmhpd->level[i] |= + buf[i * RPMH_ARC_LEVEL_SIZE + j] << (8 * j); + + /* + * The AUX data may be zero padded. These 0 valued entries at + * the end of the map must be ignored. + */ + if (i > 0 && rpmhpd->level[i] == 0) { + rpmhpd->level_count = i; + break; + } + pr_dbg("%s: ARC hlvl=%2d --> vlvl=%4u\n", rpmhpd->res_name, i, + rpmhpd->level[i]); + } + + kfree(buf); + return 0; +} + +static int rpmhpd_probe(struct platform_device *pdev) +{ + int i, ret; + size_t num; + struct genpd_onecell_data *data; + struct rpmhpd **rpmhpds; + const struct rpmhpd_desc *desc; + + desc = of_device_get_match_data(&pdev->dev); + if (!desc) + return -EINVAL; + + rpmhpds = desc->rpmhpds; + num = desc->num_pds; + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->domains = devm_kcalloc(&pdev->dev, num, sizeof(*data->domains), + GFP_KERNEL); + data->num_domains = num; + + ret = cmd_db_ready(); + if (ret) { + if (ret != -EPROBE_DEFER) + dev_err(&pdev->dev, "Command DB unavailable, ret=%d\n", + ret); + return ret; + } + + for (i = 0; i < num; i++) { + if (!rpmhpds[i]) + continue; + + rpmhpds[i]->dev = &pdev->dev; + rpmhpds[i]->addr = cmd_db_read_addr(rpmhpds[i]->res_name); + if (!rpmhpds[i]->addr) { + dev_err(&pdev->dev, "Could not find RPMh address for resource %s\n", + rpmhpds[i]->res_name); + return -ENODEV; + } + + ret = cmd_db_read_slave_id(rpmhpds[i]->res_name); + if (ret != CMD_DB_HW_ARC) { + dev_err(&pdev->dev, "RPMh slave ID mismatch\n"); + return -EINVAL; + } + + ret = rpmhpd_update_level_mapping(rpmhpds[i]); + if (ret) + return ret; + + rpmhpds[i]->pd.power_off = rpmhpd_power_off; + rpmhpds[i]->pd.power_on = rpmhpd_power_on; + rpmhpds[i]->pd.set_performance_state = rpmhpd_set_performance; + rpmhpds[i]->pd.opp_to_performance_state = rpmhpd_get_performance; + pm_genpd_init(&rpmhpds[i]->pd, NULL, true); + + data->domains[i] = &rpmhpds[i]->pd; + } + + return of_genpd_add_provider_onecell(pdev->dev.of_node, data); +} + +static int rpmhpd_remove(struct platform_device *pdev) +{ + of_genpd_del_provider(pdev->dev.of_node); + return 0; +} + +static struct platform_driver rpmhpd_driver = { + .driver = { + .name = "qcom-rpmhpd", + .of_match_table = rpmhpd_match_table, + }, + .probe = rpmhpd_probe, + .remove = rpmhpd_remove, +}; + +static int __init rpmhpd_init(void) +{ + return platform_driver_register(&rpmhpd_driver); +} +core_initcall(rpmhpd_init); + +static void __exit rpmhpd_exit(void) +{ + platform_driver_unregister(&rpmhpd_driver); +} +module_exit(rpmhpd_exit); + +MODULE_DESCRIPTION("Qualcomm RPMh Power Domain Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:qcom-rpmhpd");