1/*
2 * GPIO char driver
3 *
4 * Copyright 2006, Broadcom Corporation
5 * All Rights Reserved.
6 *
7 * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY
8 * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM
9 * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
10 * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE.
11 *
12 * $Id: gpio.c,v 1.1.1.1 2008/10/15 03:26:06 james26_jang Exp $
13 */
14
15#include <linux/module.h>
16#include <linux/init.h>
17#include <linux/fs.h>
18#include <linux/miscdevice.h>
19#include <asm/uaccess.h>
20
21#include <typedefs.h>
22#include <bcmutils.h>
23#include <sbutils.h>
24#include <bcmdevs.h>
25
26static sb_t *gpio_sbh;
27static int gpio_major;
28static devfs_handle_t gpio_dir;
29static struct {
30	char *name;
31	devfs_handle_t handle;
32} gpio_file[] = {
33	{ "in", NULL },
34	{ "out", NULL },
35	{ "outen", NULL },
36	{ "control", NULL }
37};
38
39static int
40gpio_open(struct inode *inode, struct file * file)
41{
42	if (MINOR(inode->i_rdev) > ARRAYSIZE(gpio_file))
43		return -ENODEV;
44
45	MOD_INC_USE_COUNT;
46	return 0;
47}
48
49static int
50gpio_release(struct inode *inode, struct file * file)
51{
52	MOD_DEC_USE_COUNT;
53	return 0;
54}
55
56static ssize_t
57gpio_read(struct file *file, char *buf, size_t count, loff_t *ppos)
58{
59	u32 val;
60
61	switch (MINOR(file->f_dentry->d_inode->i_rdev)) {
62	case 0:
63		val = sb_gpioin(gpio_sbh);
64		break;
65	case 1:
66		val = sb_gpioout(gpio_sbh, 0, 0);
67		break;
68	case 2:
69		val = sb_gpioouten(gpio_sbh, 0, 0);
70		break;
71	case 3:
72		val = sb_gpiocontrol(gpio_sbh, 0, 0);
73		break;
74	default:
75		return -ENODEV;
76	}
77
78	if (put_user(val, (u32 *) buf))
79		return -EFAULT;
80
81	return sizeof(val);
82}
83
84static ssize_t
85gpio_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
86{
87	u32 val;
88
89	if (get_user(val, (u32 *) buf))
90		return -EFAULT;
91
92	switch (MINOR(file->f_dentry->d_inode->i_rdev)) {
93	case 0:
94		return -EACCES;
95	case 1:
96		sb_gpioout(gpio_sbh, ~0, val);
97		break;
98	case 2:
99		sb_gpioouten(gpio_sbh, ~0, val);
100		break;
101	case 3:
102		sb_gpiocontrol(gpio_sbh, ~0, val);
103		break;
104	default:
105		return -ENODEV;
106	}
107
108	return sizeof(val);
109}
110
111static struct file_operations gpio_fops = {
112	owner:		THIS_MODULE,
113	open:		gpio_open,
114	release:	gpio_release,
115	read:		gpio_read,
116	write:		gpio_write,
117};
118
119static int __init
120gpio_init(void)
121{
122	int i;
123
124	if (!(gpio_sbh = sb_kattach(SB_OSH)))
125		return -ENODEV;
126
127	sb_gpiosetcore(gpio_sbh);
128
129	if ((gpio_major = devfs_register_chrdev(0, "gpio", &gpio_fops)) < 0)
130		return gpio_major;
131
132	gpio_dir = devfs_mk_dir(NULL, "gpio", NULL);
133
134	for (i = 0; i < ARRAYSIZE(gpio_file); i++) {
135		gpio_file[i].handle = devfs_register(gpio_dir,
136						     gpio_file[i].name,
137						     DEVFS_FL_DEFAULT, gpio_major, i,
138						     S_IFCHR | S_IRUGO | S_IWUGO,
139						     &gpio_fops, NULL);
140	}
141
142	return 0;
143}
144
145static void __exit
146gpio_exit(void)
147{
148	int i;
149
150	for (i = 0; i < ARRAYSIZE(gpio_file); i++)
151		devfs_unregister(gpio_file[i].handle);
152	devfs_unregister(gpio_dir);
153	devfs_unregister_chrdev(gpio_major, "gpio");
154	sb_detach(gpio_sbh);
155}
156
157module_init(gpio_init);
158module_exit(gpio_exit);
159