Root/target/linux/lantiq/patches-3.0/0017-MIPS-lantiq-adds-GPTU-driver.patch

1From 45dbb232686978816e8148753e12f27caa2b2eb3 Mon Sep 17 00:00:00 2001
2From: John Crispin <blogic@openwrt.org>
3Date: Thu, 29 Sep 2011 17:16:38 +0200
4Subject: [PATCH 17/24] MIPS: lantiq: adds GPTU driver
5
6---
7 arch/mips/include/asm/mach-lantiq/lantiq_timer.h | 155 ++++
8 arch/mips/lantiq/xway/Makefile | 2 +-
9 arch/mips/lantiq/xway/timer.c | 830 ++++++++++++++++++++++
10 3 files changed, 986 insertions(+), 1 deletions(-)
11 create mode 100644 arch/mips/include/asm/mach-lantiq/lantiq_timer.h
12 create mode 100644 arch/mips/lantiq/xway/timer.c
13
14diff --git a/arch/mips/include/asm/mach-lantiq/lantiq_timer.h b/arch/mips/include/asm/mach-lantiq/lantiq_timer.h
15new file mode 100644
16index 0000000..ef564ab
17--- /dev/null
18+++ b/arch/mips/include/asm/mach-lantiq/lantiq_timer.h
19@@ -0,0 +1,155 @@
20+#ifndef __DANUBE_GPTU_DEV_H__2005_07_26__10_19__
21+#define __DANUBE_GPTU_DEV_H__2005_07_26__10_19__
22+
23+
24+/******************************************************************************
25+ Copyright (c) 2002, Infineon Technologies. All rights reserved.
26+
27+ No Warranty
28+ Because the program is licensed free of charge, there is no warranty for
29+ the program, to the extent permitted by applicable law. Except when
30+ otherwise stated in writing the copyright holders and/or other parties
31+ provide the program "as is" without warranty of any kind, either
32+ expressed or implied, including, but not limited to, the implied
33+ warranties of merchantability and fitness for a particular purpose. The
34+ entire risk as to the quality and performance of the program is with
35+ you. should the program prove defective, you assume the cost of all
36+ necessary servicing, repair or correction.
37+
38+ In no event unless required by applicable law or agreed to in writing
39+ will any copyright holder, or any other party who may modify and/or
40+ redistribute the program as permitted above, be liable to you for
41+ damages, including any general, special, incidental or consequential
42+ damages arising out of the use or inability to use the program
43+ (including but not limited to loss of data or data being rendered
44+ inaccurate or losses sustained by you or third parties or a failure of
45+ the program to operate with any other programs), even if such holder or
46+ other party has been advised of the possibility of such damages.
47+******************************************************************************/
48+
49+
50+/*
51+ * ####################################
52+ * Definition
53+ * ####################################
54+ */
55+
56+/*
57+ * Available Timer/Counter Index
58+ */
59+#define TIMER(n, X) (n * 2 + (X ? 1 : 0))
60+#define TIMER_ANY 0x00
61+#define TIMER1A TIMER(1, 0)
62+#define TIMER1B TIMER(1, 1)
63+#define TIMER2A TIMER(2, 0)
64+#define TIMER2B TIMER(2, 1)
65+#define TIMER3A TIMER(3, 0)
66+#define TIMER3B TIMER(3, 1)
67+
68+/*
69+ * Flag of Timer/Counter
70+ * These flags specify the way in which timer is configured.
71+ */
72+/* Bit size of timer/counter. */
73+#define TIMER_FLAG_16BIT 0x0000
74+#define TIMER_FLAG_32BIT 0x0001
75+/* Switch between timer and counter. */
76+#define TIMER_FLAG_TIMER 0x0000
77+#define TIMER_FLAG_COUNTER 0x0002
78+/* Stop or continue when overflowing/underflowing. */
79+#define TIMER_FLAG_ONCE 0x0000
80+#define TIMER_FLAG_CYCLIC 0x0004
81+/* Count up or counter down. */
82+#define TIMER_FLAG_UP 0x0000
83+#define TIMER_FLAG_DOWN 0x0008
84+/* Count on specific level or edge. */
85+#define TIMER_FLAG_HIGH_LEVEL_SENSITIVE 0x0000
86+#define TIMER_FLAG_LOW_LEVEL_SENSITIVE 0x0040
87+#define TIMER_FLAG_RISE_EDGE 0x0010
88+#define TIMER_FLAG_FALL_EDGE 0x0020
89+#define TIMER_FLAG_ANY_EDGE 0x0030
90+/* Signal is syncronous to module clock or not. */
91+#define TIMER_FLAG_UNSYNC 0x0000
92+#define TIMER_FLAG_SYNC 0x0080
93+/* Different interrupt handle type. */
94+#define TIMER_FLAG_NO_HANDLE 0x0000
95+#if defined(__KERNEL__)
96+ #define TIMER_FLAG_CALLBACK_IN_IRQ 0x0100
97+#endif // defined(__KERNEL__)
98+#define TIMER_FLAG_SIGNAL 0x0300
99+/* Internal clock source or external clock source */
100+#define TIMER_FLAG_INT_SRC 0x0000
101+#define TIMER_FLAG_EXT_SRC 0x1000
102+
103+
104+/*
105+ * ioctl Command
106+ */
107+#define GPTU_REQUEST_TIMER 0x01 /* General method to setup timer/counter. */
108+#define GPTU_FREE_TIMER 0x02 /* Free timer/counter. */
109+#define GPTU_START_TIMER 0x03 /* Start or resume timer/counter. */
110+#define GPTU_STOP_TIMER 0x04 /* Suspend timer/counter. */
111+#define GPTU_GET_COUNT_VALUE 0x05 /* Get current count value. */
112+#define GPTU_CALCULATE_DIVIDER 0x06 /* Calculate timer divider from given freq.*/
113+#define GPTU_SET_TIMER 0x07 /* Simplified method to setup timer. */
114+#define GPTU_SET_COUNTER 0x08 /* Simplified method to setup counter. */
115+
116+/*
117+ * Data Type Used to Call ioctl
118+ */
119+struct gptu_ioctl_param {
120+ unsigned int timer; /* In command GPTU_REQUEST_TIMER, GPTU_SET_TIMER, and *
121+ * GPTU_SET_COUNTER, this field is ID of expected *
122+ * timer/counter. If it's zero, a timer/counter would *
123+ * be dynamically allocated and ID would be stored in *
124+ * this field. *
125+ * In command GPTU_GET_COUNT_VALUE, this field is *
126+ * ignored. *
127+ * In other command, this field is ID of timer/counter *
128+ * allocated. */
129+ unsigned int flag; /* In command GPTU_REQUEST_TIMER, GPTU_SET_TIMER, and *
130+ * GPTU_SET_COUNTER, this field contains flags to *
131+ * specify how to configure timer/counter. *
132+ * In command GPTU_START_TIMER, zero indicate start *
133+ * and non-zero indicate resume timer/counter. *
134+ * In other command, this field is ignored. */
135+ unsigned long value; /* In command GPTU_REQUEST_TIMER, this field contains *
136+ * init/reload value. *
137+ * In command GPTU_SET_TIMER, this field contains *
138+ * frequency (0.001Hz) of timer. *
139+ * In command GPTU_GET_COUNT_VALUE, current count *
140+ * value would be stored in this field. *
141+ * In command GPTU_CALCULATE_DIVIDER, this field *
142+ * contains frequency wanted, and after calculation, *
143+ * divider would be stored in this field to overwrite *
144+ * the frequency. *
145+ * In other command, this field is ignored. */
146+ int pid; /* In command GPTU_REQUEST_TIMER and GPTU_SET_TIMER, *
147+ * if signal is required, this field contains process *
148+ * ID to which signal would be sent. *
149+ * In other command, this field is ignored. */
150+ int sig; /* In command GPTU_REQUEST_TIMER and GPTU_SET_TIMER, *
151+ * if signal is required, this field contains signal *
152+ * number which would be sent. *
153+ * In other command, this field is ignored. */
154+};
155+
156+/*
157+ * ####################################
158+ * Data Type
159+ * ####################################
160+ */
161+typedef void (*timer_callback)(unsigned long arg);
162+
163+extern int lq_request_timer(unsigned int, unsigned int, unsigned long, unsigned long, unsigned long);
164+extern int lq_free_timer(unsigned int);
165+extern int lq_start_timer(unsigned int, int);
166+extern int lq_stop_timer(unsigned int);
167+extern int lq_reset_counter_flags(u32 timer, u32 flags);
168+extern int lq_get_count_value(unsigned int, unsigned long *);
169+extern u32 lq_cal_divider(unsigned long);
170+extern int lq_set_timer(unsigned int, unsigned int, int, int, unsigned int, unsigned long, unsigned long);
171+extern int lq_set_counter(unsigned int timer, unsigned int flag,
172+ u32 reload, unsigned long arg1, unsigned long arg2);
173+
174+#endif /* __DANUBE_GPTU_DEV_H__2005_07_26__10_19__ */
175diff --git a/arch/mips/lantiq/xway/Makefile b/arch/mips/lantiq/xway/Makefile
176index ac7cc34..1f41239 100644
177--- a/arch/mips/lantiq/xway/Makefile
178+++ b/arch/mips/lantiq/xway/Makefile
179@@ -1,4 +1,4 @@
180-obj-y := sysctrl.o reset.o gpio.o gpio_stp.o gpio_ebu.o devices.o dma.o nand.o
181+obj-y := sysctrl.o reset.o gpio.o gpio_stp.o gpio_ebu.o devices.o dma.o nand.o timer.o
182 
183 obj-$(CONFIG_SOC_XWAY) += clk-xway.o prom-xway.o
184 obj-$(CONFIG_SOC_AMAZON_SE) += clk-ase.o prom-ase.o
185diff --git a/arch/mips/lantiq/xway/timer.c b/arch/mips/lantiq/xway/timer.c
186new file mode 100644
187index 0000000..cac2ea8
188--- /dev/null
189+++ b/arch/mips/lantiq/xway/timer.c
190@@ -0,0 +1,830 @@
191+#include <linux/kernel.h>
192+#include <linux/module.h>
193+#include <linux/version.h>
194+#include <linux/types.h>
195+#include <linux/fs.h>
196+#include <linux/miscdevice.h>
197+#include <linux/init.h>
198+#include <linux/uaccess.h>
199+#include <linux/unistd.h>
200+#include <linux/errno.h>
201+#include <linux/interrupt.h>
202+#include <linux/sched.h>
203+
204+#include <asm/irq.h>
205+#include <asm/div64.h>
206+
207+#include <lantiq_soc.h>
208+#include <lantiq_irq.h>
209+#include <lantiq_timer.h>
210+
211+#define MAX_NUM_OF_32BIT_TIMER_BLOCKS 6
212+
213+#ifdef TIMER1A
214+#define FIRST_TIMER TIMER1A
215+#else
216+#define FIRST_TIMER 2
217+#endif
218+
219+/*
220+ * GPTC divider is set or not.
221+ */
222+#define GPTU_CLC_RMC_IS_SET 0
223+
224+/*
225+ * Timer Interrupt (IRQ)
226+ */
227+/* Must be adjusted when ICU driver is available */
228+#define TIMER_INTERRUPT (INT_NUM_IM3_IRL0 + 22)
229+
230+/*
231+ * Bits Operation
232+ */
233+#define GET_BITS(x, msb, lsb) \
234+ (((x) & ((1 << ((msb) + 1)) - 1)) >> (lsb))
235+#define SET_BITS(x, msb, lsb, value) \
236+ (((x) & ~(((1 << ((msb) + 1)) - 1) ^ ((1 << (lsb)) - 1))) | \
237+ (((value) & ((1 << (1 + (msb) - (lsb))) - 1)) << (lsb)))
238+
239+/*
240+ * GPTU Register Mapping
241+ */
242+#define LQ_GPTU (KSEG1 + 0x1E100A00)
243+#define LQ_GPTU_CLC ((volatile u32 *)(LQ_GPTU + 0x0000))
244+#define LQ_GPTU_ID ((volatile u32 *)(LQ_GPTU + 0x0008))
245+#define LQ_GPTU_CON(n, X) ((volatile u32 *)(LQ_GPTU + 0x0010 + ((X) * 4) + ((n) - 1) * 0x0020)) /* X must be either A or B */
246+#define LQ_GPTU_RUN(n, X) ((volatile u32 *)(LQ_GPTU + 0x0018 + ((X) * 4) + ((n) - 1) * 0x0020)) /* X must be either A or B */
247+#define LQ_GPTU_RELOAD(n, X) ((volatile u32 *)(LQ_GPTU + 0x0020 + ((X) * 4) + ((n) - 1) * 0x0020)) /* X must be either A or B */
248+#define LQ_GPTU_COUNT(n, X) ((volatile u32 *)(LQ_GPTU + 0x0028 + ((X) * 4) + ((n) - 1) * 0x0020)) /* X must be either A or B */
249+#define LQ_GPTU_IRNEN ((volatile u32 *)(LQ_GPTU + 0x00F4))
250+#define LQ_GPTU_IRNICR ((volatile u32 *)(LQ_GPTU + 0x00F8))
251+#define LQ_GPTU_IRNCR ((volatile u32 *)(LQ_GPTU + 0x00FC))
252+
253+/*
254+ * Clock Control Register
255+ */
256+#define GPTU_CLC_SMC GET_BITS(*LQ_GPTU_CLC, 23, 16)
257+#define GPTU_CLC_RMC GET_BITS(*LQ_GPTU_CLC, 15, 8)
258+#define GPTU_CLC_FSOE (*LQ_GPTU_CLC & (1 << 5))
259+#define GPTU_CLC_EDIS (*LQ_GPTU_CLC & (1 << 3))
260+#define GPTU_CLC_SPEN (*LQ_GPTU_CLC & (1 << 2))
261+#define GPTU_CLC_DISS (*LQ_GPTU_CLC & (1 << 1))
262+#define GPTU_CLC_DISR (*LQ_GPTU_CLC & (1 << 0))
263+
264+#define GPTU_CLC_SMC_SET(value) SET_BITS(0, 23, 16, (value))
265+#define GPTU_CLC_RMC_SET(value) SET_BITS(0, 15, 8, (value))
266+#define GPTU_CLC_FSOE_SET(value) ((value) ? (1 << 5) : 0)
267+#define GPTU_CLC_SBWE_SET(value) ((value) ? (1 << 4) : 0)
268+#define GPTU_CLC_EDIS_SET(value) ((value) ? (1 << 3) : 0)
269+#define GPTU_CLC_SPEN_SET(value) ((value) ? (1 << 2) : 0)
270+#define GPTU_CLC_DISR_SET(value) ((value) ? (1 << 0) : 0)
271+
272+/*
273+ * ID Register
274+ */
275+#define GPTU_ID_ID GET_BITS(*LQ_GPTU_ID, 15, 8)
276+#define GPTU_ID_CFG GET_BITS(*LQ_GPTU_ID, 7, 5)
277+#define GPTU_ID_REV GET_BITS(*LQ_GPTU_ID, 4, 0)
278+
279+/*
280+ * Control Register of Timer/Counter nX
281+ * n is the index of block (1 based index)
282+ * X is either A or B
283+ */
284+#define GPTU_CON_SRC_EG(n, X) (*LQ_GPTU_CON(n, X) & (1 << 10))
285+#define GPTU_CON_SRC_EXT(n, X) (*LQ_GPTU_CON(n, X) & (1 << 9))
286+#define GPTU_CON_SYNC(n, X) (*LQ_GPTU_CON(n, X) & (1 << 8))
287+#define GPTU_CON_EDGE(n, X) GET_BITS(*LQ_GPTU_CON(n, X), 7, 6)
288+#define GPTU_CON_INV(n, X) (*LQ_GPTU_CON(n, X) & (1 << 5))
289+#define GPTU_CON_EXT(n, X) (*LQ_GPTU_CON(n, A) & (1 << 4)) /* Timer/Counter B does not have this bit */
290+#define GPTU_CON_STP(n, X) (*LQ_GPTU_CON(n, X) & (1 << 3))
291+#define GPTU_CON_CNT(n, X) (*LQ_GPTU_CON(n, X) & (1 << 2))
292+#define GPTU_CON_DIR(n, X) (*LQ_GPTU_CON(n, X) & (1 << 1))
293+#define GPTU_CON_EN(n, X) (*LQ_GPTU_CON(n, X) & (1 << 0))
294+
295+#define GPTU_CON_SRC_EG_SET(value) ((value) ? 0 : (1 << 10))
296+#define GPTU_CON_SRC_EXT_SET(value) ((value) ? (1 << 9) : 0)
297+#define GPTU_CON_SYNC_SET(value) ((value) ? (1 << 8) : 0)
298+#define GPTU_CON_EDGE_SET(value) SET_BITS(0, 7, 6, (value))
299+#define GPTU_CON_INV_SET(value) ((value) ? (1 << 5) : 0)
300+#define GPTU_CON_EXT_SET(value) ((value) ? (1 << 4) : 0)
301+#define GPTU_CON_STP_SET(value) ((value) ? (1 << 3) : 0)
302+#define GPTU_CON_CNT_SET(value) ((value) ? (1 << 2) : 0)
303+#define GPTU_CON_DIR_SET(value) ((value) ? (1 << 1) : 0)
304+
305+#define GPTU_RUN_RL_SET(value) ((value) ? (1 << 2) : 0)
306+#define GPTU_RUN_CEN_SET(value) ((value) ? (1 << 1) : 0)
307+#define GPTU_RUN_SEN_SET(value) ((value) ? (1 << 0) : 0)
308+
309+#define GPTU_IRNEN_TC_SET(n, X, value) ((value) ? (1 << (((n) - 1) * 2 + (X))) : 0)
310+#define GPTU_IRNCR_TC_SET(n, X, value) ((value) ? (1 << (((n) - 1) * 2 + (X))) : 0)
311+
312+#define TIMER_FLAG_MASK_SIZE(x) (x & 0x0001)
313+#define TIMER_FLAG_MASK_TYPE(x) (x & 0x0002)
314+#define TIMER_FLAG_MASK_STOP(x) (x & 0x0004)
315+#define TIMER_FLAG_MASK_DIR(x) (x & 0x0008)
316+#define TIMER_FLAG_NONE_EDGE 0x0000
317+#define TIMER_FLAG_MASK_EDGE(x) (x & 0x0030)
318+#define TIMER_FLAG_REAL 0x0000
319+#define TIMER_FLAG_INVERT 0x0040
320+#define TIMER_FLAG_MASK_INVERT(x) (x & 0x0040)
321+#define TIMER_FLAG_MASK_TRIGGER(x) (x & 0x0070)
322+#define TIMER_FLAG_MASK_SYNC(x) (x & 0x0080)
323+#define TIMER_FLAG_CALLBACK_IN_HB 0x0200
324+#define TIMER_FLAG_MASK_HANDLE(x) (x & 0x0300)
325+#define TIMER_FLAG_MASK_SRC(x) (x & 0x1000)
326+
327+struct timer_dev_timer {
328+ unsigned int f_irq_on;
329+ unsigned int irq;
330+ unsigned int flag;
331+ unsigned long arg1;
332+ unsigned long arg2;
333+};
334+
335+struct timer_dev {
336+ struct mutex gptu_mutex;
337+ unsigned int number_of_timers;
338+ unsigned int occupation;
339+ unsigned int f_gptu_on;
340+ struct timer_dev_timer timer[MAX_NUM_OF_32BIT_TIMER_BLOCKS * 2];
341+};
342+
343+unsigned int ltq_get_fpi_bus_clock(int fpi);
344+
345+static long gptu_ioctl(struct file *, unsigned int, unsigned long);
346+static int gptu_open(struct inode *, struct file *);
347+static int gptu_release(struct inode *, struct file *);
348+
349+static struct file_operations gptu_fops = {
350+ .owner = THIS_MODULE,
351+ .unlocked_ioctl = gptu_ioctl,
352+ .open = gptu_open,
353+ .release = gptu_release
354+};
355+
356+static struct miscdevice gptu_miscdev = {
357+ .minor = MISC_DYNAMIC_MINOR,
358+ .name = "gptu",
359+ .fops = &gptu_fops,
360+};
361+
362+static struct timer_dev timer_dev;
363+
364+static irqreturn_t timer_irq_handler(int irq, void *p)
365+{
366+ unsigned int timer;
367+ unsigned int flag;
368+ struct timer_dev_timer *dev_timer = (struct timer_dev_timer *)p;
369+
370+ timer = irq - TIMER_INTERRUPT;
371+ if (timer < timer_dev.number_of_timers
372+ && dev_timer == &timer_dev.timer[timer]) {
373+ /* Clear interrupt. */
374+ ltq_w32(1 << timer, LQ_GPTU_IRNCR);
375+
376+ /* Call user hanler or signal. */
377+ flag = dev_timer->flag;
378+ if (!(timer & 0x01)
379+ || TIMER_FLAG_MASK_SIZE(flag) == TIMER_FLAG_16BIT) {
380+ /* 16-bit timer or timer A of 32-bit timer */
381+ switch (TIMER_FLAG_MASK_HANDLE(flag)) {
382+ case TIMER_FLAG_CALLBACK_IN_IRQ:
383+ case TIMER_FLAG_CALLBACK_IN_HB:
384+ if (dev_timer->arg1)
385+ (*(timer_callback)dev_timer->arg1)(dev_timer->arg2);
386+ break;
387+ case TIMER_FLAG_SIGNAL:
388+ send_sig((int)dev_timer->arg2, (struct task_struct *)dev_timer->arg1, 0);
389+ break;
390+ }
391+ }
392+ }
393+ return IRQ_HANDLED;
394+}
395+
396+static inline void lq_enable_gptu(void)
397+{
398+ ltq_pmu_enable(PMU_GPT);
399+
400+ /* Set divider as 1, disable write protection for SPEN, enable module. */
401+ *LQ_GPTU_CLC =
402+ GPTU_CLC_SMC_SET(0x00) |
403+ GPTU_CLC_RMC_SET(0x01) |
404+ GPTU_CLC_FSOE_SET(0) |
405+ GPTU_CLC_SBWE_SET(1) |
406+ GPTU_CLC_EDIS_SET(0) |
407+ GPTU_CLC_SPEN_SET(0) |
408+ GPTU_CLC_DISR_SET(0);
409+}
410+
411+static inline void lq_disable_gptu(void)
412+{
413+ ltq_w32(0x00, LQ_GPTU_IRNEN);
414+ ltq_w32(0xfff, LQ_GPTU_IRNCR);
415+
416+ /* Set divider as 0, enable write protection for SPEN, disable module. */
417+ *LQ_GPTU_CLC =
418+ GPTU_CLC_SMC_SET(0x00) |
419+ GPTU_CLC_RMC_SET(0x00) |
420+ GPTU_CLC_FSOE_SET(0) |
421+ GPTU_CLC_SBWE_SET(0) |
422+ GPTU_CLC_EDIS_SET(0) |
423+ GPTU_CLC_SPEN_SET(0) |
424+ GPTU_CLC_DISR_SET(1);
425+
426+ ltq_pmu_disable(PMU_GPT);
427+}
428+
429+int lq_request_timer(unsigned int timer, unsigned int flag,
430+ unsigned long value, unsigned long arg1, unsigned long arg2)
431+{
432+ int ret = 0;
433+ unsigned int con_reg, irnen_reg;
434+ int n, X;
435+
436+ if (timer >= FIRST_TIMER + timer_dev.number_of_timers)
437+ return -EINVAL;
438+
439+ printk(KERN_INFO "request_timer(%d, 0x%08X, %lu)...",
440+ timer, flag, value);
441+
442+ if (TIMER_FLAG_MASK_SIZE(flag) == TIMER_FLAG_16BIT)
443+ value &= 0xFFFF;
444+ else
445+ timer &= ~0x01;
446+
447+ mutex_lock(&timer_dev.gptu_mutex);
448+
449+ /*
450+ * Allocate timer.
451+ */
452+ if (timer < FIRST_TIMER) {
453+ unsigned int mask;
454+ unsigned int shift;
455+ /* This takes care of TIMER1B which is the only choice for Voice TAPI system */
456+ unsigned int offset = TIMER2A;
457+
458+ /*
459+ * Pick up a free timer.
460+ */
461+ if (TIMER_FLAG_MASK_SIZE(flag) == TIMER_FLAG_16BIT) {
462+ mask = 1 << offset;
463+ shift = 1;
464+ } else {
465+ mask = 3 << offset;
466+ shift = 2;
467+ }
468+ for (timer = offset;
469+ timer < offset + timer_dev.number_of_timers;
470+ timer += shift, mask <<= shift)
471+ if (!(timer_dev.occupation & mask)) {
472+ timer_dev.occupation |= mask;
473+ break;
474+ }
475+ if (timer >= offset + timer_dev.number_of_timers) {
476+ printk("failed![%d]\n", __LINE__);
477+ mutex_unlock(&timer_dev.gptu_mutex);
478+ return -EINVAL;
479+ } else
480+ ret = timer;
481+ } else {
482+ register unsigned int mask;
483+
484+ /*
485+ * Check if the requested timer is free.
486+ */
487+ mask = (TIMER_FLAG_MASK_SIZE(flag) == TIMER_FLAG_16BIT ? 1 : 3) << timer;
488+ if ((timer_dev.occupation & mask)) {
489+ printk("failed![%d] mask %#x, timer_dev.occupation %#x\n",
490+ __LINE__, mask, timer_dev.occupation);
491+ mutex_unlock(&timer_dev.gptu_mutex);
492+ return -EBUSY;
493+ } else {
494+ timer_dev.occupation |= mask;
495+ ret = 0;
496+ }
497+ }
498+
499+ /*
500+ * Prepare control register value.
501+ */
502+ switch (TIMER_FLAG_MASK_EDGE(flag)) {
503+ default:
504+ case TIMER_FLAG_NONE_EDGE:
505+ con_reg = GPTU_CON_EDGE_SET(0x00);
506+ break;
507+ case TIMER_FLAG_RISE_EDGE:
508+ con_reg = GPTU_CON_EDGE_SET(0x01);
509+ break;
510+ case TIMER_FLAG_FALL_EDGE:
511+ con_reg = GPTU_CON_EDGE_SET(0x02);
512+ break;
513+ case TIMER_FLAG_ANY_EDGE:
514+ con_reg = GPTU_CON_EDGE_SET(0x03);
515+ break;
516+ }
517+ if (TIMER_FLAG_MASK_TYPE(flag) == TIMER_FLAG_TIMER)
518+ con_reg |=
519+ TIMER_FLAG_MASK_SRC(flag) ==
520+ TIMER_FLAG_EXT_SRC ? GPTU_CON_SRC_EXT_SET(1) :
521+ GPTU_CON_SRC_EXT_SET(0);
522+ else
523+ con_reg |=
524+ TIMER_FLAG_MASK_SRC(flag) ==
525+ TIMER_FLAG_EXT_SRC ? GPTU_CON_SRC_EG_SET(1) :
526+ GPTU_CON_SRC_EG_SET(0);
527+ con_reg |=
528+ TIMER_FLAG_MASK_SYNC(flag) ==
529+ TIMER_FLAG_UNSYNC ? GPTU_CON_SYNC_SET(0) :
530+ GPTU_CON_SYNC_SET(1);
531+ con_reg |=
532+ TIMER_FLAG_MASK_INVERT(flag) ==
533+ TIMER_FLAG_REAL ? GPTU_CON_INV_SET(0) : GPTU_CON_INV_SET(1);
534+ con_reg |=
535+ TIMER_FLAG_MASK_SIZE(flag) ==
536+ TIMER_FLAG_16BIT ? GPTU_CON_EXT_SET(0) :
537+ GPTU_CON_EXT_SET(1);
538+ con_reg |=
539+ TIMER_FLAG_MASK_STOP(flag) ==
540+ TIMER_FLAG_ONCE ? GPTU_CON_STP_SET(1) : GPTU_CON_STP_SET(0);
541+ con_reg |=
542+ TIMER_FLAG_MASK_TYPE(flag) ==
543+ TIMER_FLAG_TIMER ? GPTU_CON_CNT_SET(0) :
544+ GPTU_CON_CNT_SET(1);
545+ con_reg |=
546+ TIMER_FLAG_MASK_DIR(flag) ==
547+ TIMER_FLAG_UP ? GPTU_CON_DIR_SET(1) : GPTU_CON_DIR_SET(0);
548+
549+ /*
550+ * Fill up running data.
551+ */
552+ timer_dev.timer[timer - FIRST_TIMER].flag = flag;
553+ timer_dev.timer[timer - FIRST_TIMER].arg1 = arg1;
554+ timer_dev.timer[timer - FIRST_TIMER].arg2 = arg2;
555+ if (TIMER_FLAG_MASK_SIZE(flag) != TIMER_FLAG_16BIT)
556+ timer_dev.timer[timer - FIRST_TIMER + 1].flag = flag;
557+
558+ /*
559+ * Enable GPTU module.
560+ */
561+ if (!timer_dev.f_gptu_on) {
562+ lq_enable_gptu();
563+ timer_dev.f_gptu_on = 1;
564+ }
565+
566+ /*
567+ * Enable IRQ.
568+ */
569+ if (TIMER_FLAG_MASK_HANDLE(flag) != TIMER_FLAG_NO_HANDLE) {
570+ if (TIMER_FLAG_MASK_HANDLE(flag) == TIMER_FLAG_SIGNAL)
571+ timer_dev.timer[timer - FIRST_TIMER].arg1 =
572+ (unsigned long) find_task_by_vpid((int) arg1);
573+
574+ irnen_reg = 1 << (timer - FIRST_TIMER);
575+
576+ if (TIMER_FLAG_MASK_HANDLE(flag) == TIMER_FLAG_SIGNAL
577+ || (TIMER_FLAG_MASK_HANDLE(flag) ==
578+ TIMER_FLAG_CALLBACK_IN_IRQ
579+ && timer_dev.timer[timer - FIRST_TIMER].arg1)) {
580+ enable_irq(timer_dev.timer[timer - FIRST_TIMER].irq);
581+ timer_dev.timer[timer - FIRST_TIMER].f_irq_on = 1;
582+ }
583+ } else
584+ irnen_reg = 0;
585+
586+ /*
587+ * Write config register, reload value and enable interrupt.
588+ */
589+ n = timer >> 1;
590+ X = timer & 0x01;
591+ *LQ_GPTU_CON(n, X) = con_reg;
592+ *LQ_GPTU_RELOAD(n, X) = value;
593+ /* printk("reload value = %d\n", (u32)value); */
594+ *LQ_GPTU_IRNEN |= irnen_reg;
595+
596+ mutex_unlock(&timer_dev.gptu_mutex);
597+ printk("successful!\n");
598+ return ret;
599+}
600+EXPORT_SYMBOL(lq_request_timer);
601+
602+int lq_free_timer(unsigned int timer)
603+{
604+ unsigned int flag;
605+ unsigned int mask;
606+ int n, X;
607+
608+ if (!timer_dev.f_gptu_on)
609+ return -EINVAL;
610+
611+ if (timer < FIRST_TIMER || timer >= FIRST_TIMER + timer_dev.number_of_timers)
612+ return -EINVAL;
613+
614+ mutex_lock(&timer_dev.gptu_mutex);
615+
616+ flag = timer_dev.timer[timer - FIRST_TIMER].flag;
617+ if (TIMER_FLAG_MASK_SIZE(flag) != TIMER_FLAG_16BIT)
618+ timer &= ~0x01;
619+
620+ mask = (TIMER_FLAG_MASK_SIZE(flag) == TIMER_FLAG_16BIT ? 1 : 3) << timer;
621+ if (((timer_dev.occupation & mask) ^ mask)) {
622+ mutex_unlock(&timer_dev.gptu_mutex);
623+ return -EINVAL;
624+ }
625+
626+ n = timer >> 1;
627+ X = timer & 0x01;
628+
629+ if (GPTU_CON_EN(n, X))
630+ *LQ_GPTU_RUN(n, X) = GPTU_RUN_CEN_SET(1);
631+
632+ *LQ_GPTU_IRNEN &= ~GPTU_IRNEN_TC_SET(n, X, 1);
633+ *LQ_GPTU_IRNCR |= GPTU_IRNCR_TC_SET(n, X, 1);
634+
635+ if (timer_dev.timer[timer - FIRST_TIMER].f_irq_on) {
636+ disable_irq(timer_dev.timer[timer - FIRST_TIMER].irq);
637+ timer_dev.timer[timer - FIRST_TIMER].f_irq_on = 0;
638+ }
639+
640+ timer_dev.occupation &= ~mask;
641+ if (!timer_dev.occupation && timer_dev.f_gptu_on) {
642+ lq_disable_gptu();
643+ timer_dev.f_gptu_on = 0;
644+ }
645+
646+ mutex_unlock(&timer_dev.gptu_mutex);
647+
648+ return 0;
649+}
650+EXPORT_SYMBOL(lq_free_timer);
651+
652+int lq_start_timer(unsigned int timer, int is_resume)
653+{
654+ unsigned int flag;
655+ unsigned int mask;
656+ int n, X;
657+
658+ if (!timer_dev.f_gptu_on)
659+ return -EINVAL;
660+
661+ if (timer < FIRST_TIMER || timer >= FIRST_TIMER + timer_dev.number_of_timers)
662+ return -EINVAL;
663+
664+ mutex_lock(&timer_dev.gptu_mutex);
665+
666+ flag = timer_dev.timer[timer - FIRST_TIMER].flag;
667+ if (TIMER_FLAG_MASK_SIZE(flag) != TIMER_FLAG_16BIT)
668+ timer &= ~0x01;
669+
670+ mask = (TIMER_FLAG_MASK_SIZE(flag) ==
671+ TIMER_FLAG_16BIT ? 1 : 3) << timer;
672+ if (((timer_dev.occupation & mask) ^ mask)) {
673+ mutex_unlock(&timer_dev.gptu_mutex);
674+ return -EINVAL;
675+ }
676+
677+ n = timer >> 1;
678+ X = timer & 0x01;
679+
680+ *LQ_GPTU_RUN(n, X) = GPTU_RUN_RL_SET(!is_resume) | GPTU_RUN_SEN_SET(1);
681+
682+ mutex_unlock(&timer_dev.gptu_mutex);
683+
684+ return 0;
685+}
686+EXPORT_SYMBOL(lq_start_timer);
687+
688+int lq_stop_timer(unsigned int timer)
689+{
690+ unsigned int flag;
691+ unsigned int mask;
692+ int n, X;
693+
694+ if (!timer_dev.f_gptu_on)
695+ return -EINVAL;
696+
697+ if (timer < FIRST_TIMER
698+ || timer >= FIRST_TIMER + timer_dev.number_of_timers)
699+ return -EINVAL;
700+
701+ mutex_lock(&timer_dev.gptu_mutex);
702+
703+ flag = timer_dev.timer[timer - FIRST_TIMER].flag;
704+ if (TIMER_FLAG_MASK_SIZE(flag) != TIMER_FLAG_16BIT)
705+ timer &= ~0x01;
706+
707+ mask = (TIMER_FLAG_MASK_SIZE(flag) == TIMER_FLAG_16BIT ? 1 : 3) << timer;
708+ if (((timer_dev.occupation & mask) ^ mask)) {
709+ mutex_unlock(&timer_dev.gptu_mutex);
710+ return -EINVAL;
711+ }
712+
713+ n = timer >> 1;
714+ X = timer & 0x01;
715+
716+ *LQ_GPTU_RUN(n, X) = GPTU_RUN_CEN_SET(1);
717+
718+ mutex_unlock(&timer_dev.gptu_mutex);
719+
720+ return 0;
721+}
722+EXPORT_SYMBOL(lq_stop_timer);
723+
724+int lq_reset_counter_flags(u32 timer, u32 flags)
725+{
726+ unsigned int oflag;
727+ unsigned int mask, con_reg;
728+ int n, X;
729+
730+ if (!timer_dev.f_gptu_on)
731+ return -EINVAL;
732+
733+ if (timer < FIRST_TIMER || timer >= FIRST_TIMER + timer_dev.number_of_timers)
734+ return -EINVAL;
735+
736+ mutex_lock(&timer_dev.gptu_mutex);
737+
738+ oflag = timer_dev.timer[timer - FIRST_TIMER].flag;
739+ if (TIMER_FLAG_MASK_SIZE(oflag) != TIMER_FLAG_16BIT)
740+ timer &= ~0x01;
741+
742+ mask = (TIMER_FLAG_MASK_SIZE(oflag) == TIMER_FLAG_16BIT ? 1 : 3) << timer;
743+ if (((timer_dev.occupation & mask) ^ mask)) {
744+ mutex_unlock(&timer_dev.gptu_mutex);
745+ return -EINVAL;
746+ }
747+
748+ switch (TIMER_FLAG_MASK_EDGE(flags)) {
749+ default:
750+ case TIMER_FLAG_NONE_EDGE:
751+ con_reg = GPTU_CON_EDGE_SET(0x00);
752+ break;
753+ case TIMER_FLAG_RISE_EDGE:
754+ con_reg = GPTU_CON_EDGE_SET(0x01);
755+ break;
756+ case TIMER_FLAG_FALL_EDGE:
757+ con_reg = GPTU_CON_EDGE_SET(0x02);
758+ break;
759+ case TIMER_FLAG_ANY_EDGE:
760+ con_reg = GPTU_CON_EDGE_SET(0x03);
761+ break;
762+ }
763+ if (TIMER_FLAG_MASK_TYPE(flags) == TIMER_FLAG_TIMER)
764+ con_reg |= TIMER_FLAG_MASK_SRC(flags) == TIMER_FLAG_EXT_SRC ? GPTU_CON_SRC_EXT_SET(1) : GPTU_CON_SRC_EXT_SET(0);
765+ else
766+ con_reg |= TIMER_FLAG_MASK_SRC(flags) == TIMER_FLAG_EXT_SRC ? GPTU_CON_SRC_EG_SET(1) : GPTU_CON_SRC_EG_SET(0);
767+ con_reg |= TIMER_FLAG_MASK_SYNC(flags) == TIMER_FLAG_UNSYNC ? GPTU_CON_SYNC_SET(0) : GPTU_CON_SYNC_SET(1);
768+ con_reg |= TIMER_FLAG_MASK_INVERT(flags) == TIMER_FLAG_REAL ? GPTU_CON_INV_SET(0) : GPTU_CON_INV_SET(1);
769+ con_reg |= TIMER_FLAG_MASK_SIZE(flags) == TIMER_FLAG_16BIT ? GPTU_CON_EXT_SET(0) : GPTU_CON_EXT_SET(1);
770+ con_reg |= TIMER_FLAG_MASK_STOP(flags) == TIMER_FLAG_ONCE ? GPTU_CON_STP_SET(1) : GPTU_CON_STP_SET(0);
771+ con_reg |= TIMER_FLAG_MASK_TYPE(flags) == TIMER_FLAG_TIMER ? GPTU_CON_CNT_SET(0) : GPTU_CON_CNT_SET(1);
772+ con_reg |= TIMER_FLAG_MASK_DIR(flags) == TIMER_FLAG_UP ? GPTU_CON_DIR_SET(1) : GPTU_CON_DIR_SET(0);
773+
774+ timer_dev.timer[timer - FIRST_TIMER].flag = flags;
775+ if (TIMER_FLAG_MASK_SIZE(flags) != TIMER_FLAG_16BIT)
776+ timer_dev.timer[timer - FIRST_TIMER + 1].flag = flags;
777+
778+ n = timer >> 1;
779+ X = timer & 0x01;
780+
781+ *LQ_GPTU_CON(n, X) = con_reg;
782+ smp_wmb();
783+ printk(KERN_INFO "[%s]: counter%d oflags %#x, nflags %#x, GPTU_CON %#x\n", __func__, timer, oflag, flags, *LQ_GPTU_CON(n, X));
784+ mutex_unlock(&timer_dev.gptu_mutex);
785+ return 0;
786+}
787+EXPORT_SYMBOL(lq_reset_counter_flags);
788+
789+int lq_get_count_value(unsigned int timer, unsigned long *value)
790+{
791+ unsigned int flag;
792+ unsigned int mask;
793+ int n, X;
794+
795+ if (!timer_dev.f_gptu_on)
796+ return -EINVAL;
797+
798+ if (timer < FIRST_TIMER
799+ || timer >= FIRST_TIMER + timer_dev.number_of_timers)
800+ return -EINVAL;
801+
802+ mutex_lock(&timer_dev.gptu_mutex);
803+
804+ flag = timer_dev.timer[timer - FIRST_TIMER].flag;
805+ if (TIMER_FLAG_MASK_SIZE(flag) != TIMER_FLAG_16BIT)
806+ timer &= ~0x01;
807+
808+ mask = (TIMER_FLAG_MASK_SIZE(flag) == TIMER_FLAG_16BIT ? 1 : 3) << timer;
809+ if (((timer_dev.occupation & mask) ^ mask)) {
810+ mutex_unlock(&timer_dev.gptu_mutex);
811+ return -EINVAL;
812+ }
813+
814+ n = timer >> 1;
815+ X = timer & 0x01;
816+
817+ *value = *LQ_GPTU_COUNT(n, X);
818+
819+ mutex_unlock(&timer_dev.gptu_mutex);
820+
821+ return 0;
822+}
823+EXPORT_SYMBOL(lq_get_count_value);
824+
825+u32 lq_cal_divider(unsigned long freq)
826+{
827+ u64 module_freq, fpi = ltq_get_fpi_bus_clock(2);
828+ u32 clock_divider = 1;
829+ module_freq = fpi * 1000;
830+ do_div(module_freq, clock_divider * freq);
831+ return module_freq;
832+}
833+EXPORT_SYMBOL(lq_cal_divider);
834+
835+int lq_set_timer(unsigned int timer, unsigned int freq, int is_cyclic,
836+ int is_ext_src, unsigned int handle_flag, unsigned long arg1,
837+ unsigned long arg2)
838+{
839+ unsigned long divider;
840+ unsigned int flag;
841+
842+ divider = lq_cal_divider(freq);
843+ if (divider == 0)
844+ return -EINVAL;
845+ flag = ((divider & ~0xFFFF) ? TIMER_FLAG_32BIT : TIMER_FLAG_16BIT)
846+ | (is_cyclic ? TIMER_FLAG_CYCLIC : TIMER_FLAG_ONCE)
847+ | (is_ext_src ? TIMER_FLAG_EXT_SRC : TIMER_FLAG_INT_SRC)
848+ | TIMER_FLAG_TIMER | TIMER_FLAG_DOWN
849+ | TIMER_FLAG_MASK_HANDLE(handle_flag);
850+
851+ printk(KERN_INFO "lq_set_timer(%d, %d), divider = %lu\n",
852+ timer, freq, divider);
853+ return lq_request_timer(timer, flag, divider, arg1, arg2);
854+}
855+EXPORT_SYMBOL(lq_set_timer);
856+
857+int lq_set_counter(unsigned int timer, unsigned int flag, u32 reload,
858+ unsigned long arg1, unsigned long arg2)
859+{
860+ printk(KERN_INFO "lq_set_counter(%d, %#x, %d)\n", timer, flag, reload);
861+ return lq_request_timer(timer, flag, reload, arg1, arg2);
862+}
863+EXPORT_SYMBOL(lq_set_counter);
864+
865+static long gptu_ioctl(struct file *file, unsigned int cmd,
866+ unsigned long arg)
867+{
868+ int ret;
869+ struct gptu_ioctl_param param;
870+
871+ if (!access_ok(VERIFY_READ, arg, sizeof(struct gptu_ioctl_param)))
872+ return -EFAULT;
873+ copy_from_user(&param, (void *) arg, sizeof(param));
874+
875+ if ((((cmd == GPTU_REQUEST_TIMER || cmd == GPTU_SET_TIMER
876+ || GPTU_SET_COUNTER) && param.timer < 2)
877+ || cmd == GPTU_GET_COUNT_VALUE || cmd == GPTU_CALCULATE_DIVIDER)
878+ && !access_ok(VERIFY_WRITE, arg,
879+ sizeof(struct gptu_ioctl_param)))
880+ return -EFAULT;
881+
882+ switch (cmd) {
883+ case GPTU_REQUEST_TIMER:
884+ ret = lq_request_timer(param.timer, param.flag, param.value,
885+ (unsigned long) param.pid,
886+ (unsigned long) param.sig);
887+ if (ret > 0) {
888+ copy_to_user(&((struct gptu_ioctl_param *) arg)->
889+ timer, &ret, sizeof(&ret));
890+ ret = 0;
891+ }
892+ break;
893+ case GPTU_FREE_TIMER:
894+ ret = lq_free_timer(param.timer);
895+ break;
896+ case GPTU_START_TIMER:
897+ ret = lq_start_timer(param.timer, param.flag);
898+ break;
899+ case GPTU_STOP_TIMER:
900+ ret = lq_stop_timer(param.timer);
901+ break;
902+ case GPTU_GET_COUNT_VALUE:
903+ ret = lq_get_count_value(param.timer, &param.value);
904+ if (!ret)
905+ copy_to_user(&((struct gptu_ioctl_param *) arg)->
906+ value, &param.value,
907+ sizeof(param.value));
908+ break;
909+ case GPTU_CALCULATE_DIVIDER:
910+ param.value = lq_cal_divider(param.value);
911+ if (param.value == 0)
912+ ret = -EINVAL;
913+ else {
914+ copy_to_user(&((struct gptu_ioctl_param *) arg)->
915+ value, &param.value,
916+ sizeof(param.value));
917+ ret = 0;
918+ }
919+ break;
920+ case GPTU_SET_TIMER:
921+ ret = lq_set_timer(param.timer, param.value,
922+ TIMER_FLAG_MASK_STOP(param.flag) !=
923+ TIMER_FLAG_ONCE ? 1 : 0,
924+ TIMER_FLAG_MASK_SRC(param.flag) ==
925+ TIMER_FLAG_EXT_SRC ? 1 : 0,
926+ TIMER_FLAG_MASK_HANDLE(param.flag) ==
927+ TIMER_FLAG_SIGNAL ? TIMER_FLAG_SIGNAL :
928+ TIMER_FLAG_NO_HANDLE,
929+ (unsigned long) param.pid,
930+ (unsigned long) param.sig);
931+ if (ret > 0) {
932+ copy_to_user(&((struct gptu_ioctl_param *) arg)->
933+ timer, &ret, sizeof(&ret));
934+ ret = 0;
935+ }
936+ break;
937+ case GPTU_SET_COUNTER:
938+ lq_set_counter(param.timer, param.flag, param.value, 0, 0);
939+ if (ret > 0) {
940+ copy_to_user(&((struct gptu_ioctl_param *) arg)->
941+ timer, &ret, sizeof(&ret));
942+ ret = 0;
943+ }
944+ break;
945+ default:
946+ ret = -ENOTTY;
947+ }
948+
949+ return ret;
950+}
951+
952+static int gptu_open(struct inode *inode, struct file *file)
953+{
954+ return 0;
955+}
956+
957+static int gptu_release(struct inode *inode, struct file *file)
958+{
959+ return 0;
960+}
961+
962+int __init lq_gptu_init(void)
963+{
964+ int ret;
965+ unsigned int i;
966+
967+ ltq_w32(0, LQ_GPTU_IRNEN);
968+ ltq_w32(0xfff, LQ_GPTU_IRNCR);
969+
970+ memset(&timer_dev, 0, sizeof(timer_dev));
971+ mutex_init(&timer_dev.gptu_mutex);
972+
973+ lq_enable_gptu();
974+ timer_dev.number_of_timers = GPTU_ID_CFG * 2;
975+ lq_disable_gptu();
976+ if (timer_dev.number_of_timers > MAX_NUM_OF_32BIT_TIMER_BLOCKS * 2)
977+ timer_dev.number_of_timers = MAX_NUM_OF_32BIT_TIMER_BLOCKS * 2;
978+ printk(KERN_INFO "gptu: totally %d 16-bit timers/counters\n", timer_dev.number_of_timers);
979+
980+ ret = misc_register(&gptu_miscdev);
981+ if (ret) {
982+ printk(KERN_ERR "gptu: can't misc_register, get error %d\n", -ret);
983+ return ret;
984+ } else {
985+ printk(KERN_INFO "gptu: misc_register on minor %d\n", gptu_miscdev.minor);
986+ }
987+
988+ for (i = 0; i < timer_dev.number_of_timers; i++) {
989+ ret = request_irq(TIMER_INTERRUPT + i, timer_irq_handler, IRQF_TIMER, gptu_miscdev.name, &timer_dev.timer[i]);
990+ if (ret) {
991+ for (; i >= 0; i--)
992+ free_irq(TIMER_INTERRUPT + i, &timer_dev.timer[i]);
993+ misc_deregister(&gptu_miscdev);
994+ printk(KERN_ERR "gptu: failed in requesting irq (%d), get error %d\n", i, -ret);
995+ return ret;
996+ } else {
997+ timer_dev.timer[i].irq = TIMER_INTERRUPT + i;
998+ disable_irq(timer_dev.timer[i].irq);
999+ printk(KERN_INFO "gptu: succeeded to request irq %d\n", timer_dev.timer[i].irq);
1000+ }
1001+ }
1002+
1003+ return 0;
1004+}
1005+
1006+void __exit lq_gptu_exit(void)
1007+{
1008+ unsigned int i;
1009+
1010+ for (i = 0; i < timer_dev.number_of_timers; i++) {
1011+ if (timer_dev.timer[i].f_irq_on)
1012+ disable_irq(timer_dev.timer[i].irq);
1013+ free_irq(timer_dev.timer[i].irq, &timer_dev.timer[i]);
1014+ }
1015+ lq_disable_gptu();
1016+ misc_deregister(&gptu_miscdev);
1017+}
1018+
1019+module_init(lq_gptu_init);
1020+module_exit(lq_gptu_exit);
1021--
10221.7.5.4
1023
1024

Archive Download this file



interactive