blob: d372171d731adc0fa198dd255127153a1f314f09 [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
2 *
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) {
162 case DFE_IOCTL_IS_UMTS:
163 return __raw_readl(MSM_TCSR_BASE + 0x0008) & 0x01;
164
165 case DFE_IOCTL_READ_REGISTER:
166 {
167 unsigned int offset, value;
168
169 if (get_user(offset, argp))
170 return -EFAULT;
171 if (!HH_OFFSET_VALID(offset))
172 return -EINVAL;
173 value = __raw_readl(HH_REG_IOADDR(offset));
174 if (put_user(value, argp))
175 return -EFAULT;
176 }
177 break;
178
179 case DFE_IOCTL_WRITE_REGISTER:
180 {
181 struct dfe_write_register_param param;
182
183 if (copy_from_user(&param, argp, sizeof param))
184 return -EFAULT;
185 if (!HH_OFFSET_VALID(param.offset))
186 return -EINVAL;
187 __raw_writel(param.value,
188 HH_REG_IOADDR(param.offset));
189 }
190 break;
191
192 case DFE_IOCTL_WRITE_REGISTER_WITH_MASK:
193 {
194 struct dfe_write_register_mask_param param;
195 unsigned int value;
196 unsigned long irq_flags;
197
198 if (copy_from_user(&param, argp, sizeof param))
199 return -EFAULT;
200 if (!HH_OFFSET_VALID(param.offset))
201 return -EINVAL;
202 spin_lock_irqsave(&hh_dev_info.hh_lock,
203 irq_flags);
204 value = __raw_readl(HH_REG_IOADDR(param.offset));
205 value &= ~param.mask;
206 value |= param.value & param.mask;
207 __raw_writel(value, HH_REG_IOADDR(param.offset));
208 spin_unlock_irqrestore(&hh_dev_info.hh_lock,
209 irq_flags);
210 }
211 break;
212
213 case DFE_IOCTL_READ_REGISTER_ARRAY:
214 case DFE_IOCTL_WRITE_REGISTER_ARRAY:
215 {
216 struct dfe_read_write_array_param param;
217 unsigned int req_sz;
218 unsigned long irq_flags;
219 unsigned int i;
220 void *addr;
221
222 if (copy_from_user(&param, argp, sizeof param))
223 return -EFAULT;
224 if (!HH_OFFSET_VALID(param.offset))
225 return -EINVAL;
226 if (param.num == 0)
227 break;
228 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;
276 if (param.num == 0)
277 break;
278 req_sz = sizeof(struct dfe_command_entry) * param.num;
279
280 if (pdfi->cmd_num < param.num) {
281 void *pmem;
282
283 pmem = kmalloc(req_sz, GFP_KERNEL);
284 if (pmem == NULL)
285 return -ENOMEM;
286 pdfi->pcmd = (struct dfe_command_entry *) pmem;
287 pdfi->cmd_num = param.num;
288 }
289
290 if (copy_from_user(pdfi->pcmd, param.pEntry, req_sz))
291 return -EFAULT;
292
293 pcmd = pdfi->pcmd;
294
295 spin_lock_irqsave(&hh_dev_info.hh_lock,
296 irq_flags);
297 for (i = 0; i < param.num; ++i, ++pcmd) {
298 if (!HH_OFFSET_VALID(pcmd->offset))
299 return -EINVAL;
300 addr = HH_REG_IOADDR(pcmd->offset);
301
302 switch (pcmd->code) {
303 case DFE_IOCTL_COMMAND_CODE_WRITE:
304 __raw_writel(pcmd->value, addr);
305 break;
306 case DFE_IOCTL_COMMAND_CODE_WRITE_WITH_MASK:
307 value = __raw_readl(addr);
308 value &= ~pcmd->mask;
309 value |= pcmd->value & pcmd->mask;
310 __raw_writel(value, addr);
311 break;
312 }
313 }
314 spin_unlock_irqrestore(&hh_dev_info.hh_lock,
315 irq_flags);
316 }
317 break;
318
319 default:
320 return -EINVAL;
321 }
322
323 return 0;
324}
325
326static unsigned int hh_poll(struct file *filp,
327 struct poll_table_struct *wait)
328{
329 unsigned mask = 0;
330
331 if (!HH_IRQ_FIFO_EMPTY(&hh_dev_info))
332 mask |= POLLIN;
333
334 if (mask == 0) {
335 poll_wait(filp, &hh_dev_info.wq, wait);
336 if (!HH_IRQ_FIFO_EMPTY(&hh_dev_info))
337 mask |= POLLIN;
338 }
339
340 return mask;
341}
342
343static const struct file_operations hh_fops = {
344 .owner = THIS_MODULE,
345 .open = hh_open,
346 .release = hh_release,
347 .read = hh_read,
348 .write = hh_write,
349 .unlocked_ioctl = hh_ioctl,
350 .poll = hh_poll,
351};
352
353/*
354 * Interrupt handling
355 */
356
357static irqreturn_t hh_irq_handler(int irq, void *data)
358{
359 unsigned int irq_enable, irq_flag, irq_mask;
360 int i;
361
362 irq_enable = __raw_readl(HH_REG_SCPN_IREQ_MASK);
363 irq_flag = __raw_readl(HH_REG_SCPN_IREQ_FLAG);
364 irq_flag &= irq_enable;
365
366 /* Disables interrupts */
367 irq_enable &= ~irq_flag;
368 __raw_writel(irq_enable, HH_REG_SCPN_IREQ_MASK);
369
370 /* Adds the pending interrupts to irq_fifo */
371 spin_lock(&hh_dev_info.hh_lock);
372 for (i = 0, irq_mask = 1; i < 32; ++i, irq_mask <<= 1) {
373 if (HH_IRQ_FIFO_FULL(&hh_dev_info))
374 break;
375 if (irq_flag & irq_mask) {
376 hh_dev_info.irq_fifo[hh_dev_info.irq_fifo_tail] = \
377 (char) i;
378 if (++hh_dev_info.irq_fifo_tail == HH_IRQ_FIFO_SIZE)
379 hh_dev_info.irq_fifo_tail = 0;
380 }
381 }
382 spin_unlock(&hh_dev_info.hh_lock);
383
384 /* Wakes up pending processes */
385 wake_up_interruptible(&hh_dev_info.wq);
386
387 return IRQ_HANDLED;
388}
389
390/*
391 * Driver initialization & cleanup
392 */
393
394static struct miscdevice hh_misc_dev = {
395 .minor = MISC_DYNAMIC_MINOR,
396 .name = DFE_HH_DEVICE_NAME,
397 .fops = &hh_fops,
398};
399
400static int __init hh_init(void)
401{
402 int ret;
403
404 /* lock initialization */
405 spin_lock_init(&hh_dev_info.hh_lock);
406
407 /* interrupt handler */
408 hh_dev_info.irq_fifo_head = 0;
409 hh_dev_info.irq_fifo_tail = 0;
410 ret = request_irq(INT_HH_SUPSS_IRQ, hh_irq_handler,
411 IRQF_TRIGGER_RISING, "hh_dev", 0);
412 if (ret < 0) {
413 pr_err("Cannot register HH interrupt handler.\n");
414 return ret;
415 }
416
417 /* wait queue */
418 init_waitqueue_head(&hh_dev_info.wq);
419
420 return misc_register(&hh_misc_dev);
421}
422
423static void __exit hh_exit(void)
424{
425 misc_deregister(&hh_misc_dev);
426 free_irq(INT_HH_SUPSS_IRQ, 0);
427}
428
429MODULE_LICENSE("GPL v2");
430MODULE_AUTHOR("Rohit Vaswani <rvaswani@codeaurora.org>");
431MODULE_DESCRIPTION("Qualcomm Hammerhead Digital Front End driver");
432MODULE_VERSION("1.0");
433
434module_init(hh_init);
435module_exit(hh_exit);
436