1/*
2 * Linux Broadcom BCM47xx GPIO char driver
3 *
4 * Copyright (C) 2010, Broadcom Corporation. All Rights Reserved.
5 *
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
13 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
15 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
16 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 * $Id: linux_gpio.c,v 1.10 2008/03/28 19:25:35 Exp $
18 *
19 */
20#include <linux/module.h>
21#include <linux/init.h>
22#include <linux/fs.h>
23#include <linux/miscdevice.h>
24#include <asm/uaccess.h>
25
26#include <typedefs.h>
27#include <bcmutils.h>
28#include <siutils.h>
29#include <bcmdevs.h>
30
31#include <linux_gpio.h>
32
33/* handle to the sb */
34static si_t *gpio_sih;
35
36/* major number assigned to the device and device handles */
37static int gpio_major;
38#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
39static struct class *gpiodev_class = NULL;
40#else
41devfs_handle_t gpiodev_handle;
42#endif
43
44static int
45gpio_open(struct inode *inode, struct file * file)
46{
47	MOD_INC_USE_COUNT;
48	return 0;
49}
50
51static int
52gpio_release(struct inode *inode, struct file * file)
53{
54	MOD_DEC_USE_COUNT;
55	return 0;
56}
57
58static int
59gpio_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
60{
61	struct gpio_ioctl gpioioc;
62
63	if (copy_from_user(&gpioioc, (struct gpio_ioctl *)arg, sizeof(struct gpio_ioctl)))
64		return -EFAULT;
65
66	switch (cmd) {
67		case GPIO_IOC_RESERVE:
68			gpioioc.val = si_gpioreserve(gpio_sih, gpioioc.mask, GPIO_APP_PRIORITY);
69			break;
70		case GPIO_IOC_RELEASE:
71			/*
72			 * releasing the gpio doesn't change the current
73			 * value on the GPIO last write value
74			 * persists till some one overwrites it
75			 */
76			gpioioc.val = si_gpiorelease(gpio_sih, gpioioc.mask, GPIO_APP_PRIORITY);
77			break;
78		case GPIO_IOC_OUT:
79			gpioioc.val = si_gpioout(gpio_sih, gpioioc.mask, gpioioc.val,
80			                         GPIO_APP_PRIORITY);
81			break;
82		case GPIO_IOC_OUTEN:
83			gpioioc.val = si_gpioouten(gpio_sih, gpioioc.mask, gpioioc.val,
84			                           GPIO_APP_PRIORITY);
85			break;
86		case GPIO_IOC_IN:
87			gpioioc.val = si_gpioin(gpio_sih);
88			break;
89		default:
90			break;
91	}
92	if (copy_to_user((struct gpio_ioctl *)arg, &gpioioc, sizeof(struct gpio_ioctl)))
93		return -EFAULT;
94
95	return 0;
96
97}
98static struct file_operations gpio_fops = {
99	owner:		THIS_MODULE,
100	open:		gpio_open,
101	release:	gpio_release,
102	ioctl:		gpio_ioctl
103};
104
105static int __init
106gpio_init(void)
107{
108	if (!(gpio_sih = si_kattach(SI_OSH)))
109		return -ENODEV;
110
111#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
112	if ((gpio_major = register_chrdev(0, "gpio", &gpio_fops)) < 0)
113#else
114	if ((gpio_major = devfs_register_chrdev(0, "gpio", &gpio_fops)) < 0)
115#endif
116		return gpio_major;
117#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
118	gpiodev_class = class_create(THIS_MODULE, "gpio");
119	if (IS_ERR(gpiodev_class)) {
120		printk("Error creating gpio class\n");
121		return -1;
122	}
123
124	/* Add the device gpio0 */
125	class_device_create(gpiodev_class, NULL, MKDEV(gpio_major, 0), NULL, "gpio");
126#else
127	gpiodev_handle = devfs_register(NULL, "gpio", DEVFS_FL_DEFAULT,
128	                                gpio_major, 0, S_IFCHR | S_IRUGO | S_IWUGO,
129	                                &gpio_fops, NULL);
130#endif
131
132	return 0;
133}
134
135static void __exit
136gpio_exit(void)
137{
138#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
139	if (gpiodev_class != NULL) {
140		class_device_destroy(gpiodev_class, MKDEV(gpio_major, 0));
141		class_destroy(gpiodev_class);
142	}
143
144	gpiodev_class = NULL;
145	if (gpio_major >= 0)
146		unregister_chrdev(gpio_major, "gpio");
147#else
148	if (gpiodev_handle != NULL)
149		devfs_unregister(gpiodev_handle);
150	gpiodev_handle = NULL;
151	devfs_unregister_chrdev(gpio_major, "gpio");
152#endif
153	si_detach(gpio_sih);
154}
155
156module_init(gpio_init);
157module_exit(gpio_exit);
158