2019-01-06 08:13:36

by Bjorn Andersson

[permalink] [raw]
Subject: [PATCH v2 0/7] Qualcomm AOSS QMP side channel binding and driver

Add binding and driver for the QMP based side-channel communication mechanism
to the AOSS, which is used to control resources not exposed through the RPMh
interface.

Currently implemented is a genpd provider, but pending some improvements in the
thermal framework a cooling device will be added at a later point. Patch 7 is
an "RFC" for how we define and probe these cooling devices.

The remoteproc patches updates the MSS driver to vote for the necessary genpd
corners on SDM845.

Lastly patch 6 makes it possible to have a device on the amba bus where the
apb_pclk is controlled as part of a power-domain.

Bjorn Andersson (6):
dt-bindings: soc: qcom: Add AOSS QMP binding
soc: qcom: Add AOSS QMP communication driver
soc: qcom: Add AOSS QMP genpd provider
remoteproc: q6v5-mss: Active powerdomain for SDM845
amba: Allow pclk to be controlled by power domain
soc: qcom: aoss-qmp: Add cooling device support

Rajendra Nayak (1):
remoteproc: q6v5-mss: Vote for rpmh power domains

.../bindings/soc/qcom/qcom,aoss-qmp.txt | 75 ++++
drivers/amba/bus.c | 9 +-
drivers/remoteproc/qcom_q6v5_mss.c | 142 ++++++-
drivers/soc/qcom/Kconfig | 18 +
drivers/soc/qcom/Makefile | 2 +
drivers/soc/qcom/aoss-qmp-pd.c | 135 +++++++
drivers/soc/qcom/aoss-qmp.c | 349 ++++++++++++++++++
include/dt-bindings/power/qcom-aoss-qmp.h | 15 +
include/linux/soc/qcom/aoss-qmp.h | 12 +
9 files changed, 751 insertions(+), 6 deletions(-)
create mode 100644 Documentation/devicetree/bindings/soc/qcom/qcom,aoss-qmp.txt
create mode 100644 drivers/soc/qcom/aoss-qmp-pd.c
create mode 100644 drivers/soc/qcom/aoss-qmp.c
create mode 100644 include/dt-bindings/power/qcom-aoss-qmp.h
create mode 100644 include/linux/soc/qcom/aoss-qmp.h

--
2.18.0



2019-01-06 08:12:16

by Bjorn Andersson

[permalink] [raw]
Subject: [PATCH v2 6/7] amba: Allow pclk to be controlled by power domain

On the Qualcomm SDM845 platform the apb_pclk is controlled as part of
the QDSS power/clock domain. Handle this by allowing amba to operate
without direct apb_pclk control, when a powerdomain is attached and no
clock is described.

Signed-off-by: Bjorn Andersson <[email protected]>
---

Changes since v1:
- New patch

drivers/amba/bus.c | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/drivers/amba/bus.c b/drivers/amba/bus.c
index 41b706403ef7..3e13050c6d59 100644
--- a/drivers/amba/bus.c
+++ b/drivers/amba/bus.c
@@ -219,8 +219,13 @@ static int amba_get_enable_pclk(struct amba_device *pcdev)
int ret;

pcdev->pclk = clk_get(&pcdev->dev, "apb_pclk");
- if (IS_ERR(pcdev->pclk))
- return PTR_ERR(pcdev->pclk);
+ if (IS_ERR(pcdev->pclk)) {
+ /* Continue with no clock specified, but pm_domain attached */
+ if (PTR_ERR(pcdev->pclk) == -ENOENT && pcdev->dev.pm_domain)
+ pcdev->pclk = NULL;
+ else
+ return PTR_ERR(pcdev->pclk);
+ }

ret = clk_prepare_enable(pcdev->pclk);
if (ret)
--
2.18.0


2019-01-06 08:12:16

by Bjorn Andersson

[permalink] [raw]
Subject: [(RFC) PATCH v2 7/7] soc: qcom: aoss-qmp: Add cooling device support

The AOSS provides three cooling devices "cx", "mx" and "ebi" that must
be enabled when temperature goes below a certain level to counter low
temperature issues. Probe these devices, when described in DeviceTree.

Signed-off-by: Bjorn Andersson <[email protected]>
---

We do not yet have the necessary support in the thermal framework to implement
the cooling device associated with the QMP, so I've just included this patch as
an RFC in this series.

.../bindings/soc/qcom/qcom,aoss-qmp.txt | 18 ++++++++++
drivers/soc/qcom/aoss-qmp.c | 36 +++++++++++++++++++
2 files changed, 54 insertions(+)

diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,aoss-qmp.txt b/Documentation/devicetree/bindings/soc/qcom/qcom,aoss-qmp.txt
index 9b0d9785efe0..aae300f32421 100644
--- a/Documentation/devicetree/bindings/soc/qcom/qcom,aoss-qmp.txt
+++ b/Documentation/devicetree/bindings/soc/qcom/qcom,aoss-qmp.txt
@@ -42,6 +42,16 @@ power-domains.
modem state (3), SLPI state (4), SPSS state (5) and Venus
state (6).

+= SUBNODES
+The AOSS side channel also provides the controls for three cooling devices,
+these are expressed as subnodes of the QMP node. The name of the node is used
+to identify the resource and must therefor be "cx", "mx" or "ebi".
+
+- #cooling-cells:
+ Usage: optional
+ Value type: <u32>
+ Definition: must be 2
+
= EXAMPLE

The following example represents the AOSS side-channel message RAM and the
@@ -54,4 +64,12 @@ mechanism exposing the power-domains, as found in SDM845.
mboxes = <&apss_shared 0>;

#power-domain-cells = <1>;
+
+ cx_cdev: cx {
+ #cooling-cells = <2>;
+ };
+
+ mx_cdev: mx {
+ #cooling-cells = <2>;
+ };
};
diff --git a/drivers/soc/qcom/aoss-qmp.c b/drivers/soc/qcom/aoss-qmp.c
index de52703b96b6..6e9299e3b2bd 100644
--- a/drivers/soc/qcom/aoss-qmp.c
+++ b/drivers/soc/qcom/aoss-qmp.c
@@ -33,6 +33,8 @@
#define QMP_MAGIC 0x4d41494c
#define QMP_VERSION 1

+#define QMP_MAX_COOLING_DEVICES 3
+
/**
* struct qmp - driver state for QMP implementation
* @msgram: iomem referencing the message RAM used for communication
@@ -44,6 +46,8 @@
* @event: wait_queue for synchronization with the IRQ
* @tx_lock: provides syncrhonization between multiple callers of qmp_send()
* @pd_pdev: platform device for the power-domain child device
+ * @cdev_pdevs: platform device for the cooling devices
+ * @cdev_count: number of valid @cdev_pdevs
*/
struct qmp {
void __iomem *msgram;
@@ -60,6 +64,9 @@ struct qmp {
struct mutex tx_lock;

struct platform_device *pd_pdev;
+
+ struct platform_device *cdev_pdevs[QMP_MAX_COOLING_DEVICES];
+ size_t cdev_count;
};

static void qmp_kick(struct qmp *qmp)
@@ -230,6 +237,8 @@ EXPORT_SYMBOL(qmp_send);

static int qmp_probe(struct platform_device *pdev)
{
+ struct platform_device *cdev;
+ struct device_node *cdev_node;
struct resource *res;
struct qmp *qmp;
int irq;
@@ -279,13 +288,40 @@ static int qmp_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "failed to register AOSS PD\n");
}

+ for_each_available_child_of_node(pdev->dev.of_node, cdev_node) {
+ if (!of_property_read_bool(cdev_node, "#cooling-cells"))
+ continue;
+
+ /* Register cooling device, with its device_node as platform_data */
+ cdev = platform_device_register_data(&pdev->dev,
+ "aoss_qmp_cdev",
+ PLATFORM_DEVID_AUTO,
+ of_node_get(cdev_node),
+ sizeof(cdev_node));
+ if (IS_ERR(cdev)) {
+ dev_err(&pdev->dev,
+ "failed to register cooling device: %pOFn\n",
+ cdev_node);
+ continue;
+ }
+
+ qmp->cdev_pdevs[qmp->cdev_count++] = cdev;
+ }
+
return 0;
}

static int qmp_remove(struct platform_device *pdev)
{
struct qmp *qmp = platform_get_drvdata(pdev);
+ struct device_node *np;
+ int i;

+ for (i = 0; i < qmp->cdev_count; i++) {
+ np = dev_get_platdata(&qmp->cdev_pdevs[i]->dev);
+ platform_device_unregister(qmp->cdev_pdevs[i]);
+ of_node_put(np);
+ }
platform_device_unregister(qmp->pd_pdev);

qmp_close(qmp);
--
2.18.0


2019-01-06 08:12:16

by Bjorn Andersson

[permalink] [raw]
Subject: [PATCH v2 1/7] dt-bindings: soc: qcom: Add AOSS QMP binding

Add binding for the QMP based side-channel communication mechanism to
the AOSS, which is used to control resources not exposed through the
RPMh interface.

Signed-off-by: Bjorn Andersson <[email protected]>
---

Changes since v1:
- Don't describe power-domain as a separate child node

.../bindings/soc/qcom/qcom,aoss-qmp.txt | 57 +++++++++++++++++++
include/dt-bindings/power/qcom-aoss-qmp.h | 15 +++++
2 files changed, 72 insertions(+)
create mode 100644 Documentation/devicetree/bindings/soc/qcom/qcom,aoss-qmp.txt
create mode 100644 include/dt-bindings/power/qcom-aoss-qmp.h

diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,aoss-qmp.txt b/Documentation/devicetree/bindings/soc/qcom/qcom,aoss-qmp.txt
new file mode 100644
index 000000000000..9b0d9785efe0
--- /dev/null
+++ b/Documentation/devicetree/bindings/soc/qcom/qcom,aoss-qmp.txt
@@ -0,0 +1,57 @@
+Qualcomm Always-On Subsystem side channel binding
+
+This binding describes the hardware component responsible for side channel
+requests to the always-on subsystem (AOSS), used for certain power management
+requests that is not handled by the standard RPMh interface. Each client in the
+SoC has it's own block of message RAM and IRQ for communication with the AOSS.
+The protocol used to communicate in the message RAM is known as QMP.
+
+The AOSS side channel exposes control over a set of resources, used to control
+a set of debug related clocks and to affect the low power state of resources
+related to the secondary subsystems. These resources are exposed as a set of
+power-domains.
+
+- compatible:
+ Usage: required
+ Value type: <string>
+ Definition: must be "qcom,sdm845-aoss-qmp"
+
+- reg:
+ Usage: required
+ Value type: <prop-encoded-array>
+ Definition: the base address and size of the message RAM for this
+ client's communication with the AOSS
+
+- interrupts:
+ Usage: required
+ Value type: <prop-encoded-array>
+ Definition: should specify the AOSS message IRQ for this client
+
+- mboxes:
+ Usage: required
+ Value type: <prop-encoded-array>
+ Definition: reference to the mailbox representing the outgoing doorbell
+ in APCS for this client, as described in mailbox/mailbox.txt
+
+- #power-domain-cells:
+ Usage: optional
+ Value type: <u32>
+ Definition: must be 1
+ The provided power-domains are:
+ QDSS clock-domain (0), CDSP state (1), LPASS state (2),
+ modem state (3), SLPI state (4), SPSS state (5) and Venus
+ state (6).
+
+= EXAMPLE
+
+The following example represents the AOSS side-channel message RAM and the
+mechanism exposing the power-domains, as found in SDM845.
+
+ aoss_qmp: qmp@c300000 {
+ compatible = "qcom,sdm845-aoss-qmp";
+ reg = <0x0c300000 0x100000>;
+ interrupts = <GIC_SPI 389 IRQ_TYPE_EDGE_RISING>;
+ mboxes = <&apss_shared 0>;
+
+ #power-domain-cells = <1>;
+ };
diff --git a/include/dt-bindings/power/qcom-aoss-qmp.h b/include/dt-bindings/power/qcom-aoss-qmp.h
new file mode 100644
index 000000000000..7d8ac1a4f90c
--- /dev/null
+++ b/include/dt-bindings/power/qcom-aoss-qmp.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2018, Linaro Ltd. */
+
+#ifndef __DT_BINDINGS_POWER_QCOM_AOSS_QMP_H
+#define __DT_BINDINGS_POWER_QCOM_AOSS_QMP_H
+
+#define AOSS_QMP_QDSS_CLK 0
+#define AOSS_QMP_LS_CDSP 1
+#define AOSS_QMP_LS_LPASS 2
+#define AOSS_QMP_LS_MODEM 3
+#define AOSS_QMP_LS_SLPI 4
+#define AOSS_QMP_LS_SPSS 5
+#define AOSS_QMP_LS_VENUS 6
+
+#endif
--
2.18.0


2019-01-06 08:12:16

by Bjorn Andersson

[permalink] [raw]
Subject: [PATCH v2 4/7] remoteproc: q6v5-mss: Vote for rpmh power domains

From: Rajendra Nayak <[email protected]>

With rpmh ARC resources being modelled as power domains with performance
state, we need to proxy vote on these for SDM845.
Add support to vote on multiple of them, now that genpd supports
associating mutliple power domains to a device.

Signed-off-by: Rajendra Nayak <[email protected]>
[bjorn: Drop device link, improve error handling, name things "proxy"]
Signed-off-by: Bjorn Andersson <[email protected]>
---

This is v3 of this patch, but updated to cover "loadstate". v2 can be found here:
https://lore.kernel.org/lkml/[email protected]/

Changes since v2:
- Drop device links, as we can do active and proxy votes using device links
- Improved error handling, by unrolling some votes on failure
- Rename things proxy, to follow naming of "proxy" and "active"

drivers/remoteproc/qcom_q6v5_mss.c | 115 ++++++++++++++++++++++++++++-
1 file changed, 111 insertions(+), 4 deletions(-)

diff --git a/drivers/remoteproc/qcom_q6v5_mss.c b/drivers/remoteproc/qcom_q6v5_mss.c
index 01be7314e176..62cf16ddb7af 100644
--- a/drivers/remoteproc/qcom_q6v5_mss.c
+++ b/drivers/remoteproc/qcom_q6v5_mss.c
@@ -25,6 +25,8 @@
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
+#include <linux/pm_domain.h>
+#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/remoteproc.h>
@@ -131,6 +133,7 @@ struct rproc_hexagon_res {
char **proxy_clk_names;
char **reset_clk_names;
char **active_clk_names;
+ char **proxy_pd_names;
int version;
bool need_mem_protection;
bool has_alt_reset;
@@ -156,9 +159,11 @@ struct q6v5 {
struct clk *active_clks[8];
struct clk *reset_clks[4];
struct clk *proxy_clks[4];
+ struct device *proxy_pds[3];
int active_clk_count;
int reset_clk_count;
int proxy_clk_count;
+ int proxy_pd_count;

struct reg_info active_regs[1];
struct reg_info proxy_regs[3];
@@ -321,6 +326,41 @@ static void q6v5_clk_disable(struct device *dev,
clk_disable_unprepare(clks[i]);
}

+static int q6v5_pds_enable(struct q6v5 *qproc, struct device **pds,
+ size_t pd_count)
+{
+ int ret;
+ int i;
+
+ for (i = 0; i < pd_count; i++) {
+ dev_pm_genpd_set_performance_state(pds[i], INT_MAX);
+ ret = pm_runtime_get_sync(pds[i]);
+ if (ret < 0)
+ goto unroll_pd_votes;
+ }
+
+ return 0;
+
+unroll_pd_votes:
+ for (i--; i >= 0; i--) {
+ dev_pm_genpd_set_performance_state(pds[i], 0);
+ pm_runtime_put(pds[i]);
+ }
+
+ return ret;
+};
+
+static void q6v5_pds_disable(struct q6v5 *qproc, struct device **pds,
+ size_t pd_count)
+{
+ int i;
+
+ for (i = 0; i < pd_count; i++) {
+ dev_pm_genpd_set_performance_state(pds[i], 0);
+ pm_runtime_put(pds[i]);
+ }
+}
+
static int q6v5_xfer_mem_ownership(struct q6v5 *qproc, int *current_perm,
bool remote_owner, phys_addr_t addr,
size_t size)
@@ -690,11 +730,17 @@ static int q6v5_mba_load(struct q6v5 *qproc)

qcom_q6v5_prepare(&qproc->q6v5);

+ ret = q6v5_pds_enable(qproc, qproc->proxy_pds, qproc->proxy_pd_count);
+ if (ret < 0) {
+ dev_err(qproc->dev, "failed to enable proxy power domains\n");
+ goto disable_irqs;
+ }
+
ret = q6v5_regulator_enable(qproc, qproc->proxy_regs,
qproc->proxy_reg_count);
if (ret) {
dev_err(qproc->dev, "failed to enable proxy supplies\n");
- goto disable_irqs;
+ goto disable_proxy_pds;
}

ret = q6v5_clk_enable(qproc->dev, qproc->proxy_clks,
@@ -791,6 +837,8 @@ static int q6v5_mba_load(struct q6v5 *qproc)
disable_proxy_reg:
q6v5_regulator_disable(qproc, qproc->proxy_regs,
qproc->proxy_reg_count);
+disable_proxy_pds:
+ q6v5_pds_disable(qproc, qproc->proxy_pds, qproc->proxy_pd_count);
disable_irqs:
qcom_q6v5_unprepare(&qproc->q6v5);

@@ -1121,6 +1169,7 @@ static void qcom_msa_handover(struct qcom_q6v5 *q6v5)
qproc->proxy_clk_count);
q6v5_regulator_disable(qproc, qproc->proxy_regs,
qproc->proxy_reg_count);
+ q6v5_pds_disable(qproc, qproc->proxy_pds, qproc->proxy_pd_count);
}

static int q6v5_init_mem(struct q6v5 *qproc, struct platform_device *pdev)
@@ -1181,6 +1230,45 @@ static int q6v5_init_clocks(struct device *dev, struct clk **clks,
return i;
}

+static int q6v5_pds_attach(struct device *dev, struct device **devs,
+ char **pd_names)
+{
+ size_t num_pds = 0;
+ int ret;
+ int i;
+
+ if (!pd_names)
+ return 0;
+
+ while (pd_names[num_pds])
+ num_pds++;
+
+ for (i = 0; i < num_pds; i++) {
+ devs[i] = dev_pm_domain_attach_by_name(dev, pd_names[i]);
+ if (IS_ERR(devs[i])) {
+ ret = PTR_ERR(devs[i]);
+ goto unroll_attach;
+ }
+ }
+
+ return num_pds;
+
+unroll_attach:
+ for (i--; i >= 0; i--)
+ dev_pm_domain_detach(devs[i], false);
+
+ return ret;
+};
+
+static void q6v5_pds_detach(struct q6v5 *qproc, struct device **pds,
+ size_t pd_count)
+{
+ int i;
+
+ for (i = 0; i < pd_count; i++)
+ dev_pm_domain_detach(pds[i], false);
+}
+
static int q6v5_init_reset(struct q6v5 *qproc)
{
qproc->mss_restart = devm_reset_control_get_exclusive(qproc->dev,
@@ -1322,10 +1410,18 @@ static int q6v5_probe(struct platform_device *pdev)
}
qproc->active_reg_count = ret;

+ ret = q6v5_pds_attach(&pdev->dev, qproc->proxy_pds,
+ desc->proxy_pd_names);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to init power domains\n");
+ goto free_rproc;
+ }
+ qproc->proxy_pd_count = ret;
+
qproc->has_alt_reset = desc->has_alt_reset;
ret = q6v5_init_reset(qproc);
if (ret)
- goto free_rproc;
+ goto detach_proxy_pds;

qproc->version = desc->version;
qproc->need_mem_protection = desc->need_mem_protection;
@@ -1333,7 +1429,7 @@ static int q6v5_probe(struct platform_device *pdev)
ret = qcom_q6v5_init(&qproc->q6v5, pdev, rproc, MPSS_CRASH_REASON_SMEM,
qcom_msa_handover);
if (ret)
- goto free_rproc;
+ goto detach_proxy_pds;

qproc->mpss_perm = BIT(QCOM_SCM_VMID_HLOS);
qproc->mba_perm = BIT(QCOM_SCM_VMID_HLOS);
@@ -1344,10 +1440,12 @@ static int q6v5_probe(struct platform_device *pdev)

ret = rproc_add(rproc);
if (ret)
- goto free_rproc;
+ goto detach_proxy_pds;

return 0;

+detach_proxy_pds:
+ q6v5_pds_detach(qproc, qproc->proxy_pds, qproc->proxy_pd_count);
free_rproc:
rproc_free(rproc);

@@ -1364,6 +1462,9 @@ static int q6v5_remove(struct platform_device *pdev)
qcom_remove_glink_subdev(qproc->rproc, &qproc->glink_subdev);
qcom_remove_smd_subdev(qproc->rproc, &qproc->smd_subdev);
qcom_remove_ssr_subdev(qproc->rproc, &qproc->ssr_subdev);
+
+ q6v5_pds_detach(qproc, qproc->proxy_pds, qproc->proxy_pd_count);
+
rproc_free(qproc->rproc);

return 0;
@@ -1388,6 +1489,12 @@ static const struct rproc_hexagon_res sdm845_mss = {
"mnoc_axi",
NULL
},
+ .proxy_pd_names = (char*[]){
+ "cx",
+ "mx",
+ "mss",
+ NULL
+ },
.need_mem_protection = true,
.has_alt_reset = true,
.version = MSS_SDM845,
--
2.18.0


2019-01-06 08:14:03

by Bjorn Andersson

[permalink] [raw]
Subject: [PATCH v2 2/7] soc: qcom: Add AOSS QMP communication driver

The AOSS QMP driver is used to communicate with the AOSS for certain
side-channel requests, that are not enabled through the RPMh interface.

The communication is a very simple synchronous mechanism of messages
being written in message RAM and a doorbell in the AOSS is rung. As the
AOSS has processed the message length is cleared and an interrupt is
fired by the AOSS as acknowledgment.

Signed-off-by: Bjorn Andersson <[email protected]>
---

Changes since v1:
- Skip check in send for empty TX buffer
- Don't follow WARN_ON() with dev_err()
- Register platform_device rather than populate based on of-children

drivers/soc/qcom/Kconfig | 9 +
drivers/soc/qcom/Makefile | 1 +
drivers/soc/qcom/aoss-qmp.c | 313 ++++++++++++++++++++++++++++++
include/linux/soc/qcom/aoss-qmp.h | 12 ++
4 files changed, 335 insertions(+)
create mode 100644 drivers/soc/qcom/aoss-qmp.c
create mode 100644 include/linux/soc/qcom/aoss-qmp.h

diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
index a51458022d21..dda19471057f 100644
--- a/drivers/soc/qcom/Kconfig
+++ b/drivers/soc/qcom/Kconfig
@@ -3,6 +3,15 @@
#
menu "Qualcomm SoC drivers"

+config QCOM_AOSS_QMP
+ tristate "Qualcomm AOSS Messaging Driver"
+ depends on ARCH_QCOM || COMPILE_TEST
+ depends on MAILBOX
+ help
+ This driver provides the means for communicating with the
+ micro-controller in the AOSS, using QMP, to control certain resource
+ that are not exposed through RPMh.
+
config QCOM_COMMAND_DB
bool "Qualcomm Command DB"
depends on ARCH_QCOM || COMPILE_TEST
diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
index 67cb85d0373c..d0d7fdc94d9a 100644
--- a/drivers/soc/qcom/Makefile
+++ b/drivers/soc/qcom/Makefile
@@ -1,5 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
CFLAGS_rpmh-rsc.o := -I$(src)
+obj-$(CONFIG_QCOM_AOSS_QMP) += aoss-qmp.o
obj-$(CONFIG_QCOM_GENI_SE) += qcom-geni-se.o
obj-$(CONFIG_QCOM_COMMAND_DB) += cmd-db.o
obj-$(CONFIG_QCOM_GLINK_SSR) += glink_ssr.o
diff --git a/drivers/soc/qcom/aoss-qmp.c b/drivers/soc/qcom/aoss-qmp.c
new file mode 100644
index 000000000000..de52703b96b6
--- /dev/null
+++ b/drivers/soc/qcom/aoss-qmp.c
@@ -0,0 +1,313 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018, Linaro Ltd
+ */
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/mailbox_client.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/soc/qcom/aoss-qmp.h>
+
+#define QMP_DESC_MAGIC 0x0
+#define QMP_DESC_VERSION 0x4
+#define QMP_DESC_FEATURES 0x8
+
+#define QMP_DESC_UCORE_LINK_STATE 0xc
+#define QMP_DESC_UCORE_LINK_STATE_ACK 0x10
+#define QMP_DESC_UCORE_CH_STATE 0x14
+#define QMP_DESC_UCORE_CH_STATE_ACK 0x18
+#define QMP_DESC_UCORE_MBOX_SIZE 0x1c
+#define QMP_DESC_UCORE_MBOX_OFFSET 0x20
+
+#define QMP_DESC_MCORE_LINK_STATE 0x24
+#define QMP_DESC_MCORE_LINK_STATE_ACK 0x28
+#define QMP_DESC_MCORE_CH_STATE 0x2c
+#define QMP_DESC_MCORE_CH_STATE_ACK 0x30
+#define QMP_DESC_MCORE_MBOX_SIZE 0x34
+#define QMP_DESC_MCORE_MBOX_OFFSET 0x38
+
+#define QMP_STATE_UP 0x0000ffff
+#define QMP_STATE_DOWN 0xffff0000
+
+#define QMP_MAGIC 0x4d41494c
+#define QMP_VERSION 1
+
+/**
+ * struct qmp - driver state for QMP implementation
+ * @msgram: iomem referencing the message RAM used for communication
+ * @dev: reference to QMP device
+ * @mbox_client: mailbox client used to ring the doorbell on transmit
+ * @mbox_chan: mailbox channel used to ring the doorbell on transmit
+ * @offset: offset within @msgram where messages should be written
+ * @size: maximum size of the messages to be transmitted
+ * @event: wait_queue for synchronization with the IRQ
+ * @tx_lock: provides syncrhonization between multiple callers of qmp_send()
+ * @pd_pdev: platform device for the power-domain child device
+ */
+struct qmp {
+ void __iomem *msgram;
+ struct device *dev;
+
+ struct mbox_client mbox_client;
+ struct mbox_chan *mbox_chan;
+
+ size_t offset;
+ size_t size;
+
+ wait_queue_head_t event;
+
+ struct mutex tx_lock;
+
+ struct platform_device *pd_pdev;
+};
+
+static void qmp_kick(struct qmp *qmp)
+{
+ mbox_send_message(qmp->mbox_chan, NULL);
+ mbox_client_txdone(qmp->mbox_chan, 0);
+}
+
+static bool qmp_magic_valid(struct qmp *qmp)
+{
+ return readl(qmp->msgram + QMP_DESC_MAGIC) == QMP_MAGIC;
+}
+
+static bool qmp_link_acked(struct qmp *qmp)
+{
+ return readl(qmp->msgram + QMP_DESC_MCORE_LINK_STATE_ACK) == QMP_STATE_UP;
+}
+
+static bool qmp_mcore_channel_acked(struct qmp *qmp)
+{
+ return readl(qmp->msgram + QMP_DESC_MCORE_CH_STATE_ACK) == QMP_STATE_UP;
+}
+
+static bool qmp_ucore_channel_up(struct qmp *qmp)
+{
+ return readl(qmp->msgram + QMP_DESC_UCORE_CH_STATE) == QMP_STATE_UP;
+}
+
+static int qmp_open(struct qmp *qmp)
+{
+ int ret;
+ u32 val;
+
+ ret = wait_event_timeout(qmp->event, qmp_magic_valid(qmp), HZ);
+ if (!ret) {
+ dev_err(qmp->dev, "QMP magic doesn't match\n");
+ return -ETIMEDOUT;
+ }
+
+ val = readl(qmp->msgram + QMP_DESC_VERSION);
+ if (val != QMP_VERSION) {
+ dev_err(qmp->dev, "unsupported QMP version %d\n", val);
+ return -EINVAL;
+ }
+
+ qmp->offset = readl(qmp->msgram + QMP_DESC_MCORE_MBOX_OFFSET);
+ qmp->size = readl(qmp->msgram + QMP_DESC_MCORE_MBOX_SIZE);
+ if (!qmp->size) {
+ dev_err(qmp->dev, "invalid mailbox size 0x%zx\n", qmp->size);
+ return -EINVAL;
+ }
+
+ /* Ack remote core's link state */
+ val = readl(qmp->msgram + QMP_DESC_UCORE_LINK_STATE);
+ writel(val, qmp->msgram + QMP_DESC_UCORE_LINK_STATE_ACK);
+
+ /* Set local core's link state to up */
+ writel(QMP_STATE_UP, qmp->msgram + QMP_DESC_MCORE_LINK_STATE);
+
+ qmp_kick(qmp);
+
+ ret = wait_event_timeout(qmp->event, qmp_link_acked(qmp), HZ);
+ if (!ret) {
+ dev_err(qmp->dev, "ucore didn't ack link\n");
+ goto timeout_close_link;
+ }
+
+ writel(QMP_STATE_UP, qmp->msgram + QMP_DESC_MCORE_CH_STATE);
+
+ ret = wait_event_timeout(qmp->event, qmp_ucore_channel_up(qmp), HZ);
+ if (!ret) {
+ dev_err(qmp->dev, "ucore didn't open channel\n");
+ goto timeout_close_channel;
+ }
+
+ /* Ack remote core's channel state */
+ val = readl(qmp->msgram + QMP_DESC_UCORE_CH_STATE);
+ writel(val, qmp->msgram + QMP_DESC_UCORE_CH_STATE_ACK);
+
+ qmp_kick(qmp);
+
+ ret = wait_event_timeout(qmp->event, qmp_mcore_channel_acked(qmp), HZ);
+ if (!ret) {
+ dev_err(qmp->dev, "ucore didn't ack channel\n");
+ goto timeout_close_channel;
+ }
+
+ return 0;
+
+timeout_close_channel:
+ writel(QMP_STATE_DOWN, qmp->msgram + QMP_DESC_MCORE_CH_STATE);
+
+timeout_close_link:
+ writel(QMP_STATE_DOWN, qmp->msgram + QMP_DESC_MCORE_LINK_STATE);
+ qmp_kick(qmp);
+
+ return -ETIMEDOUT;
+}
+
+static void qmp_close(struct qmp *qmp)
+{
+ writel(QMP_STATE_DOWN, qmp->msgram + QMP_DESC_MCORE_CH_STATE);
+ writel(QMP_STATE_DOWN, qmp->msgram + QMP_DESC_MCORE_LINK_STATE);
+ qmp_kick(qmp);
+}
+
+static irqreturn_t qmp_intr(int irq, void *data)
+{
+ struct qmp *qmp = data;
+
+ wake_up_interruptible_all(&qmp->event);
+
+ return IRQ_HANDLED;
+}
+
+static bool qmp_message_empty(struct qmp *qmp)
+{
+ return readl(qmp->msgram + qmp->offset) == 0;
+}
+
+/**
+ * qmp_send() - send a message to the AOSS
+ * @qmp: qmp context
+ * @data: message to be sent
+ * @len: length of the message
+ *
+ * Transmit @data to AOSS and wait for the AOSS to acknowledge the message.
+ * @len must be a multiple of 4 and not longer than the mailbox size. Access is
+ * synchronized by this implementation.
+ *
+ * Return: 0 on success, negative errno on failure
+ */
+int qmp_send(struct qmp *qmp, const void *data, size_t len)
+{
+ int ret;
+
+ if (WARN_ON(len + sizeof(u32) > qmp->size))
+ return -EINVAL;
+
+ if (WARN_ON(len % sizeof(u32)))
+ return -EINVAL;
+
+ mutex_lock(&qmp->tx_lock);
+
+ /* The message RAM only implements 32-bit accesses */
+ __iowrite32_copy(qmp->msgram + qmp->offset + sizeof(u32),
+ data, len / sizeof(u32));
+ writel(len, qmp->msgram + qmp->offset);
+ qmp_kick(qmp);
+
+ ret = wait_event_interruptible_timeout(qmp->event,
+ qmp_message_empty(qmp), HZ);
+ if (!ret) {
+ dev_err(qmp->dev, "ucore did not ack channel\n");
+ ret = -ETIMEDOUT;
+
+ /* Clear message from buffer */
+ writel(0, qmp->msgram + qmp->offset);
+ } else {
+ ret = 0;
+ }
+
+ mutex_unlock(&qmp->tx_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL(qmp_send);
+
+static int qmp_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct qmp *qmp;
+ int irq;
+ int ret;
+
+ qmp = devm_kzalloc(&pdev->dev, sizeof(*qmp), GFP_KERNEL);
+ if (!qmp)
+ return -ENOMEM;
+
+ qmp->dev = &pdev->dev;
+ init_waitqueue_head(&qmp->event);
+ mutex_init(&qmp->tx_lock);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ qmp->msgram = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(qmp->msgram))
+ return PTR_ERR(qmp->msgram);
+
+ qmp->mbox_client.dev = &pdev->dev;
+ qmp->mbox_client.knows_txdone = true;
+ qmp->mbox_chan = mbox_request_channel(&qmp->mbox_client, 0);
+ if (IS_ERR(qmp->mbox_chan)) {
+ dev_err(&pdev->dev, "failed to acquire ipc mailbox\n");
+ return PTR_ERR(qmp->mbox_chan);
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ ret = devm_request_irq(&pdev->dev, irq, qmp_intr, IRQF_ONESHOT,
+ "aoss-qmp", qmp);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to request interrupt\n");
+ return ret;
+ }
+
+ ret = qmp_open(qmp);
+ if (ret < 0)
+ return ret;
+
+ platform_set_drvdata(pdev, qmp);
+
+ if (of_property_read_bool(pdev->dev.of_node, "#power-domain-cells")) {
+ qmp->pd_pdev = platform_device_register_data(&pdev->dev,
+ "aoss_qmp_pd",
+ PLATFORM_DEVID_NONE,
+ NULL, 0);
+ if (IS_ERR(qmp->pd_pdev))
+ dev_err(&pdev->dev, "failed to register AOSS PD\n");
+ }
+
+ return 0;
+}
+
+static int qmp_remove(struct platform_device *pdev)
+{
+ struct qmp *qmp = platform_get_drvdata(pdev);
+
+ platform_device_unregister(qmp->pd_pdev);
+
+ qmp_close(qmp);
+
+ return 0;
+}
+
+static const struct of_device_id qmp_dt_match[] = {
+ { .compatible = "qcom,sdm845-aoss-qmp", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, qmp_dt_match);
+
+static struct platform_driver qmp_driver = {
+ .driver = {
+ .name = "aoss_qmp",
+ .of_match_table = qmp_dt_match,
+ },
+ .probe = qmp_probe,
+ .remove = qmp_remove,
+};
+module_platform_driver(qmp_driver);
+
+MODULE_DESCRIPTION("Qualcomm AOSS QMP driver");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/soc/qcom/aoss-qmp.h b/include/linux/soc/qcom/aoss-qmp.h
new file mode 100644
index 000000000000..32ccaa091a9f
--- /dev/null
+++ b/include/linux/soc/qcom/aoss-qmp.h
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2018, Linaro Ltd
+ */
+#ifndef __AOP_QMP_H__
+#define __AOP_QMP_H__
+
+struct qmp;
+
+int qmp_send(struct qmp *qmp, const void *data, size_t len);
+
+#endif
--
2.18.0


2019-01-06 08:14:39

by Bjorn Andersson

[permalink] [raw]
Subject: [PATCH v2 3/7] soc: qcom: Add AOSS QMP genpd provider

The AOSS QMP genpd provider implements control over power-related
resources related to low-power state associated with the remoteprocs in
the system as well as control over a set of clocks related to debug
hardware in the SoC.

Signed-off-by: Bjorn Andersson <[email protected]>
---

Changes since v1:
- Drop compatible, as the QMP DT node is flattened
- Correct size of send

drivers/soc/qcom/Kconfig | 9 +++
drivers/soc/qcom/Makefile | 1 +
drivers/soc/qcom/aoss-qmp-pd.c | 135 +++++++++++++++++++++++++++++++++
3 files changed, 145 insertions(+)
create mode 100644 drivers/soc/qcom/aoss-qmp-pd.c

diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
index dda19471057f..d81256ef5055 100644
--- a/drivers/soc/qcom/Kconfig
+++ b/drivers/soc/qcom/Kconfig
@@ -12,6 +12,15 @@ config QCOM_AOSS_QMP
micro-controller in the AOSS, using QMP, to control certain resource
that are not exposed through RPMh.

+config QCOM_AOSS_QMP_PD
+ tristate "Qualcomm AOSS Messaging Power Domain driver"
+ depends on QCOM_AOSS_QMP
+ select PM_GENERIC_DOMAINS
+ help
+ This driver provides the means of controlling the AOSSs handling of
+ low-power state for resources related to the remoteproc subsystems as
+ well as controlling the debug clocks.
+
config QCOM_COMMAND_DB
bool "Qualcomm Command DB"
depends on ARCH_QCOM || COMPILE_TEST
diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
index d0d7fdc94d9a..ebfa414a5b77 100644
--- a/drivers/soc/qcom/Makefile
+++ b/drivers/soc/qcom/Makefile
@@ -1,6 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
CFLAGS_rpmh-rsc.o := -I$(src)
obj-$(CONFIG_QCOM_AOSS_QMP) += aoss-qmp.o
+obj-$(CONFIG_QCOM_AOSS_QMP_PD) += aoss-qmp-pd.o
obj-$(CONFIG_QCOM_GENI_SE) += qcom-geni-se.o
obj-$(CONFIG_QCOM_COMMAND_DB) += cmd-db.o
obj-$(CONFIG_QCOM_GLINK_SSR) += glink_ssr.o
diff --git a/drivers/soc/qcom/aoss-qmp-pd.c b/drivers/soc/qcom/aoss-qmp-pd.c
new file mode 100644
index 000000000000..62b8fcb9d09e
--- /dev/null
+++ b/drivers/soc/qcom/aoss-qmp-pd.c
@@ -0,0 +1,135 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018, Linaro Ltd
+ */
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm_domain.h>
+#include <linux/soc/qcom/aoss-qmp.h>
+#include <dt-bindings/power/qcom-aoss-qmp.h>
+
+struct qmp_pd {
+ struct qmp *qmp;
+
+ struct generic_pm_domain pd;
+
+ const char *name;
+};
+
+#define to_qmp_pd_resource(res) container_of(res, struct qmp_pd, pd)
+
+struct qmp_pd_resource {
+ const char *name;
+ int (*on)(struct generic_pm_domain *domain);
+ int (*off)(struct generic_pm_domain *domain);
+};
+
+static int qmp_pd_clock_toggle(struct qmp_pd *res, bool enable)
+{
+ char buf[96];
+
+ snprintf(buf, sizeof(buf), "{class: clock, res: %s, val: %d}",
+ res->name, !!enable);
+ return qmp_send(res->qmp, buf, sizeof(buf));
+}
+
+static int qmp_pd_clock_on(struct generic_pm_domain *domain)
+{
+ return qmp_pd_clock_toggle(to_qmp_pd_resource(domain), true);
+}
+
+static int qmp_pd_clock_off(struct generic_pm_domain *domain)
+{
+ return qmp_pd_clock_toggle(to_qmp_pd_resource(domain), false);
+}
+
+static int qmp_pd_image_toggle(struct qmp_pd *res, bool enable)
+{
+ char buf[96];
+
+ snprintf(buf, sizeof(buf),
+ "{class: image, res: load_state, name: %s, val: %s}",
+ res->name, enable ? "on" : "off");
+ return qmp_send(res->qmp, buf, sizeof(buf));
+}
+
+static int qmp_pd_image_on(struct generic_pm_domain *domain)
+{
+ return qmp_pd_image_toggle(to_qmp_pd_resource(domain), true);
+}
+
+static int qmp_pd_image_off(struct generic_pm_domain *domain)
+{
+ return qmp_pd_image_toggle(to_qmp_pd_resource(domain), false);
+}
+
+static const struct qmp_pd_resource sdm845_resources[] = {
+ [AOSS_QMP_QDSS_CLK] = { "qdss", qmp_pd_clock_on, qmp_pd_clock_off },
+ [AOSS_QMP_LS_CDSP] = { "cdsp", qmp_pd_image_on, qmp_pd_image_off },
+ [AOSS_QMP_LS_LPASS] = { "adsp", qmp_pd_image_on, qmp_pd_image_off },
+ [AOSS_QMP_LS_MODEM] = { "modem", qmp_pd_image_on, qmp_pd_image_off },
+ [AOSS_QMP_LS_SLPI] = { "slpi", qmp_pd_image_on, qmp_pd_image_off },
+ [AOSS_QMP_LS_SPSS] = { "spss", qmp_pd_image_on, qmp_pd_image_off },
+ [AOSS_QMP_LS_VENUS] = { "venus", qmp_pd_image_on, qmp_pd_image_off },
+};
+
+static int qmp_pd_probe(struct platform_device *pdev)
+{
+ struct genpd_onecell_data *data;
+ struct device *parent = pdev->dev.parent;
+ struct qmp_pd *res;
+ struct qmp *qmp;
+ size_t num = ARRAY_SIZE(sdm845_resources);
+ int i;
+
+ qmp = dev_get_drvdata(pdev->dev.parent);
+ if (!qmp)
+ return -EINVAL;
+
+ res = devm_kcalloc(&pdev->dev, num, sizeof(*res), GFP_KERNEL);
+ if (!res)
+ return -ENOMEM;
+
+ 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);
+
+ for (i = 0; i < num; i++) {
+ pm_genpd_init(&res[i].pd, NULL, true);
+ res[i].qmp = qmp;
+ res[i].name = sdm845_resources[i].name;
+
+ res[i].pd.name = sdm845_resources[i].name;
+ res[i].pd.power_on = sdm845_resources[i].on;
+ res[i].pd.power_off = sdm845_resources[i].off;
+
+ data->domains[data->num_domains++] = &res[i].pd;
+ }
+
+ return of_genpd_add_provider_onecell(parent->of_node, data);
+}
+
+static int qmp_pd_remove(struct platform_device *pdev)
+{
+ struct device *parent = pdev->dev.parent;
+
+ of_genpd_del_provider(parent->of_node);
+
+ return 0;
+}
+
+static struct platform_driver qmp_pd_driver = {
+ .driver = {
+ .name = "aoss_qmp_pd",
+ },
+ .probe = qmp_pd_probe,
+ .remove = qmp_pd_remove,
+};
+module_platform_driver(qmp_pd_driver);
+
+MODULE_ALIAS("platform:aoss_qmp_pd");
+MODULE_DESCRIPTION("Qualcomm AOSS QMP load-state driver");
+MODULE_LICENSE("GPL v2");
--
2.18.0


2019-01-06 08:25:04

by Bjorn Andersson

[permalink] [raw]
Subject: [PATCH v2 5/7] remoteproc: q6v5-mss: Active powerdomain for SDM845

The SDM845 MSS needs the load_state powerdomain voted for during the
duration of the MSS being powered on, to let the AOSS know that it may
not perform certain power save measures. So vote for this.

Signed-off-by: Bjorn Andersson <[email protected]>
---

Changes since v1:
- New patch

drivers/remoteproc/qcom_q6v5_mss.c | 31 ++++++++++++++++++++++++++++--
1 file changed, 29 insertions(+), 2 deletions(-)

diff --git a/drivers/remoteproc/qcom_q6v5_mss.c b/drivers/remoteproc/qcom_q6v5_mss.c
index 62cf16ddb7af..7f86d9c551c4 100644
--- a/drivers/remoteproc/qcom_q6v5_mss.c
+++ b/drivers/remoteproc/qcom_q6v5_mss.c
@@ -133,6 +133,7 @@ struct rproc_hexagon_res {
char **proxy_clk_names;
char **reset_clk_names;
char **active_clk_names;
+ char **active_pd_names;
char **proxy_pd_names;
int version;
bool need_mem_protection;
@@ -159,10 +160,12 @@ struct q6v5 {
struct clk *active_clks[8];
struct clk *reset_clks[4];
struct clk *proxy_clks[4];
+ struct device *active_pds[1];
struct device *proxy_pds[3];
int active_clk_count;
int reset_clk_count;
int proxy_clk_count;
+ int active_pd_count;
int proxy_pd_count;

struct reg_info active_regs[1];
@@ -730,10 +733,16 @@ static int q6v5_mba_load(struct q6v5 *qproc)

qcom_q6v5_prepare(&qproc->q6v5);

+ ret = q6v5_pds_enable(qproc, qproc->active_pds, qproc->active_pd_count);
+ if (ret < 0) {
+ dev_err(qproc->dev, "failed to enable active power domains\n");
+ goto disable_irqs;
+ }
+
ret = q6v5_pds_enable(qproc, qproc->proxy_pds, qproc->proxy_pd_count);
if (ret < 0) {
dev_err(qproc->dev, "failed to enable proxy power domains\n");
- goto disable_irqs;
+ goto disable_active_pds;
}

ret = q6v5_regulator_enable(qproc, qproc->proxy_regs,
@@ -839,6 +848,8 @@ static int q6v5_mba_load(struct q6v5 *qproc)
qproc->proxy_reg_count);
disable_proxy_pds:
q6v5_pds_disable(qproc, qproc->proxy_pds, qproc->proxy_pd_count);
+disable_active_pds:
+ q6v5_pds_disable(qproc, qproc->active_pds, qproc->active_pd_count);
disable_irqs:
qcom_q6v5_unprepare(&qproc->q6v5);

@@ -878,6 +889,7 @@ static void q6v5_mba_reclaim(struct q6v5 *qproc)
qproc->active_clk_count);
q6v5_regulator_disable(qproc, qproc->active_regs,
qproc->active_reg_count);
+ q6v5_pds_disable(qproc, qproc->active_pds, qproc->active_pd_count);

/* In case of failure or coredump scenario where reclaiming MBA memory
* could not happen reclaim it here.
@@ -1410,11 +1422,19 @@ static int q6v5_probe(struct platform_device *pdev)
}
qproc->active_reg_count = ret;

+ ret = q6v5_pds_attach(&pdev->dev, qproc->active_pds,
+ desc->active_pd_names);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to attach active power domains\n");
+ goto free_rproc;
+ }
+ qproc->active_pd_count = ret;
+
ret = q6v5_pds_attach(&pdev->dev, qproc->proxy_pds,
desc->proxy_pd_names);
if (ret < 0) {
dev_err(&pdev->dev, "Failed to init power domains\n");
- goto free_rproc;
+ goto detach_active_pds;
}
qproc->proxy_pd_count = ret;

@@ -1446,6 +1466,8 @@ static int q6v5_probe(struct platform_device *pdev)

detach_proxy_pds:
q6v5_pds_detach(qproc, qproc->proxy_pds, qproc->proxy_pd_count);
+detach_active_pds:
+ q6v5_pds_detach(qproc, qproc->active_pds, qproc->active_pd_count);
free_rproc:
rproc_free(rproc);

@@ -1463,6 +1485,7 @@ static int q6v5_remove(struct platform_device *pdev)
qcom_remove_smd_subdev(qproc->rproc, &qproc->smd_subdev);
qcom_remove_ssr_subdev(qproc->rproc, &qproc->ssr_subdev);

+ q6v5_pds_detach(qproc, qproc->active_pds, qproc->active_pd_count);
q6v5_pds_detach(qproc, qproc->proxy_pds, qproc->proxy_pd_count);

rproc_free(qproc->rproc);
@@ -1489,6 +1512,10 @@ static const struct rproc_hexagon_res sdm845_mss = {
"mnoc_axi",
NULL
},
+ .active_pd_names = (char*[]){
+ "load_state",
+ NULL
+ },
.proxy_pd_names = (char*[]){
"cx",
"mx",
--
2.18.0


2019-01-09 15:38:06

by Sibi Sankar

[permalink] [raw]
Subject: Re: [PATCH v2 5/7] remoteproc: q6v5-mss: Active powerdomain for SDM845

On 2019-01-06 13:39, Bjorn Andersson wrote:
> The SDM845 MSS needs the load_state powerdomain voted for during the
> duration of the MSS being powered on, to let the AOSS know that it may
> not perform certain power save measures. So vote for this.
>
> Signed-off-by: Bjorn Andersson <[email protected]>
> ---
>
> Changes since v1:
> - New patch
>

Reviewed-by: Sibi Sankar <[email protected]>
Tested-by: Sibi Sankar <[email protected]>

> drivers/remoteproc/qcom_q6v5_mss.c | 31 ++++++++++++++++++++++++++++--
> 1 file changed, 29 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/remoteproc/qcom_q6v5_mss.c
> b/drivers/remoteproc/qcom_q6v5_mss.c
> index 62cf16ddb7af..7f86d9c551c4 100644
> --- a/drivers/remoteproc/qcom_q6v5_mss.c
> +++ b/drivers/remoteproc/qcom_q6v5_mss.c
> @@ -133,6 +133,7 @@ struct rproc_hexagon_res {
> char **proxy_clk_names;
> char **reset_clk_names;
> char **active_clk_names;
> + char **active_pd_names;
> char **proxy_pd_names;
> int version;
> bool need_mem_protection;
> @@ -159,10 +160,12 @@ struct q6v5 {
> struct clk *active_clks[8];
> struct clk *reset_clks[4];
> struct clk *proxy_clks[4];
> + struct device *active_pds[1];
> struct device *proxy_pds[3];
> int active_clk_count;
> int reset_clk_count;
> int proxy_clk_count;
> + int active_pd_count;
> int proxy_pd_count;
>
> struct reg_info active_regs[1];
> @@ -730,10 +733,16 @@ static int q6v5_mba_load(struct q6v5 *qproc)
>
> qcom_q6v5_prepare(&qproc->q6v5);
>
> + ret = q6v5_pds_enable(qproc, qproc->active_pds,
> qproc->active_pd_count);
> + if (ret < 0) {
> + dev_err(qproc->dev, "failed to enable active power domains\n");
> + goto disable_irqs;
> + }
> +
> ret = q6v5_pds_enable(qproc, qproc->proxy_pds,
> qproc->proxy_pd_count);
> if (ret < 0) {
> dev_err(qproc->dev, "failed to enable proxy power domains\n");
> - goto disable_irqs;
> + goto disable_active_pds;
> }
>
> ret = q6v5_regulator_enable(qproc, qproc->proxy_regs,
> @@ -839,6 +848,8 @@ static int q6v5_mba_load(struct q6v5 *qproc)
> qproc->proxy_reg_count);
> disable_proxy_pds:
> q6v5_pds_disable(qproc, qproc->proxy_pds, qproc->proxy_pd_count);
> +disable_active_pds:
> + q6v5_pds_disable(qproc, qproc->active_pds, qproc->active_pd_count);
> disable_irqs:
> qcom_q6v5_unprepare(&qproc->q6v5);
>
> @@ -878,6 +889,7 @@ static void q6v5_mba_reclaim(struct q6v5 *qproc)
> qproc->active_clk_count);
> q6v5_regulator_disable(qproc, qproc->active_regs,
> qproc->active_reg_count);
> + q6v5_pds_disable(qproc, qproc->active_pds, qproc->active_pd_count);
>
> /* In case of failure or coredump scenario where reclaiming MBA
> memory
> * could not happen reclaim it here.
> @@ -1410,11 +1422,19 @@ static int q6v5_probe(struct platform_device
> *pdev)
> }
> qproc->active_reg_count = ret;
>
> + ret = q6v5_pds_attach(&pdev->dev, qproc->active_pds,
> + desc->active_pd_names);
> + if (ret < 0) {
> + dev_err(&pdev->dev, "Failed to attach active power domains\n");
> + goto free_rproc;
> + }
> + qproc->active_pd_count = ret;
> +
> ret = q6v5_pds_attach(&pdev->dev, qproc->proxy_pds,
> desc->proxy_pd_names);
> if (ret < 0) {
> dev_err(&pdev->dev, "Failed to init power domains\n");
> - goto free_rproc;
> + goto detach_active_pds;
> }
> qproc->proxy_pd_count = ret;
>
> @@ -1446,6 +1466,8 @@ static int q6v5_probe(struct platform_device
> *pdev)
>
> detach_proxy_pds:
> q6v5_pds_detach(qproc, qproc->proxy_pds, qproc->proxy_pd_count);
> +detach_active_pds:
> + q6v5_pds_detach(qproc, qproc->active_pds, qproc->active_pd_count);
> free_rproc:
> rproc_free(rproc);
>
> @@ -1463,6 +1485,7 @@ static int q6v5_remove(struct platform_device
> *pdev)
> qcom_remove_smd_subdev(qproc->rproc, &qproc->smd_subdev);
> qcom_remove_ssr_subdev(qproc->rproc, &qproc->ssr_subdev);
>
> + q6v5_pds_detach(qproc, qproc->active_pds, qproc->active_pd_count);
> q6v5_pds_detach(qproc, qproc->proxy_pds, qproc->proxy_pd_count);
>
> rproc_free(qproc->rproc);
> @@ -1489,6 +1512,10 @@ static const struct rproc_hexagon_res sdm845_mss
> = {
> "mnoc_axi",
> NULL
> },
> + .active_pd_names = (char*[]){
> + "load_state",
> + NULL
> + },
> .proxy_pd_names = (char*[]){
> "cx",
> "mx",

--
-- Sibi Sankar --
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project.

2019-01-09 16:03:24

by Sibi Sankar

[permalink] [raw]
Subject: Re: [PATCH v2 4/7] remoteproc: q6v5-mss: Vote for rpmh power domains

Hi Bjorn,
With the changes suggested below:

Reviewed-by: Sibi Sankar <[email protected]>
Tested-by: Sibi Sankar <[email protected]>

On 2019-01-06 13:39, Bjorn Andersson wrote:
> From: Rajendra Nayak <[email protected]>
>
> With rpmh ARC resources being modelled as power domains with
> performance
> state, we need to proxy vote on these for SDM845.
> Add support to vote on multiple of them, now that genpd supports
> associating mutliple power domains to a device.
>
> Signed-off-by: Rajendra Nayak <[email protected]>
> [bjorn: Drop device link, improve error handling, name things "proxy"]
> Signed-off-by: Bjorn Andersson <[email protected]>
> ---
>
> This is v3 of this patch, but updated to cover "loadstate". v2 can be
> found here:
> https://lore.kernel.org/lkml/[email protected]/
>
> Changes since v2:
> - Drop device links, as we can do active and proxy votes using device
> links
> - Improved error handling, by unrolling some votes on failure
> - Rename things proxy, to follow naming of "proxy" and "active"
>
> drivers/remoteproc/qcom_q6v5_mss.c | 115 ++++++++++++++++++++++++++++-
> 1 file changed, 111 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/remoteproc/qcom_q6v5_mss.c
> b/drivers/remoteproc/qcom_q6v5_mss.c
> index 01be7314e176..62cf16ddb7af 100644
> --- a/drivers/remoteproc/qcom_q6v5_mss.c
> +++ b/drivers/remoteproc/qcom_q6v5_mss.c
> @@ -25,6 +25,8 @@
> #include <linux/of_address.h>
> #include <linux/of_device.h>
> #include <linux/platform_device.h>
> +#include <linux/pm_domain.h>
> +#include <linux/pm_runtime.h>
> #include <linux/regmap.h>
> #include <linux/regulator/consumer.h>
> #include <linux/remoteproc.h>
> @@ -131,6 +133,7 @@ struct rproc_hexagon_res {
> char **proxy_clk_names;
> char **reset_clk_names;
> char **active_clk_names;
> + char **proxy_pd_names;
> int version;
> bool need_mem_protection;
> bool has_alt_reset;
> @@ -156,9 +159,11 @@ struct q6v5 {
> struct clk *active_clks[8];
> struct clk *reset_clks[4];
> struct clk *proxy_clks[4];
> + struct device *proxy_pds[3];
> int active_clk_count;
> int reset_clk_count;
> int proxy_clk_count;
> + int proxy_pd_count;
>
> struct reg_info active_regs[1];
> struct reg_info proxy_regs[3];
> @@ -321,6 +326,41 @@ static void q6v5_clk_disable(struct device *dev,
> clk_disable_unprepare(clks[i]);
> }
>
> +static int q6v5_pds_enable(struct q6v5 *qproc, struct device **pds,
> + size_t pd_count)
> +{
> + int ret;
> + int i;
> +
> + for (i = 0; i < pd_count; i++) {
> + dev_pm_genpd_set_performance_state(pds[i], INT_MAX);
> + ret = pm_runtime_get_sync(pds[i]);
> + if (ret < 0)
> + goto unroll_pd_votes;
> + }
> +
> + return 0;
> +
> +unroll_pd_votes:
> + for (i--; i >= 0; i--) {
> + dev_pm_genpd_set_performance_state(pds[i], 0);
> + pm_runtime_put(pds[i]);
> + }
> +
> + return ret;
> +};
> +
> +static void q6v5_pds_disable(struct q6v5 *qproc, struct device **pds,
> + size_t pd_count)
> +{
> + int i;
> +
> + for (i = 0; i < pd_count; i++) {
> + dev_pm_genpd_set_performance_state(pds[i], 0);
> + pm_runtime_put(pds[i]);
> + }
> +}
> +
> static int q6v5_xfer_mem_ownership(struct q6v5 *qproc, int
> *current_perm,
> bool remote_owner, phys_addr_t addr,
> size_t size)
> @@ -690,11 +730,17 @@ static int q6v5_mba_load(struct q6v5 *qproc)
>
> qcom_q6v5_prepare(&qproc->q6v5);
>
> + ret = q6v5_pds_enable(qproc, qproc->proxy_pds,
> qproc->proxy_pd_count);
> + if (ret < 0) {
> + dev_err(qproc->dev, "failed to enable proxy power domains\n");
> + goto disable_irqs;
> + }
> +
> ret = q6v5_regulator_enable(qproc, qproc->proxy_regs,
> qproc->proxy_reg_count);
> if (ret) {
> dev_err(qproc->dev, "failed to enable proxy supplies\n");
> - goto disable_irqs;
> + goto disable_proxy_pds;
> }
>
> ret = q6v5_clk_enable(qproc->dev, qproc->proxy_clks,
> @@ -791,6 +837,8 @@ static int q6v5_mba_load(struct q6v5 *qproc)
> disable_proxy_reg:
> q6v5_regulator_disable(qproc, qproc->proxy_regs,
> qproc->proxy_reg_count);
> +disable_proxy_pds:
> + q6v5_pds_disable(qproc, qproc->proxy_pds, qproc->proxy_pd_count);
> disable_irqs:
> qcom_q6v5_unprepare(&qproc->q6v5);
>
> @@ -1121,6 +1169,7 @@ static void qcom_msa_handover(struct qcom_q6v5
> *q6v5)
> qproc->proxy_clk_count);
> q6v5_regulator_disable(qproc, qproc->proxy_regs,
> qproc->proxy_reg_count);
> + q6v5_pds_disable(qproc, qproc->proxy_pds, qproc->proxy_pd_count);

we should call proxy pds_disable on mba_reclaim
as well:

ret = qcom_q6v5_unprepare(&qproc->q6v5);
if (ret) {
...
q6v5_pds_disable(...);
}

> }
>
> static int q6v5_init_mem(struct q6v5 *qproc, struct platform_device
> *pdev)
> @@ -1181,6 +1230,45 @@ static int q6v5_init_clocks(struct device *dev,
> struct clk **clks,
> return i;
> }
>
> +static int q6v5_pds_attach(struct device *dev, struct device **devs,
> + char **pd_names)
> +{
> + size_t num_pds = 0;
> + int ret;
> + int i;
> +
> + if (!pd_names)
> + return 0;
> +
> + while (pd_names[num_pds])
> + num_pds++;
> +
> + for (i = 0; i < num_pds; i++) {
> + devs[i] = dev_pm_domain_attach_by_name(dev, pd_names[i]);
> + if (IS_ERR(devs[i])) {
> + ret = PTR_ERR(devs[i]);
> + goto unroll_attach;
> + }
> + }
> +
> + return num_pds;
> +
> +unroll_attach:
> + for (i--; i >= 0; i--)
> + dev_pm_domain_detach(devs[i], false);
> +
> + return ret;
> +};
> +
> +static void q6v5_pds_detach(struct q6v5 *qproc, struct device **pds,
> + size_t pd_count)
> +{
> + int i;
> +
> + for (i = 0; i < pd_count; i++)
> + dev_pm_domain_detach(pds[i], false);
> +}
> +
> static int q6v5_init_reset(struct q6v5 *qproc)
> {
> qproc->mss_restart = devm_reset_control_get_exclusive(qproc->dev,
> @@ -1322,10 +1410,18 @@ static int q6v5_probe(struct platform_device
> *pdev)
> }
> qproc->active_reg_count = ret;
>
> + ret = q6v5_pds_attach(&pdev->dev, qproc->proxy_pds,
> + desc->proxy_pd_names);
> + if (ret < 0) {
> + dev_err(&pdev->dev, "Failed to init power domains\n");

Should be "Failed to attach proxy power domains"

> + goto free_rproc;
> + }
> + qproc->proxy_pd_count = ret;
> +
> qproc->has_alt_reset = desc->has_alt_reset;
> ret = q6v5_init_reset(qproc);
> if (ret)
> - goto free_rproc;
> + goto detach_proxy_pds;
>
> qproc->version = desc->version;
> qproc->need_mem_protection = desc->need_mem_protection;
> @@ -1333,7 +1429,7 @@ static int q6v5_probe(struct platform_device
> *pdev)
> ret = qcom_q6v5_init(&qproc->q6v5, pdev, rproc,
> MPSS_CRASH_REASON_SMEM,
> qcom_msa_handover);
> if (ret)
> - goto free_rproc;
> + goto detach_proxy_pds;
>
> qproc->mpss_perm = BIT(QCOM_SCM_VMID_HLOS);
> qproc->mba_perm = BIT(QCOM_SCM_VMID_HLOS);
> @@ -1344,10 +1440,12 @@ static int q6v5_probe(struct platform_device
> *pdev)
>
> ret = rproc_add(rproc);
> if (ret)
> - goto free_rproc;
> + goto detach_proxy_pds;
>
> return 0;
>
> +detach_proxy_pds:
> + q6v5_pds_detach(qproc, qproc->proxy_pds, qproc->proxy_pd_count);
> free_rproc:
> rproc_free(rproc);
>
> @@ -1364,6 +1462,9 @@ static int q6v5_remove(struct platform_device
> *pdev)
> qcom_remove_glink_subdev(qproc->rproc, &qproc->glink_subdev);
> qcom_remove_smd_subdev(qproc->rproc, &qproc->smd_subdev);
> qcom_remove_ssr_subdev(qproc->rproc, &qproc->ssr_subdev);
> +
> + q6v5_pds_detach(qproc, qproc->proxy_pds, qproc->proxy_pd_count);
> +
> rproc_free(qproc->rproc);
>
> return 0;
> @@ -1388,6 +1489,12 @@ static const struct rproc_hexagon_res sdm845_mss
> = {
> "mnoc_axi",
> NULL
> },
> + .proxy_pd_names = (char*[]){
> + "cx",
> + "mx",
> + "mss",
> + NULL
> + },
> .need_mem_protection = true,
> .has_alt_reset = true,
> .version = MSS_SDM845,

--
-- Sibi Sankar --
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project.

2019-01-10 05:03:38

by Sai Prakash Ranjan

[permalink] [raw]
Subject: Re: [PATCH v2 3/7] soc: qcom: Add AOSS QMP genpd provider

Hi Bjorn,

On 1/6/2019 1:39 PM, Bjorn Andersson wrote:
> The AOSS QMP genpd provider implements control over power-related
> resources related to low-power state associated with the remoteprocs in
> the system as well as control over a set of clocks related to debug
> hardware in the SoC.
>
> Signed-off-by: Bjorn Andersson <[email protected]>
> ---
>
> Changes since v1:
> - Drop compatible, as the QMP DT node is flattened
> - Correct size of send
>

Thanks for the patch. I tested QDSS functionality with this on SDM845
and it works fine. Also posted the patch for the same here:
- https://lore.kernel.org/patchwork/patch/1030510/

Tested-by: Sai Prakash Ranjan <[email protected]>

--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member
of Code Aurora Forum, hosted by The Linux Foundation

2019-01-10 12:50:09

by Arun Kumar Neelakantam

[permalink] [raw]
Subject: Re: [PATCH v2 2/7] soc: qcom: Add AOSS QMP communication driver


On 1/6/2019 1:39 PM, Bjorn Andersson wrote:
> The AOSS QMP driver is used to communicate with the AOSS for certain
> side-channel requests, that are not enabled through the RPMh interface.
>
> The communication is a very simple synchronous mechanism of messages
> being written in message RAM and a doorbell in the AOSS is rung. As the
> AOSS has processed the message length is cleared and an interrupt is
> fired by the AOSS as acknowledgment.
>
> Signed-off-by: Bjorn Andersson <[email protected]>
> ---
Reviewed-by: Arun Kumar Neelakantam <[email protected]>
> Changes since v1:
> - Skip check in send for empty TX buffer
> - Don't follow WARN_ON() with dev_err()
> - Register platform_device rather than populate based on of-children
>
> drivers/soc/qcom/Kconfig | 9 +
> drivers/soc/qcom/Makefile | 1 +
> drivers/soc/qcom/aoss-qmp.c | 313 ++++++++++++++++++++++++++++++
> include/linux/soc/qcom/aoss-qmp.h | 12 ++
> 4 files changed, 335 insertions(+)
> create mode 100644 drivers/soc/qcom/aoss-qmp.c
> create mode 100644 include/linux/soc/qcom/aoss-qmp.h
>
> diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
> index a51458022d21..dda19471057f 100644
> --- a/drivers/soc/qcom/Kconfig
> +++ b/drivers/soc/qcom/Kconfig
> @@ -3,6 +3,15 @@
> #
> menu "Qualcomm SoC drivers"
>
> +config QCOM_AOSS_QMP
> + tristate "Qualcomm AOSS Messaging Driver"
> + depends on ARCH_QCOM || COMPILE_TEST
> + depends on MAILBOX
> + help
> + This driver provides the means for communicating with the
> + micro-controller in the AOSS, using QMP, to control certain resource
> + that are not exposed through RPMh.
> +
> config QCOM_COMMAND_DB
> bool "Qualcomm Command DB"
> depends on ARCH_QCOM || COMPILE_TEST
> diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
> index 67cb85d0373c..d0d7fdc94d9a 100644
> --- a/drivers/soc/qcom/Makefile
> +++ b/drivers/soc/qcom/Makefile
> @@ -1,5 +1,6 @@
> # SPDX-License-Identifier: GPL-2.0
> CFLAGS_rpmh-rsc.o := -I$(src)
> +obj-$(CONFIG_QCOM_AOSS_QMP) += aoss-qmp.o
> obj-$(CONFIG_QCOM_GENI_SE) += qcom-geni-se.o
> obj-$(CONFIG_QCOM_COMMAND_DB) += cmd-db.o
> obj-$(CONFIG_QCOM_GLINK_SSR) += glink_ssr.o
> diff --git a/drivers/soc/qcom/aoss-qmp.c b/drivers/soc/qcom/aoss-qmp.c
> new file mode 100644
> index 000000000000..de52703b96b6
> --- /dev/null
> +++ b/drivers/soc/qcom/aoss-qmp.c
> @@ -0,0 +1,313 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2018, Linaro Ltd
> + */
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/mailbox_client.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/soc/qcom/aoss-qmp.h>
> +
> +#define QMP_DESC_MAGIC 0x0
> +#define QMP_DESC_VERSION 0x4
> +#define QMP_DESC_FEATURES 0x8
> +
> +#define QMP_DESC_UCORE_LINK_STATE 0xc
> +#define QMP_DESC_UCORE_LINK_STATE_ACK 0x10
> +#define QMP_DESC_UCORE_CH_STATE 0x14
> +#define QMP_DESC_UCORE_CH_STATE_ACK 0x18
> +#define QMP_DESC_UCORE_MBOX_SIZE 0x1c
> +#define QMP_DESC_UCORE_MBOX_OFFSET 0x20
> +
> +#define QMP_DESC_MCORE_LINK_STATE 0x24
> +#define QMP_DESC_MCORE_LINK_STATE_ACK 0x28
> +#define QMP_DESC_MCORE_CH_STATE 0x2c
> +#define QMP_DESC_MCORE_CH_STATE_ACK 0x30
> +#define QMP_DESC_MCORE_MBOX_SIZE 0x34
> +#define QMP_DESC_MCORE_MBOX_OFFSET 0x38
> +
> +#define QMP_STATE_UP 0x0000ffff
> +#define QMP_STATE_DOWN 0xffff0000
> +
> +#define QMP_MAGIC 0x4d41494c
> +#define QMP_VERSION 1
> +
> +/**
> + * struct qmp - driver state for QMP implementation
> + * @msgram: iomem referencing the message RAM used for communication
> + * @dev: reference to QMP device
> + * @mbox_client: mailbox client used to ring the doorbell on transmit
> + * @mbox_chan: mailbox channel used to ring the doorbell on transmit
> + * @offset: offset within @msgram where messages should be written
> + * @size: maximum size of the messages to be transmitted
> + * @event: wait_queue for synchronization with the IRQ
> + * @tx_lock: provides syncrhonization between multiple callers of qmp_send()
> + * @pd_pdev: platform device for the power-domain child device
> + */
> +struct qmp {
> + void __iomem *msgram;
> + struct device *dev;
> +
> + struct mbox_client mbox_client;
> + struct mbox_chan *mbox_chan;
> +
> + size_t offset;
> + size_t size;
> +
> + wait_queue_head_t event;
> +
> + struct mutex tx_lock;
> +
> + struct platform_device *pd_pdev;
> +};
> +
> +static void qmp_kick(struct qmp *qmp)
> +{
> + mbox_send_message(qmp->mbox_chan, NULL);
> + mbox_client_txdone(qmp->mbox_chan, 0);
> +}
> +
> +static bool qmp_magic_valid(struct qmp *qmp)
> +{
> + return readl(qmp->msgram + QMP_DESC_MAGIC) == QMP_MAGIC;
> +}
> +
> +static bool qmp_link_acked(struct qmp *qmp)
> +{
> + return readl(qmp->msgram + QMP_DESC_MCORE_LINK_STATE_ACK) == QMP_STATE_UP;
> +}
> +
> +static bool qmp_mcore_channel_acked(struct qmp *qmp)
> +{
> + return readl(qmp->msgram + QMP_DESC_MCORE_CH_STATE_ACK) == QMP_STATE_UP;
> +}
> +
> +static bool qmp_ucore_channel_up(struct qmp *qmp)
> +{
> + return readl(qmp->msgram + QMP_DESC_UCORE_CH_STATE) == QMP_STATE_UP;
> +}
> +
> +static int qmp_open(struct qmp *qmp)
> +{
> + int ret;
> + u32 val;
> +
> + ret = wait_event_timeout(qmp->event, qmp_magic_valid(qmp), HZ);
> + if (!ret) {
> + dev_err(qmp->dev, "QMP magic doesn't match\n");
> + return -ETIMEDOUT;
> + }
> +
> + val = readl(qmp->msgram + QMP_DESC_VERSION);
> + if (val != QMP_VERSION) {
> + dev_err(qmp->dev, "unsupported QMP version %d\n", val);
> + return -EINVAL;
> + }
> +
> + qmp->offset = readl(qmp->msgram + QMP_DESC_MCORE_MBOX_OFFSET);
> + qmp->size = readl(qmp->msgram + QMP_DESC_MCORE_MBOX_SIZE);
> + if (!qmp->size) {
> + dev_err(qmp->dev, "invalid mailbox size 0x%zx\n", qmp->size);
> + return -EINVAL;
> + }
> +
> + /* Ack remote core's link state */
> + val = readl(qmp->msgram + QMP_DESC_UCORE_LINK_STATE);
> + writel(val, qmp->msgram + QMP_DESC_UCORE_LINK_STATE_ACK);
> +
> + /* Set local core's link state to up */
> + writel(QMP_STATE_UP, qmp->msgram + QMP_DESC_MCORE_LINK_STATE);
> +
> + qmp_kick(qmp);
> +
> + ret = wait_event_timeout(qmp->event, qmp_link_acked(qmp), HZ);
> + if (!ret) {
> + dev_err(qmp->dev, "ucore didn't ack link\n");
> + goto timeout_close_link;
> + }
> +
> + writel(QMP_STATE_UP, qmp->msgram + QMP_DESC_MCORE_CH_STATE);
> +
> + ret = wait_event_timeout(qmp->event, qmp_ucore_channel_up(qmp), HZ);
> + if (!ret) {
> + dev_err(qmp->dev, "ucore didn't open channel\n");
> + goto timeout_close_channel;
> + }
> +
> + /* Ack remote core's channel state */
> + val = readl(qmp->msgram + QMP_DESC_UCORE_CH_STATE);
> + writel(val, qmp->msgram + QMP_DESC_UCORE_CH_STATE_ACK);
> +
> + qmp_kick(qmp);
> +
> + ret = wait_event_timeout(qmp->event, qmp_mcore_channel_acked(qmp), HZ);
> + if (!ret) {
> + dev_err(qmp->dev, "ucore didn't ack channel\n");
> + goto timeout_close_channel;
> + }
> +
> + return 0;
> +
> +timeout_close_channel:
> + writel(QMP_STATE_DOWN, qmp->msgram + QMP_DESC_MCORE_CH_STATE);
> +
> +timeout_close_link:
> + writel(QMP_STATE_DOWN, qmp->msgram + QMP_DESC_MCORE_LINK_STATE);
> + qmp_kick(qmp);
> +
> + return -ETIMEDOUT;
> +}
> +
> +static void qmp_close(struct qmp *qmp)
> +{
> + writel(QMP_STATE_DOWN, qmp->msgram + QMP_DESC_MCORE_CH_STATE);
> + writel(QMP_STATE_DOWN, qmp->msgram + QMP_DESC_MCORE_LINK_STATE);
> + qmp_kick(qmp);
> +}
> +
> +static irqreturn_t qmp_intr(int irq, void *data)
> +{
> + struct qmp *qmp = data;
> +
> + wake_up_interruptible_all(&qmp->event);
> +
> + return IRQ_HANDLED;
> +}
> +
> +static bool qmp_message_empty(struct qmp *qmp)
> +{
> + return readl(qmp->msgram + qmp->offset) == 0;
> +}
> +
> +/**
> + * qmp_send() - send a message to the AOSS
> + * @qmp: qmp context
> + * @data: message to be sent
> + * @len: length of the message
> + *
> + * Transmit @data to AOSS and wait for the AOSS to acknowledge the message.
> + * @len must be a multiple of 4 and not longer than the mailbox size. Access is
> + * synchronized by this implementation.
> + *
> + * Return: 0 on success, negative errno on failure
> + */
> +int qmp_send(struct qmp *qmp, const void *data, size_t len)
> +{
> + int ret;
> +
> + if (WARN_ON(len + sizeof(u32) > qmp->size))
> + return -EINVAL;
> +
> + if (WARN_ON(len % sizeof(u32)))
> + return -EINVAL;
> +
> + mutex_lock(&qmp->tx_lock);
> +
> + /* The message RAM only implements 32-bit accesses */
> + __iowrite32_copy(qmp->msgram + qmp->offset + sizeof(u32),
> + data, len / sizeof(u32));
> + writel(len, qmp->msgram + qmp->offset);
> + qmp_kick(qmp);
> +
> + ret = wait_event_interruptible_timeout(qmp->event,
> + qmp_message_empty(qmp), HZ);
> + if (!ret) {
> + dev_err(qmp->dev, "ucore did not ack channel\n");
> + ret = -ETIMEDOUT;
> +
> + /* Clear message from buffer */
> + writel(0, qmp->msgram + qmp->offset);
> + } else {
> + ret = 0;
> + }
> +
> + mutex_unlock(&qmp->tx_lock);
> +
> + return ret;
> +}
> +EXPORT_SYMBOL(qmp_send);
> +
> +static int qmp_probe(struct platform_device *pdev)
> +{
> + struct resource *res;
> + struct qmp *qmp;
> + int irq;
> + int ret;
> +
> + qmp = devm_kzalloc(&pdev->dev, sizeof(*qmp), GFP_KERNEL);
> + if (!qmp)
> + return -ENOMEM;
> +
> + qmp->dev = &pdev->dev;
> + init_waitqueue_head(&qmp->event);
> + mutex_init(&qmp->tx_lock);
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + qmp->msgram = devm_ioremap_resource(&pdev->dev, res);
> + if (IS_ERR(qmp->msgram))
> + return PTR_ERR(qmp->msgram);
> +
> + qmp->mbox_client.dev = &pdev->dev;
> + qmp->mbox_client.knows_txdone = true;
> + qmp->mbox_chan = mbox_request_channel(&qmp->mbox_client, 0);
> + if (IS_ERR(qmp->mbox_chan)) {
> + dev_err(&pdev->dev, "failed to acquire ipc mailbox\n");
> + return PTR_ERR(qmp->mbox_chan);
> + }
> +
> + irq = platform_get_irq(pdev, 0);
> + ret = devm_request_irq(&pdev->dev, irq, qmp_intr, IRQF_ONESHOT,
> + "aoss-qmp", qmp);
> + if (ret < 0) {
> + dev_err(&pdev->dev, "failed to request interrupt\n");
> + return ret;
> + }
> +
> + ret = qmp_open(qmp);
> + if (ret < 0)
> + return ret;
> +
> + platform_set_drvdata(pdev, qmp);
> +
> + if (of_property_read_bool(pdev->dev.of_node, "#power-domain-cells")) {
> + qmp->pd_pdev = platform_device_register_data(&pdev->dev,
> + "aoss_qmp_pd",
> + PLATFORM_DEVID_NONE,
> + NULL, 0);
> + if (IS_ERR(qmp->pd_pdev))
> + dev_err(&pdev->dev, "failed to register AOSS PD\n");
> + }
> +
> + return 0;
> +}
> +
> +static int qmp_remove(struct platform_device *pdev)
> +{
> + struct qmp *qmp = platform_get_drvdata(pdev);
> +
> + platform_device_unregister(qmp->pd_pdev);
> +
> + qmp_close(qmp);
> +
> + return 0;
> +}
> +
> +static const struct of_device_id qmp_dt_match[] = {
> + { .compatible = "qcom,sdm845-aoss-qmp", },
> + {}
> +};
> +MODULE_DEVICE_TABLE(of, qmp_dt_match);
> +
> +static struct platform_driver qmp_driver = {
> + .driver = {
> + .name = "aoss_qmp",
> + .of_match_table = qmp_dt_match,
> + },
> + .probe = qmp_probe,
> + .remove = qmp_remove,
> +};
> +module_platform_driver(qmp_driver);
> +
> +MODULE_DESCRIPTION("Qualcomm AOSS QMP driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/include/linux/soc/qcom/aoss-qmp.h b/include/linux/soc/qcom/aoss-qmp.h
> new file mode 100644
> index 000000000000..32ccaa091a9f
> --- /dev/null
> +++ b/include/linux/soc/qcom/aoss-qmp.h
> @@ -0,0 +1,12 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2018, Linaro Ltd
> + */
> +#ifndef __AOP_QMP_H__
> +#define __AOP_QMP_H__
> +
> +struct qmp;
> +
> +int qmp_send(struct qmp *qmp, const void *data, size_t len);
> +
> +#endif

2019-01-11 22:12:42

by Rob Herring (Arm)

[permalink] [raw]
Subject: Re: [PATCH v2 1/7] dt-bindings: soc: qcom: Add AOSS QMP binding

On Sun, 6 Jan 2019 00:09:09 -0800, Bjorn Andersson wrote:
> Add binding for the QMP based side-channel communication mechanism to
> the AOSS, which is used to control resources not exposed through the
> RPMh interface.
>
> Signed-off-by: Bjorn Andersson <[email protected]>
> ---
>
> Changes since v1:
> - Don't describe power-domain as a separate child node
>
> .../bindings/soc/qcom/qcom,aoss-qmp.txt | 57 +++++++++++++++++++
> include/dt-bindings/power/qcom-aoss-qmp.h | 15 +++++
> 2 files changed, 72 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/soc/qcom/qcom,aoss-qmp.txt
> create mode 100644 include/dt-bindings/power/qcom-aoss-qmp.h
>

Reviewed-by: Rob Herring <[email protected]>

2019-01-11 22:13:13

by Rob Herring (Arm)

[permalink] [raw]
Subject: Re: [(RFC) PATCH v2 7/7] soc: qcom: aoss-qmp: Add cooling device support

On Sun, Jan 06, 2019 at 12:09:15AM -0800, Bjorn Andersson wrote:
> The AOSS provides three cooling devices "cx", "mx" and "ebi" that must
> be enabled when temperature goes below a certain level to counter low
> temperature issues. Probe these devices, when described in DeviceTree.
>
> Signed-off-by: Bjorn Andersson <[email protected]>
> ---
>
> We do not yet have the necessary support in the thermal framework to implement
> the cooling device associated with the QMP, so I've just included this patch as
> an RFC in this series.

I'd be fine if you include the binding part now. Looks fine to me.

>
> .../bindings/soc/qcom/qcom,aoss-qmp.txt | 18 ++++++++++
> drivers/soc/qcom/aoss-qmp.c | 36 +++++++++++++++++++
> 2 files changed, 54 insertions(+)

2019-01-14 22:32:48

by Stephen Boyd

[permalink] [raw]
Subject: Re: [PATCH v2 1/7] dt-bindings: soc: qcom: Add AOSS QMP binding

Quoting Bjorn Andersson (2019-01-06 00:09:09)
> Add binding for the QMP based side-channel communication mechanism to
> the AOSS, which is used to control resources not exposed through the
> RPMh interface.
>
> Signed-off-by: Bjorn Andersson <[email protected]>

Reviewed-by: Stephen Boyd <[email protected]>

one nit below.

> diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,aoss-qmp.txt b/Documentation/devicetree/bindings/soc/qcom/qcom,aoss-qmp.txt
> new file mode 100644
> index 000000000000..9b0d9785efe0
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/soc/qcom/qcom,aoss-qmp.txt
> @@ -0,0 +1,57 @@
> += EXAMPLE
> +
> +The following example represents the AOSS side-channel message RAM and the
> +mechanism exposing the power-domains, as found in SDM845.
> +
> + aoss_qmp: qmp@c300000 {
> + compatible = "qcom,sdm845-aoss-qmp";
> + reg = <0x0c300000 0x100000>;
> + interrupts = <GIC_SPI 389 IRQ_TYPE_EDGE_RISING>;
> + mboxes = <&apss_shared 0>;
> +
> + #power-domain-cells = <1>;

This is weirdly tabbed out.


2019-01-14 22:38:06

by Stephen Boyd

[permalink] [raw]
Subject: Re: [PATCH v2 2/7] soc: qcom: Add AOSS QMP communication driver

Quoting Bjorn Andersson (2019-01-06 00:09:10)
> diff --git a/drivers/soc/qcom/aoss-qmp.c b/drivers/soc/qcom/aoss-qmp.c
> new file mode 100644
> index 000000000000..de52703b96b6
> --- /dev/null
> +++ b/drivers/soc/qcom/aoss-qmp.c
> @@ -0,0 +1,313 @@
[...]
> +
> +static int qmp_probe(struct platform_device *pdev)
> +{
> + struct resource *res;
> + struct qmp *qmp;
> + int irq;
> + int ret;
> +
> + qmp = devm_kzalloc(&pdev->dev, sizeof(*qmp), GFP_KERNEL);
> + if (!qmp)
> + return -ENOMEM;
> +
> + qmp->dev = &pdev->dev;
> + init_waitqueue_head(&qmp->event);
> + mutex_init(&qmp->tx_lock);
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + qmp->msgram = devm_ioremap_resource(&pdev->dev, res);
> + if (IS_ERR(qmp->msgram))
> + return PTR_ERR(qmp->msgram);
> +
> + qmp->mbox_client.dev = &pdev->dev;
> + qmp->mbox_client.knows_txdone = true;
> + qmp->mbox_chan = mbox_request_channel(&qmp->mbox_client, 0);
> + if (IS_ERR(qmp->mbox_chan)) {
> + dev_err(&pdev->dev, "failed to acquire ipc mailbox\n");
> + return PTR_ERR(qmp->mbox_chan);
> + }
> +
> + irq = platform_get_irq(pdev, 0);
> + ret = devm_request_irq(&pdev->dev, irq, qmp_intr, IRQF_ONESHOT,
> + "aoss-qmp", qmp);
> + if (ret < 0) {
> + dev_err(&pdev->dev, "failed to request interrupt\n");

Don't we need to free_mbox_channel() all over the place here?

> + return ret;
> + }
> +
> + ret = qmp_open(qmp);
> + if (ret < 0)
> + return ret;
> +
> + platform_set_drvdata(pdev, qmp);
> +
> + if (of_property_read_bool(pdev->dev.of_node, "#power-domain-cells")) {
> + qmp->pd_pdev = platform_device_register_data(&pdev->dev,
> + "aoss_qmp_pd",
> + PLATFORM_DEVID_NONE,
> + NULL, 0);
> + if (IS_ERR(qmp->pd_pdev))
> + dev_err(&pdev->dev, "failed to register AOSS PD\n");
> + }
> +
> + return 0;
> +}
[...]
> diff --git a/include/linux/soc/qcom/aoss-qmp.h b/include/linux/soc/qcom/aoss-qmp.h
> new file mode 100644
> index 000000000000..32ccaa091a9f
> --- /dev/null
> +++ b/include/linux/soc/qcom/aoss-qmp.h
> @@ -0,0 +1,12 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2018, Linaro Ltd
> + */
> +#ifndef __AOP_QMP_H__
> +#define __AOP_QMP_H__

include <linux/types.h> for size_t usage?

> +
> +struct qmp;
> +
> +int qmp_send(struct qmp *qmp, const void *data, size_t len);
> +

2019-01-14 22:42:16

by Stephen Boyd

[permalink] [raw]
Subject: Re: [PATCH v2 3/7] soc: qcom: Add AOSS QMP genpd provider

Quoting Bjorn Andersson (2019-01-06 00:09:11)
> diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
> index dda19471057f..d81256ef5055 100644
> --- a/drivers/soc/qcom/Kconfig
> +++ b/drivers/soc/qcom/Kconfig
> @@ -12,6 +12,15 @@ config QCOM_AOSS_QMP
> micro-controller in the AOSS, using QMP, to control certain resource
> that are not exposed through RPMh.
>
> +config QCOM_AOSS_QMP_PD
> + tristate "Qualcomm AOSS Messaging Power Domain driver"
> + depends on QCOM_AOSS_QMP
> + select PM_GENERIC_DOMAINS
> + help
> + This driver provides the means of controlling the AOSSs handling of

Is that possesive? AOSS's?

> + low-power state for resources related to the remoteproc subsystems as
> + well as controlling the debug clocks.
> +
> config QCOM_COMMAND_DB
> bool "Qualcomm Command DB"
> depends on ARCH_QCOM || COMPILE_TEST
> diff --git a/drivers/soc/qcom/aoss-qmp-pd.c b/drivers/soc/qcom/aoss-qmp-pd.c
> new file mode 100644
> index 000000000000..62b8fcb9d09e
> --- /dev/null
> +++ b/drivers/soc/qcom/aoss-qmp-pd.c
> @@ -0,0 +1,135 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2018, Linaro Ltd
> + */
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_domain.h>
> +#include <linux/soc/qcom/aoss-qmp.h>
> +#include <dt-bindings/power/qcom-aoss-qmp.h>
> +
> +struct qmp_pd {
> + struct qmp *qmp;
> +
> + struct generic_pm_domain pd;
> +
> + const char *name;
> +};
> +
> +#define to_qmp_pd_resource(res) container_of(res, struct qmp_pd, pd)
> +
> +struct qmp_pd_resource {
> + const char *name;
> + int (*on)(struct generic_pm_domain *domain);
> + int (*off)(struct generic_pm_domain *domain);
> +};
> +
> +static int qmp_pd_clock_toggle(struct qmp_pd *res, bool enable)
> +{
> + char buf[96];

Is 96 calculated from somewhere? Can it be a define because it's quite
magical.


2019-01-14 23:22:43

by Bjorn Andersson

[permalink] [raw]
Subject: Re: [PATCH v2 2/7] soc: qcom: Add AOSS QMP communication driver

On Mon 14 Jan 14:36 PST 2019, Stephen Boyd wrote:

> Quoting Bjorn Andersson (2019-01-06 00:09:10)
> > diff --git a/drivers/soc/qcom/aoss-qmp.c b/drivers/soc/qcom/aoss-qmp.c
> > new file mode 100644
> > index 000000000000..de52703b96b6
> > --- /dev/null
> > +++ b/drivers/soc/qcom/aoss-qmp.c
> > @@ -0,0 +1,313 @@
> [...]
> > +
> > +static int qmp_probe(struct platform_device *pdev)
> > +{
> > + struct resource *res;
> > + struct qmp *qmp;
> > + int irq;
> > + int ret;
> > +
> > + qmp = devm_kzalloc(&pdev->dev, sizeof(*qmp), GFP_KERNEL);
> > + if (!qmp)
> > + return -ENOMEM;
> > +
> > + qmp->dev = &pdev->dev;
> > + init_waitqueue_head(&qmp->event);
> > + mutex_init(&qmp->tx_lock);
> > +
> > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > + qmp->msgram = devm_ioremap_resource(&pdev->dev, res);
> > + if (IS_ERR(qmp->msgram))
> > + return PTR_ERR(qmp->msgram);
> > +
> > + qmp->mbox_client.dev = &pdev->dev;
> > + qmp->mbox_client.knows_txdone = true;
> > + qmp->mbox_chan = mbox_request_channel(&qmp->mbox_client, 0);
> > + if (IS_ERR(qmp->mbox_chan)) {
> > + dev_err(&pdev->dev, "failed to acquire ipc mailbox\n");
> > + return PTR_ERR(qmp->mbox_chan);
> > + }
> > +
> > + irq = platform_get_irq(pdev, 0);
> > + ret = devm_request_irq(&pdev->dev, irq, qmp_intr, IRQF_ONESHOT,
> > + "aoss-qmp", qmp);
> > + if (ret < 0) {
> > + dev_err(&pdev->dev, "failed to request interrupt\n");
>
> Don't we need to free_mbox_channel() all over the place here?
>

We do, thanks for spotting this.

[..]
> > diff --git a/include/linux/soc/qcom/aoss-qmp.h b/include/linux/soc/qcom/aoss-qmp.h
> > new file mode 100644
> > index 000000000000..32ccaa091a9f
> > --- /dev/null
> > +++ b/include/linux/soc/qcom/aoss-qmp.h
> > @@ -0,0 +1,12 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/*
> > + * Copyright (c) 2018, Linaro Ltd
> > + */
> > +#ifndef __AOP_QMP_H__
> > +#define __AOP_QMP_H__
>
> include <linux/types.h> for size_t usage?
>

That wouldn't hurt.

> > +
> > +struct qmp;
> > +
> > +int qmp_send(struct qmp *qmp, const void *data, size_t len);
> > +

Thanks,
Bjorn

2019-01-14 23:30:58

by Bjorn Andersson

[permalink] [raw]
Subject: Re: [PATCH v2 3/7] soc: qcom: Add AOSS QMP genpd provider

On Mon 14 Jan 14:40 PST 2019, Stephen Boyd wrote:

> Quoting Bjorn Andersson (2019-01-06 00:09:11)
> > diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
> > index dda19471057f..d81256ef5055 100644
> > --- a/drivers/soc/qcom/Kconfig
> > +++ b/drivers/soc/qcom/Kconfig
> > @@ -12,6 +12,15 @@ config QCOM_AOSS_QMP
> > micro-controller in the AOSS, using QMP, to control certain resource
> > that are not exposed through RPMh.
> >
> > +config QCOM_AOSS_QMP_PD
> > + tristate "Qualcomm AOSS Messaging Power Domain driver"
> > + depends on QCOM_AOSS_QMP
> > + select PM_GENERIC_DOMAINS
> > + help
> > + This driver provides the means of controlling the AOSSs handling of
>
> Is that possesive? AOSS's?
>

That's correct, will fix.

> > + low-power state for resources related to the remoteproc subsystems as
> > + well as controlling the debug clocks.
> > +
> > config QCOM_COMMAND_DB
> > bool "Qualcomm Command DB"
> > depends on ARCH_QCOM || COMPILE_TEST
> > diff --git a/drivers/soc/qcom/aoss-qmp-pd.c b/drivers/soc/qcom/aoss-qmp-pd.c
> > new file mode 100644
> > index 000000000000..62b8fcb9d09e
> > --- /dev/null
> > +++ b/drivers/soc/qcom/aoss-qmp-pd.c
> > @@ -0,0 +1,135 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Copyright (c) 2018, Linaro Ltd
> > + */
> > +#include <linux/module.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/pm_domain.h>
> > +#include <linux/soc/qcom/aoss-qmp.h>
> > +#include <dt-bindings/power/qcom-aoss-qmp.h>
> > +
> > +struct qmp_pd {
> > + struct qmp *qmp;
> > +
> > + struct generic_pm_domain pd;
> > +
> > + const char *name;
> > +};
> > +
> > +#define to_qmp_pd_resource(res) container_of(res, struct qmp_pd, pd)
> > +
> > +struct qmp_pd_resource {
> > + const char *name;
> > + int (*on)(struct generic_pm_domain *domain);
> > + int (*off)(struct generic_pm_domain *domain);
> > +};
> > +
> > +static int qmp_pd_clock_toggle(struct qmp_pd *res, bool enable)
> > +{
> > + char buf[96];
>
> Is 96 calculated from somewhere? Can it be a define because it's quite
> magical.
>

This is inherited by downstream, I know it has to be % 4, I'll check and
update accordingly.

Thanks,
Bjorn