rmtfs_mem.c 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Copyright (c) 2017 Linaro Ltd.
  4. */
  5. #include <linux/kernel.h>
  6. #include <linux/cdev.h>
  7. #include <linux/err.h>
  8. #include <linux/module.h>
  9. #include <linux/platform_device.h>
  10. #include <linux/of.h>
  11. #include <linux/of_reserved_mem.h>
  12. #include <linux/dma-mapping.h>
  13. #include <linux/slab.h>
  14. #include <linux/uaccess.h>
  15. #include <linux/io.h>
  16. #include <linux/qcom_scm.h>
  17. #define QCOM_RMTFS_MEM_DEV_MAX (MINORMASK + 1)
  18. static dev_t qcom_rmtfs_mem_major;
  19. struct qcom_rmtfs_mem {
  20. struct device dev;
  21. struct cdev cdev;
  22. void *base;
  23. phys_addr_t addr;
  24. phys_addr_t size;
  25. unsigned int client_id;
  26. unsigned int perms;
  27. };
  28. static ssize_t qcom_rmtfs_mem_show(struct device *dev,
  29. struct device_attribute *attr,
  30. char *buf);
  31. static DEVICE_ATTR(phys_addr, 0444, qcom_rmtfs_mem_show, NULL);
  32. static DEVICE_ATTR(size, 0444, qcom_rmtfs_mem_show, NULL);
  33. static DEVICE_ATTR(client_id, 0444, qcom_rmtfs_mem_show, NULL);
  34. static ssize_t qcom_rmtfs_mem_show(struct device *dev,
  35. struct device_attribute *attr,
  36. char *buf)
  37. {
  38. struct qcom_rmtfs_mem *rmtfs_mem = container_of(dev,
  39. struct qcom_rmtfs_mem,
  40. dev);
  41. if (attr == &dev_attr_phys_addr)
  42. return sprintf(buf, "%pa\n", &rmtfs_mem->addr);
  43. if (attr == &dev_attr_size)
  44. return sprintf(buf, "%pa\n", &rmtfs_mem->size);
  45. if (attr == &dev_attr_client_id)
  46. return sprintf(buf, "%d\n", rmtfs_mem->client_id);
  47. return -EINVAL;
  48. }
  49. static struct attribute *qcom_rmtfs_mem_attrs[] = {
  50. &dev_attr_phys_addr.attr,
  51. &dev_attr_size.attr,
  52. &dev_attr_client_id.attr,
  53. NULL
  54. };
  55. ATTRIBUTE_GROUPS(qcom_rmtfs_mem);
  56. static int qcom_rmtfs_mem_open(struct inode *inode, struct file *filp)
  57. {
  58. struct qcom_rmtfs_mem *rmtfs_mem = container_of(inode->i_cdev,
  59. struct qcom_rmtfs_mem,
  60. cdev);
  61. get_device(&rmtfs_mem->dev);
  62. filp->private_data = rmtfs_mem;
  63. return 0;
  64. }
  65. static ssize_t qcom_rmtfs_mem_read(struct file *filp,
  66. char __user *buf, size_t count, loff_t *f_pos)
  67. {
  68. struct qcom_rmtfs_mem *rmtfs_mem = filp->private_data;
  69. if (*f_pos >= rmtfs_mem->size)
  70. return 0;
  71. if (*f_pos + count >= rmtfs_mem->size)
  72. count = rmtfs_mem->size - *f_pos;
  73. if (copy_to_user(buf, rmtfs_mem->base + *f_pos, count))
  74. return -EFAULT;
  75. *f_pos += count;
  76. return count;
  77. }
  78. static ssize_t qcom_rmtfs_mem_write(struct file *filp,
  79. const char __user *buf, size_t count,
  80. loff_t *f_pos)
  81. {
  82. struct qcom_rmtfs_mem *rmtfs_mem = filp->private_data;
  83. if (*f_pos >= rmtfs_mem->size)
  84. return 0;
  85. if (*f_pos + count >= rmtfs_mem->size)
  86. count = rmtfs_mem->size - *f_pos;
  87. if (copy_from_user(rmtfs_mem->base + *f_pos, buf, count))
  88. return -EFAULT;
  89. *f_pos += count;
  90. return count;
  91. }
  92. static int qcom_rmtfs_mem_release(struct inode *inode, struct file *filp)
  93. {
  94. struct qcom_rmtfs_mem *rmtfs_mem = filp->private_data;
  95. put_device(&rmtfs_mem->dev);
  96. return 0;
  97. }
  98. static struct class rmtfs_class = {
  99. .owner = THIS_MODULE,
  100. .name = "rmtfs",
  101. };
  102. static int qcom_rmtfs_mem_mmap(struct file *filep, struct vm_area_struct *vma)
  103. {
  104. struct qcom_rmtfs_mem *rmtfs_mem = filep->private_data;
  105. if (vma->vm_end - vma->vm_start > rmtfs_mem->size) {
  106. dev_dbg(&rmtfs_mem->dev,
  107. "vm_end[%lu] - vm_start[%lu] [%lu] > mem->size[%pa]\n",
  108. vma->vm_end, vma->vm_start,
  109. (vma->vm_end - vma->vm_start), &rmtfs_mem->size);
  110. return -EINVAL;
  111. }
  112. vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
  113. return remap_pfn_range(vma,
  114. vma->vm_start,
  115. rmtfs_mem->addr >> PAGE_SHIFT,
  116. vma->vm_end - vma->vm_start,
  117. vma->vm_page_prot);
  118. }
  119. static const struct file_operations qcom_rmtfs_mem_fops = {
  120. .owner = THIS_MODULE,
  121. .open = qcom_rmtfs_mem_open,
  122. .read = qcom_rmtfs_mem_read,
  123. .write = qcom_rmtfs_mem_write,
  124. .release = qcom_rmtfs_mem_release,
  125. .llseek = default_llseek,
  126. .mmap = qcom_rmtfs_mem_mmap,
  127. };
  128. static void qcom_rmtfs_mem_release_device(struct device *dev)
  129. {
  130. struct qcom_rmtfs_mem *rmtfs_mem = container_of(dev,
  131. struct qcom_rmtfs_mem,
  132. dev);
  133. kfree(rmtfs_mem);
  134. }
  135. static int qcom_rmtfs_mem_probe(struct platform_device *pdev)
  136. {
  137. struct device_node *node = pdev->dev.of_node;
  138. struct qcom_scm_vmperm perms[2];
  139. struct reserved_mem *rmem;
  140. struct qcom_rmtfs_mem *rmtfs_mem;
  141. u32 client_id;
  142. u32 vmid;
  143. int ret;
  144. rmem = of_reserved_mem_lookup(node);
  145. if (!rmem) {
  146. dev_err(&pdev->dev, "failed to acquire memory region\n");
  147. return -EINVAL;
  148. }
  149. ret = of_property_read_u32(node, "qcom,client-id", &client_id);
  150. if (ret) {
  151. dev_err(&pdev->dev, "failed to parse \"qcom,client-id\"\n");
  152. return ret;
  153. }
  154. rmtfs_mem = kzalloc(sizeof(*rmtfs_mem), GFP_KERNEL);
  155. if (!rmtfs_mem)
  156. return -ENOMEM;
  157. rmtfs_mem->addr = rmem->base;
  158. rmtfs_mem->client_id = client_id;
  159. rmtfs_mem->size = rmem->size;
  160. device_initialize(&rmtfs_mem->dev);
  161. rmtfs_mem->dev.parent = &pdev->dev;
  162. rmtfs_mem->dev.groups = qcom_rmtfs_mem_groups;
  163. rmtfs_mem->dev.release = qcom_rmtfs_mem_release_device;
  164. rmtfs_mem->base = devm_memremap(&rmtfs_mem->dev, rmtfs_mem->addr,
  165. rmtfs_mem->size, MEMREMAP_WC);
  166. if (IS_ERR(rmtfs_mem->base)) {
  167. dev_err(&pdev->dev, "failed to remap rmtfs_mem region\n");
  168. ret = PTR_ERR(rmtfs_mem->base);
  169. goto put_device;
  170. }
  171. cdev_init(&rmtfs_mem->cdev, &qcom_rmtfs_mem_fops);
  172. rmtfs_mem->cdev.owner = THIS_MODULE;
  173. dev_set_name(&rmtfs_mem->dev, "qcom_rmtfs_mem%d", client_id);
  174. rmtfs_mem->dev.id = client_id;
  175. rmtfs_mem->dev.class = &rmtfs_class;
  176. rmtfs_mem->dev.devt = MKDEV(MAJOR(qcom_rmtfs_mem_major), client_id);
  177. ret = cdev_device_add(&rmtfs_mem->cdev, &rmtfs_mem->dev);
  178. if (ret) {
  179. dev_err(&pdev->dev, "failed to add cdev: %d\n", ret);
  180. goto put_device;
  181. }
  182. ret = of_property_read_u32(node, "qcom,vmid", &vmid);
  183. if (ret < 0 && ret != -EINVAL) {
  184. dev_err(&pdev->dev, "failed to parse qcom,vmid\n");
  185. goto remove_cdev;
  186. } else if (!ret) {
  187. if (!qcom_scm_is_available()) {
  188. ret = -EPROBE_DEFER;
  189. goto remove_cdev;
  190. }
  191. perms[0].vmid = QCOM_SCM_VMID_HLOS;
  192. perms[0].perm = QCOM_SCM_PERM_RW;
  193. perms[1].vmid = vmid;
  194. perms[1].perm = QCOM_SCM_PERM_RW;
  195. rmtfs_mem->perms = BIT(QCOM_SCM_VMID_HLOS);
  196. ret = qcom_scm_assign_mem(rmtfs_mem->addr, rmtfs_mem->size,
  197. &rmtfs_mem->perms, perms, 2);
  198. if (ret < 0) {
  199. dev_err(&pdev->dev, "assign memory failed\n");
  200. goto remove_cdev;
  201. }
  202. }
  203. dev_set_drvdata(&pdev->dev, rmtfs_mem);
  204. return 0;
  205. remove_cdev:
  206. cdev_device_del(&rmtfs_mem->cdev, &rmtfs_mem->dev);
  207. put_device:
  208. put_device(&rmtfs_mem->dev);
  209. return ret;
  210. }
  211. static int qcom_rmtfs_mem_remove(struct platform_device *pdev)
  212. {
  213. struct qcom_rmtfs_mem *rmtfs_mem = dev_get_drvdata(&pdev->dev);
  214. struct qcom_scm_vmperm perm;
  215. if (rmtfs_mem->perms) {
  216. perm.vmid = QCOM_SCM_VMID_HLOS;
  217. perm.perm = QCOM_SCM_PERM_RW;
  218. qcom_scm_assign_mem(rmtfs_mem->addr, rmtfs_mem->size,
  219. &rmtfs_mem->perms, &perm, 1);
  220. }
  221. cdev_device_del(&rmtfs_mem->cdev, &rmtfs_mem->dev);
  222. put_device(&rmtfs_mem->dev);
  223. return 0;
  224. }
  225. static const struct of_device_id qcom_rmtfs_mem_of_match[] = {
  226. { .compatible = "qcom,rmtfs-mem" },
  227. {}
  228. };
  229. MODULE_DEVICE_TABLE(of, qcom_rmtfs_mem_of_match);
  230. static struct platform_driver qcom_rmtfs_mem_driver = {
  231. .probe = qcom_rmtfs_mem_probe,
  232. .remove = qcom_rmtfs_mem_remove,
  233. .driver = {
  234. .name = "qcom_rmtfs_mem",
  235. .of_match_table = qcom_rmtfs_mem_of_match,
  236. },
  237. };
  238. static int __init qcom_rmtfs_mem_init(void)
  239. {
  240. int ret;
  241. ret = class_register(&rmtfs_class);
  242. if (ret)
  243. return ret;
  244. ret = alloc_chrdev_region(&qcom_rmtfs_mem_major, 0,
  245. QCOM_RMTFS_MEM_DEV_MAX, "qcom_rmtfs_mem");
  246. if (ret < 0) {
  247. pr_err("qcom_rmtfs_mem: failed to allocate char dev region\n");
  248. goto unregister_class;
  249. }
  250. ret = platform_driver_register(&qcom_rmtfs_mem_driver);
  251. if (ret < 0) {
  252. pr_err("qcom_rmtfs_mem: failed to register rmtfs_mem driver\n");
  253. goto unregister_chrdev;
  254. }
  255. return 0;
  256. unregister_chrdev:
  257. unregister_chrdev_region(qcom_rmtfs_mem_major, QCOM_RMTFS_MEM_DEV_MAX);
  258. unregister_class:
  259. class_unregister(&rmtfs_class);
  260. return ret;
  261. }
  262. module_init(qcom_rmtfs_mem_init);
  263. static void __exit qcom_rmtfs_mem_exit(void)
  264. {
  265. platform_driver_unregister(&qcom_rmtfs_mem_driver);
  266. unregister_chrdev_region(qcom_rmtfs_mem_major, QCOM_RMTFS_MEM_DEV_MAX);
  267. class_unregister(&rmtfs_class);
  268. }
  269. module_exit(qcom_rmtfs_mem_exit);
  270. MODULE_AUTHOR("Linaro Ltd");
  271. MODULE_DESCRIPTION("Qualcomm Remote Filesystem memory driver");
  272. MODULE_LICENSE("GPL v2");