18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (c) 2019, Linaro Ltd
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci#include <dt-bindings/power/qcom-aoss-qmp.h>
68c2ecf20Sopenharmony_ci#include <linux/clk-provider.h>
78c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
88c2ecf20Sopenharmony_ci#include <linux/io.h>
98c2ecf20Sopenharmony_ci#include <linux/mailbox_client.h>
108c2ecf20Sopenharmony_ci#include <linux/module.h>
118c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
128c2ecf20Sopenharmony_ci#include <linux/pm_domain.h>
138c2ecf20Sopenharmony_ci#include <linux/thermal.h>
148c2ecf20Sopenharmony_ci#include <linux/slab.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#define QMP_DESC_MAGIC			0x0
178c2ecf20Sopenharmony_ci#define QMP_DESC_VERSION		0x4
188c2ecf20Sopenharmony_ci#define QMP_DESC_FEATURES		0x8
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci/* AOP-side offsets */
218c2ecf20Sopenharmony_ci#define QMP_DESC_UCORE_LINK_STATE	0xc
228c2ecf20Sopenharmony_ci#define QMP_DESC_UCORE_LINK_STATE_ACK	0x10
238c2ecf20Sopenharmony_ci#define QMP_DESC_UCORE_CH_STATE		0x14
248c2ecf20Sopenharmony_ci#define QMP_DESC_UCORE_CH_STATE_ACK	0x18
258c2ecf20Sopenharmony_ci#define QMP_DESC_UCORE_MBOX_SIZE	0x1c
268c2ecf20Sopenharmony_ci#define QMP_DESC_UCORE_MBOX_OFFSET	0x20
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci/* Linux-side offsets */
298c2ecf20Sopenharmony_ci#define QMP_DESC_MCORE_LINK_STATE	0x24
308c2ecf20Sopenharmony_ci#define QMP_DESC_MCORE_LINK_STATE_ACK	0x28
318c2ecf20Sopenharmony_ci#define QMP_DESC_MCORE_CH_STATE		0x2c
328c2ecf20Sopenharmony_ci#define QMP_DESC_MCORE_CH_STATE_ACK	0x30
338c2ecf20Sopenharmony_ci#define QMP_DESC_MCORE_MBOX_SIZE	0x34
348c2ecf20Sopenharmony_ci#define QMP_DESC_MCORE_MBOX_OFFSET	0x38
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci#define QMP_STATE_UP			GENMASK(15, 0)
378c2ecf20Sopenharmony_ci#define QMP_STATE_DOWN			GENMASK(31, 16)
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci#define QMP_MAGIC			0x4d41494c /* mail */
408c2ecf20Sopenharmony_ci#define QMP_VERSION			1
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci/* 64 bytes is enough to store the requests and provides padding to 4 bytes */
438c2ecf20Sopenharmony_ci#define QMP_MSG_LEN			64
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci#define QMP_NUM_COOLING_RESOURCES	2
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_cistatic bool qmp_cdev_max_state = 1;
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_cistruct qmp_cooling_device {
508c2ecf20Sopenharmony_ci	struct thermal_cooling_device *cdev;
518c2ecf20Sopenharmony_ci	struct qmp *qmp;
528c2ecf20Sopenharmony_ci	char *name;
538c2ecf20Sopenharmony_ci	bool state;
548c2ecf20Sopenharmony_ci};
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci/**
578c2ecf20Sopenharmony_ci * struct qmp - driver state for QMP implementation
588c2ecf20Sopenharmony_ci * @msgram: iomem referencing the message RAM used for communication
598c2ecf20Sopenharmony_ci * @dev: reference to QMP device
608c2ecf20Sopenharmony_ci * @mbox_client: mailbox client used to ring the doorbell on transmit
618c2ecf20Sopenharmony_ci * @mbox_chan: mailbox channel used to ring the doorbell on transmit
628c2ecf20Sopenharmony_ci * @offset: offset within @msgram where messages should be written
638c2ecf20Sopenharmony_ci * @size: maximum size of the messages to be transmitted
648c2ecf20Sopenharmony_ci * @event: wait_queue for synchronization with the IRQ
658c2ecf20Sopenharmony_ci * @tx_lock: provides synchronization between multiple callers of qmp_send()
668c2ecf20Sopenharmony_ci * @qdss_clk: QDSS clock hw struct
678c2ecf20Sopenharmony_ci * @pd_data: genpd data
688c2ecf20Sopenharmony_ci */
698c2ecf20Sopenharmony_cistruct qmp {
708c2ecf20Sopenharmony_ci	void __iomem *msgram;
718c2ecf20Sopenharmony_ci	struct device *dev;
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	struct mbox_client mbox_client;
748c2ecf20Sopenharmony_ci	struct mbox_chan *mbox_chan;
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	size_t offset;
778c2ecf20Sopenharmony_ci	size_t size;
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	wait_queue_head_t event;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	struct mutex tx_lock;
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	struct clk_hw qdss_clk;
848c2ecf20Sopenharmony_ci	struct genpd_onecell_data pd_data;
858c2ecf20Sopenharmony_ci	struct qmp_cooling_device *cooling_devs;
868c2ecf20Sopenharmony_ci};
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_cistruct qmp_pd {
898c2ecf20Sopenharmony_ci	struct qmp *qmp;
908c2ecf20Sopenharmony_ci	struct generic_pm_domain pd;
918c2ecf20Sopenharmony_ci};
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci#define to_qmp_pd_resource(res) container_of(res, struct qmp_pd, pd)
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_cistatic void qmp_kick(struct qmp *qmp)
968c2ecf20Sopenharmony_ci{
978c2ecf20Sopenharmony_ci	mbox_send_message(qmp->mbox_chan, NULL);
988c2ecf20Sopenharmony_ci	mbox_client_txdone(qmp->mbox_chan, 0);
998c2ecf20Sopenharmony_ci}
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_cistatic bool qmp_magic_valid(struct qmp *qmp)
1028c2ecf20Sopenharmony_ci{
1038c2ecf20Sopenharmony_ci	return readl(qmp->msgram + QMP_DESC_MAGIC) == QMP_MAGIC;
1048c2ecf20Sopenharmony_ci}
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_cistatic bool qmp_link_acked(struct qmp *qmp)
1078c2ecf20Sopenharmony_ci{
1088c2ecf20Sopenharmony_ci	return readl(qmp->msgram + QMP_DESC_MCORE_LINK_STATE_ACK) == QMP_STATE_UP;
1098c2ecf20Sopenharmony_ci}
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_cistatic bool qmp_mcore_channel_acked(struct qmp *qmp)
1128c2ecf20Sopenharmony_ci{
1138c2ecf20Sopenharmony_ci	return readl(qmp->msgram + QMP_DESC_MCORE_CH_STATE_ACK) == QMP_STATE_UP;
1148c2ecf20Sopenharmony_ci}
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_cistatic bool qmp_ucore_channel_up(struct qmp *qmp)
1178c2ecf20Sopenharmony_ci{
1188c2ecf20Sopenharmony_ci	return readl(qmp->msgram + QMP_DESC_UCORE_CH_STATE) == QMP_STATE_UP;
1198c2ecf20Sopenharmony_ci}
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_cistatic int qmp_open(struct qmp *qmp)
1228c2ecf20Sopenharmony_ci{
1238c2ecf20Sopenharmony_ci	int ret;
1248c2ecf20Sopenharmony_ci	u32 val;
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	if (!qmp_magic_valid(qmp)) {
1278c2ecf20Sopenharmony_ci		dev_err(qmp->dev, "QMP magic doesn't match\n");
1288c2ecf20Sopenharmony_ci		return -EINVAL;
1298c2ecf20Sopenharmony_ci	}
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	val = readl(qmp->msgram + QMP_DESC_VERSION);
1328c2ecf20Sopenharmony_ci	if (val != QMP_VERSION) {
1338c2ecf20Sopenharmony_ci		dev_err(qmp->dev, "unsupported QMP version %d\n", val);
1348c2ecf20Sopenharmony_ci		return -EINVAL;
1358c2ecf20Sopenharmony_ci	}
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	qmp->offset = readl(qmp->msgram + QMP_DESC_MCORE_MBOX_OFFSET);
1388c2ecf20Sopenharmony_ci	qmp->size = readl(qmp->msgram + QMP_DESC_MCORE_MBOX_SIZE);
1398c2ecf20Sopenharmony_ci	if (!qmp->size) {
1408c2ecf20Sopenharmony_ci		dev_err(qmp->dev, "invalid mailbox size\n");
1418c2ecf20Sopenharmony_ci		return -EINVAL;
1428c2ecf20Sopenharmony_ci	}
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	/* Ack remote core's link state */
1458c2ecf20Sopenharmony_ci	val = readl(qmp->msgram + QMP_DESC_UCORE_LINK_STATE);
1468c2ecf20Sopenharmony_ci	writel(val, qmp->msgram + QMP_DESC_UCORE_LINK_STATE_ACK);
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	/* Set local core's link state to up */
1498c2ecf20Sopenharmony_ci	writel(QMP_STATE_UP, qmp->msgram + QMP_DESC_MCORE_LINK_STATE);
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	qmp_kick(qmp);
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	ret = wait_event_timeout(qmp->event, qmp_link_acked(qmp), HZ);
1548c2ecf20Sopenharmony_ci	if (!ret) {
1558c2ecf20Sopenharmony_ci		dev_err(qmp->dev, "ucore didn't ack link\n");
1568c2ecf20Sopenharmony_ci		goto timeout_close_link;
1578c2ecf20Sopenharmony_ci	}
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	writel(QMP_STATE_UP, qmp->msgram + QMP_DESC_MCORE_CH_STATE);
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	qmp_kick(qmp);
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	ret = wait_event_timeout(qmp->event, qmp_ucore_channel_up(qmp), HZ);
1648c2ecf20Sopenharmony_ci	if (!ret) {
1658c2ecf20Sopenharmony_ci		dev_err(qmp->dev, "ucore didn't open channel\n");
1668c2ecf20Sopenharmony_ci		goto timeout_close_channel;
1678c2ecf20Sopenharmony_ci	}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	/* Ack remote core's channel state */
1708c2ecf20Sopenharmony_ci	writel(QMP_STATE_UP, qmp->msgram + QMP_DESC_UCORE_CH_STATE_ACK);
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	qmp_kick(qmp);
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	ret = wait_event_timeout(qmp->event, qmp_mcore_channel_acked(qmp), HZ);
1758c2ecf20Sopenharmony_ci	if (!ret) {
1768c2ecf20Sopenharmony_ci		dev_err(qmp->dev, "ucore didn't ack channel\n");
1778c2ecf20Sopenharmony_ci		goto timeout_close_channel;
1788c2ecf20Sopenharmony_ci	}
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	return 0;
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_citimeout_close_channel:
1838c2ecf20Sopenharmony_ci	writel(QMP_STATE_DOWN, qmp->msgram + QMP_DESC_MCORE_CH_STATE);
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_citimeout_close_link:
1868c2ecf20Sopenharmony_ci	writel(QMP_STATE_DOWN, qmp->msgram + QMP_DESC_MCORE_LINK_STATE);
1878c2ecf20Sopenharmony_ci	qmp_kick(qmp);
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	return -ETIMEDOUT;
1908c2ecf20Sopenharmony_ci}
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_cistatic void qmp_close(struct qmp *qmp)
1938c2ecf20Sopenharmony_ci{
1948c2ecf20Sopenharmony_ci	writel(QMP_STATE_DOWN, qmp->msgram + QMP_DESC_MCORE_CH_STATE);
1958c2ecf20Sopenharmony_ci	writel(QMP_STATE_DOWN, qmp->msgram + QMP_DESC_MCORE_LINK_STATE);
1968c2ecf20Sopenharmony_ci	qmp_kick(qmp);
1978c2ecf20Sopenharmony_ci}
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_cistatic irqreturn_t qmp_intr(int irq, void *data)
2008c2ecf20Sopenharmony_ci{
2018c2ecf20Sopenharmony_ci	struct qmp *qmp = data;
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	wake_up_all(&qmp->event);
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
2068c2ecf20Sopenharmony_ci}
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_cistatic bool qmp_message_empty(struct qmp *qmp)
2098c2ecf20Sopenharmony_ci{
2108c2ecf20Sopenharmony_ci	return readl(qmp->msgram + qmp->offset) == 0;
2118c2ecf20Sopenharmony_ci}
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci/**
2148c2ecf20Sopenharmony_ci * qmp_send() - send a message to the AOSS
2158c2ecf20Sopenharmony_ci * @qmp: qmp context
2168c2ecf20Sopenharmony_ci * @data: message to be sent
2178c2ecf20Sopenharmony_ci * @len: length of the message
2188c2ecf20Sopenharmony_ci *
2198c2ecf20Sopenharmony_ci * Transmit @data to AOSS and wait for the AOSS to acknowledge the message.
2208c2ecf20Sopenharmony_ci * @len must be a multiple of 4 and not longer than the mailbox size. Access is
2218c2ecf20Sopenharmony_ci * synchronized by this implementation.
2228c2ecf20Sopenharmony_ci *
2238c2ecf20Sopenharmony_ci * Return: 0 on success, negative errno on failure
2248c2ecf20Sopenharmony_ci */
2258c2ecf20Sopenharmony_cistatic int qmp_send(struct qmp *qmp, const void *data, size_t len)
2268c2ecf20Sopenharmony_ci{
2278c2ecf20Sopenharmony_ci	long time_left;
2288c2ecf20Sopenharmony_ci	size_t tlen;
2298c2ecf20Sopenharmony_ci	int ret;
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	if (WARN_ON(len + sizeof(u32) > qmp->size))
2328c2ecf20Sopenharmony_ci		return -EINVAL;
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	if (WARN_ON(len % sizeof(u32)))
2358c2ecf20Sopenharmony_ci		return -EINVAL;
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	mutex_lock(&qmp->tx_lock);
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	/* The message RAM only implements 32-bit accesses */
2408c2ecf20Sopenharmony_ci	__iowrite32_copy(qmp->msgram + qmp->offset + sizeof(u32),
2418c2ecf20Sopenharmony_ci			 data, len / sizeof(u32));
2428c2ecf20Sopenharmony_ci	writel(len, qmp->msgram + qmp->offset);
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	/* Read back len to confirm data written in message RAM */
2458c2ecf20Sopenharmony_ci	tlen = readl(qmp->msgram + qmp->offset);
2468c2ecf20Sopenharmony_ci	qmp_kick(qmp);
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	time_left = wait_event_interruptible_timeout(qmp->event,
2498c2ecf20Sopenharmony_ci						     qmp_message_empty(qmp), HZ);
2508c2ecf20Sopenharmony_ci	if (!time_left) {
2518c2ecf20Sopenharmony_ci		dev_err(qmp->dev, "ucore did not ack channel\n");
2528c2ecf20Sopenharmony_ci		ret = -ETIMEDOUT;
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci		/* Clear message from buffer */
2558c2ecf20Sopenharmony_ci		writel(0, qmp->msgram + qmp->offset);
2568c2ecf20Sopenharmony_ci	} else {
2578c2ecf20Sopenharmony_ci		ret = 0;
2588c2ecf20Sopenharmony_ci	}
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	mutex_unlock(&qmp->tx_lock);
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	return ret;
2638c2ecf20Sopenharmony_ci}
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_cistatic int qmp_qdss_clk_prepare(struct clk_hw *hw)
2668c2ecf20Sopenharmony_ci{
2678c2ecf20Sopenharmony_ci	static const char buf[QMP_MSG_LEN] = "{class: clock, res: qdss, val: 1}";
2688c2ecf20Sopenharmony_ci	struct qmp *qmp = container_of(hw, struct qmp, qdss_clk);
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	return qmp_send(qmp, buf, sizeof(buf));
2718c2ecf20Sopenharmony_ci}
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_cistatic void qmp_qdss_clk_unprepare(struct clk_hw *hw)
2748c2ecf20Sopenharmony_ci{
2758c2ecf20Sopenharmony_ci	static const char buf[QMP_MSG_LEN] = "{class: clock, res: qdss, val: 0}";
2768c2ecf20Sopenharmony_ci	struct qmp *qmp = container_of(hw, struct qmp, qdss_clk);
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	qmp_send(qmp, buf, sizeof(buf));
2798c2ecf20Sopenharmony_ci}
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_cistatic const struct clk_ops qmp_qdss_clk_ops = {
2828c2ecf20Sopenharmony_ci	.prepare = qmp_qdss_clk_prepare,
2838c2ecf20Sopenharmony_ci	.unprepare = qmp_qdss_clk_unprepare,
2848c2ecf20Sopenharmony_ci};
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_cistatic int qmp_qdss_clk_add(struct qmp *qmp)
2878c2ecf20Sopenharmony_ci{
2888c2ecf20Sopenharmony_ci	static const struct clk_init_data qdss_init = {
2898c2ecf20Sopenharmony_ci		.ops = &qmp_qdss_clk_ops,
2908c2ecf20Sopenharmony_ci		.name = "qdss",
2918c2ecf20Sopenharmony_ci	};
2928c2ecf20Sopenharmony_ci	int ret;
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	qmp->qdss_clk.init = &qdss_init;
2958c2ecf20Sopenharmony_ci	ret = clk_hw_register(qmp->dev, &qmp->qdss_clk);
2968c2ecf20Sopenharmony_ci	if (ret < 0) {
2978c2ecf20Sopenharmony_ci		dev_err(qmp->dev, "failed to register qdss clock\n");
2988c2ecf20Sopenharmony_ci		return ret;
2998c2ecf20Sopenharmony_ci	}
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	ret = of_clk_add_hw_provider(qmp->dev->of_node, of_clk_hw_simple_get,
3028c2ecf20Sopenharmony_ci				     &qmp->qdss_clk);
3038c2ecf20Sopenharmony_ci	if (ret < 0) {
3048c2ecf20Sopenharmony_ci		dev_err(qmp->dev, "unable to register of clk hw provider\n");
3058c2ecf20Sopenharmony_ci		clk_hw_unregister(&qmp->qdss_clk);
3068c2ecf20Sopenharmony_ci	}
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	return ret;
3098c2ecf20Sopenharmony_ci}
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_cistatic void qmp_qdss_clk_remove(struct qmp *qmp)
3128c2ecf20Sopenharmony_ci{
3138c2ecf20Sopenharmony_ci	of_clk_del_provider(qmp->dev->of_node);
3148c2ecf20Sopenharmony_ci	clk_hw_unregister(&qmp->qdss_clk);
3158c2ecf20Sopenharmony_ci}
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_cistatic int qmp_pd_power_toggle(struct qmp_pd *res, bool enable)
3188c2ecf20Sopenharmony_ci{
3198c2ecf20Sopenharmony_ci	char buf[QMP_MSG_LEN] = {};
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	snprintf(buf, sizeof(buf),
3228c2ecf20Sopenharmony_ci		 "{class: image, res: load_state, name: %s, val: %s}",
3238c2ecf20Sopenharmony_ci		 res->pd.name, enable ? "on" : "off");
3248c2ecf20Sopenharmony_ci	return qmp_send(res->qmp, buf, sizeof(buf));
3258c2ecf20Sopenharmony_ci}
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_cistatic int qmp_pd_power_on(struct generic_pm_domain *domain)
3288c2ecf20Sopenharmony_ci{
3298c2ecf20Sopenharmony_ci	return qmp_pd_power_toggle(to_qmp_pd_resource(domain), true);
3308c2ecf20Sopenharmony_ci}
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_cistatic int qmp_pd_power_off(struct generic_pm_domain *domain)
3338c2ecf20Sopenharmony_ci{
3348c2ecf20Sopenharmony_ci	return qmp_pd_power_toggle(to_qmp_pd_resource(domain), false);
3358c2ecf20Sopenharmony_ci}
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_cistatic const char * const sdm845_resources[] = {
3388c2ecf20Sopenharmony_ci	[AOSS_QMP_LS_CDSP] = "cdsp",
3398c2ecf20Sopenharmony_ci	[AOSS_QMP_LS_LPASS] = "adsp",
3408c2ecf20Sopenharmony_ci	[AOSS_QMP_LS_MODEM] = "modem",
3418c2ecf20Sopenharmony_ci	[AOSS_QMP_LS_SLPI] = "slpi",
3428c2ecf20Sopenharmony_ci	[AOSS_QMP_LS_SPSS] = "spss",
3438c2ecf20Sopenharmony_ci	[AOSS_QMP_LS_VENUS] = "venus",
3448c2ecf20Sopenharmony_ci};
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_cistatic int qmp_pd_add(struct qmp *qmp)
3478c2ecf20Sopenharmony_ci{
3488c2ecf20Sopenharmony_ci	struct genpd_onecell_data *data = &qmp->pd_data;
3498c2ecf20Sopenharmony_ci	struct device *dev = qmp->dev;
3508c2ecf20Sopenharmony_ci	struct qmp_pd *res;
3518c2ecf20Sopenharmony_ci	size_t num = ARRAY_SIZE(sdm845_resources);
3528c2ecf20Sopenharmony_ci	int ret;
3538c2ecf20Sopenharmony_ci	int i;
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	res = devm_kcalloc(dev, num, sizeof(*res), GFP_KERNEL);
3568c2ecf20Sopenharmony_ci	if (!res)
3578c2ecf20Sopenharmony_ci		return -ENOMEM;
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	data->domains = devm_kcalloc(dev, num, sizeof(*data->domains),
3608c2ecf20Sopenharmony_ci				     GFP_KERNEL);
3618c2ecf20Sopenharmony_ci	if (!data->domains)
3628c2ecf20Sopenharmony_ci		return -ENOMEM;
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	for (i = 0; i < num; i++) {
3658c2ecf20Sopenharmony_ci		res[i].qmp = qmp;
3668c2ecf20Sopenharmony_ci		res[i].pd.name = sdm845_resources[i];
3678c2ecf20Sopenharmony_ci		res[i].pd.power_on = qmp_pd_power_on;
3688c2ecf20Sopenharmony_ci		res[i].pd.power_off = qmp_pd_power_off;
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci		ret = pm_genpd_init(&res[i].pd, NULL, true);
3718c2ecf20Sopenharmony_ci		if (ret < 0) {
3728c2ecf20Sopenharmony_ci			dev_err(dev, "failed to init genpd\n");
3738c2ecf20Sopenharmony_ci			goto unroll_genpds;
3748c2ecf20Sopenharmony_ci		}
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci		data->domains[i] = &res[i].pd;
3778c2ecf20Sopenharmony_ci	}
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	data->num_domains = i;
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	ret = of_genpd_add_provider_onecell(dev->of_node, data);
3828c2ecf20Sopenharmony_ci	if (ret < 0)
3838c2ecf20Sopenharmony_ci		goto unroll_genpds;
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci	return 0;
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ciunroll_genpds:
3888c2ecf20Sopenharmony_ci	for (i--; i >= 0; i--)
3898c2ecf20Sopenharmony_ci		pm_genpd_remove(data->domains[i]);
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	return ret;
3928c2ecf20Sopenharmony_ci}
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_cistatic void qmp_pd_remove(struct qmp *qmp)
3958c2ecf20Sopenharmony_ci{
3968c2ecf20Sopenharmony_ci	struct genpd_onecell_data *data = &qmp->pd_data;
3978c2ecf20Sopenharmony_ci	struct device *dev = qmp->dev;
3988c2ecf20Sopenharmony_ci	int i;
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	of_genpd_del_provider(dev->of_node);
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci	for (i = 0; i < data->num_domains; i++)
4038c2ecf20Sopenharmony_ci		pm_genpd_remove(data->domains[i]);
4048c2ecf20Sopenharmony_ci}
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_cistatic int qmp_cdev_get_max_state(struct thermal_cooling_device *cdev,
4078c2ecf20Sopenharmony_ci				  unsigned long *state)
4088c2ecf20Sopenharmony_ci{
4098c2ecf20Sopenharmony_ci	*state = qmp_cdev_max_state;
4108c2ecf20Sopenharmony_ci	return 0;
4118c2ecf20Sopenharmony_ci}
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_cistatic int qmp_cdev_get_cur_state(struct thermal_cooling_device *cdev,
4148c2ecf20Sopenharmony_ci				  unsigned long *state)
4158c2ecf20Sopenharmony_ci{
4168c2ecf20Sopenharmony_ci	struct qmp_cooling_device *qmp_cdev = cdev->devdata;
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	*state = qmp_cdev->state;
4198c2ecf20Sopenharmony_ci	return 0;
4208c2ecf20Sopenharmony_ci}
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_cistatic int qmp_cdev_set_cur_state(struct thermal_cooling_device *cdev,
4238c2ecf20Sopenharmony_ci				  unsigned long state)
4248c2ecf20Sopenharmony_ci{
4258c2ecf20Sopenharmony_ci	struct qmp_cooling_device *qmp_cdev = cdev->devdata;
4268c2ecf20Sopenharmony_ci	char buf[QMP_MSG_LEN] = {};
4278c2ecf20Sopenharmony_ci	bool cdev_state;
4288c2ecf20Sopenharmony_ci	int ret;
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci	/* Normalize state */
4318c2ecf20Sopenharmony_ci	cdev_state = !!state;
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci	if (qmp_cdev->state == state)
4348c2ecf20Sopenharmony_ci		return 0;
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci	snprintf(buf, sizeof(buf),
4378c2ecf20Sopenharmony_ci		 "{class: volt_flr, event:zero_temp, res:%s, value:%s}",
4388c2ecf20Sopenharmony_ci			qmp_cdev->name,
4398c2ecf20Sopenharmony_ci			cdev_state ? "on" : "off");
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	ret = qmp_send(qmp_cdev->qmp, buf, sizeof(buf));
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci	if (!ret)
4448c2ecf20Sopenharmony_ci		qmp_cdev->state = cdev_state;
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci	return ret;
4478c2ecf20Sopenharmony_ci}
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_cistatic struct thermal_cooling_device_ops qmp_cooling_device_ops = {
4508c2ecf20Sopenharmony_ci	.get_max_state = qmp_cdev_get_max_state,
4518c2ecf20Sopenharmony_ci	.get_cur_state = qmp_cdev_get_cur_state,
4528c2ecf20Sopenharmony_ci	.set_cur_state = qmp_cdev_set_cur_state,
4538c2ecf20Sopenharmony_ci};
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_cistatic int qmp_cooling_device_add(struct qmp *qmp,
4568c2ecf20Sopenharmony_ci				  struct qmp_cooling_device *qmp_cdev,
4578c2ecf20Sopenharmony_ci				  struct device_node *node)
4588c2ecf20Sopenharmony_ci{
4598c2ecf20Sopenharmony_ci	char *cdev_name = (char *)node->name;
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	qmp_cdev->qmp = qmp;
4628c2ecf20Sopenharmony_ci	qmp_cdev->state = !qmp_cdev_max_state;
4638c2ecf20Sopenharmony_ci	qmp_cdev->name = cdev_name;
4648c2ecf20Sopenharmony_ci	qmp_cdev->cdev = devm_thermal_of_cooling_device_register
4658c2ecf20Sopenharmony_ci				(qmp->dev, node,
4668c2ecf20Sopenharmony_ci				cdev_name,
4678c2ecf20Sopenharmony_ci				qmp_cdev, &qmp_cooling_device_ops);
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci	if (IS_ERR(qmp_cdev->cdev))
4708c2ecf20Sopenharmony_ci		dev_err(qmp->dev, "unable to register %s cooling device\n",
4718c2ecf20Sopenharmony_ci			cdev_name);
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci	return PTR_ERR_OR_ZERO(qmp_cdev->cdev);
4748c2ecf20Sopenharmony_ci}
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_cistatic int qmp_cooling_devices_register(struct qmp *qmp)
4778c2ecf20Sopenharmony_ci{
4788c2ecf20Sopenharmony_ci	struct device_node *np, *child;
4798c2ecf20Sopenharmony_ci	int count = 0;
4808c2ecf20Sopenharmony_ci	int ret;
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci	np = qmp->dev->of_node;
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci	qmp->cooling_devs = devm_kcalloc(qmp->dev, QMP_NUM_COOLING_RESOURCES,
4858c2ecf20Sopenharmony_ci					 sizeof(*qmp->cooling_devs),
4868c2ecf20Sopenharmony_ci					 GFP_KERNEL);
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci	if (!qmp->cooling_devs)
4898c2ecf20Sopenharmony_ci		return -ENOMEM;
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	for_each_available_child_of_node(np, child) {
4928c2ecf20Sopenharmony_ci		if (!of_find_property(child, "#cooling-cells", NULL))
4938c2ecf20Sopenharmony_ci			continue;
4948c2ecf20Sopenharmony_ci		ret = qmp_cooling_device_add(qmp, &qmp->cooling_devs[count++],
4958c2ecf20Sopenharmony_ci					     child);
4968c2ecf20Sopenharmony_ci		if (ret) {
4978c2ecf20Sopenharmony_ci			of_node_put(child);
4988c2ecf20Sopenharmony_ci			goto unroll;
4998c2ecf20Sopenharmony_ci		}
5008c2ecf20Sopenharmony_ci	}
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci	if (!count)
5038c2ecf20Sopenharmony_ci		devm_kfree(qmp->dev, qmp->cooling_devs);
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ci	return 0;
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ciunroll:
5088c2ecf20Sopenharmony_ci	while (--count >= 0)
5098c2ecf20Sopenharmony_ci		thermal_cooling_device_unregister
5108c2ecf20Sopenharmony_ci			(qmp->cooling_devs[count].cdev);
5118c2ecf20Sopenharmony_ci	devm_kfree(qmp->dev, qmp->cooling_devs);
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci	return ret;
5148c2ecf20Sopenharmony_ci}
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_cistatic void qmp_cooling_devices_remove(struct qmp *qmp)
5178c2ecf20Sopenharmony_ci{
5188c2ecf20Sopenharmony_ci	int i;
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_ci	for (i = 0; i < QMP_NUM_COOLING_RESOURCES; i++)
5218c2ecf20Sopenharmony_ci		thermal_cooling_device_unregister(qmp->cooling_devs[i].cdev);
5228c2ecf20Sopenharmony_ci}
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_cistatic int qmp_probe(struct platform_device *pdev)
5258c2ecf20Sopenharmony_ci{
5268c2ecf20Sopenharmony_ci	struct resource *res;
5278c2ecf20Sopenharmony_ci	struct qmp *qmp;
5288c2ecf20Sopenharmony_ci	int irq;
5298c2ecf20Sopenharmony_ci	int ret;
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci	qmp = devm_kzalloc(&pdev->dev, sizeof(*qmp), GFP_KERNEL);
5328c2ecf20Sopenharmony_ci	if (!qmp)
5338c2ecf20Sopenharmony_ci		return -ENOMEM;
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_ci	qmp->dev = &pdev->dev;
5368c2ecf20Sopenharmony_ci	init_waitqueue_head(&qmp->event);
5378c2ecf20Sopenharmony_ci	mutex_init(&qmp->tx_lock);
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
5408c2ecf20Sopenharmony_ci	qmp->msgram = devm_ioremap_resource(&pdev->dev, res);
5418c2ecf20Sopenharmony_ci	if (IS_ERR(qmp->msgram))
5428c2ecf20Sopenharmony_ci		return PTR_ERR(qmp->msgram);
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci	qmp->mbox_client.dev = &pdev->dev;
5458c2ecf20Sopenharmony_ci	qmp->mbox_client.knows_txdone = true;
5468c2ecf20Sopenharmony_ci	qmp->mbox_chan = mbox_request_channel(&qmp->mbox_client, 0);
5478c2ecf20Sopenharmony_ci	if (IS_ERR(qmp->mbox_chan)) {
5488c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to acquire ipc mailbox\n");
5498c2ecf20Sopenharmony_ci		return PTR_ERR(qmp->mbox_chan);
5508c2ecf20Sopenharmony_ci	}
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ci	irq = platform_get_irq(pdev, 0);
5538c2ecf20Sopenharmony_ci	ret = devm_request_irq(&pdev->dev, irq, qmp_intr, 0,
5548c2ecf20Sopenharmony_ci			       "aoss-qmp", qmp);
5558c2ecf20Sopenharmony_ci	if (ret < 0) {
5568c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to request interrupt\n");
5578c2ecf20Sopenharmony_ci		goto err_free_mbox;
5588c2ecf20Sopenharmony_ci	}
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_ci	ret = qmp_open(qmp);
5618c2ecf20Sopenharmony_ci	if (ret < 0)
5628c2ecf20Sopenharmony_ci		goto err_free_mbox;
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_ci	ret = qmp_qdss_clk_add(qmp);
5658c2ecf20Sopenharmony_ci	if (ret)
5668c2ecf20Sopenharmony_ci		goto err_close_qmp;
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci	ret = qmp_pd_add(qmp);
5698c2ecf20Sopenharmony_ci	if (ret)
5708c2ecf20Sopenharmony_ci		goto err_remove_qdss_clk;
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ci	ret = qmp_cooling_devices_register(qmp);
5738c2ecf20Sopenharmony_ci	if (ret)
5748c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to register aoss cooling devices\n");
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, qmp);
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_ci	return 0;
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_cierr_remove_qdss_clk:
5818c2ecf20Sopenharmony_ci	qmp_qdss_clk_remove(qmp);
5828c2ecf20Sopenharmony_cierr_close_qmp:
5838c2ecf20Sopenharmony_ci	qmp_close(qmp);
5848c2ecf20Sopenharmony_cierr_free_mbox:
5858c2ecf20Sopenharmony_ci	mbox_free_channel(qmp->mbox_chan);
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ci	return ret;
5888c2ecf20Sopenharmony_ci}
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_cistatic int qmp_remove(struct platform_device *pdev)
5918c2ecf20Sopenharmony_ci{
5928c2ecf20Sopenharmony_ci	struct qmp *qmp = platform_get_drvdata(pdev);
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_ci	qmp_qdss_clk_remove(qmp);
5958c2ecf20Sopenharmony_ci	qmp_pd_remove(qmp);
5968c2ecf20Sopenharmony_ci	qmp_cooling_devices_remove(qmp);
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_ci	qmp_close(qmp);
5998c2ecf20Sopenharmony_ci	mbox_free_channel(qmp->mbox_chan);
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_ci	return 0;
6028c2ecf20Sopenharmony_ci}
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_cistatic const struct of_device_id qmp_dt_match[] = {
6058c2ecf20Sopenharmony_ci	{ .compatible = "qcom,sc7180-aoss-qmp", },
6068c2ecf20Sopenharmony_ci	{ .compatible = "qcom,sdm845-aoss-qmp", },
6078c2ecf20Sopenharmony_ci	{ .compatible = "qcom,sm8150-aoss-qmp", },
6088c2ecf20Sopenharmony_ci	{ .compatible = "qcom,sm8250-aoss-qmp", },
6098c2ecf20Sopenharmony_ci	{}
6108c2ecf20Sopenharmony_ci};
6118c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, qmp_dt_match);
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_cistatic struct platform_driver qmp_driver = {
6148c2ecf20Sopenharmony_ci	.driver = {
6158c2ecf20Sopenharmony_ci		.name		= "qcom_aoss_qmp",
6168c2ecf20Sopenharmony_ci		.of_match_table	= qmp_dt_match,
6178c2ecf20Sopenharmony_ci	},
6188c2ecf20Sopenharmony_ci	.probe = qmp_probe,
6198c2ecf20Sopenharmony_ci	.remove	= qmp_remove,
6208c2ecf20Sopenharmony_ci};
6218c2ecf20Sopenharmony_cimodule_platform_driver(qmp_driver);
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Qualcomm AOSS QMP driver");
6248c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
625