blob: b25f55a547953eb985e038c9a583fed69b23d618 [file] [log] [blame]
Nikhilesh Reddy3d452512016-03-16 14:31:23 -07001/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
Nikhilesh Reddybc69c702015-06-01 16:08:32 -07002 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12
13#define DRIVER_NAME "msm_sharedmem"
14#define pr_fmt(fmt) DRIVER_NAME ": %s: " fmt, __func__
15
16#include <linux/uio_driver.h>
17#include <linux/module.h>
18#include <linux/platform_device.h>
19#include <linux/err.h>
20#include <linux/of.h>
21#include <linux/dma-mapping.h>
Nikhilesh Reddy36f30122015-06-04 11:31:42 -070022
23#include <soc/qcom/secure_buffer.h>
24
Nikhilesh Reddybc69c702015-06-01 16:08:32 -070025#include "sharedmem_qmi.h"
26
27#define CLIENT_ID_PROP "qcom,client-id"
28
Nikhilesh Reddy36f30122015-06-04 11:31:42 -070029#define MPSS_RMTS_CLIENT_ID 1
30
Nikhilesh Reddybc69c702015-06-01 16:08:32 -070031static int uio_get_mem_index(struct uio_info *info, struct vm_area_struct *vma)
32{
33 if (vma->vm_pgoff >= MAX_UIO_MAPS)
34 return -EINVAL;
35
36 if (info->mem[vma->vm_pgoff].size == 0)
37 return -EINVAL;
38
39 return (int)vma->vm_pgoff;
40}
41
42static int sharedmem_mmap(struct uio_info *info, struct vm_area_struct *vma)
43{
44 int result;
45 struct uio_mem *mem;
46 int mem_index = uio_get_mem_index(info, vma);
47
48 if (mem_index < 0) {
49 pr_err("mem_index is invalid errno %d\n", mem_index);
50 return mem_index;
51 }
52
53 mem = info->mem + mem_index;
54
55 if (vma->vm_end - vma->vm_start > mem->size) {
Nikhilesh Reddy3d452512016-03-16 14:31:23 -070056 pr_err("vm_end[%lu] - vm_start[%lu] [%lu] > mem->size[%pa]\n",
Nikhilesh Reddybc69c702015-06-01 16:08:32 -070057 vma->vm_end, vma->vm_start,
Nikhilesh Reddy3d452512016-03-16 14:31:23 -070058 (vma->vm_end - vma->vm_start), &mem->size);
Nikhilesh Reddybc69c702015-06-01 16:08:32 -070059 return -EINVAL;
60 }
61 pr_debug("Attempting to setup mmap.\n");
62
63 vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
64
65 result = remap_pfn_range(vma,
66 vma->vm_start,
67 mem->addr >> PAGE_SHIFT,
68 vma->vm_end - vma->vm_start,
69 vma->vm_page_prot);
70 if (result != 0)
71 pr_err("mmap Failed with errno %d\n", result);
72 else
73 pr_debug("mmap success\n");
74
75 return result;
76}
77
Nikhilesh Reddy36f30122015-06-04 11:31:42 -070078/* Setup the shared ram permissions.
79 * This function currently supports the mpss client only.
80 */
81static void setup_shared_ram_perms(u32 client_id, phys_addr_t addr, u32 size)
82{
83 int ret;
84 u32 source_vmlist[1] = {VMID_HLOS};
85 int dest_vmids[2] = {VMID_HLOS, VMID_MSS_MSA};
86 int dest_perms[2] = {PERM_READ|PERM_WRITE,
87 PERM_READ|PERM_WRITE};
88
89 if (client_id != MPSS_RMTS_CLIENT_ID)
90 return;
91
92 ret = hyp_assign_phys(addr, size, source_vmlist, 1, dest_vmids,
93 dest_perms, 2);
94 if (ret != 0) {
95 if (ret == -EINVAL)
96 pr_warn("hyp_assign_phys is not supported!");
97 else
Prasad Sodagudid9cd67b2015-10-07 23:04:40 +053098 pr_err("hyp_assign_phys failed IPA=0x016%pa size=%u err=%d\n",
99 &addr, size, ret);
Nikhilesh Reddy36f30122015-06-04 11:31:42 -0700100 }
101}
102
Nikhilesh Reddybc69c702015-06-01 16:08:32 -0700103static int msm_sharedmem_probe(struct platform_device *pdev)
104{
105 int ret = 0;
106 struct uio_info *info = NULL;
107 struct resource *clnt_res = NULL;
108 u32 client_id = ((u32)~0U);
109 u32 shared_mem_size = 0;
110 void *shared_mem = NULL;
111 phys_addr_t shared_mem_pyhsical = 0;
112 bool is_addr_dynamic = false;
113 struct sharemem_qmi_entry qmi_entry;
114
115 /* Get the addresses from platform-data */
116 if (!pdev->dev.of_node) {
117 pr_err("Node not found\n");
118 ret = -ENODEV;
119 goto out;
120 }
121 clnt_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
122 if (!clnt_res) {
123 pr_err("resource not found\n");
124 return -ENODEV;
125 }
126
127 ret = of_property_read_u32(pdev->dev.of_node, CLIENT_ID_PROP,
128 &client_id);
129 if (ret) {
130 client_id = ((u32)~0U);
131 pr_warn("qcom,client-id property not found\n");
132 }
133
134 info = devm_kzalloc(&pdev->dev, sizeof(struct uio_info), GFP_KERNEL);
135 if (!info)
136 return -ENOMEM;
137
138 shared_mem_size = resource_size(clnt_res);
139 shared_mem_pyhsical = clnt_res->start;
140
141 if (shared_mem_size == 0) {
142 pr_err("Shared memory size is zero\n");
143 return -EINVAL;
144 }
145
146 if (shared_mem_pyhsical == 0) {
147 is_addr_dynamic = true;
148 shared_mem = dma_alloc_coherent(&pdev->dev, shared_mem_size,
149 &shared_mem_pyhsical, GFP_KERNEL);
150 if (shared_mem == NULL) {
151 pr_err("Shared mem alloc client=%s, size=%u\n",
152 clnt_res->name, shared_mem_size);
153 return -ENOMEM;
154 }
155 }
156
Nikhilesh Reddy36f30122015-06-04 11:31:42 -0700157 /* Set up the permissions for the shared ram that was allocated. */
158 setup_shared_ram_perms(client_id, shared_mem_pyhsical, shared_mem_size);
159
Nikhilesh Reddybc69c702015-06-01 16:08:32 -0700160 /* Setup device */
161 info->mmap = sharedmem_mmap; /* Custom mmap function. */
162 info->name = clnt_res->name;
163 info->version = "1.0";
164 info->mem[0].addr = shared_mem_pyhsical;
165 info->mem[0].size = shared_mem_size;
166 info->mem[0].memtype = UIO_MEM_PHYS;
167
168 ret = uio_register_device(&pdev->dev, info);
169 if (ret) {
170 pr_err("uio register failed ret=%d\n", ret);
171 goto out;
172 }
173 dev_set_drvdata(&pdev->dev, info);
174
175 qmi_entry.client_id = client_id;
176 qmi_entry.client_name = info->name;
177 qmi_entry.address = info->mem[0].addr;
178 qmi_entry.size = info->mem[0].size;
179 qmi_entry.is_addr_dynamic = is_addr_dynamic;
180
181 sharedmem_qmi_add_entry(&qmi_entry);
182 pr_info("Device created for client '%s'\n", clnt_res->name);
183out:
184 return ret;
185}
186
187static int msm_sharedmem_remove(struct platform_device *pdev)
188{
189 struct uio_info *info = dev_get_drvdata(&pdev->dev);
190
191 uio_unregister_device(info);
192
193 return 0;
194}
195
196static const struct of_device_id msm_sharedmem_of_match[] = {
197 {.compatible = "qcom,sharedmem-uio",},
198 {},
199};
200MODULE_DEVICE_TABLE(of, msm_sharedmem_of_match);
201
202static struct platform_driver msm_sharedmem_driver = {
203 .probe = msm_sharedmem_probe,
204 .remove = msm_sharedmem_remove,
205 .driver = {
206 .name = DRIVER_NAME,
207 .owner = THIS_MODULE,
208 .of_match_table = msm_sharedmem_of_match,
209 },
210};
211
212
213static int __init msm_sharedmem_init(void)
214{
215 int result;
216
217 result = sharedmem_qmi_init();
218 if (result < 0) {
219 pr_err("sharedmem_qmi_init failed result = %d\n", result);
220 return result;
221 }
222
223 result = platform_driver_register(&msm_sharedmem_driver);
224 if (result != 0) {
225 pr_err("Platform driver registration failed\n");
226 return result;
227 }
228 return 0;
229}
230
231static void __exit msm_sharedmem_exit(void)
232{
233 platform_driver_unregister(&msm_sharedmem_driver);
234 sharedmem_qmi_exit();
235}
236
237module_init(msm_sharedmem_init);
238module_exit(msm_sharedmem_exit);
239
240MODULE_LICENSE("GPL v2");