Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

soc: qcom: Driver for the Qualcomm RPM over SMD

Driver for the Resource Power Manager (RPM) found in Qualcomm 8974 based
devices.
The driver exposes resources that child drivers can operate on; to
implementing regulator, clock and bus frequency drivers.

Signed-off-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>
Signed-off-by: Andy Gross <agross@codeaurora.org>

authored by

Bjorn Andersson and committed by
Andy Gross
936f14cf f2ab3298

+294
+14
drivers/soc/qcom/Kconfig
··· 27 27 providing communication channels to remote processors in Qualcomm 28 28 platforms. 29 29 30 + config QCOM_SMD_RPM 31 + tristate "Qualcomm Resource Power Manager (RPM) over SMD" 32 + depends on QCOM_SMD && OF 33 + help 34 + If you say yes to this option, support will be included for the 35 + Resource Power Manager system found in the Qualcomm 8974 based 36 + devices. 37 + 38 + This is required to access many regulators, clocks and bus 39 + frequencies controlled by the RPM on these devices. 40 + 41 + Say M here if you want to include support for the Qualcomm RPM as a 42 + module. This will build a module called "qcom-smd-rpm". 43 + 30 44 config QCOM_SMEM 31 45 tristate "Qualcomm Shared Memory Manager (SMEM)" 32 46 depends on ARCH_QCOM
+1
drivers/soc/qcom/Makefile
··· 1 1 obj-$(CONFIG_QCOM_GSBI) += qcom_gsbi.o 2 2 obj-$(CONFIG_QCOM_PM) += spm.o 3 3 obj-$(CONFIG_QCOM_SMD) += smd.o 4 + obj-$(CONFIG_QCOM_SMD_RPM) += smd-rpm.o 4 5 obj-$(CONFIG_QCOM_SMEM) += smem.o
+244
drivers/soc/qcom/smd-rpm.c
··· 1 + /* 2 + * Copyright (c) 2015, Sony Mobile Communications AB. 3 + * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. 4 + * 5 + * This program is free software; you can redistribute it and/or modify 6 + * it under the terms of the GNU General Public License version 2 and 7 + * only version 2 as published by the Free Software Foundation. 8 + * 9 + * This program is distributed in the hope that it will be useful, 10 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 + * GNU General Public License for more details. 13 + */ 14 + 15 + #include <linux/module.h> 16 + #include <linux/platform_device.h> 17 + #include <linux/of_platform.h> 18 + #include <linux/io.h> 19 + #include <linux/interrupt.h> 20 + 21 + #include <linux/soc/qcom/smd.h> 22 + #include <linux/soc/qcom/smd-rpm.h> 23 + 24 + #define RPM_REQUEST_TIMEOUT (5 * HZ) 25 + 26 + /** 27 + * struct qcom_smd_rpm - state of the rpm device driver 28 + * @rpm_channel: reference to the smd channel 29 + * @ack: completion for acks 30 + * @lock: mutual exclusion around the send/complete pair 31 + * @ack_status: result of the rpm request 32 + */ 33 + struct qcom_smd_rpm { 34 + struct qcom_smd_channel *rpm_channel; 35 + 36 + struct completion ack; 37 + struct mutex lock; 38 + int ack_status; 39 + }; 40 + 41 + /** 42 + * struct qcom_rpm_header - header for all rpm requests and responses 43 + * @service_type: identifier of the service 44 + * @length: length of the payload 45 + */ 46 + struct qcom_rpm_header { 47 + u32 service_type; 48 + u32 length; 49 + }; 50 + 51 + /** 52 + * struct qcom_rpm_request - request message to the rpm 53 + * @msg_id: identifier of the outgoing message 54 + * @flags: active/sleep state flags 55 + * @type: resource type 56 + * @id: resource id 57 + * @data_len: length of the payload following this header 58 + */ 59 + struct qcom_rpm_request { 60 + u32 msg_id; 61 + u32 flags; 62 + u32 type; 63 + u32 id; 64 + u32 data_len; 65 + }; 66 + 67 + /** 68 + * struct qcom_rpm_message - response message from the rpm 69 + * @msg_type: indicator of the type of message 70 + * @length: the size of this message, including the message header 71 + * @msg_id: message id 72 + * @message: textual message from the rpm 73 + * 74 + * Multiple of these messages can be stacked in an rpm message. 75 + */ 76 + struct qcom_rpm_message { 77 + u32 msg_type; 78 + u32 length; 79 + union { 80 + u32 msg_id; 81 + u8 message[0]; 82 + }; 83 + }; 84 + 85 + #define RPM_SERVICE_TYPE_REQUEST 0x00716572 /* "req\0" */ 86 + 87 + #define RPM_MSG_TYPE_ERR 0x00727265 /* "err\0" */ 88 + #define RPM_MSG_TYPE_MSG_ID 0x2367736d /* "msg#" */ 89 + 90 + /** 91 + * qcom_rpm_smd_write - write @buf to @type:@id 92 + * @rpm: rpm handle 93 + * @type: resource type 94 + * @id: resource identifier 95 + * @buf: the data to be written 96 + * @count: number of bytes in @buf 97 + */ 98 + int qcom_rpm_smd_write(struct qcom_smd_rpm *rpm, 99 + int state, 100 + u32 type, u32 id, 101 + void *buf, 102 + size_t count) 103 + { 104 + static unsigned msg_id = 1; 105 + int left; 106 + int ret; 107 + 108 + struct { 109 + struct qcom_rpm_header hdr; 110 + struct qcom_rpm_request req; 111 + u8 payload[count]; 112 + } pkt; 113 + 114 + /* SMD packets to the RPM may not exceed 256 bytes */ 115 + if (WARN_ON(sizeof(pkt) >= 256)) 116 + return -EINVAL; 117 + 118 + mutex_lock(&rpm->lock); 119 + 120 + pkt.hdr.service_type = RPM_SERVICE_TYPE_REQUEST; 121 + pkt.hdr.length = sizeof(struct qcom_rpm_request) + count; 122 + 123 + pkt.req.msg_id = msg_id++; 124 + pkt.req.flags = BIT(state); 125 + pkt.req.type = type; 126 + pkt.req.id = id; 127 + pkt.req.data_len = count; 128 + memcpy(pkt.payload, buf, count); 129 + 130 + ret = qcom_smd_send(rpm->rpm_channel, &pkt, sizeof(pkt)); 131 + if (ret) 132 + goto out; 133 + 134 + left = wait_for_completion_timeout(&rpm->ack, RPM_REQUEST_TIMEOUT); 135 + if (!left) 136 + ret = -ETIMEDOUT; 137 + else 138 + ret = rpm->ack_status; 139 + 140 + out: 141 + mutex_unlock(&rpm->lock); 142 + return ret; 143 + } 144 + EXPORT_SYMBOL(qcom_rpm_smd_write); 145 + 146 + static int qcom_smd_rpm_callback(struct qcom_smd_device *qsdev, 147 + const void *data, 148 + size_t count) 149 + { 150 + const struct qcom_rpm_header *hdr = data; 151 + const struct qcom_rpm_message *msg; 152 + struct qcom_smd_rpm *rpm = dev_get_drvdata(&qsdev->dev); 153 + const u8 *buf = data + sizeof(struct qcom_rpm_header); 154 + const u8 *end = buf + hdr->length; 155 + char msgbuf[32]; 156 + int status = 0; 157 + u32 len; 158 + 159 + if (hdr->service_type != RPM_SERVICE_TYPE_REQUEST || 160 + hdr->length < sizeof(struct qcom_rpm_message)) { 161 + dev_err(&qsdev->dev, "invalid request\n"); 162 + return 0; 163 + } 164 + 165 + while (buf < end) { 166 + msg = (struct qcom_rpm_message *)buf; 167 + switch (msg->msg_type) { 168 + case RPM_MSG_TYPE_MSG_ID: 169 + break; 170 + case RPM_MSG_TYPE_ERR: 171 + len = min_t(u32, ALIGN(msg->length, 4), sizeof(msgbuf)); 172 + memcpy_fromio(msgbuf, msg->message, len); 173 + msgbuf[len - 1] = 0; 174 + 175 + if (!strcmp(msgbuf, "resource does not exist")) 176 + status = -ENXIO; 177 + else 178 + status = -EINVAL; 179 + break; 180 + } 181 + 182 + buf = PTR_ALIGN(buf + 2 * sizeof(u32) + msg->length, 4); 183 + } 184 + 185 + rpm->ack_status = status; 186 + complete(&rpm->ack); 187 + return 0; 188 + } 189 + 190 + static int qcom_smd_rpm_probe(struct qcom_smd_device *sdev) 191 + { 192 + struct qcom_smd_rpm *rpm; 193 + 194 + rpm = devm_kzalloc(&sdev->dev, sizeof(*rpm), GFP_KERNEL); 195 + if (!rpm) 196 + return -ENOMEM; 197 + 198 + mutex_init(&rpm->lock); 199 + init_completion(&rpm->ack); 200 + 201 + rpm->rpm_channel = sdev->channel; 202 + 203 + dev_set_drvdata(&sdev->dev, rpm); 204 + 205 + return of_platform_populate(sdev->dev.of_node, NULL, NULL, &sdev->dev); 206 + } 207 + 208 + static void qcom_smd_rpm_remove(struct qcom_smd_device *sdev) 209 + { 210 + of_platform_depopulate(&sdev->dev); 211 + } 212 + 213 + static const struct of_device_id qcom_smd_rpm_of_match[] = { 214 + { .compatible = "qcom,rpm-msm8974" }, 215 + {} 216 + }; 217 + MODULE_DEVICE_TABLE(of, qcom_smd_rpm_of_match); 218 + 219 + static struct qcom_smd_driver qcom_smd_rpm_driver = { 220 + .probe = qcom_smd_rpm_probe, 221 + .remove = qcom_smd_rpm_remove, 222 + .callback = qcom_smd_rpm_callback, 223 + .driver = { 224 + .name = "qcom_smd_rpm", 225 + .owner = THIS_MODULE, 226 + .of_match_table = qcom_smd_rpm_of_match, 227 + }, 228 + }; 229 + 230 + static int __init qcom_smd_rpm_init(void) 231 + { 232 + return qcom_smd_driver_register(&qcom_smd_rpm_driver); 233 + } 234 + arch_initcall(qcom_smd_rpm_init); 235 + 236 + static void __exit qcom_smd_rpm_exit(void) 237 + { 238 + qcom_smd_driver_unregister(&qcom_smd_rpm_driver); 239 + } 240 + module_exit(qcom_smd_rpm_exit); 241 + 242 + MODULE_AUTHOR("Bjorn Andersson <bjorn.andersson@sonymobile.com>"); 243 + MODULE_DESCRIPTION("Qualcomm SMD backed RPM driver"); 244 + MODULE_LICENSE("GPL v2");
+35
include/linux/soc/qcom/smd-rpm.h
··· 1 + #ifndef __QCOM_SMD_RPM_H__ 2 + #define __QCOM_SMD_RPM_H__ 3 + 4 + struct qcom_smd_rpm; 5 + 6 + #define QCOM_SMD_RPM_ACTIVE_STATE 0 7 + #define QCOM_SMD_RPM_SLEEP_STATE 1 8 + 9 + /* 10 + * Constants used for addressing resources in the RPM. 11 + */ 12 + #define QCOM_SMD_RPM_BOOST 0x61747362 13 + #define QCOM_SMD_RPM_BUS_CLK 0x316b6c63 14 + #define QCOM_SMD_RPM_BUS_MASTER 0x73616d62 15 + #define QCOM_SMD_RPM_BUS_SLAVE 0x766c7362 16 + #define QCOM_SMD_RPM_CLK_BUF_A 0x616B6C63 17 + #define QCOM_SMD_RPM_LDOA 0x616f646c 18 + #define QCOM_SMD_RPM_LDOB 0x626F646C 19 + #define QCOM_SMD_RPM_MEM_CLK 0x326b6c63 20 + #define QCOM_SMD_RPM_MISC_CLK 0x306b6c63 21 + #define QCOM_SMD_RPM_NCPA 0x6170636E 22 + #define QCOM_SMD_RPM_NCPB 0x6270636E 23 + #define QCOM_SMD_RPM_OCMEM_PWR 0x706d636f 24 + #define QCOM_SMD_RPM_QPIC_CLK 0x63697071 25 + #define QCOM_SMD_RPM_SMPA 0x61706d73 26 + #define QCOM_SMD_RPM_SMPB 0x62706d73 27 + #define QCOM_SMD_RPM_SPDM 0x63707362 28 + #define QCOM_SMD_RPM_VSA 0x00617376 29 + 30 + int qcom_rpm_smd_write(struct qcom_smd_rpm *rpm, 31 + int state, 32 + u32 resource_type, u32 resource_id, 33 + void *buf, size_t count); 34 + 35 + #endif