blob: ec0f1bdd3dd4f457ad17b4b696dc0935a4abdf9e [file] [log] [blame]
Gagan Macb315a202012-07-18 15:32:10 -06001/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
Harini Jayaramanef7805f2011-09-28 12:45:31 -06002 *
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 DEBUG */
14
15#include <linux/module.h>
16#include <linux/fs.h>
17#include <linux/mm.h>
18#include <linux/err.h>
19#include <linux/slab.h>
20#include <linux/errno.h>
21#include <linux/device.h>
22#include <linux/uaccess.h>
23#include <linux/miscdevice.h>
24#include <linux/memory_alloc.h>
Gagan Mac0d5d49e2012-08-28 19:41:37 -060025#include <mach/rpm-smd.h>
Harini Jayaramanef7805f2011-09-28 12:45:31 -060026#include "msm-buspm-dev.h"
27
28#define MSM_BUSPM_DRV_NAME "msm-buspm-dev"
29
Gagan Mac0d5d49e2012-08-28 19:41:37 -060030enum msm_buspm_spdm_res {
31 SPDM_RES_ID = 0,
32 SPDM_RES_TYPE = 0x63707362,
33 SPDM_KEY = 0x00006e65,
34 SPDM_SIZE = 4,
35};
Harini Jayaramanef7805f2011-09-28 12:45:31 -060036/*
37 * Allocate kernel buffer.
38 * Currently limited to one buffer per file descriptor. If alloc() is
39 * called twice for the same descriptor, the original buffer is freed.
40 * There is also no locking protection so the same descriptor can not be shared.
41 */
42
43static inline void *msm_buspm_dev_get_vaddr(struct file *filp)
44{
45 struct msm_buspm_map_dev *dev = filp->private_data;
46
47 return (dev) ? dev->vaddr : NULL;
48}
49
Gagan Macb315a202012-07-18 15:32:10 -060050static inline unsigned int msm_buspm_dev_get_buflen(struct file *filp)
51{
52 struct msm_buspm_map_dev *dev = filp->private_data;
53
54 return dev ? dev->buflen : 0;
55}
56
Harini Jayaramanef7805f2011-09-28 12:45:31 -060057static inline unsigned long msm_buspm_dev_get_paddr(struct file *filp)
58{
59 struct msm_buspm_map_dev *dev = filp->private_data;
60
61 return (dev) ? dev->paddr : 0L;
62}
63
64static void msm_buspm_dev_free(struct file *filp)
65{
66 struct msm_buspm_map_dev *dev = filp->private_data;
67
68 if (dev) {
69 pr_debug("freeing memory at 0x%p\n", dev->vaddr);
70 free_contiguous_memory(dev->vaddr);
71 dev->paddr = 0L;
72 dev->vaddr = NULL;
73 }
74}
75
76static int msm_buspm_dev_open(struct inode *inode, struct file *filp)
77{
78 struct msm_buspm_map_dev *dev;
79
80 if (capable(CAP_SYS_ADMIN)) {
81 dev = kzalloc(sizeof(*dev), GFP_KERNEL);
82 if (dev)
83 filp->private_data = dev;
84 else
85 return -ENOMEM;
86 } else {
87 return -EPERM;
88 }
89
90 return 0;
91}
92
93static int
94msm_buspm_dev_alloc(struct file *filp, struct buspm_alloc_params data)
95{
96 unsigned long paddr;
97 void *vaddr;
98 struct msm_buspm_map_dev *dev = filp->private_data;
99
100 /* If buffer already allocated, then free it */
101 if (dev->vaddr)
102 msm_buspm_dev_free(filp);
103
104 /* Allocate uncached memory */
105 vaddr = allocate_contiguous_ebi(data.size, PAGE_SIZE, 0);
106 paddr = (vaddr) ? memory_pool_node_paddr(vaddr) : 0L;
107
108 if (vaddr == NULL) {
109 pr_err("allocation of 0x%x bytes failed", data.size);
110 return -ENOMEM;
111 }
112
113 dev->vaddr = vaddr;
114 dev->paddr = paddr;
115 dev->buflen = data.size;
116 filp->f_pos = 0;
117 pr_debug("virt addr = 0x%p\n", dev->vaddr);
118 pr_debug("phys addr = 0x%lx\n", dev->paddr);
119
120 return 0;
121}
122
Gagan Mac0d5d49e2012-08-28 19:41:37 -0600123static int msm_bus_rpm_req(u32 rsc_type, u32 key, u32 hwid,
124 int ctx, u32 val)
125{
126 struct msm_rpm_request *rpm_req;
127 int ret, msg_id;
128
129 rpm_req = msm_rpm_create_request(ctx, rsc_type, SPDM_RES_ID, 1);
130 if (rpm_req == NULL) {
131 pr_err("RPM: Couldn't create RPM Request\n");
132 return -ENXIO;
133 }
134
135 ret = msm_rpm_add_kvp_data(rpm_req, key, (const uint8_t *)&val,
136 (int)(sizeof(uint32_t)));
137 if (ret) {
138 pr_err("RPM: Add KVP failed for RPM Req:%u\n",
139 rsc_type);
140 goto err;
141 }
142
143 pr_debug("Added Key: %d, Val: %u, size: %d\n", key,
144 (uint32_t)val, sizeof(uint32_t));
145 msg_id = msm_rpm_send_request(rpm_req);
146 if (!msg_id) {
147 pr_err("RPM: No message ID for req\n");
148 ret = -ENXIO;
149 goto err;
150 }
151
152 ret = msm_rpm_wait_for_ack(msg_id);
153 if (ret) {
154 pr_err("RPM: Ack failed\n");
155 goto err;
156 }
157
158err:
159 msm_rpm_free_request(rpm_req);
160 return ret;
161}
162
163static int msm_buspm_ioc_cmds(uint32_t arg)
164{
165 switch (arg) {
166 case MSM_BUSPM_SPDM_CLK_DIS:
167 case MSM_BUSPM_SPDM_CLK_EN:
168 return msm_bus_rpm_req(SPDM_RES_TYPE, SPDM_KEY, 0,
169 MSM_RPM_CTX_ACTIVE_SET, arg);
170 default:
171 pr_warn("Unsupported ioctl command: %d\n", arg);
172 return -EINVAL;
173 }
174}
175
176
177
Harini Jayaramanef7805f2011-09-28 12:45:31 -0600178static long
179msm_buspm_dev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
180{
181 struct buspm_xfer_req xfer;
182 struct buspm_alloc_params alloc_data;
183 unsigned long paddr;
184 int retval = 0;
185 void *buf = msm_buspm_dev_get_vaddr(filp);
Gagan Macb315a202012-07-18 15:32:10 -0600186 unsigned int buflen = msm_buspm_dev_get_buflen(filp);
Harini Jayaramanef7805f2011-09-28 12:45:31 -0600187 unsigned char *dbgbuf = buf;
188
Gagan Mac0d5d49e2012-08-28 19:41:37 -0600189 if (_IOC_TYPE(cmd) != MSM_BUSPM_IOC_MAGIC) {
190 pr_err("Wrong IOC_MAGIC.Exiting\n");
191 return -ENOTTY;
192 }
193
Harini Jayaramanef7805f2011-09-28 12:45:31 -0600194 switch (cmd) {
195 case MSM_BUSPM_IOC_FREE:
196 pr_debug("cmd = 0x%x (FREE)\n", cmd);
197 msm_buspm_dev_free(filp);
198 break;
199
200 case MSM_BUSPM_IOC_ALLOC:
201 pr_debug("cmd = 0x%x (ALLOC)\n", cmd);
202 retval = __get_user(alloc_data.size, (size_t __user *)arg);
203
204 if (retval == 0)
205 retval = msm_buspm_dev_alloc(filp, alloc_data);
206 break;
207
208 case MSM_BUSPM_IOC_RD_PHYS_ADDR:
209 pr_debug("Read Physical Address\n");
210 paddr = msm_buspm_dev_get_paddr(filp);
211 if (paddr == 0L) {
212 retval = -EINVAL;
213 } else {
214 pr_debug("phys addr = 0x%lx\n", paddr);
215 retval = __put_user(paddr,
216 (unsigned long __user *)arg);
217 }
218 break;
219
220 case MSM_BUSPM_IOC_RDBUF:
221 pr_debug("Read Buffer: 0x%x%x%x%x\n",
222 dbgbuf[0], dbgbuf[1], dbgbuf[2], dbgbuf[3]);
223
224 if (!buf) {
225 retval = -EINVAL;
226 break;
227 }
228
229 if (copy_from_user(&xfer, (void __user *)arg, sizeof(xfer))) {
230 retval = -EFAULT;
231 break;
232 }
233
Gagan Macb315a202012-07-18 15:32:10 -0600234 if ((xfer.size <= buflen) &&
Harini Jayaramanef7805f2011-09-28 12:45:31 -0600235 (copy_to_user((void __user *)xfer.data, buf,
236 xfer.size))) {
237 retval = -EFAULT;
238 break;
239 }
240 break;
241
242 case MSM_BUSPM_IOC_WRBUF:
243 pr_debug("Write Buffer\n");
244
245 if (!buf) {
246 retval = -EINVAL;
247 break;
248 }
249
250 if (copy_from_user(&xfer, (void __user *)arg, sizeof(xfer))) {
251 retval = -EFAULT;
252 break;
253 }
254
Gagan Macb315a202012-07-18 15:32:10 -0600255 if ((buflen <= xfer.size) &&
Harini Jayaramanef7805f2011-09-28 12:45:31 -0600256 (copy_from_user(buf, (void __user *)xfer.data,
257 xfer.size))) {
258 retval = -EFAULT;
259 break;
260 }
261 break;
262
Gagan Mac0d5d49e2012-08-28 19:41:37 -0600263 case MSM_BUSPM_IOC_CMD:
264 pr_debug("IOCTL command: cmd: %d arg: %lu\n", cmd, arg);
265 retval = msm_buspm_ioc_cmds(arg);
266 break;
267
Harini Jayaramanef7805f2011-09-28 12:45:31 -0600268 default:
269 pr_debug("Unknown command 0x%x\n", cmd);
270 retval = -EINVAL;
271 break;
272 }
273
274 return retval;
275}
276
277static int msm_buspm_dev_release(struct inode *inode, struct file *filp)
278{
279 struct msm_buspm_map_dev *dev = filp->private_data;
280
281 msm_buspm_dev_free(filp);
282 kfree(dev);
283 filp->private_data = NULL;
284
285 return 0;
286}
287
288static int msm_buspm_dev_mmap(struct file *filp, struct vm_area_struct *vma)
289{
290 pr_debug("vma = 0x%p\n", vma);
291
292 /* Mappings are uncached */
293 vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
294 if (remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
295 vma->vm_end - vma->vm_start, vma->vm_page_prot))
296 return -EFAULT;
297
298 return 0;
299}
300
301static const struct file_operations msm_buspm_dev_fops = {
302 .owner = THIS_MODULE,
303 .mmap = msm_buspm_dev_mmap,
304 .open = msm_buspm_dev_open,
305 .unlocked_ioctl = msm_buspm_dev_ioctl,
306 .llseek = noop_llseek,
307 .release = msm_buspm_dev_release,
308};
309
310struct miscdevice msm_buspm_misc = {
311 .minor = MISC_DYNAMIC_MINOR,
312 .name = MSM_BUSPM_DRV_NAME,
313 .fops = &msm_buspm_dev_fops,
314};
315
316static int __init msm_buspm_dev_init(void)
317{
318 int ret = 0;
319
320 ret = misc_register(&msm_buspm_misc);
321 if (ret < 0)
322 pr_err("%s: Cannot register misc device\n", __func__);
323
324 return ret;
325}
326
327static void __exit msm_buspm_dev_exit(void)
328{
329 misc_deregister(&msm_buspm_misc);
330}
331module_init(msm_buspm_dev_init);
332module_exit(msm_buspm_dev_exit);
333
334MODULE_LICENSE("GPL v2");
335MODULE_VERSION("1.0");
336MODULE_ALIAS("platform:"MSM_BUSPM_DRV_NAME);