1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (c) 2015, Sony Mobile Communications AB. 4 * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. 5 */ 6 7 #include <linux/module.h> 8 #include <linux/platform_device.h> 9 #include <linux/of_platform.h> 10 #include <linux/io.h> 11 #include <linux/interrupt.h> 12 #include <linux/slab.h> 13 14 #include <linux/rpmsg.h> 15 #include <linux/soc/qcom/smd-rpm.h> 16 17 #define RPM_REQUEST_TIMEOUT (5 * HZ) 18 19 /** 20 * struct qcom_smd_rpm - state of the rpm device driver 21 * @rpm_channel: reference to the smd channel 22 * @ack: completion for acks 23 * @lock: mutual exclusion around the send/complete pair 24 * @ack_status: result of the rpm request 25 */ 26 struct qcom_smd_rpm { 27 struct rpmsg_endpoint *rpm_channel; 28 struct device *dev; 29 30 struct completion ack; 31 struct mutex lock; 32 int ack_status; 33 }; 34 35 /** 36 * struct qcom_rpm_header - header for all rpm requests and responses 37 * @service_type: identifier of the service 38 * @length: length of the payload 39 */ 40 struct qcom_rpm_header { 41 __le32 service_type; 42 __le32 length; 43 }; 44 45 /** 46 * struct qcom_rpm_request - request message to the rpm 47 * @msg_id: identifier of the outgoing message 48 * @flags: active/sleep state flags 49 * @type: resource type 50 * @id: resource id 51 * @data_len: length of the payload following this header 52 */ 53 struct qcom_rpm_request { 54 __le32 msg_id; 55 __le32 flags; 56 __le32 type; 57 __le32 id; 58 __le32 data_len; 59 }; 60 61 /** 62 * struct qcom_rpm_message - response message from the rpm 63 * @msg_type: indicator of the type of message 64 * @length: the size of this message, including the message header 65 * @msg_id: message id 66 * @message: textual message from the rpm 67 * 68 * Multiple of these messages can be stacked in an rpm message. 69 */ 70 struct qcom_rpm_message { 71 __le32 msg_type; 72 __le32 length; 73 union { 74 __le32 msg_id; 75 u8 message[0]; 76 }; 77 }; 78 79 #define RPM_SERVICE_TYPE_REQUEST 0x00716572 /* "req\0" */ 80 81 #define RPM_MSG_TYPE_ERR 0x00727265 /* "err\0" */ 82 #define RPM_MSG_TYPE_MSG_ID 0x2367736d /* "msg#" */ 83 84 /** 85 * qcom_rpm_smd_write - write @buf to @type:@id 86 * @rpm: rpm handle 87 * @type: resource type 88 * @id: resource identifier 89 * @buf: the data to be written 90 * @count: number of bytes in @buf 91 */ 92 int qcom_rpm_smd_write(struct qcom_smd_rpm *rpm, 93 int state, 94 u32 type, u32 id, 95 void *buf, 96 size_t count) 97 { 98 static unsigned msg_id = 1; 99 int left; 100 int ret; 101 struct { 102 struct qcom_rpm_header hdr; 103 struct qcom_rpm_request req; 104 u8 payload[]; 105 } *pkt; 106 size_t size = sizeof(*pkt) + count; 107 108 /* SMD packets to the RPM may not exceed 256 bytes */ 109 if (WARN_ON(size >= 256)) 110 return -EINVAL; 111 112 pkt = kmalloc(size, GFP_KERNEL); 113 if (!pkt) 114 return -ENOMEM; 115 116 mutex_lock(&rpm->lock); 117 118 pkt->hdr.service_type = cpu_to_le32(RPM_SERVICE_TYPE_REQUEST); 119 pkt->hdr.length = cpu_to_le32(sizeof(struct qcom_rpm_request) + count); 120 121 pkt->req.msg_id = cpu_to_le32(msg_id++); 122 pkt->req.flags = cpu_to_le32(state); 123 pkt->req.type = cpu_to_le32(type); 124 pkt->req.id = cpu_to_le32(id); 125 pkt->req.data_len = cpu_to_le32(count); 126 memcpy(pkt->payload, buf, count); 127 128 ret = rpmsg_send(rpm->rpm_channel, pkt, size); 129 if (ret) 130 goto out; 131 132 left = wait_for_completion_timeout(&rpm->ack, RPM_REQUEST_TIMEOUT); 133 if (!left) 134 ret = -ETIMEDOUT; 135 else 136 ret = rpm->ack_status; 137 138 out: 139 kfree(pkt); 140 mutex_unlock(&rpm->lock); 141 return ret; 142 } 143 EXPORT_SYMBOL(qcom_rpm_smd_write); 144 145 static int qcom_smd_rpm_callback(struct rpmsg_device *rpdev, 146 void *data, 147 int count, 148 void *priv, 149 u32 addr) 150 { 151 const struct qcom_rpm_header *hdr = data; 152 size_t hdr_length = le32_to_cpu(hdr->length); 153 const struct qcom_rpm_message *msg; 154 struct qcom_smd_rpm *rpm = dev_get_drvdata(&rpdev->dev); 155 const u8 *buf = data + sizeof(struct qcom_rpm_header); 156 const u8 *end = buf + hdr_length; 157 char msgbuf[32]; 158 int status = 0; 159 u32 len, msg_length; 160 161 if (le32_to_cpu(hdr->service_type) != RPM_SERVICE_TYPE_REQUEST || 162 hdr_length < sizeof(struct qcom_rpm_message)) { 163 dev_err(rpm->dev, "invalid request\n"); 164 return 0; 165 } 166 167 while (buf < end) { 168 msg = (struct qcom_rpm_message *)buf; 169 msg_length = le32_to_cpu(msg->length); 170 switch (le32_to_cpu(msg->msg_type)) { 171 case RPM_MSG_TYPE_MSG_ID: 172 break; 173 case RPM_MSG_TYPE_ERR: 174 len = min_t(u32, ALIGN(msg_length, 4), sizeof(msgbuf)); 175 memcpy_fromio(msgbuf, msg->message, len); 176 msgbuf[len - 1] = 0; 177 178 if (!strcmp(msgbuf, "resource does not exist")) 179 status = -ENXIO; 180 else 181 status = -EINVAL; 182 break; 183 } 184 185 buf = PTR_ALIGN(buf + 2 * sizeof(u32) + msg_length, 4); 186 } 187 188 rpm->ack_status = status; 189 complete(&rpm->ack); 190 return 0; 191 } 192 193 static int qcom_smd_rpm_probe(struct rpmsg_device *rpdev) 194 { 195 struct qcom_smd_rpm *rpm; 196 197 rpm = devm_kzalloc(&rpdev->dev, sizeof(*rpm), GFP_KERNEL); 198 if (!rpm) 199 return -ENOMEM; 200 201 mutex_init(&rpm->lock); 202 init_completion(&rpm->ack); 203 204 rpm->dev = &rpdev->dev; 205 rpm->rpm_channel = rpdev->ept; 206 dev_set_drvdata(&rpdev->dev, rpm); 207 208 return of_platform_populate(rpdev->dev.of_node, NULL, NULL, &rpdev->dev); 209 } 210 211 static void qcom_smd_rpm_remove(struct rpmsg_device *rpdev) 212 { 213 of_platform_depopulate(&rpdev->dev); 214 } 215 216 static const struct of_device_id qcom_smd_rpm_of_match[] = { 217 { .compatible = "qcom,rpm-apq8084" }, 218 { .compatible = "qcom,rpm-msm8916" }, 219 { .compatible = "qcom,rpm-msm8974" }, 220 { .compatible = "qcom,rpm-msm8996" }, 221 { .compatible = "qcom,rpm-msm8998" }, 222 { .compatible = "qcom,rpm-sdm660" }, 223 { .compatible = "qcom,rpm-qcs404" }, 224 {} 225 }; 226 MODULE_DEVICE_TABLE(of, qcom_smd_rpm_of_match); 227 228 static struct rpmsg_driver qcom_smd_rpm_driver = { 229 .probe = qcom_smd_rpm_probe, 230 .remove = qcom_smd_rpm_remove, 231 .callback = qcom_smd_rpm_callback, 232 .drv = { 233 .name = "qcom_smd_rpm", 234 .of_match_table = qcom_smd_rpm_of_match, 235 }, 236 }; 237 238 static int __init qcom_smd_rpm_init(void) 239 { 240 return register_rpmsg_driver(&qcom_smd_rpm_driver); 241 } 242 arch_initcall(qcom_smd_rpm_init); 243 244 static void __exit qcom_smd_rpm_exit(void) 245 { 246 unregister_rpmsg_driver(&qcom_smd_rpm_driver); 247 } 248 module_exit(qcom_smd_rpm_exit); 249 250 MODULE_AUTHOR("Bjorn Andersson <bjorn.andersson@sonymobile.com>"); 251 MODULE_DESCRIPTION("Qualcomm SMD backed RPM driver"); 252 MODULE_LICENSE("GPL v2"); 253