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/qcom_scm.h>
19 #define QCOM_RMTFS_MEM_DEV_MAX (MINORMASK + 1)
21 static dev_t qcom_rmtfs_mem_major
;
23 struct qcom_rmtfs_mem
{
31 unsigned int client_id
;
36 static ssize_t
qcom_rmtfs_mem_show(struct device
*dev
,
37 struct device_attribute
*attr
,
40 static DEVICE_ATTR(phys_addr
, 0444, qcom_rmtfs_mem_show
, NULL
);
41 static DEVICE_ATTR(size
, 0444, qcom_rmtfs_mem_show
, NULL
);
42 static DEVICE_ATTR(client_id
, 0444, qcom_rmtfs_mem_show
, NULL
);
44 static ssize_t
qcom_rmtfs_mem_show(struct device
*dev
,
45 struct device_attribute
*attr
,
48 struct qcom_rmtfs_mem
*rmtfs_mem
= container_of(dev
,
49 struct qcom_rmtfs_mem
,
52 if (attr
== &dev_attr_phys_addr
)
53 return sprintf(buf
, "%pa\n", &rmtfs_mem
->addr
);
54 if (attr
== &dev_attr_size
)
55 return sprintf(buf
, "%pa\n", &rmtfs_mem
->size
);
56 if (attr
== &dev_attr_client_id
)
57 return sprintf(buf
, "%d\n", rmtfs_mem
->client_id
);
62 static struct attribute
*qcom_rmtfs_mem_attrs
[] = {
63 &dev_attr_phys_addr
.attr
,
65 &dev_attr_client_id
.attr
,
68 ATTRIBUTE_GROUPS(qcom_rmtfs_mem
);
70 static int qcom_rmtfs_mem_open(struct inode
*inode
, struct file
*filp
)
72 struct qcom_rmtfs_mem
*rmtfs_mem
= container_of(inode
->i_cdev
,
73 struct qcom_rmtfs_mem
,
76 get_device(&rmtfs_mem
->dev
);
77 filp
->private_data
= rmtfs_mem
;
81 static ssize_t
qcom_rmtfs_mem_read(struct file
*filp
,
82 char __user
*buf
, size_t count
, loff_t
*f_pos
)
84 struct qcom_rmtfs_mem
*rmtfs_mem
= filp
->private_data
;
86 if (*f_pos
>= rmtfs_mem
->size
)
89 if (*f_pos
+ count
>= rmtfs_mem
->size
)
90 count
= rmtfs_mem
->size
- *f_pos
;
92 if (copy_to_user(buf
, rmtfs_mem
->base
+ *f_pos
, count
))
99 static ssize_t
qcom_rmtfs_mem_write(struct file
*filp
,
100 const char __user
*buf
, size_t count
,
103 struct qcom_rmtfs_mem
*rmtfs_mem
= filp
->private_data
;
105 if (*f_pos
>= rmtfs_mem
->size
)
108 if (*f_pos
+ count
>= rmtfs_mem
->size
)
109 count
= rmtfs_mem
->size
- *f_pos
;
111 if (copy_from_user(rmtfs_mem
->base
+ *f_pos
, buf
, count
))
118 static int qcom_rmtfs_mem_release(struct inode
*inode
, struct file
*filp
)
120 struct qcom_rmtfs_mem
*rmtfs_mem
= filp
->private_data
;
122 put_device(&rmtfs_mem
->dev
);
127 static struct class rmtfs_class
= {
128 .owner
= THIS_MODULE
,
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
[2];
175 struct reserved_mem
*rmem
;
176 struct qcom_rmtfs_mem
*rmtfs_mem
;
181 rmem
= of_reserved_mem_lookup(node
);
183 dev_err(&pdev
->dev
, "failed to acquire memory region\n");
187 ret
= of_property_read_u32(node
, "qcom,client-id", &client_id
);
189 dev_err(&pdev
->dev
, "failed to parse \"qcom,client-id\"\n");
194 rmtfs_mem
= kzalloc(sizeof(*rmtfs_mem
), GFP_KERNEL
);
198 rmtfs_mem
->addr
= rmem
->base
;
199 rmtfs_mem
->client_id
= client_id
;
200 rmtfs_mem
->size
= rmem
->size
;
202 device_initialize(&rmtfs_mem
->dev
);
203 rmtfs_mem
->dev
.parent
= &pdev
->dev
;
204 rmtfs_mem
->dev
.groups
= qcom_rmtfs_mem_groups
;
205 rmtfs_mem
->dev
.release
= qcom_rmtfs_mem_release_device
;
207 rmtfs_mem
->base
= devm_memremap(&rmtfs_mem
->dev
, rmtfs_mem
->addr
,
208 rmtfs_mem
->size
, MEMREMAP_WC
);
209 if (IS_ERR(rmtfs_mem
->base
)) {
210 dev_err(&pdev
->dev
, "failed to remap rmtfs_mem region\n");
211 ret
= PTR_ERR(rmtfs_mem
->base
);
215 cdev_init(&rmtfs_mem
->cdev
, &qcom_rmtfs_mem_fops
);
216 rmtfs_mem
->cdev
.owner
= THIS_MODULE
;
218 dev_set_name(&rmtfs_mem
->dev
, "qcom_rmtfs_mem%d", client_id
);
219 rmtfs_mem
->dev
.id
= client_id
;
220 rmtfs_mem
->dev
.class = &rmtfs_class
;
221 rmtfs_mem
->dev
.devt
= MKDEV(MAJOR(qcom_rmtfs_mem_major
), client_id
);
223 ret
= cdev_device_add(&rmtfs_mem
->cdev
, &rmtfs_mem
->dev
);
225 dev_err(&pdev
->dev
, "failed to add cdev: %d\n", ret
);
229 ret
= of_property_read_u32(node
, "qcom,vmid", &vmid
);
230 if (ret
< 0 && ret
!= -EINVAL
) {
231 dev_err(&pdev
->dev
, "failed to parse qcom,vmid\n");
234 if (!qcom_scm_is_available()) {
239 perms
[0].vmid
= QCOM_SCM_VMID_HLOS
;
240 perms
[0].perm
= QCOM_SCM_PERM_RW
;
241 perms
[1].vmid
= vmid
;
242 perms
[1].perm
= QCOM_SCM_PERM_RW
;
244 rmtfs_mem
->perms
= BIT(QCOM_SCM_VMID_HLOS
);
245 ret
= qcom_scm_assign_mem(rmtfs_mem
->addr
, rmtfs_mem
->size
,
246 &rmtfs_mem
->perms
, perms
, 2);
248 dev_err(&pdev
->dev
, "assign memory failed\n");
253 dev_set_drvdata(&pdev
->dev
, rmtfs_mem
);
258 cdev_device_del(&rmtfs_mem
->cdev
, &rmtfs_mem
->dev
);
260 put_device(&rmtfs_mem
->dev
);
265 static int qcom_rmtfs_mem_remove(struct platform_device
*pdev
)
267 struct qcom_rmtfs_mem
*rmtfs_mem
= dev_get_drvdata(&pdev
->dev
);
268 struct qcom_scm_vmperm perm
;
270 if (rmtfs_mem
->perms
) {
271 perm
.vmid
= QCOM_SCM_VMID_HLOS
;
272 perm
.perm
= QCOM_SCM_PERM_RW
;
274 qcom_scm_assign_mem(rmtfs_mem
->addr
, rmtfs_mem
->size
,
275 &rmtfs_mem
->perms
, &perm
, 1);
278 cdev_device_del(&rmtfs_mem
->cdev
, &rmtfs_mem
->dev
);
279 put_device(&rmtfs_mem
->dev
);
284 static const struct of_device_id qcom_rmtfs_mem_of_match
[] = {
285 { .compatible
= "qcom,rmtfs-mem" },
288 MODULE_DEVICE_TABLE(of
, qcom_rmtfs_mem_of_match
);
290 static struct platform_driver qcom_rmtfs_mem_driver
= {
291 .probe
= qcom_rmtfs_mem_probe
,
292 .remove
= qcom_rmtfs_mem_remove
,
294 .name
= "qcom_rmtfs_mem",
295 .of_match_table
= qcom_rmtfs_mem_of_match
,
299 static int __init
qcom_rmtfs_mem_init(void)
303 ret
= class_register(&rmtfs_class
);
307 ret
= alloc_chrdev_region(&qcom_rmtfs_mem_major
, 0,
308 QCOM_RMTFS_MEM_DEV_MAX
, "qcom_rmtfs_mem");
310 pr_err("qcom_rmtfs_mem: failed to allocate char dev region\n");
311 goto unregister_class
;
314 ret
= platform_driver_register(&qcom_rmtfs_mem_driver
);
316 pr_err("qcom_rmtfs_mem: failed to register rmtfs_mem driver\n");
317 goto unregister_chrdev
;
323 unregister_chrdev_region(qcom_rmtfs_mem_major
, QCOM_RMTFS_MEM_DEV_MAX
);
325 class_unregister(&rmtfs_class
);
328 module_init(qcom_rmtfs_mem_init
);
330 static void __exit
qcom_rmtfs_mem_exit(void)
332 platform_driver_unregister(&qcom_rmtfs_mem_driver
);
333 unregister_chrdev_region(qcom_rmtfs_mem_major
, QCOM_RMTFS_MEM_DEV_MAX
);
334 class_unregister(&rmtfs_class
);
336 module_exit(qcom_rmtfs_mem_exit
);
338 MODULE_AUTHOR("Linaro Ltd");
339 MODULE_DESCRIPTION("Qualcomm Remote Filesystem memory driver");
340 MODULE_LICENSE("GPL v2");