2 * Copyright (c) 2017 Linaro Ltd.
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.
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.
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>
20 #include <linux/of_reserved_mem.h>
21 #include <linux/dma-mapping.h>
22 #include <linux/slab.h>
23 #include <linux/uaccess.h>
25 #include <linux/qcom_scm.h>
27 #define QCOM_RMTFS_MEM_DEV_MAX (MINORMASK + 1)
29 static dev_t qcom_rmtfs_mem_major
;
31 struct qcom_rmtfs_mem
{
39 unsigned int client_id
;
44 static ssize_t
qcom_rmtfs_mem_show(struct device
*dev
,
45 struct device_attribute
*attr
,
48 static DEVICE_ATTR(phys_addr
, 0400, qcom_rmtfs_mem_show
, NULL
);
49 static DEVICE_ATTR(size
, 0400, qcom_rmtfs_mem_show
, NULL
);
50 static DEVICE_ATTR(client_id
, 0400, qcom_rmtfs_mem_show
, NULL
);
52 static ssize_t
qcom_rmtfs_mem_show(struct device
*dev
,
53 struct device_attribute
*attr
,
56 struct qcom_rmtfs_mem
*rmtfs_mem
= container_of(dev
,
57 struct qcom_rmtfs_mem
,
60 if (attr
== &dev_attr_phys_addr
)
61 return sprintf(buf
, "%pa\n", &rmtfs_mem
->addr
);
62 if (attr
== &dev_attr_size
)
63 return sprintf(buf
, "%pa\n", &rmtfs_mem
->size
);
64 if (attr
== &dev_attr_client_id
)
65 return sprintf(buf
, "%d\n", rmtfs_mem
->client_id
);
70 static struct attribute
*qcom_rmtfs_mem_attrs
[] = {
71 &dev_attr_phys_addr
.attr
,
73 &dev_attr_client_id
.attr
,
76 ATTRIBUTE_GROUPS(qcom_rmtfs_mem
);
78 static int qcom_rmtfs_mem_open(struct inode
*inode
, struct file
*filp
)
80 struct qcom_rmtfs_mem
*rmtfs_mem
= container_of(inode
->i_cdev
,
81 struct qcom_rmtfs_mem
,
84 get_device(&rmtfs_mem
->dev
);
85 filp
->private_data
= rmtfs_mem
;
89 static ssize_t
qcom_rmtfs_mem_read(struct file
*filp
,
90 char __user
*buf
, size_t count
, loff_t
*f_pos
)
92 struct qcom_rmtfs_mem
*rmtfs_mem
= filp
->private_data
;
94 if (*f_pos
>= rmtfs_mem
->size
)
97 if (*f_pos
+ count
>= rmtfs_mem
->size
)
98 count
= rmtfs_mem
->size
- *f_pos
;
100 if (copy_to_user(buf
, rmtfs_mem
->base
+ *f_pos
, count
))
107 static ssize_t
qcom_rmtfs_mem_write(struct file
*filp
,
108 const char __user
*buf
, size_t count
,
111 struct qcom_rmtfs_mem
*rmtfs_mem
= filp
->private_data
;
113 if (*f_pos
>= rmtfs_mem
->size
)
116 if (*f_pos
+ count
>= rmtfs_mem
->size
)
117 count
= rmtfs_mem
->size
- *f_pos
;
119 if (copy_from_user(rmtfs_mem
->base
+ *f_pos
, buf
, count
))
126 static int qcom_rmtfs_mem_release(struct inode
*inode
, struct file
*filp
)
128 struct qcom_rmtfs_mem
*rmtfs_mem
= filp
->private_data
;
130 put_device(&rmtfs_mem
->dev
);
135 static const struct file_operations qcom_rmtfs_mem_fops
= {
136 .owner
= THIS_MODULE
,
137 .open
= qcom_rmtfs_mem_open
,
138 .read
= qcom_rmtfs_mem_read
,
139 .write
= qcom_rmtfs_mem_write
,
140 .release
= qcom_rmtfs_mem_release
,
141 .llseek
= default_llseek
,
144 static void qcom_rmtfs_mem_release_device(struct device
*dev
)
146 struct qcom_rmtfs_mem
*rmtfs_mem
= container_of(dev
,
147 struct qcom_rmtfs_mem
,
153 static int qcom_rmtfs_mem_probe(struct platform_device
*pdev
)
155 struct device_node
*node
= pdev
->dev
.of_node
;
156 struct qcom_scm_vmperm perms
[2];
157 struct reserved_mem
*rmem
;
158 struct qcom_rmtfs_mem
*rmtfs_mem
;
163 rmem
= of_reserved_mem_lookup(node
);
165 dev_err(&pdev
->dev
, "failed to acquire memory region\n");
169 ret
= of_property_read_u32(node
, "qcom,client-id", &client_id
);
171 dev_err(&pdev
->dev
, "failed to parse \"qcom,client-id\"\n");
176 rmtfs_mem
= kzalloc(sizeof(*rmtfs_mem
), GFP_KERNEL
);
180 rmtfs_mem
->addr
= rmem
->base
;
181 rmtfs_mem
->client_id
= client_id
;
182 rmtfs_mem
->size
= rmem
->size
;
184 device_initialize(&rmtfs_mem
->dev
);
185 rmtfs_mem
->dev
.parent
= &pdev
->dev
;
186 rmtfs_mem
->dev
.groups
= qcom_rmtfs_mem_groups
;
187 rmtfs_mem
->dev
.release
= qcom_rmtfs_mem_release_device
;
189 rmtfs_mem
->base
= devm_memremap(&rmtfs_mem
->dev
, rmtfs_mem
->addr
,
190 rmtfs_mem
->size
, MEMREMAP_WC
);
191 if (IS_ERR(rmtfs_mem
->base
)) {
192 dev_err(&pdev
->dev
, "failed to remap rmtfs_mem region\n");
193 ret
= PTR_ERR(rmtfs_mem
->base
);
197 cdev_init(&rmtfs_mem
->cdev
, &qcom_rmtfs_mem_fops
);
198 rmtfs_mem
->cdev
.owner
= THIS_MODULE
;
200 dev_set_name(&rmtfs_mem
->dev
, "qcom_rmtfs_mem%d", client_id
);
201 rmtfs_mem
->dev
.id
= client_id
;
202 rmtfs_mem
->dev
.devt
= MKDEV(MAJOR(qcom_rmtfs_mem_major
), client_id
);
204 ret
= cdev_device_add(&rmtfs_mem
->cdev
, &rmtfs_mem
->dev
);
206 dev_err(&pdev
->dev
, "failed to add cdev: %d\n", ret
);
210 ret
= of_property_read_u32(node
, "qcom,vmid", &vmid
);
211 if (ret
< 0 && ret
!= -EINVAL
) {
212 dev_err(&pdev
->dev
, "failed to parse qcom,vmid\n");
215 if (!qcom_scm_is_available()) {
220 perms
[0].vmid
= QCOM_SCM_VMID_HLOS
;
221 perms
[0].perm
= QCOM_SCM_PERM_RW
;
222 perms
[1].vmid
= vmid
;
223 perms
[1].perm
= QCOM_SCM_PERM_RW
;
225 rmtfs_mem
->perms
= BIT(QCOM_SCM_VMID_HLOS
);
226 ret
= qcom_scm_assign_mem(rmtfs_mem
->addr
, rmtfs_mem
->size
,
227 &rmtfs_mem
->perms
, perms
, 2);
229 dev_err(&pdev
->dev
, "assign memory failed\n");
234 dev_set_drvdata(&pdev
->dev
, rmtfs_mem
);
239 cdev_device_del(&rmtfs_mem
->cdev
, &rmtfs_mem
->dev
);
241 put_device(&rmtfs_mem
->dev
);
246 static int qcom_rmtfs_mem_remove(struct platform_device
*pdev
)
248 struct qcom_rmtfs_mem
*rmtfs_mem
= dev_get_drvdata(&pdev
->dev
);
249 struct qcom_scm_vmperm perm
;
251 if (rmtfs_mem
->perms
) {
252 perm
.vmid
= QCOM_SCM_VMID_HLOS
;
253 perm
.perm
= QCOM_SCM_PERM_RW
;
255 qcom_scm_assign_mem(rmtfs_mem
->addr
, rmtfs_mem
->size
,
256 &rmtfs_mem
->perms
, &perm
, 1);
259 cdev_device_del(&rmtfs_mem
->cdev
, &rmtfs_mem
->dev
);
260 put_device(&rmtfs_mem
->dev
);
265 static const struct of_device_id qcom_rmtfs_mem_of_match
[] = {
266 { .compatible
= "qcom,rmtfs-mem" },
269 MODULE_DEVICE_TABLE(of
, qcom_rmtfs_mem_of_match
);
271 static struct platform_driver qcom_rmtfs_mem_driver
= {
272 .probe
= qcom_rmtfs_mem_probe
,
273 .remove
= qcom_rmtfs_mem_remove
,
275 .name
= "qcom_rmtfs_mem",
276 .of_match_table
= qcom_rmtfs_mem_of_match
,
280 static int qcom_rmtfs_mem_init(void)
284 ret
= alloc_chrdev_region(&qcom_rmtfs_mem_major
, 0,
285 QCOM_RMTFS_MEM_DEV_MAX
, "qcom_rmtfs_mem");
287 pr_err("qcom_rmtfs_mem: failed to allocate char dev region\n");
291 ret
= platform_driver_register(&qcom_rmtfs_mem_driver
);
293 pr_err("qcom_rmtfs_mem: failed to register rmtfs_mem driver\n");
294 unregister_chrdev_region(qcom_rmtfs_mem_major
,
295 QCOM_RMTFS_MEM_DEV_MAX
);
300 module_init(qcom_rmtfs_mem_init
);
302 static void qcom_rmtfs_mem_exit(void)
304 platform_driver_unregister(&qcom_rmtfs_mem_driver
);
305 unregister_chrdev_region(qcom_rmtfs_mem_major
, QCOM_RMTFS_MEM_DEV_MAX
);
307 module_exit(qcom_rmtfs_mem_exit
);
309 MODULE_AUTHOR("Linaro Ltd");
310 MODULE_DESCRIPTION("Qualcomm Remote Filesystem memory driver");
311 MODULE_LICENSE("GPL v2");