1/*
2 * arch/sh/boards/landisk/gio.c - driver for landisk
3 *
4 * This driver will also support the I-O DATA Device, Inc. LANDISK Board.
5 * LANDISK and USL-5P Button, LED and GIO driver drive function.
6 *
7 *   Copylight (C) 2006 kogiidena
8 *   Copylight (C) 2002 Atom Create Engineering Co., Ltd. *
9 *
10 * This file is subject to the terms and conditions of the GNU General Public
11 * License.  See the file "COPYING" in the main directory of this archive
12 * for more details.
13 *
14 */
15#include <linux/module.h>
16#include <linux/init.h>
17#include <linux/kdev_t.h>
18#include <linux/cdev.h>
19#include <linux/fs.h>
20#include <asm/io.h>
21#include <asm/uaccess.h>
22#include <mach-landisk/mach/gio.h>
23#include <mach-landisk/mach/iodata_landisk.h>
24
25#define DEVCOUNT                4
26#define GIO_MINOR	        2	/* GIO minor no. */
27
28static dev_t dev;
29static struct cdev *cdev_p;
30static int openCnt;
31
32static int gio_open(struct inode *inode, struct file *filp)
33{
34	int minor;
35	int ret = -ENOENT;
36
37	preempt_disable();
38	minor = MINOR(inode->i_rdev);
39	if (minor < DEVCOUNT) {
40		if (openCnt > 0) {
41			ret = -EALREADY;
42		} else {
43			openCnt++;
44			ret = 0;
45		}
46	}
47	preempt_enable();
48	return ret;
49}
50
51static int gio_close(struct inode *inode, struct file *filp)
52{
53	int minor;
54
55	minor = MINOR(inode->i_rdev);
56	if (minor < DEVCOUNT) {
57		openCnt--;
58	}
59	return 0;
60}
61
62static long gio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
63{
64	unsigned int data;
65	static unsigned int addr = 0;
66
67	if (cmd & 0x01) {	/* write */
68		if (copy_from_user(&data, (int *)arg, sizeof(int))) {
69			return -EFAULT;
70		}
71	}
72
73	switch (cmd) {
74	case GIODRV_IOCSGIOSETADDR:	/* address set */
75		addr = data;
76		break;
77
78	case GIODRV_IOCSGIODATA1:	/* write byte */
79		__raw_writeb((unsigned char)(0x0ff & data), addr);
80		break;
81
82	case GIODRV_IOCSGIODATA2:	/* write word */
83		if (addr & 0x01) {
84			return -EFAULT;
85		}
86		__raw_writew((unsigned short int)(0x0ffff & data), addr);
87		break;
88
89	case GIODRV_IOCSGIODATA4:	/* write long */
90		if (addr & 0x03) {
91			return -EFAULT;
92		}
93		__raw_writel(data, addr);
94		break;
95
96	case GIODRV_IOCGGIODATA1:	/* read byte */
97		data = __raw_readb(addr);
98		break;
99
100	case GIODRV_IOCGGIODATA2:	/* read word */
101		if (addr & 0x01) {
102			return -EFAULT;
103		}
104		data = __raw_readw(addr);
105		break;
106
107	case GIODRV_IOCGGIODATA4:	/* read long */
108		if (addr & 0x03) {
109			return -EFAULT;
110		}
111		data = __raw_readl(addr);
112		break;
113	default:
114		return -EFAULT;
115		break;
116	}
117
118	if ((cmd & 0x01) == 0) {	/* read */
119		if (copy_to_user((int *)arg, &data, sizeof(int))) {
120			return -EFAULT;
121		}
122	}
123	return 0;
124}
125
126static const struct file_operations gio_fops = {
127	.owner = THIS_MODULE,
128	.open = gio_open,	/* open */
129	.release = gio_close,	/* release */
130	.unlocked_ioctl = gio_ioctl,
131	.llseek = noop_llseek,
132};
133
134static int __init gio_init(void)
135{
136	int error;
137
138	printk(KERN_INFO "gio: driver initialized\n");
139
140	openCnt = 0;
141
142	if ((error = alloc_chrdev_region(&dev, 0, DEVCOUNT, "gio")) < 0) {
143		printk(KERN_ERR
144		       "gio: Couldn't alloc_chrdev_region, error=%d\n",
145		       error);
146		return 1;
147	}
148
149	cdev_p = cdev_alloc();
150	cdev_p->ops = &gio_fops;
151	error = cdev_add(cdev_p, dev, DEVCOUNT);
152	if (error) {
153		printk(KERN_ERR
154		       "gio: Couldn't cdev_add, error=%d\n", error);
155		return 1;
156	}
157
158	return 0;
159}
160
161static void __exit gio_exit(void)
162{
163	cdev_del(cdev_p);
164	unregister_chrdev_region(dev, DEVCOUNT);
165}
166
167module_init(gio_init);
168module_exit(gio_exit);
169
170MODULE_LICENSE("GPL");
171