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