summaryrefslogtreecommitdiff
path: root/drivers/iq/sprd_iq.c
blob: 93325fc409c05794da8879d848cfde18aa2066f2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
/*
 * Copyright (C) 2012 Spreadtrum Communications Inc.
 *
 * This software is licensed under the terms of the GNU General Public
 * License version 2, as published by the Free Software Foundation, and
 * may be copied, distributed, and modified under those terms.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/wait.h>
#include <linux/interrupt.h>
#include <linux/sched.h>
#include <linux/kthread.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <soc/sprd/board.h>
#include <asm/memory.h>
#include <linux/memblock.h>
#include <linux/devfreq.h>
#include <linux/mm.h>
#include <linux/miscdevice.h>
#include <linux/debugfs.h>
#include <soc/sprd/hardware.h>


#define IQ_BUF_INIT                0x5A5A5A5A
#define IQ_BUF_WRITE_FINISHED      0x5A5A8181
#define IQ_BUF_READ_FINISHED       0x81815A5A
#define IQ_BUF_READING             0x81005a00

#define IQ_TRANSFER_SIZE (500*1024)

struct iq_header{
	volatile u32  WR_RD_FLAG;
	u32  data_addr;
	u32  data_len;
	u32  reserved;
};

struct sprd_iq {
	struct iq_header *head_1;
	struct iq_header *head_2;
	wait_queue_head_t wait;
};

static struct sprd_iq g_iq = {0};

static struct task_struct *iq_thread;

static uint iq_base;

static u32 iq_mode = 0;

module_param_named(iq_base, iq_base, uint, 0444);

extern ssize_t vser_iq_write(char *buf, size_t count);
extern void kernel_vser_register_callback(void *function);
extern phys_addr_t sprd_iq_addr(void);

static int __init early_mode(char *str)
{
	printk("early_mode \n");
	if(!memcmp(str, "iq", 2))
		iq_mode = 1;

    return 0;
}

int in_iqmode(void)
{
	return (iq_mode == 1);
}

early_param("androidboot.mode", early_mode);


static ssize_t sprd_iq_write(u32 paddr, u32 length)
{
	void *vaddr;
	u32 len;
	u32 send_num = 0;
	ssize_t ret;
	printk(KERN_ERR "sprd_iq_write 0x%x, 0x%x \n", paddr, length);
	while(length - send_num > 0) {
		vaddr = NULL;

		len =( length -send_num > IQ_TRANSFER_SIZE) ? IQ_TRANSFER_SIZE: (length -send_num);
		vaddr = __va(paddr+send_num);
		if(!vaddr) {
			printk(KERN_ERR "sprd iq no memory  \n");
			msleep(10);
			continue;
		}
		//pr_debug("sprd_iq_write 0x%x, 0x%x \n", paddr+send_num, len);
		//printk("sprd_iq_write 0x%x, 0x%x  %x\n", paddr+send_num, (u32)vaddr, len);
		ret = vser_iq_write(vaddr, len);
		if(ret < 0) {
			printk(KERN_ERR "sprd iq usb send error %d \n", ret);
			msleep(200);
		}
		else
		{
			send_num += ret;
			//printk(KERN_ERR "sprd iq send_num = 0x%x, ret = 0x%x \n", send_num, ret);
		}
	}
	return send_num;
}

static int sprd_iq_thread(void *data)
{
	struct sprd_iq *p_iq = (struct sprd_iq*)data;
	struct sched_param param = {.sched_priority = 80};

	sched_setscheduler(current, SCHED_RR, &param);

	while (!kthread_should_stop()) {

		//printk(KERN_ERR "sprd_iq_thread flag1 = 0x%x, flag2 = 0x%x \n", p_iq->head_1->WR_RD_FLAG, p_iq->head_2->WR_RD_FLAG);
		//wait_event(p_iq->wait, p_iq->bstart);
		if(p_iq->head_1->WR_RD_FLAG == IQ_BUF_WRITE_FINISHED) {
			p_iq->head_1->WR_RD_FLAG = IQ_BUF_READING;
			sprd_iq_write(p_iq->head_1->data_addr, p_iq->head_1->data_len);
			//p_iq->head_1->WR_RD_FLAG = IQ_BUF_READ_FINISHED;
		} else if(p_iq->head_2->WR_RD_FLAG == IQ_BUF_WRITE_FINISHED) {
			p_iq->head_2->WR_RD_FLAG = IQ_BUF_READING;
			sprd_iq_write(p_iq->head_2->data_addr, p_iq->head_2->data_len);
			//p_iq->head_2->WR_RD_FLAG = IQ_BUF_READ_FINISHED;
		} else
			msleep(1000);
	}
	return 0;
}

static void sprd_iq_complete(char *buf,  int length)
{
	u32 vaddr = (u32)buf;
	pr_debug("sprd_iq_complete 0x%x, 0x%x \n", vaddr, length);
	if(vaddr + length == (u32)__va(g_iq.head_1->data_addr + g_iq.head_1->data_len))
		g_iq.head_1->WR_RD_FLAG = IQ_BUF_READ_FINISHED;
	if(vaddr + length == (u32)__va(g_iq.head_2->data_addr + g_iq.head_2->data_len))
		g_iq.head_2->WR_RD_FLAG = IQ_BUF_READ_FINISHED;
}

#ifdef CONFIG_SPRD_SCXX30_DMC_FREQ
static unsigned int sprd_iq_dmc_notify(struct devfreq_dbs *h, unsigned int state)
{
	if(state == DEVFREQ_PRE_CHANGE)
		return 1;
}


struct devfreq_dbs dmc_iq_notify = {
	.level = 0,
	.devfreq_notifier = sprd_iq_dmc_notify,
};
#endif

static long iq_mem_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
    return 0;
}

static int iq_mem_nocache_mmap(struct file *filp, struct vm_area_struct *vma)
{
    size_t size = vma->vm_end - vma->vm_start;

    printk(KERN_INFO "iq_mem_nocache_mmap\n");

    vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);

    if (remap_pfn_range(vma,vma->vm_start, vma->vm_pgoff,
                        vma->vm_end - vma->vm_start, vma->vm_page_prot))
    {
        printk(KERN_ERR "remap_pfn_range failed\n");
        return -EAGAIN;
    }

    printk(KERN_INFO "iq mmap %x,%x,%x\n", (unsigned int)PAGE_SHIFT,
           (unsigned int)vma->vm_start,
           (unsigned int)(vma->vm_end - vma->vm_start));
    return 0;
}

static int iq_mem_open(struct inode *inode, struct file *filp)
{
    printk(KERN_INFO "iq_mem_open called\n");
    return 0;
}

static int iq_mem_release (struct inode *inode, struct file *filp)
{
    printk(KERN_INFO "iq_mem_release\n");
    return 0;
}


static const struct file_operations iq_mem_fops =
{
    .owner = THIS_MODULE,
    .unlocked_ioctl =iq_mem_ioctl,
    .mmap  =iq_mem_nocache_mmap,
    .open =iq_mem_open,
    .release =iq_mem_release,
};

static struct miscdevice iq_mem_dev = {
    .minor   = MISC_DYNAMIC_MINOR,
    .name   = "iq_mem",
    .fops   = &iq_mem_fops,
};


static int __init  sprd_iq_init(void)
{
    int ret;
    ret = misc_register(&iq_mem_dev);
    if (ret) {
        printk(KERN_ERR "cannot register iq_mmap_dev ret = (%d)\n",ret);
    }

	if(!in_iqmode())
		return -EINVAL;
	if (sprd_iq_addr() == 0xffffffff)
		return -ENOMEM;
#ifdef CONFIG_SPRD_SCXX30_DMC_FREQ
	devfreq_notifier_register(&dmc_iq_notify);
#endif
	init_waitqueue_head(&g_iq.wait);
	g_iq.head_1 = (struct iq_header*)__va(sprd_iq_addr());

	g_iq.head_2 = (struct iq_header*)__va(sprd_iq_addr()+SPRD_IQ_SIZE - sizeof(struct iq_header));

	g_iq.head_1->WR_RD_FLAG = IQ_BUF_INIT;// IQ_BUF_READ_FINISHED;
	g_iq.head_2->WR_RD_FLAG = IQ_BUF_INIT;//IQ_BUF_READ_FINISHED;

	iq_base = sprd_iq_addr();

	kernel_vser_register_callback((void*)sprd_iq_complete);

	iq_thread = kthread_create(sprd_iq_thread, (void*)&g_iq, "iq_thread");
	if (IS_ERR(iq_thread)) {
		printk(KERN_ERR "mux_ipc_thread error!.\n");
		return PTR_ERR(iq_thread);
	}

	wake_up_process(iq_thread);
	return 0;
}

static void __exit sprd_iq_exit(void)
{
    misc_deregister(&iq_mem_dev);
}




module_init(sprd_iq_init);
module_exit(sprd_iq_exit);

MODULE_AUTHOR("zhongping tan");
MODULE_DESCRIPTION("SPRD IQ driver");
MODULE_LICENSE("GPL");