blob: 3470710350052f023737acdacde95ce26d30bbf9 [file] [log] [blame]
Venkatesh Yadav Abbarapu574b4012013-11-11 18:02:27 +05301/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -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#include <linux/init.h>
14#include <linux/module.h>
15#include <linux/fs.h>
16#include <linux/slab.h>
17#include <linux/miscdevice.h>
18#include <linux/spinlock.h>
19#include <linux/uaccess.h>
20#include <linux/io.h>
21#include <linux/irq.h>
22#include <linux/interrupt.h>
23#include <linux/sched.h>
24#include <linux/wait.h>
25#include <linux/poll.h>
26#include <mach/msm_iomap.h>
27
28#include <linux/fsm_dfe_hh.h>
29
30/*
31 * DFE of FSM9XXX
32 */
33
34#define HH_ADDR_MASK 0x000ffffc
35#define HH_OFFSET_VALID(offset) (((offset) & ~HH_ADDR_MASK) == 0)
36#define HH_REG_IOADDR(offset) ((uint8_t *) MSM_HH_BASE + (offset))
37#define HH_MAKE_OFFSET(blk, adr) (((blk)&0x1F)<<15|((adr)&0x1FFF)<<2)
38
39#define HH_REG_SCPN_IREQ_MASK HH_REG_IOADDR(HH_MAKE_OFFSET(5, 0x12))
40#define HH_REG_SCPN_IREQ_FLAG HH_REG_IOADDR(HH_MAKE_OFFSET(5, 0x13))
41
42/*
43 * Device private information per device node
44 */
45
46#define HH_IRQ_FIFO_SIZE 64
47#define HH_IRQ_FIFO_EMPTY(pdev) ((pdev)->irq_fifo_head == \
48 (pdev)->irq_fifo_tail)
49#define HH_IRQ_FIFO_FULL(pdev) ((((pdev)->irq_fifo_tail + 1) % \
50 HH_IRQ_FIFO_SIZE) == \
51 (pdev)->irq_fifo_head)
52
Venkatesh Yadav Abbarapu574b4012013-11-11 18:02:27 +053053#define UINT32_MAX (0xFFFFFFFFU)
54
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070055static struct hh_dev_node_info {
56 spinlock_t hh_lock;
57 char irq_fifo[HH_IRQ_FIFO_SIZE];
58 unsigned int irq_fifo_head, irq_fifo_tail;
59 wait_queue_head_t wq;
60} hh_dev_info;
61
62/*
63 * Device private information per file
64 */
65
66struct hh_dev_file_info {
67 /* Buffer */
68 unsigned int *parray;
69 unsigned int array_num;
70
71 struct dfe_command_entry *pcmd;
72 unsigned int cmd_num;
73};
74
75/*
76 * File interface
77 */
78
79static int hh_open(struct inode *inode, struct file *file)
80{
81 struct hh_dev_file_info *pdfi;
82
83 /* private data allocation */
84 pdfi = kmalloc(sizeof(*pdfi), GFP_KERNEL);
85 if (pdfi == NULL)
86 return -ENOMEM;
87 file->private_data = pdfi;
88
89 /* buffer initialization */
90 pdfi->parray = NULL;
91 pdfi->array_num = 0;
92 pdfi->pcmd = NULL;
93 pdfi->cmd_num = 0;
94
95 return 0;
96}
97
98static int hh_release(struct inode *inode, struct file *file)
99{
100 struct hh_dev_file_info *pdfi;
101
102 pdfi = (struct hh_dev_file_info *) file->private_data;
103
104 kfree(pdfi->parray);
105 pdfi->parray = NULL;
106 pdfi->array_num = 0;
107
108 kfree(pdfi->pcmd);
109 pdfi->pcmd = NULL;
110 pdfi->cmd_num = 0;
111
112 kfree(file->private_data);
113 file->private_data = NULL;
114
115 return 0;
116}
117
118static ssize_t hh_read(struct file *filp, char __user *buf, size_t count,
119 loff_t *f_pos)
120{
121 signed char irq = -1;
122 unsigned long irq_flags;
123
124 do {
125 spin_lock_irqsave(&hh_dev_info.hh_lock, irq_flags);
126 if (!HH_IRQ_FIFO_EMPTY(&hh_dev_info)) {
127 irq = hh_dev_info.irq_fifo[hh_dev_info.irq_fifo_head];
128 if (++hh_dev_info.irq_fifo_head == HH_IRQ_FIFO_SIZE)
129 hh_dev_info.irq_fifo_head = 0;
130 }
131 spin_unlock_irqrestore(&hh_dev_info.hh_lock, irq_flags);
132
133 if (irq < 0)
134 if (wait_event_interruptible(hh_dev_info.wq,
135 !HH_IRQ_FIFO_EMPTY(&hh_dev_info)) < 0)
136 break;
137 } while (irq < 0);
138
139 if (irq < 0) {
140 /* No pending interrupt */
141 return 0;
142 } else {
143 put_user(irq, buf);
144 return 1;
145 }
146
147 return 0;
148}
149
150static ssize_t hh_write(struct file *file, const char __user *buffer,
151 size_t count, loff_t *ppos)
152{
153 return 0;
154}
155
156static long hh_ioctl(struct file *file,
157 unsigned int cmd, unsigned long arg)
158{
159 unsigned int __user *argp = (unsigned int __user *) arg;
160 struct hh_dev_file_info *pdfi =
161 (struct hh_dev_file_info *) file->private_data;
162
163 switch (cmd) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700164 case DFE_IOCTL_READ_REGISTER:
165 {
166 unsigned int offset, value;
167
168 if (get_user(offset, argp))
169 return -EFAULT;
170 if (!HH_OFFSET_VALID(offset))
171 return -EINVAL;
172 value = __raw_readl(HH_REG_IOADDR(offset));
173 if (put_user(value, argp))
174 return -EFAULT;
175 }
176 break;
177
178 case DFE_IOCTL_WRITE_REGISTER:
179 {
180 struct dfe_write_register_param param;
181
182 if (copy_from_user(&param, argp, sizeof param))
183 return -EFAULT;
184 if (!HH_OFFSET_VALID(param.offset))
185 return -EINVAL;
186 __raw_writel(param.value,
187 HH_REG_IOADDR(param.offset));
188 }
189 break;
190
191 case DFE_IOCTL_WRITE_REGISTER_WITH_MASK:
192 {
193 struct dfe_write_register_mask_param param;
194 unsigned int value;
195 unsigned long irq_flags;
196
197 if (copy_from_user(&param, argp, sizeof param))
198 return -EFAULT;
199 if (!HH_OFFSET_VALID(param.offset))
200 return -EINVAL;
201 spin_lock_irqsave(&hh_dev_info.hh_lock,
202 irq_flags);
203 value = __raw_readl(HH_REG_IOADDR(param.offset));
204 value &= ~param.mask;
205 value |= param.value & param.mask;
206 __raw_writel(value, HH_REG_IOADDR(param.offset));
207 spin_unlock_irqrestore(&hh_dev_info.hh_lock,
208 irq_flags);
209 }
210 break;
211
212 case DFE_IOCTL_READ_REGISTER_ARRAY:
213 case DFE_IOCTL_WRITE_REGISTER_ARRAY:
214 {
215 struct dfe_read_write_array_param param;
216 unsigned int req_sz;
217 unsigned long irq_flags;
218 unsigned int i;
219 void *addr;
220
221 if (copy_from_user(&param, argp, sizeof param))
222 return -EFAULT;
223 if (!HH_OFFSET_VALID(param.offset))
224 return -EINVAL;
Venkatesh Yadav Abbarapua3c7f0a2013-11-28 13:06:18 +0530225 if ((param.num == 0) ||
226 (param.num >= (UINT32_MAX / sizeof(unsigned int))))
227 return -EINVAL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700228 req_sz = sizeof(unsigned int) * param.num;
229
230 if (pdfi->array_num < param.num) {
231 void *pmem;
232
233 pmem = kmalloc(req_sz, GFP_KERNEL);
234 if (pmem == NULL)
235 return -ENOMEM;
236 pdfi->parray = (unsigned int *) pmem;
237 pdfi->array_num = param.num;
238 }
239
240 if (cmd == DFE_IOCTL_WRITE_REGISTER_ARRAY)
241 if (copy_from_user(pdfi->parray,
242 param.pArray, req_sz))
243 return -EFAULT;
244
245 addr = HH_REG_IOADDR(param.offset);
246
247 spin_lock_irqsave(&hh_dev_info.hh_lock,
248 irq_flags);
249 for (i = 0; i < param.num; ++i, addr += 4) {
250 if (cmd == DFE_IOCTL_READ_REGISTER_ARRAY)
251 pdfi->parray[i] = __raw_readl(addr);
252 else
253 __raw_writel(pdfi->parray[i], addr);
254 }
255 spin_unlock_irqrestore(&hh_dev_info.hh_lock,
256 irq_flags);
257
258 if (cmd == DFE_IOCTL_READ_REGISTER_ARRAY)
259 if (copy_to_user(pdfi->parray,
260 param.pArray, req_sz))
261 return -EFAULT;
262 }
263 break;
264
265 case DFE_IOCTL_COMMAND:
266 {
267 struct dfe_command_param param;
268 unsigned int req_sz;
269 unsigned long irq_flags;
270 unsigned int i, value;
271 struct dfe_command_entry *pcmd;
272 void *addr;
273
274 if (copy_from_user(&param, argp, sizeof param))
275 return -EFAULT;
Venkatesh Yadav Abbarapu574b4012013-11-11 18:02:27 +0530276 if ((param.num == 0) ||
277 (param.num >= (UINT32_MAX /
278 sizeof(struct dfe_command_entry))))
279 return -EINVAL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700280 req_sz = sizeof(struct dfe_command_entry) * param.num;
281
282 if (pdfi->cmd_num < param.num) {
283 void *pmem;
284
285 pmem = kmalloc(req_sz, GFP_KERNEL);
286 if (pmem == NULL)
287 return -ENOMEM;
288 pdfi->pcmd = (struct dfe_command_entry *) pmem;
289 pdfi->cmd_num = param.num;
290 }
291
292 if (copy_from_user(pdfi->pcmd, param.pEntry, req_sz))
293 return -EFAULT;
294
295 pcmd = pdfi->pcmd;
296
297 spin_lock_irqsave(&hh_dev_info.hh_lock,
298 irq_flags);
299 for (i = 0; i < param.num; ++i, ++pcmd) {
300 if (!HH_OFFSET_VALID(pcmd->offset))
301 return -EINVAL;
302 addr = HH_REG_IOADDR(pcmd->offset);
303
304 switch (pcmd->code) {
305 case DFE_IOCTL_COMMAND_CODE_WRITE:
306 __raw_writel(pcmd->value, addr);
307 break;
308 case DFE_IOCTL_COMMAND_CODE_WRITE_WITH_MASK:
309 value = __raw_readl(addr);
310 value &= ~pcmd->mask;
311 value |= pcmd->value & pcmd->mask;
312 __raw_writel(value, addr);
313 break;
314 }
315 }
316 spin_unlock_irqrestore(&hh_dev_info.hh_lock,
317 irq_flags);
318 }
319 break;
320
321 default:
322 return -EINVAL;
323 }
324
325 return 0;
326}
327
328static unsigned int hh_poll(struct file *filp,
329 struct poll_table_struct *wait)
330{
331 unsigned mask = 0;
332
333 if (!HH_IRQ_FIFO_EMPTY(&hh_dev_info))
334 mask |= POLLIN;
335
336 if (mask == 0) {
337 poll_wait(filp, &hh_dev_info.wq, wait);
338 if (!HH_IRQ_FIFO_EMPTY(&hh_dev_info))
339 mask |= POLLIN;
340 }
341
342 return mask;
343}
344
345static const struct file_operations hh_fops = {
346 .owner = THIS_MODULE,
347 .open = hh_open,
348 .release = hh_release,
349 .read = hh_read,
350 .write = hh_write,
351 .unlocked_ioctl = hh_ioctl,
352 .poll = hh_poll,
353};
354
355/*
356 * Interrupt handling
357 */
358
359static irqreturn_t hh_irq_handler(int irq, void *data)
360{
361 unsigned int irq_enable, irq_flag, irq_mask;
362 int i;
363
364 irq_enable = __raw_readl(HH_REG_SCPN_IREQ_MASK);
365 irq_flag = __raw_readl(HH_REG_SCPN_IREQ_FLAG);
366 irq_flag &= irq_enable;
367
368 /* Disables interrupts */
369 irq_enable &= ~irq_flag;
370 __raw_writel(irq_enable, HH_REG_SCPN_IREQ_MASK);
371
372 /* Adds the pending interrupts to irq_fifo */
373 spin_lock(&hh_dev_info.hh_lock);
374 for (i = 0, irq_mask = 1; i < 32; ++i, irq_mask <<= 1) {
375 if (HH_IRQ_FIFO_FULL(&hh_dev_info))
376 break;
377 if (irq_flag & irq_mask) {
378 hh_dev_info.irq_fifo[hh_dev_info.irq_fifo_tail] = \
379 (char) i;
380 if (++hh_dev_info.irq_fifo_tail == HH_IRQ_FIFO_SIZE)
381 hh_dev_info.irq_fifo_tail = 0;
382 }
383 }
384 spin_unlock(&hh_dev_info.hh_lock);
385
386 /* Wakes up pending processes */
387 wake_up_interruptible(&hh_dev_info.wq);
388
389 return IRQ_HANDLED;
390}
391
392/*
393 * Driver initialization & cleanup
394 */
395
396static struct miscdevice hh_misc_dev = {
397 .minor = MISC_DYNAMIC_MINOR,
398 .name = DFE_HH_DEVICE_NAME,
399 .fops = &hh_fops,
400};
401
402static int __init hh_init(void)
403{
404 int ret;
405
406 /* lock initialization */
407 spin_lock_init(&hh_dev_info.hh_lock);
408
409 /* interrupt handler */
410 hh_dev_info.irq_fifo_head = 0;
411 hh_dev_info.irq_fifo_tail = 0;
412 ret = request_irq(INT_HH_SUPSS_IRQ, hh_irq_handler,
413 IRQF_TRIGGER_RISING, "hh_dev", 0);
414 if (ret < 0) {
415 pr_err("Cannot register HH interrupt handler.\n");
416 return ret;
417 }
418
419 /* wait queue */
420 init_waitqueue_head(&hh_dev_info.wq);
421
422 return misc_register(&hh_misc_dev);
423}
424
425static void __exit hh_exit(void)
426{
427 misc_deregister(&hh_misc_dev);
428 free_irq(INT_HH_SUPSS_IRQ, 0);
429}
430
431MODULE_LICENSE("GPL v2");
432MODULE_AUTHOR("Rohit Vaswani <rvaswani@codeaurora.org>");
433MODULE_DESCRIPTION("Qualcomm Hammerhead Digital Front End driver");
434MODULE_VERSION("1.0");
435
436module_init(hh_init);
437module_exit(hh_exit);
438