lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <db203813-95ac-e555-9e1b-7cf7df44f768@codeaurora.org>
Date:   Mon, 13 May 2019 19:40:22 +0530
From:   Sibi Sankar <sibis@...eaurora.org>
To:     Bjorn Andersson <bjorn.andersson@...aro.org>,
        Andy Gross <agross@...nel.org>,
        David Brown <david.brown@...aro.org>,
        Stephen Boyd <swboyd@...omium.org>
Cc:     Rob Herring <robh+dt@...nel.org>,
        Mark Rutland <mark.rutland@....com>,
        linux-arm-msm@...r.kernel.org, devicetree@...r.kernel.org,
        linux-kernel@...r.kernel.org
Subject: Re: [PATCH v7 2/4] soc: qcom: Add AOSS QMP driver

Hey Bjorn,

On 5/1/19 10:07 AM, Bjorn Andersson wrote:
> The Always On Subsystem (AOSS) Qualcomm Messaging Protocol (QMP) driver
> is used to communicate with the AOSS for certain side-channel requests,
> that are not available 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.
> 
> The driver exposes the QDSS clock as a clock and the low-power state
> associated with the remoteprocs in the system as a set of power-domains.
> 
> Signed-off-by: Bjorn Andersson <bjorn.andersson@...aro.org>
> ---
> 
> Changes since v6:
> - Squash the pd into the same driver as the communication, to simplify
>    the interaction.
> - Representing the QDSS clocks as a clock/power domain turns out to
>    cascade into a request to make all Coresight drivers have a secondary
>    compatible to replace the required bus clock with a required power
>    domain. So in v7 this is exposed as a clock instead.
> - Some error checking updates, as reported by Doug.
> 
>   drivers/soc/qcom/Kconfig     |  11 +
>   drivers/soc/qcom/Makefile    |   1 +
>   drivers/soc/qcom/qcom_aoss.c | 473 +++++++++++++++++++++++++++++++++++
>   3 files changed, 485 insertions(+)
>   create mode 100644 drivers/soc/qcom/qcom_aoss.c
> 
> diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
> index 1ee298f6bf17..3e460b334b47 100644
> --- a/drivers/soc/qcom/Kconfig
> +++ b/drivers/soc/qcom/Kconfig
> @@ -3,6 +3,17 @@
>   #
>   menu "Qualcomm SoC drivers"
>   
> +config QCOM_AOSS_QMP
> +	tristate "Qualcomm AOSS Driver"
> +	depends on ARCH_QCOM || COMPILE_TEST
> +	depends on MAILBOX
> +	select PM_GENERIC_DOMAINS
> +	help
> +	  This driver provides the means of communicating with and controlling
> +	  the low-power state for resources related to the remoteproc
> +	  subsystems as well as controlling the debug clocks exposed by the Always On
> +	  Subsystem (AOSS) using Qualcomm Messaging Protocol (QMP).
> +
>   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 ffe519b0cb66..eeb088beb15f 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) +=	qcom_aoss.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/qcom_aoss.c b/drivers/soc/qcom/qcom_aoss.c
> new file mode 100644
> index 000000000000..f1fc26ab2e36
> --- /dev/null
> +++ b/drivers/soc/qcom/qcom_aoss.c
> @@ -0,0 +1,473 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2019, Linaro Ltd
> + */
> +#include <linux/clk-provider.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/mailbox_client.h>
> +#include <linux/pm_domain.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>

alphabetical order..

> +#include <dt-bindings/power/qcom-aoss-qmp.h>
> +
> +#define QMP_DESC_MAGIC			0x0
> +#define QMP_DESC_VERSION		0x4
> +#define QMP_DESC_FEATURES		0x8
> +
> +/* AOP-side offsets */
> +#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
> +
> +/* Linux-side offsets */
> +#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

you may want to mention that the magic stands for MAIL

> +#define QMP_VERSION	1
> +
> +/* Requests are expected to be 96 bytes long */
> +#define QMP_MSG_LEN	96
> +
> +/**
> + * 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()
> + * @qdss_clk: QDSS clock hw struct
> + * @pd_data: genpd data
> + */
> +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 clk_hw qdss_clk;
> +	struct genpd_onecell_data pd_data;
> +};
> +
> +struct qmp_pd {
> +	struct qmp *qmp;
> +	struct generic_pm_domain pd;
> +};
> +
> +#define to_qmp_pd_resource(res) container_of(res, struct qmp_pd, pd)
> +
> +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;
> +
> +	if (!qmp_magic_valid(qmp)) {
> +		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\n");
> +		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);
> +
> +	qmp_kick(qmp);
> +
> +	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 */
> +	writel(QMP_STATE_UP, 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;
> +}
> +
> +static int qmp_qdss_clk_prepare(struct clk_hw *hw)
> +{
> +	struct qmp *qmp = container_of(hw, struct qmp, qdss_clk);
> +	char buf[QMP_MSG_LEN] = "{class: clock, res: qdss, val: 1}";
> +
> +	return qmp_send(qmp, buf, sizeof(buf));
> +}
> +
> +static void qmp_qdss_clk_unprepare(struct clk_hw *hw)
> +{
> +	struct qmp *qmp = container_of(hw, struct qmp, qdss_clk);
> +	char buf[QMP_MSG_LEN] = "{class: clock, res: qdss, val: 0}";
> +
> +	qmp_send(qmp, buf, sizeof(buf));
> +}
> +
> +static const struct clk_ops qmp_qdss_clk_ops = {
> +	.prepare = qmp_qdss_clk_prepare,
> +	.unprepare = qmp_qdss_clk_unprepare,
> +};
> +
> +static int qmp_qdss_clk_add(struct qmp *qmp)
> +{
> +	struct clk_init_data qdss_init = {
> +		.ops = &qmp_qdss_clk_ops,
> +		.name = "qdss",
> +	};
> +	int ret;
> +
> +	qmp->qdss_clk.init = &qdss_init;
> +	ret = clk_hw_register(qmp->dev, &qmp->qdss_clk);
> +	if (ret < 0) {
> +		dev_err(qmp->dev, "failed to register qdss clock\n");
> +		return ret;
> +	}
> +
> +	return of_clk_add_hw_provider(qmp->dev->of_node, of_clk_hw_simple_get,
> +				      &qmp->qdss_clk);
> +}
> +
> +static void qmp_qdss_clk_remove(struct qmp *qmp)
> +{
> +	clk_hw_unregister(&qmp->qdss_clk);
> +	of_clk_del_provider(qmp->dev->of_node);
> +}
> +
> +static int qmp_pd_power_toggle(struct qmp_pd *res, bool enable)
> +{
> +	char buf[QMP_MSG_LEN] = {};
> +
> +	snprintf(buf, sizeof(buf),
> +		 "{class: image, res: load_state, name: %s, val: %s}",
> +		 res->pd.name, enable ? "on" : "off");
> +	return qmp_send(res->qmp, buf, sizeof(buf));
> +}
> +
> +static int qmp_pd_power_on(struct generic_pm_domain *domain)
> +{
> +	return qmp_pd_power_toggle(to_qmp_pd_resource(domain), true);
> +}
> +
> +static int qmp_pd_power_off(struct generic_pm_domain *domain)
> +{
> +	return qmp_pd_power_toggle(to_qmp_pd_resource(domain), false);
> +}
> +
> +static const char * const sdm845_resources[] = {
> +	[AOSS_QMP_LS_CDSP] = "cdsp",
> +	[AOSS_QMP_LS_LPASS] = "adsp",
> +	[AOSS_QMP_LS_MODEM] = "modem",
> +	[AOSS_QMP_LS_SLPI] = "slpi",
> +	[AOSS_QMP_LS_SPSS] = "spss",
> +	[AOSS_QMP_LS_VENUS] = "venus",
> +};
> +
> +static int qmp_pd_add(struct qmp *qmp)
> +{
> +	struct genpd_onecell_data *data = &qmp->pd_data;
> +	struct device *dev = qmp->dev;
> +	struct qmp_pd *res;
> +	size_t num = ARRAY_SIZE(sdm845_resources);
> +	int ret;
> +	int i;
> +
> +	res = devm_kcalloc(dev, num, sizeof(*res), GFP_KERNEL);
> +	if (!res)
> +		return -ENOMEM;
> +
> +	data->domains = devm_kcalloc(dev, num, sizeof(*data->domains),
> +				     GFP_KERNEL);
> +	if (!data->domains)
> +		return -ENOMEM;
> +
> +	for (i = 0; i < num; i++) {
> +		res[i].qmp = qmp;
> +		res[i].pd.name = sdm845_resources[i];
> +		res[i].pd.power_on = qmp_pd_power_on;
> +		res[i].pd.power_off = qmp_pd_power_off;
> +
> +		ret = pm_genpd_init(&res[i].pd, NULL, true);
> +		if (ret < 0) {
> +			dev_err(dev, "failed to init genpd\n");
> +			goto unroll_genpds;
> +		}
> +
> +		data->domains[i] = &res[i].pd;
> +	}
> +
> +	data->num_domains = i;
> +
> +	ret = of_genpd_add_provider_onecell(dev->of_node, data);
> +	if (ret < 0)
> +		goto unroll_genpds;
> +
> +	return 0;
> +
> +unroll_genpds:
> +	for (i--; i >= 0; i--)
> +		pm_genpd_remove(data->domains[i]);
> +
> +	return ret;
> +}
> +
> +static void qmp_pd_remove(struct qmp *qmp)
> +{
> +	struct genpd_onecell_data *data = &qmp->pd_data;
> +	struct device *dev = qmp->dev;
> +	int i;
> +
> +	of_genpd_del_provider(dev->of_node);
> +
> +	for (i = 0; i < data->num_domains; i++)
> +		pm_genpd_remove(data->domains[i]);
> +}
> +
> +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");
> +		goto err_close_qmp;

shouldn't this be err_freebox_mbox instead? we can avoid this by doing a
request irq before mbox_request_channel...

> +	}
> +
> +	ret = qmp_open(qmp);
> +	if (ret < 0)
> +		goto err_free_mbox;
> +
> +	ret = qmp_qdss_clk_add(qmp);
> +	if (ret)
> +		goto err_close_qmp;
> +
> +	ret = qmp_pd_add(qmp);
> +	if (ret)
> +		goto err_remove_qdss_clk;
> +
> +	platform_set_drvdata(pdev, qmp);
> +
> +	return 0;
> +
> +err_remove_qdss_clk:
> +	qmp_qdss_clk_remove(qmp);
> +err_close_qmp:
> +	qmp_close(qmp);
> +err_free_mbox:
> +	mbox_free_channel(qmp->mbox_chan);
> +
> +	return ret;
> +}
> +
> +static int qmp_remove(struct platform_device *pdev)
> +{
> +	struct qmp *qmp = platform_get_drvdata(pdev);
> +
> +	qmp_qdss_clk_remove(qmp);
> +	qmp_pd_remove(qmp);
> +
> +	qmp_close(qmp);
> +	mbox_free_channel(qmp->mbox_chan);
> +
> +	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		= "qcom_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");
> 

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

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ