aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBjorn Andersson <bjorn.andersson@linaro.org>2017-10-16 14:17:08 -0400
committerAndy Gross <andy.gross@linaro.org>2017-10-22 06:06:34 -0400
commitd1de6d6c639b7827c42c4750b8101ed1049e1c72 (patch)
tree888df635b26875b4cdbc54454d12c070cf9f5788
parent2501ec14048d24d98651d2204cdf9cb33a127199 (diff)
soc: qcom: Remote filesystem memory driver
The Qualcomm remote file system protocol is used by certain remoteprocs, in particular the modem, to read and write persistent storage in platforms where only the application CPU has physical storage access. The protocol is based on a set of QMI-encoded control-messages and a shared memory buffer for exchaning the data. This driver implements the latter, providing the user space service access to the carved out chunk of memory. Acked-by: Rob Herring <robh@kernel.org> Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org> Signed-off-by: Andy Gross <andy.gross@linaro.org>
-rw-r--r--drivers/of/platform.c1
-rw-r--r--drivers/soc/qcom/Kconfig11
-rw-r--r--drivers/soc/qcom/Makefile1
-rw-r--r--drivers/soc/qcom/rmtfs_mem.c271
4 files changed, 284 insertions, 0 deletions
diff --git a/drivers/of/platform.c b/drivers/of/platform.c
index 922b46646bcc..b7cf84b29737 100644
--- a/drivers/of/platform.c
+++ b/drivers/of/platform.c
@@ -498,6 +498,7 @@ EXPORT_SYMBOL_GPL(of_platform_default_populate);
498 498
499#ifndef CONFIG_PPC 499#ifndef CONFIG_PPC
500static const struct of_device_id reserved_mem_matches[] = { 500static const struct of_device_id reserved_mem_matches[] = {
501 { .compatible = "qcom,rmtfs-mem" },
501 { .compatible = "ramoops" }, 502 { .compatible = "ramoops" },
502 {} 503 {}
503}; 504};
diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
index b00bccddcd3b..b81374bb6713 100644
--- a/drivers/soc/qcom/Kconfig
+++ b/drivers/soc/qcom/Kconfig
@@ -35,6 +35,17 @@ config QCOM_PM
35 modes. It interface with various system drivers to put the cores in 35 modes. It interface with various system drivers to put the cores in
36 low power modes. 36 low power modes.
37 37
38config QCOM_RMTFS_MEM
39 tristate "Qualcomm Remote Filesystem memory driver"
40 depends on ARCH_QCOM
41 help
42 The Qualcomm remote filesystem memory driver is used for allocating
43 and exposing regions of shared memory with remote processors for the
44 purpose of exchanging sector-data between the remote filesystem
45 service and its clients.
46
47 Say y here if you intend to boot the modem remoteproc.
48
38config QCOM_SMEM 49config QCOM_SMEM
39 tristate "Qualcomm Shared Memory Manager (SMEM)" 50 tristate "Qualcomm Shared Memory Manager (SMEM)"
40 depends on ARCH_QCOM 51 depends on ARCH_QCOM
diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
index f151de41eb93..250407faa7d9 100644
--- a/drivers/soc/qcom/Makefile
+++ b/drivers/soc/qcom/Makefile
@@ -2,6 +2,7 @@ obj-$(CONFIG_QCOM_GLINK_SSR) += glink_ssr.o
2obj-$(CONFIG_QCOM_GSBI) += qcom_gsbi.o 2obj-$(CONFIG_QCOM_GSBI) += qcom_gsbi.o
3obj-$(CONFIG_QCOM_MDT_LOADER) += mdt_loader.o 3obj-$(CONFIG_QCOM_MDT_LOADER) += mdt_loader.o
4obj-$(CONFIG_QCOM_PM) += spm.o 4obj-$(CONFIG_QCOM_PM) += spm.o
5obj-$(CONFIG_QCOM_RMTFS_MEM) += rmtfs_mem.o
5obj-$(CONFIG_QCOM_SMD_RPM) += smd-rpm.o 6obj-$(CONFIG_QCOM_SMD_RPM) += smd-rpm.o
6obj-$(CONFIG_QCOM_SMEM) += smem.o 7obj-$(CONFIG_QCOM_SMEM) += smem.o
7obj-$(CONFIG_QCOM_SMEM_STATE) += smem_state.o 8obj-$(CONFIG_QCOM_SMEM_STATE) += smem_state.o
diff --git a/drivers/soc/qcom/rmtfs_mem.c b/drivers/soc/qcom/rmtfs_mem.c
new file mode 100644
index 000000000000..f6f2f0cb3b3a
--- /dev/null
+++ b/drivers/soc/qcom/rmtfs_mem.c
@@ -0,0 +1,271 @@
1/*
2 * Copyright (c) 2017 Linaro Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 and
6 * only version 2 as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 */
13
14#include <linux/kernel.h>
15#include <linux/cdev.h>
16#include <linux/err.h>
17#include <linux/module.h>
18#include <linux/platform_device.h>
19#include <linux/of.h>
20#include <linux/of_reserved_mem.h>
21#include <linux/dma-mapping.h>
22#include <linux/slab.h>
23#include <linux/uaccess.h>
24#include <linux/io.h>
25#include <linux/qcom_scm.h>
26
27#define QCOM_RMTFS_MEM_DEV_MAX (MINORMASK + 1)
28
29static dev_t qcom_rmtfs_mem_major;
30
31struct qcom_rmtfs_mem {
32 struct device dev;
33 struct cdev cdev;
34
35 void *base;
36 phys_addr_t addr;
37 phys_addr_t size;
38
39 unsigned int client_id;
40};
41
42static ssize_t qcom_rmtfs_mem_show(struct device *dev,
43 struct device_attribute *attr,
44 char *buf);
45
46static DEVICE_ATTR(phys_addr, 0400, qcom_rmtfs_mem_show, NULL);
47static DEVICE_ATTR(size, 0400, qcom_rmtfs_mem_show, NULL);
48static DEVICE_ATTR(client_id, 0400, qcom_rmtfs_mem_show, NULL);
49
50static ssize_t qcom_rmtfs_mem_show(struct device *dev,
51 struct device_attribute *attr,
52 char *buf)
53{
54 struct qcom_rmtfs_mem *rmtfs_mem = container_of(dev,
55 struct qcom_rmtfs_mem,
56 dev);
57
58 if (attr == &dev_attr_phys_addr)
59 return sprintf(buf, "%pa\n", &rmtfs_mem->addr);
60 if (attr == &dev_attr_size)
61 return sprintf(buf, "%pa\n", &rmtfs_mem->size);
62 if (attr == &dev_attr_client_id)
63 return sprintf(buf, "%d\n", rmtfs_mem->client_id);
64
65 return -EINVAL;
66}
67
68static struct attribute *qcom_rmtfs_mem_attrs[] = {
69 &dev_attr_phys_addr.attr,
70 &dev_attr_size.attr,
71 &dev_attr_client_id.attr,
72 NULL
73};
74ATTRIBUTE_GROUPS(qcom_rmtfs_mem);
75
76static int qcom_rmtfs_mem_open(struct inode *inode, struct file *filp)
77{
78 struct qcom_rmtfs_mem *rmtfs_mem = container_of(inode->i_cdev,
79 struct qcom_rmtfs_mem,
80 cdev);
81
82 get_device(&rmtfs_mem->dev);
83 filp->private_data = rmtfs_mem;
84
85 return 0;
86}
87static ssize_t qcom_rmtfs_mem_read(struct file *filp,
88 char __user *buf, size_t count, loff_t *f_pos)
89{
90 struct qcom_rmtfs_mem *rmtfs_mem = filp->private_data;
91
92 if (*f_pos >= rmtfs_mem->size)
93 return 0;
94
95 if (*f_pos + count >= rmtfs_mem->size)
96 count = rmtfs_mem->size - *f_pos;
97
98 if (copy_to_user(buf, rmtfs_mem->base + *f_pos, count))
99 return -EFAULT;
100
101 *f_pos += count;
102 return count;
103}
104
105static ssize_t qcom_rmtfs_mem_write(struct file *filp,
106 const char __user *buf, size_t count,
107 loff_t *f_pos)
108{
109 struct qcom_rmtfs_mem *rmtfs_mem = filp->private_data;
110
111 if (*f_pos >= rmtfs_mem->size)
112 return 0;
113
114 if (*f_pos + count >= rmtfs_mem->size)
115 count = rmtfs_mem->size - *f_pos;
116
117 if (copy_from_user(rmtfs_mem->base + *f_pos, buf, count))
118 return -EFAULT;
119
120 *f_pos += count;
121 return count;
122}
123
124static int qcom_rmtfs_mem_release(struct inode *inode, struct file *filp)
125{
126 struct qcom_rmtfs_mem *rmtfs_mem = filp->private_data;
127
128 put_device(&rmtfs_mem->dev);
129
130 return 0;
131}
132
133static const struct file_operations qcom_rmtfs_mem_fops = {
134 .owner = THIS_MODULE,
135 .open = qcom_rmtfs_mem_open,
136 .read = qcom_rmtfs_mem_read,
137 .write = qcom_rmtfs_mem_write,
138 .release = qcom_rmtfs_mem_release,
139 .llseek = default_llseek,
140};
141
142static void qcom_rmtfs_mem_release_device(struct device *dev)
143{
144 struct qcom_rmtfs_mem *rmtfs_mem = container_of(dev,
145 struct qcom_rmtfs_mem,
146 dev);
147
148 kfree(rmtfs_mem);
149}
150
151static int qcom_rmtfs_mem_probe(struct platform_device *pdev)
152{
153 struct device_node *node = pdev->dev.of_node;
154 struct reserved_mem *rmem;
155 struct qcom_rmtfs_mem *rmtfs_mem;
156 u32 client_id;
157 int ret;
158
159 rmem = of_reserved_mem_lookup(node);
160 if (!rmem) {
161 dev_err(&pdev->dev, "failed to acquire memory region\n");
162 return -EINVAL;
163 }
164
165 ret = of_property_read_u32(node, "qcom,client-id", &client_id);
166 if (ret) {
167 dev_err(&pdev->dev, "failed to parse \"qcom,client-id\"\n");
168 return ret;
169
170 }
171
172 rmtfs_mem = kzalloc(sizeof(*rmtfs_mem), GFP_KERNEL);
173 if (!rmtfs_mem)
174 return -ENOMEM;
175
176 rmtfs_mem->addr = rmem->base;
177 rmtfs_mem->client_id = client_id;
178 rmtfs_mem->size = rmem->size;
179
180 device_initialize(&rmtfs_mem->dev);
181 rmtfs_mem->dev.parent = &pdev->dev;
182 rmtfs_mem->dev.groups = qcom_rmtfs_mem_groups;
183
184 rmtfs_mem->base = devm_memremap(&rmtfs_mem->dev, rmtfs_mem->addr,
185 rmtfs_mem->size, MEMREMAP_WC);
186 if (IS_ERR(rmtfs_mem->base)) {
187 dev_err(&pdev->dev, "failed to remap rmtfs_mem region\n");
188 ret = PTR_ERR(rmtfs_mem->base);
189 goto put_device;
190 }
191
192 cdev_init(&rmtfs_mem->cdev, &qcom_rmtfs_mem_fops);
193 rmtfs_mem->cdev.owner = THIS_MODULE;
194
195 dev_set_name(&rmtfs_mem->dev, "qcom_rmtfs_mem%d", client_id);
196 rmtfs_mem->dev.id = client_id;
197 rmtfs_mem->dev.devt = MKDEV(MAJOR(qcom_rmtfs_mem_major), client_id);
198
199 ret = cdev_device_add(&rmtfs_mem->cdev, &rmtfs_mem->dev);
200 if (ret) {
201 dev_err(&pdev->dev, "failed to add cdev: %d\n", ret);
202 goto put_device;
203 }
204
205 rmtfs_mem->dev.release = qcom_rmtfs_mem_release_device;
206
207 dev_set_drvdata(&pdev->dev, rmtfs_mem);
208
209 return 0;
210
211remove_cdev:
212 cdev_device_del(&rmtfs_mem->cdev, &rmtfs_mem->dev);
213put_device:
214 put_device(&rmtfs_mem->dev);
215
216 return ret;
217}
218
219static int qcom_rmtfs_mem_remove(struct platform_device *pdev)
220{
221 struct qcom_rmtfs_mem *rmtfs_mem = dev_get_drvdata(&pdev->dev);
222
223 cdev_device_del(&rmtfs_mem->cdev, &rmtfs_mem->dev);
224 put_device(&rmtfs_mem->dev);
225
226 return 0;
227}
228
229static const struct of_device_id qcom_rmtfs_mem_of_match[] = {
230 { .compatible = "qcom,rmtfs-mem" },
231 {}
232};
233MODULE_DEVICE_TABLE(of, qcom_rmtfs_mem_of_match);
234
235static struct platform_driver qcom_rmtfs_mem_driver = {
236 .probe = qcom_rmtfs_mem_probe,
237 .remove = qcom_rmtfs_mem_remove,
238 .driver = {
239 .name = "qcom_rmtfs_mem",
240 .of_match_table = qcom_rmtfs_mem_of_match,
241 },
242};
243
244static int qcom_rmtfs_mem_init(void)
245{
246 int ret;
247
248 ret = alloc_chrdev_region(&qcom_rmtfs_mem_major, 0,
249 QCOM_RMTFS_MEM_DEV_MAX, "qcom_rmtfs_mem");
250 if (ret < 0) {
251 pr_err("qcom_rmtfs_mem: failed to allocate char dev region\n");
252 return ret;
253 }
254
255 ret = platform_driver_register(&qcom_rmtfs_mem_driver);
256 if (ret < 0) {
257 pr_err("qcom_rmtfs_mem: failed to register rmtfs_mem driver\n");
258 unregister_chrdev_region(qcom_rmtfs_mem_major,
259 QCOM_RMTFS_MEM_DEV_MAX);
260 }
261
262 return ret;
263}
264module_init(qcom_rmtfs_mem_init);
265
266static void qcom_rmtfs_mem_exit(void)
267{
268 platform_driver_unregister(&qcom_rmtfs_mem_driver);
269 unregister_chrdev_region(qcom_rmtfs_mem_major, QCOM_RMTFS_MEM_DEV_MAX);
270}
271module_exit(qcom_rmtfs_mem_exit);