1/* 2 * Linux Broadcom BCM47xx GPIO char driver 3 * 4 * Copyright (C) 2013, 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 401759 2013-05-13 16:08:08Z $ 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 58#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) 59static long 60gpio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 61{ 62#else 63static int 64gpio_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) 65{ 66#endif /* linux-2.6.22 */ 67 struct gpio_ioctl gpioioc; 68 69 if (copy_from_user(&gpioioc, (struct gpio_ioctl *)arg, sizeof(struct gpio_ioctl))) 70 return -EFAULT; 71 72 switch (cmd) { 73 case GPIO_IOC_RESERVE: 74 gpioioc.val = si_gpioreserve(gpio_sih, gpioioc.mask, GPIO_APP_PRIORITY); 75 break; 76 case GPIO_IOC_RELEASE: 77 /* 78 * releasing the gpio doesn't change the current 79 * value on the GPIO last write value 80 * persists till some one overwrites it 81 */ 82 gpioioc.val = si_gpiorelease(gpio_sih, gpioioc.mask, GPIO_APP_PRIORITY); 83 break; 84 case GPIO_IOC_OUT: 85 gpioioc.val = si_gpioout(gpio_sih, gpioioc.mask, gpioioc.val, 86 GPIO_APP_PRIORITY); 87 break; 88 case GPIO_IOC_OUTEN: 89 gpioioc.val = si_gpioouten(gpio_sih, gpioioc.mask, gpioioc.val, 90 GPIO_APP_PRIORITY); 91 break; 92 case GPIO_IOC_IN: 93 gpioioc.val = si_gpioin(gpio_sih); 94 break; 95 default: 96 break; 97 } 98 if (copy_to_user((struct gpio_ioctl *)arg, &gpioioc, sizeof(struct gpio_ioctl))) 99 return -EFAULT; 100 101 return 0; 102 103} 104static struct file_operations gpio_fops = { 105 owner: THIS_MODULE, 106 open: gpio_open, 107 release: gpio_release, 108#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) 109 unlocked_ioctl: gpio_ioctl 110#else 111 ioctl: gpio_ioctl 112#endif 113}; 114 115static int __init 116gpio_init(void) 117{ 118 if (!(gpio_sih = si_kattach(SI_OSH))) 119 return -ENODEV; 120 121#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) 122 if ((gpio_major = register_chrdev(0, "gpio", &gpio_fops)) < 0) 123#else 124 if ((gpio_major = devfs_register_chrdev(0, "gpio", &gpio_fops)) < 0) 125#endif 126 return gpio_major; 127#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) 128 gpiodev_class = class_create(THIS_MODULE, "gpio"); 129 if (IS_ERR(gpiodev_class)) { 130 printk("Error creating gpio class\n"); 131 return -1; 132 } 133 134 /* Add the device gpio0 */ 135#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) 136 device_create(gpiodev_class, NULL, MKDEV(gpio_major, 0), NULL, "gpio", 0); 137#else 138 class_device_create(gpiodev_class, NULL, MKDEV(gpio_major, 0), NULL, "gpio"); 139#endif /* linux-2.6.36 */ 140#else 141 gpiodev_handle = devfs_register(NULL, "gpio", DEVFS_FL_DEFAULT, 142 gpio_major, 0, S_IFCHR | S_IRUGO | S_IWUGO, 143 &gpio_fops, NULL); 144#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) */ 145 146 return 0; 147} 148 149static void __exit 150gpio_exit(void) 151{ 152#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) 153 if (gpiodev_class != NULL) { 154#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) 155 device_destroy(gpiodev_class, MKDEV(gpio_major, 0)); 156#else 157 class_device_destroy(gpiodev_class, MKDEV(gpio_major, 0)); 158#endif 159 class_destroy(gpiodev_class); 160 } 161 162 gpiodev_class = NULL; 163 if (gpio_major >= 0) 164 unregister_chrdev(gpio_major, "gpio"); 165#else 166 if (gpiodev_handle != NULL) 167 devfs_unregister(gpiodev_handle); 168 gpiodev_handle = NULL; 169 devfs_unregister_chrdev(gpio_major, "gpio"); 170#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) */ 171 si_detach(gpio_sih); 172} 173 174module_init(gpio_init); 175module_exit(gpio_exit); 176