1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright (c) 2017 Linaro Ltd.
6 #include <linux/kernel.h>
7 #include <linux/cdev.h>
9 #include <linux/module.h>
10 #include <linux/platform_device.h>
12 #include <linux/of_reserved_mem.h>
13 #include <linux/dma-mapping.h>
14 #include <linux/slab.h>
15 #include <linux/uaccess.h>
17 #include <linux/firmware/qcom/qcom_scm.h>
19 #define QCOM_RMTFS_MEM_DEV_MAX (MINORMASK + 1)
20 #define NUM_MAX_VMIDS 2
22 static dev_t qcom_rmtfs_mem_major
;
24 struct qcom_rmtfs_mem
{
32 unsigned int client_id
;
37 static ssize_t
qcom_rmtfs_mem_show(struct device
*dev
,
38 struct device_attribute
*attr
,
41 static DEVICE_ATTR(phys_addr
, 0444, qcom_rmtfs_mem_show
, NULL
);
42 static DEVICE_ATTR(size
, 0444, qcom_rmtfs_mem_show
, NULL
);
43 static DEVICE_ATTR(client_id
, 0444, qcom_rmtfs_mem_show
, NULL
);
45 static ssize_t
qcom_rmtfs_mem_show(struct device
*dev
,
46 struct device_attribute
*attr
,
49 struct qcom_rmtfs_mem
*rmtfs_mem
= container_of(dev
,
50 struct qcom_rmtfs_mem
,
53 if (attr
== &dev_attr_phys_addr
)
54 return sprintf(buf
, "%pa\n", &rmtfs_mem
->addr
);
55 if (attr
== &dev_attr_size
)
56 return sprintf(buf
, "%pa\n", &rmtfs_mem
->size
);
57 if (attr
== &dev_attr_client_id
)
58 return sprintf(buf
, "%d\n", rmtfs_mem
->client_id
);
63 static struct attribute
*qcom_rmtfs_mem_attrs
[] = {
64 &dev_attr_phys_addr
.attr
,
66 &dev_attr_client_id
.attr
,
69 ATTRIBUTE_GROUPS(qcom_rmtfs_mem
);
71 static int qcom_rmtfs_mem_open(struct inode
*inode
, struct file
*filp
)
73 struct qcom_rmtfs_mem
*rmtfs_mem
= container_of(inode
->i_cdev
,
74 struct qcom_rmtfs_mem
,
77 get_device(&rmtfs_mem
->dev
);
78 filp
->private_data
= rmtfs_mem
;
82 static ssize_t
qcom_rmtfs_mem_read(struct file
*filp
,
83 char __user
*buf
, size_t count
, loff_t
*f_pos
)
85 struct qcom_rmtfs_mem
*rmtfs_mem
= filp
->private_data
;
87 if (*f_pos
>= rmtfs_mem
->size
)
90 if (*f_pos
+ count
>= rmtfs_mem
->size
)
91 count
= rmtfs_mem
->size
- *f_pos
;
93 if (copy_to_user(buf
, rmtfs_mem
->base
+ *f_pos
, count
))
100 static ssize_t
qcom_rmtfs_mem_write(struct file
*filp
,
101 const char __user
*buf
, size_t count
,
104 struct qcom_rmtfs_mem
*rmtfs_mem
= filp
->private_data
;
106 if (*f_pos
>= rmtfs_mem
->size
)
109 if (*f_pos
+ count
>= rmtfs_mem
->size
)
110 count
= rmtfs_mem
->size
- *f_pos
;
112 if (copy_from_user(rmtfs_mem
->base
+ *f_pos
, buf
, count
))
119 static int qcom_rmtfs_mem_release(struct inode
*inode
, struct file
*filp
)
121 struct qcom_rmtfs_mem
*rmtfs_mem
= filp
->private_data
;
123 put_device(&rmtfs_mem
->dev
);
128 static struct class rmtfs_class
= {
132 static int qcom_rmtfs_mem_mmap(struct file
*filep
, struct vm_area_struct
*vma
)
134 struct qcom_rmtfs_mem
*rmtfs_mem
= filep
->private_data
;
136 if (vma
->vm_end
- vma
->vm_start
> rmtfs_mem
->size
) {
137 dev_dbg(&rmtfs_mem
->dev
,
138 "vm_end[%lu] - vm_start[%lu] [%lu] > mem->size[%pa]\n",
139 vma
->vm_end
, vma
->vm_start
,
140 (vma
->vm_end
- vma
->vm_start
), &rmtfs_mem
->size
);
144 vma
->vm_page_prot
= pgprot_writecombine(vma
->vm_page_prot
);
145 return remap_pfn_range(vma
,
147 rmtfs_mem
->addr
>> PAGE_SHIFT
,
148 vma
->vm_end
- vma
->vm_start
,
152 static const struct file_operations qcom_rmtfs_mem_fops
= {
153 .owner
= THIS_MODULE
,
154 .open
= qcom_rmtfs_mem_open
,
155 .read
= qcom_rmtfs_mem_read
,
156 .write
= qcom_rmtfs_mem_write
,
157 .release
= qcom_rmtfs_mem_release
,
158 .llseek
= default_llseek
,
159 .mmap
= qcom_rmtfs_mem_mmap
,
162 static void qcom_rmtfs_mem_release_device(struct device
*dev
)
164 struct qcom_rmtfs_mem
*rmtfs_mem
= container_of(dev
,
165 struct qcom_rmtfs_mem
,
171 static int qcom_rmtfs_mem_probe(struct platform_device
*pdev
)
173 struct device_node
*node
= pdev
->dev
.of_node
;
174 struct qcom_scm_vmperm perms
[NUM_MAX_VMIDS
+ 1];
175 struct reserved_mem
*rmem
;
176 struct qcom_rmtfs_mem
*rmtfs_mem
;
178 u32 vmid
[NUM_MAX_VMIDS
];
182 rmem
= of_reserved_mem_lookup(node
);
184 dev_err(&pdev
->dev
, "failed to acquire memory region\n");
188 ret
= of_property_read_u32(node
, "qcom,client-id", &client_id
);
190 dev_err(&pdev
->dev
, "failed to parse \"qcom,client-id\"\n");
195 rmtfs_mem
= kzalloc(sizeof(*rmtfs_mem
), GFP_KERNEL
);
199 rmtfs_mem
->addr
= rmem
->base
;
200 rmtfs_mem
->client_id
= client_id
;
201 rmtfs_mem
->size
= rmem
->size
;
204 * If requested, discard the first and last 4k block in order to ensure
205 * that the rmtfs region isn't adjacent to other protected regions.
207 if (of_property_read_bool(node
, "qcom,use-guard-pages")) {
208 rmtfs_mem
->addr
+= SZ_4K
;
209 rmtfs_mem
->size
-= 2 * SZ_4K
;
212 device_initialize(&rmtfs_mem
->dev
);
213 rmtfs_mem
->dev
.parent
= &pdev
->dev
;
214 rmtfs_mem
->dev
.groups
= qcom_rmtfs_mem_groups
;
215 rmtfs_mem
->dev
.release
= qcom_rmtfs_mem_release_device
;
217 rmtfs_mem
->base
= devm_memremap(&rmtfs_mem
->dev
, rmtfs_mem
->addr
,
218 rmtfs_mem
->size
, MEMREMAP_WC
);
219 if (IS_ERR(rmtfs_mem
->base
)) {
220 dev_err(&pdev
->dev
, "failed to remap rmtfs_mem region\n");
221 ret
= PTR_ERR(rmtfs_mem
->base
);
225 cdev_init(&rmtfs_mem
->cdev
, &qcom_rmtfs_mem_fops
);
226 rmtfs_mem
->cdev
.owner
= THIS_MODULE
;
228 dev_set_name(&rmtfs_mem
->dev
, "qcom_rmtfs_mem%d", client_id
);
229 rmtfs_mem
->dev
.id
= client_id
;
230 rmtfs_mem
->dev
.class = &rmtfs_class
;
231 rmtfs_mem
->dev
.devt
= MKDEV(MAJOR(qcom_rmtfs_mem_major
), client_id
);
233 ret
= cdev_device_add(&rmtfs_mem
->cdev
, &rmtfs_mem
->dev
);
235 dev_err(&pdev
->dev
, "failed to add cdev: %d\n", ret
);
239 num_vmids
= of_property_count_u32_elems(node
, "qcom,vmid");
240 if (num_vmids
== -EINVAL
) {
241 /* qcom,vmid is optional */
243 } else if (num_vmids
< 0) {
244 dev_err(&pdev
->dev
, "failed to count qcom,vmid elements: %d\n", num_vmids
);
247 } else if (num_vmids
> NUM_MAX_VMIDS
) {
249 "too many VMIDs (%d) specified! Only mapping first %d entries\n",
250 num_vmids
, NUM_MAX_VMIDS
);
251 num_vmids
= NUM_MAX_VMIDS
;
254 ret
= of_property_read_u32_array(node
, "qcom,vmid", vmid
, num_vmids
);
255 if (ret
< 0 && ret
!= -EINVAL
) {
256 dev_err(&pdev
->dev
, "failed to parse qcom,vmid\n");
259 if (!qcom_scm_is_available()) {
264 perms
[0].vmid
= QCOM_SCM_VMID_HLOS
;
265 perms
[0].perm
= QCOM_SCM_PERM_RW
;
267 for (i
= 0; i
< num_vmids
; i
++) {
268 perms
[i
+ 1].vmid
= vmid
[i
];
269 perms
[i
+ 1].perm
= QCOM_SCM_PERM_RW
;
272 rmtfs_mem
->perms
= BIT(QCOM_SCM_VMID_HLOS
);
273 ret
= qcom_scm_assign_mem(rmtfs_mem
->addr
, rmtfs_mem
->size
,
274 &rmtfs_mem
->perms
, perms
, num_vmids
+ 1);
276 dev_err(&pdev
->dev
, "assign memory failed\n");
281 dev_set_drvdata(&pdev
->dev
, rmtfs_mem
);
286 cdev_device_del(&rmtfs_mem
->cdev
, &rmtfs_mem
->dev
);
288 put_device(&rmtfs_mem
->dev
);
293 static void qcom_rmtfs_mem_remove(struct platform_device
*pdev
)
295 struct qcom_rmtfs_mem
*rmtfs_mem
= dev_get_drvdata(&pdev
->dev
);
296 struct qcom_scm_vmperm perm
;
298 if (rmtfs_mem
->perms
) {
299 perm
.vmid
= QCOM_SCM_VMID_HLOS
;
300 perm
.perm
= QCOM_SCM_PERM_RW
;
302 qcom_scm_assign_mem(rmtfs_mem
->addr
, rmtfs_mem
->size
,
303 &rmtfs_mem
->perms
, &perm
, 1);
306 cdev_device_del(&rmtfs_mem
->cdev
, &rmtfs_mem
->dev
);
307 put_device(&rmtfs_mem
->dev
);
310 static const struct of_device_id qcom_rmtfs_mem_of_match
[] = {
311 { .compatible
= "qcom,rmtfs-mem" },
314 MODULE_DEVICE_TABLE(of
, qcom_rmtfs_mem_of_match
);
316 static struct platform_driver qcom_rmtfs_mem_driver
= {
317 .probe
= qcom_rmtfs_mem_probe
,
318 .remove
= qcom_rmtfs_mem_remove
,
320 .name
= "qcom_rmtfs_mem",
321 .of_match_table
= qcom_rmtfs_mem_of_match
,
325 static int __init
qcom_rmtfs_mem_init(void)
329 ret
= class_register(&rmtfs_class
);
333 ret
= alloc_chrdev_region(&qcom_rmtfs_mem_major
, 0,
334 QCOM_RMTFS_MEM_DEV_MAX
, "qcom_rmtfs_mem");
336 pr_err("qcom_rmtfs_mem: failed to allocate char dev region\n");
337 goto unregister_class
;
340 ret
= platform_driver_register(&qcom_rmtfs_mem_driver
);
342 pr_err("qcom_rmtfs_mem: failed to register rmtfs_mem driver\n");
343 goto unregister_chrdev
;
349 unregister_chrdev_region(qcom_rmtfs_mem_major
, QCOM_RMTFS_MEM_DEV_MAX
);
351 class_unregister(&rmtfs_class
);
354 module_init(qcom_rmtfs_mem_init
);
356 static void __exit
qcom_rmtfs_mem_exit(void)
358 platform_driver_unregister(&qcom_rmtfs_mem_driver
);
359 unregister_chrdev_region(qcom_rmtfs_mem_major
, QCOM_RMTFS_MEM_DEV_MAX
);
360 class_unregister(&rmtfs_class
);
362 module_exit(qcom_rmtfs_mem_exit
);
364 MODULE_AUTHOR("Linaro Ltd");
365 MODULE_DESCRIPTION("Qualcomm Remote Filesystem memory driver");
366 MODULE_LICENSE("GPL v2");