aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBjorn Andersson <bjorn.andersson@sonymobile.com>2015-07-27 23:20:32 -0400
committerAndy Gross <agross@codeaurora.org>2015-07-29 15:13:48 -0400
commit936f14cf4e67168fcd37f10cebf5a475f490fb6e (patch)
treea0bec7660c796927def34c3f315ee6addfc71ba4
parentf2ab3298fb4932358d27fc4c7ea1a1891ad7e042 (diff)
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>
-rw-r--r--drivers/soc/qcom/Kconfig14
-rw-r--r--drivers/soc/qcom/Makefile1
-rw-r--r--drivers/soc/qcom/smd-rpm.c244
-rw-r--r--include/linux/soc/qcom/smd-rpm.h35
4 files changed, 294 insertions, 0 deletions
diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
index 188295e2c9ba..ba47b70f4d85 100644
--- a/drivers/soc/qcom/Kconfig
+++ b/drivers/soc/qcom/Kconfig
@@ -27,6 +27,20 @@ config QCOM_SMD
27 providing communication channels to remote processors in Qualcomm 27 providing communication channels to remote processors in Qualcomm
28 platforms. 28 platforms.
29 29
30config 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
30config QCOM_SMEM 44config QCOM_SMEM
31 tristate "Qualcomm Shared Memory Manager (SMEM)" 45 tristate "Qualcomm Shared Memory Manager (SMEM)"
32 depends on ARCH_QCOM 46 depends on ARCH_QCOM
diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
index f961a8796ed2..10a93d168e0e 100644
--- a/drivers/soc/qcom/Makefile
+++ b/drivers/soc/qcom/Makefile
@@ -1,4 +1,5 @@
1obj-$(CONFIG_QCOM_GSBI) += qcom_gsbi.o 1obj-$(CONFIG_QCOM_GSBI) += qcom_gsbi.o
2obj-$(CONFIG_QCOM_PM) += spm.o 2obj-$(CONFIG_QCOM_PM) += spm.o
3obj-$(CONFIG_QCOM_SMD) += smd.o 3obj-$(CONFIG_QCOM_SMD) += smd.o
4obj-$(CONFIG_QCOM_SMD_RPM) += smd-rpm.o
4obj-$(CONFIG_QCOM_SMEM) += smem.o 5obj-$(CONFIG_QCOM_SMEM) += smem.o
diff --git a/drivers/soc/qcom/smd-rpm.c b/drivers/soc/qcom/smd-rpm.c
new file mode 100644
index 000000000000..1392ccf14a20
--- /dev/null
+++ b/drivers/soc/qcom/smd-rpm.c
@@ -0,0 +1,244 @@
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 */
33struct 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 */
46struct 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 */
59struct 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 */
76struct 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 */
98int 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
140out:
141 mutex_unlock(&rpm->lock);
142 return ret;
143}
144EXPORT_SYMBOL(qcom_rpm_smd_write);
145
146static 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
190static 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
208static void qcom_smd_rpm_remove(struct qcom_smd_device *sdev)
209{
210 of_platform_depopulate(&sdev->dev);
211}
212
213static const struct of_device_id qcom_smd_rpm_of_match[] = {
214 { .compatible = "qcom,rpm-msm8974" },
215 {}
216};
217MODULE_DEVICE_TABLE(of, qcom_smd_rpm_of_match);
218
219static 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
230static int __init qcom_smd_rpm_init(void)
231{
232 return qcom_smd_driver_register(&qcom_smd_rpm_driver);
233}
234arch_initcall(qcom_smd_rpm_init);
235
236static void __exit qcom_smd_rpm_exit(void)
237{
238 qcom_smd_driver_unregister(&qcom_smd_rpm_driver);
239}
240module_exit(qcom_smd_rpm_exit);
241
242MODULE_AUTHOR("Bjorn Andersson <bjorn.andersson@sonymobile.com>");
243MODULE_DESCRIPTION("Qualcomm SMD backed RPM driver");
244MODULE_LICENSE("GPL v2");
diff --git a/include/linux/soc/qcom/smd-rpm.h b/include/linux/soc/qcom/smd-rpm.h
new file mode 100644
index 000000000000..2a53dcaeeeed
--- /dev/null
+++ b/include/linux/soc/qcom/smd-rpm.h
@@ -0,0 +1,35 @@
1#ifndef __QCOM_SMD_RPM_H__
2#define __QCOM_SMD_RPM_H__
3
4struct 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
30int 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