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