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
;
42 static ssize_t
qcom_rmtfs_mem_show(struct device
*dev
,
43 struct device_attribute
*attr
,
46 static DEVICE_ATTR(phys_addr
, 0400, qcom_rmtfs_mem_show
, NULL
);
47 static DEVICE_ATTR(size
, 0400, qcom_rmtfs_mem_show
, NULL
);
48 static DEVICE_ATTR(client_id
, 0400, qcom_rmtfs_mem_show
, NULL
);
50 static ssize_t
qcom_rmtfs_mem_show(struct device
*dev
,
51 struct device_attribute
*attr
,
54 struct qcom_rmtfs_mem
*rmtfs_mem
= container_of(dev
,
55 struct qcom_rmtfs_mem
,
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
);
68 static struct attribute
*qcom_rmtfs_mem_attrs
[] = {
69 &dev_attr_phys_addr
.attr
,
71 &dev_attr_client_id
.attr
,
74 ATTRIBUTE_GROUPS(qcom_rmtfs_mem
);
76 static int qcom_rmtfs_mem_open(struct inode
*inode
, struct file
*filp
)
78 struct qcom_rmtfs_mem
*rmtfs_mem
= container_of(inode
->i_cdev
,
79 struct qcom_rmtfs_mem
,
82 get_device(&rmtfs_mem
->dev
);
83 filp
->private_data
= rmtfs_mem
;
87 static ssize_t
qcom_rmtfs_mem_read(struct file
*filp
,
88 char __user
*buf
, size_t count
, loff_t
*f_pos
)
90 struct qcom_rmtfs_mem
*rmtfs_mem
= filp
->private_data
;
92 if (*f_pos
>= rmtfs_mem
->size
)
95 if (*f_pos
+ count
>= rmtfs_mem
->size
)
96 count
= rmtfs_mem
->size
- *f_pos
;
98 if (copy_to_user(buf
, rmtfs_mem
->base
+ *f_pos
, count
))
105 static ssize_t
qcom_rmtfs_mem_write(struct file
*filp
,
106 const char __user
*buf
, size_t count
,
109 struct qcom_rmtfs_mem
*rmtfs_mem
= filp
->private_data
;
111 if (*f_pos
>= rmtfs_mem
->size
)
114 if (*f_pos
+ count
>= rmtfs_mem
->size
)
115 count
= rmtfs_mem
->size
- *f_pos
;
117 if (copy_from_user(rmtfs_mem
->base
+ *f_pos
, buf
, count
))
124 static int qcom_rmtfs_mem_release(struct inode
*inode
, struct file
*filp
)
126 struct qcom_rmtfs_mem
*rmtfs_mem
= filp
->private_data
;
128 put_device(&rmtfs_mem
->dev
);
133 static 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
,
142 static void qcom_rmtfs_mem_release_device(struct device
*dev
)
144 struct qcom_rmtfs_mem
*rmtfs_mem
= container_of(dev
,
145 struct qcom_rmtfs_mem
,
151 static int qcom_rmtfs_mem_probe(struct platform_device
*pdev
)
153 struct device_node
*node
= pdev
->dev
.of_node
;
154 struct reserved_mem
*rmem
;
155 struct qcom_rmtfs_mem
*rmtfs_mem
;
159 rmem
= of_reserved_mem_lookup(node
);
161 dev_err(&pdev
->dev
, "failed to acquire memory region\n");
165 ret
= of_property_read_u32(node
, "qcom,client-id", &client_id
);
167 dev_err(&pdev
->dev
, "failed to parse \"qcom,client-id\"\n");
172 rmtfs_mem
= kzalloc(sizeof(*rmtfs_mem
), GFP_KERNEL
);
176 rmtfs_mem
->addr
= rmem
->base
;
177 rmtfs_mem
->client_id
= client_id
;
178 rmtfs_mem
->size
= rmem
->size
;
180 device_initialize(&rmtfs_mem
->dev
);
181 rmtfs_mem
->dev
.parent
= &pdev
->dev
;
182 rmtfs_mem
->dev
.groups
= qcom_rmtfs_mem_groups
;
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
);
192 cdev_init(&rmtfs_mem
->cdev
, &qcom_rmtfs_mem_fops
);
193 rmtfs_mem
->cdev
.owner
= THIS_MODULE
;
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
);
199 ret
= cdev_device_add(&rmtfs_mem
->cdev
, &rmtfs_mem
->dev
);
201 dev_err(&pdev
->dev
, "failed to add cdev: %d\n", ret
);
205 rmtfs_mem
->dev
.release
= qcom_rmtfs_mem_release_device
;
207 dev_set_drvdata(&pdev
->dev
, rmtfs_mem
);
212 put_device(&rmtfs_mem
->dev
);
217 static int qcom_rmtfs_mem_remove(struct platform_device
*pdev
)
219 struct qcom_rmtfs_mem
*rmtfs_mem
= dev_get_drvdata(&pdev
->dev
);
221 cdev_device_del(&rmtfs_mem
->cdev
, &rmtfs_mem
->dev
);
222 put_device(&rmtfs_mem
->dev
);
227 static const struct of_device_id qcom_rmtfs_mem_of_match
[] = {
228 { .compatible
= "qcom,rmtfs-mem" },
231 MODULE_DEVICE_TABLE(of
, qcom_rmtfs_mem_of_match
);
233 static struct platform_driver qcom_rmtfs_mem_driver
= {
234 .probe
= qcom_rmtfs_mem_probe
,
235 .remove
= qcom_rmtfs_mem_remove
,
237 .name
= "qcom_rmtfs_mem",
238 .of_match_table
= qcom_rmtfs_mem_of_match
,
242 static int qcom_rmtfs_mem_init(void)
246 ret
= alloc_chrdev_region(&qcom_rmtfs_mem_major
, 0,
247 QCOM_RMTFS_MEM_DEV_MAX
, "qcom_rmtfs_mem");
249 pr_err("qcom_rmtfs_mem: failed to allocate char dev region\n");
253 ret
= platform_driver_register(&qcom_rmtfs_mem_driver
);
255 pr_err("qcom_rmtfs_mem: failed to register rmtfs_mem driver\n");
256 unregister_chrdev_region(qcom_rmtfs_mem_major
,
257 QCOM_RMTFS_MEM_DEV_MAX
);
262 module_init(qcom_rmtfs_mem_init
);
264 static void qcom_rmtfs_mem_exit(void)
266 platform_driver_unregister(&qcom_rmtfs_mem_driver
);
267 unregister_chrdev_region(qcom_rmtfs_mem_major
, QCOM_RMTFS_MEM_DEV_MAX
);
269 module_exit(qcom_rmtfs_mem_exit
);