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