Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
[pandora-kernel.git] / arch / sh / boards / landisk / landisk_pwb.c
1 /*
2  * arch/sh/boards/landisk/landisk_pwb.c -- driver for the Power control switch.
3  *
4  * This driver will also support the I-O DATA Device, Inc. LANDISK Board.
5  *
6  * This file is subject to the terms and conditions of the GNU General Public
7  * License.  See the file "COPYING" in the main directory of this archive
8  * for more details.
9  *
10  * Copylight (C) 2002 Atom Create Engineering Co., Ltd.
11  *
12  * LED control drive function added by kogiidena
13  */
14
15 #include <linux/config.h>
16 #include <linux/module.h>
17 #include <linux/errno.h>
18 #include <linux/signal.h>
19 #include <linux/major.h>
20 #include <linux/poll.h>
21 #include <linux/init.h>
22 #include <linux/delay.h>
23 #include <linux/sched.h>
24 #include <linux/timer.h>
25 #include <linux/interrupt.h>
26
27 #include <asm/system.h>
28 #include <asm/io.h>
29 #include <asm/irq.h>
30 #include <asm/uaccess.h>
31 #include <asm/landisk/iodata_landisk.h>
32
33 #define SHUTDOWN_BTN_MINOR      1       /* Shutdown button device minor no. */
34 #define LED_MINOR              21       /* LED minor no. */
35 #define BTN_MINOR              22       /* BUTTON minor no. */
36 #define GIO_MINOR              40       /* GIO minor no. */
37
38 static int openCnt;
39 static int openCntLED;
40 static int openCntGio;
41 static int openCntBtn;
42 static int landisk_btn;
43 static int landisk_btnctrlpid;
44 /*
45  * Functions prototypes
46  */
47
48 static int gio_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
49                      unsigned long arg);
50
51 static int swdrv_open(struct inode *inode, struct file *filp)
52 {
53         int minor;
54
55         minor = MINOR(inode->i_rdev);
56         filp->private_data = (void *)minor;
57
58         if (minor == SHUTDOWN_BTN_MINOR) {
59                 if (openCnt > 0) {
60                         return -EALREADY;
61                 } else {
62                         openCnt++;
63                         return 0;
64                 }
65         } else if (minor == LED_MINOR) {
66                 if (openCntLED > 0) {
67                         return -EALREADY;
68                 } else {
69                         openCntLED++;
70                         return 0;
71                 }
72         } else if (minor == BTN_MINOR) {
73                 if (openCntBtn > 0) {
74                         return -EALREADY;
75                 } else {
76                         openCntBtn++;
77                         return 0;
78                 }
79         } else if (minor == GIO_MINOR) {
80                 if (openCntGio > 0) {
81                         return -EALREADY;
82                 } else {
83                         openCntGio++;
84                         return 0;
85                 }
86         }
87         return -ENOENT;
88
89 }
90
91 static int swdrv_close(struct inode *inode, struct file *filp)
92 {
93         int minor;
94
95         minor = MINOR(inode->i_rdev);
96         if (minor == SHUTDOWN_BTN_MINOR) {
97                 openCnt--;
98         } else if (minor == LED_MINOR) {
99                 openCntLED--;
100         } else if (minor == BTN_MINOR) {
101                 openCntBtn--;
102         } else if (minor == GIO_MINOR) {
103                 openCntGio--;
104         }
105         return 0;
106 }
107
108 static int swdrv_read(struct file *filp, char *buff, size_t count,
109                       loff_t * ppos)
110 {
111         int minor;
112         minor = (int)(filp->private_data);
113
114         if (!access_ok(VERIFY_WRITE, (void *)buff, count))
115                 return -EFAULT;
116
117         if (minor == SHUTDOWN_BTN_MINOR) {
118                 if (landisk_btn & 0x10) {
119                         put_user(1, buff);
120                         return 1;
121                 } else {
122                         return 0;
123                 }
124         }
125         return 0;
126 }
127
128 static int swdrv_write(struct file *filp, const char *buff, size_t count,
129                        loff_t * ppos)
130 {
131         int minor;
132         minor = (int)(filp->private_data);
133
134         if (minor == SHUTDOWN_BTN_MINOR) {
135                 return count;
136         }
137         return count;
138 }
139
140 static irqreturn_t sw_interrupt(int irq, void *dev_id, struct pt_regs *regs)
141 {
142         landisk_btn = (0x0ff & (~ctrl_inb(PA_STATUS)));
143         disable_irq(IRQ_BUTTON);
144         disable_irq(IRQ_POWER);
145         ctrl_outb(0x00, PA_PWRINT_CLR);
146
147         if (landisk_btnctrlpid != 0) {
148                 kill_proc(landisk_btnctrlpid, SIGUSR1, 1);
149                 landisk_btnctrlpid = 0;
150         }
151
152         return IRQ_HANDLED;
153 }
154
155 static struct file_operations swdrv_fops = {
156         .read = swdrv_read,     /* read */
157         .write = swdrv_write,   /* write */
158         .open = swdrv_open,     /* open */
159         .release = swdrv_close, /* release */
160         .ioctl = gio_ioctl,     /* ioctl */
161
162 };
163
164 static char banner[] __initdata =
165     KERN_INFO "LANDISK and USL-5P Button, LED and GIO driver initialized\n";
166
167 int __init swdrv_init(void)
168 {
169         int error;
170
171         printk("%s", banner);
172
173         openCnt = 0;
174         openCntLED = 0;
175         openCntBtn = 0;
176         openCntGio = 0;
177         landisk_btn = 0;
178         landisk_btnctrlpid = 0;
179
180         if ((error = register_chrdev(SHUTDOWN_BTN_MAJOR, "swdrv", &swdrv_fops))) {
181                 printk(KERN_ERR
182                        "Button, LED and GIO driver:Couldn't register driver, error=%d\n",
183                        error);
184                 return 1;
185         }
186
187         if (request_irq(IRQ_POWER, sw_interrupt, 0, "SHUTDOWNSWITCH", NULL)) {
188                 printk(KERN_ERR "Unable to get IRQ 11.\n");
189                 return 1;
190         }
191         if (request_irq(IRQ_BUTTON, sw_interrupt, 0, "USL-5P BUTTON", NULL)) {
192                 printk(KERN_ERR "Unable to get IRQ 12.\n");
193                 return 1;
194         }
195         ctrl_outb(0x00, PA_PWRINT_CLR);
196
197         return 0;
198 }
199
200 module_init(swdrv_init);
201
202 /*
203  * gio driver
204  *
205  */
206
207 #include <asm/landisk/gio.h>
208
209 static int gio_ioctl(struct inode *inode, struct file *filp,
210                      unsigned int cmd, unsigned long arg)
211 {
212         int minor;
213         unsigned int data, mask;
214         static unsigned int addr = 0;
215
216         minor = (int)(filp->private_data);
217
218         /* access control */
219         if (minor == GIO_MINOR) {
220                 ;
221         } else if (minor == LED_MINOR) {
222                 if (((cmd & 0x0ff) >= 9) && ((cmd & 0x0ff) < 20)) {
223                         ;
224                 } else {
225                         return -EINVAL;
226                 }
227         } else if (minor == BTN_MINOR) {
228                 if (((cmd & 0x0ff) >= 20) && ((cmd & 0x0ff) < 30)) {
229                         ;
230                 } else {
231                         return -EINVAL;
232                 }
233         } else {
234                 return -EINVAL;
235         }
236
237         if (cmd & 0x01) {       /* write */
238                 if (copy_from_user(&data, (int *)arg, sizeof(int))) {
239                         return -EFAULT;
240                 }
241         }
242
243         switch (cmd) {
244         case GIODRV_IOCSGIOSETADDR:     /* addres set */
245                 addr = data;
246                 break;
247
248         case GIODRV_IOCSGIODATA1:       /* write byte */
249                 ctrl_outb((unsigned char)(0x0ff & data), addr);
250                 break;
251
252         case GIODRV_IOCSGIODATA2:       /* write word */
253                 if (addr & 0x01) {
254                         return -EFAULT;
255                 }
256                 ctrl_outw((unsigned short int)(0x0ffff & data), addr);
257                 break;
258
259         case GIODRV_IOCSGIODATA4:       /* write long */
260                 if (addr & 0x03) {
261                         return -EFAULT;
262                 }
263                 ctrl_outl(data, addr);
264                 break;
265
266         case GIODRV_IOCGGIODATA1:       /* read byte */
267                 data = ctrl_inb(addr);
268                 break;
269
270         case GIODRV_IOCGGIODATA2:       /* read word */
271                 if (addr & 0x01) {
272                         return -EFAULT;
273                 }
274                 data = ctrl_inw(addr);
275                 break;
276
277         case GIODRV_IOCGGIODATA4:       /* read long */
278                 if (addr & 0x03) {
279                         return -EFAULT;
280                 }
281                 data = ctrl_inl(addr);
282                 break;
283         case GIODRV_IOCSGIO_LED:        /* write */
284                 mask = ((data & 0x00ffffff) << 8)
285                     | ((data & 0x0000ffff) << 16)
286                     | ((data & 0x000000ff) << 24);
287                 landisk_ledparam = data & (~mask);
288                 if (landisk_arch == 0) {        /* arch == landisk */
289                         landisk_ledparam &= 0x03030303;
290                         mask = (~(landisk_ledparam >> 22)) & 0x000c;
291                         landisk_ledparam |= mask;
292                 } else {                        /* arch == usl-5p */
293                         mask = (landisk_ledparam >> 24) & 0x0001;
294                         landisk_ledparam |= mask;
295                         landisk_ledparam &= 0x007f7f7f;
296                 }
297                 landisk_ledparam |= 0x80;
298                 break;
299         case GIODRV_IOCGGIO_LED:        /* read */
300                 data = landisk_ledparam;
301                 if (landisk_arch == 0) {        /* arch == landisk */
302                         data &= 0x03030303;
303                 } else {                        /* arch == usl-5p */
304                         ;
305                 }
306                 data &= (~0x080);
307                 break;
308         case GIODRV_IOCSGIO_BUZZER:     /* write */
309                 landisk_buzzerparam = data;
310                 landisk_ledparam |= 0x80;
311                 break;
312         case GIODRV_IOCGGIO_LANDISK:    /* read */
313                 data = landisk_arch & 0x01;
314                 break;
315         case GIODRV_IOCGGIO_BTN:        /* read */
316                 data = (0x0ff & ctrl_inb(PA_PWRINT_CLR));
317                 data <<= 8;
318                 data |= (0x0ff & ctrl_inb(PA_IMASK));
319                 data <<= 8;
320                 data |= (0x0ff & landisk_btn);
321                 data <<= 8;
322                 data |= (0x0ff & (~ctrl_inb(PA_STATUS)));
323                 break;
324         case GIODRV_IOCSGIO_BTNPID:     /* write */
325                 landisk_btnctrlpid = data;
326                 landisk_btn = 0;
327                 if (irq_desc[IRQ_BUTTON].depth) {
328                         enable_irq(IRQ_BUTTON);
329                 }
330                 if (irq_desc[IRQ_POWER].depth) {
331                         enable_irq(IRQ_POWER);
332                 }
333                 break;
334         case GIODRV_IOCGGIO_BTNPID:     /* read */
335                 data = landisk_btnctrlpid;
336                 break;
337         default:
338                 return -EFAULT;
339                 break;
340         }
341
342         if ((cmd & 0x01) == 0) {        /* read */
343                 if (copy_to_user((int *)arg, &data, sizeof(int))) {
344                         return -EFAULT;
345                 }
346         }
347         return 0;
348 }