/* * mx21_gpio.c * * author: Tarun Tuli * Created: Sept 22, 2007 * Copyright: 2007 Virtual Cogs Embedded Systems Inc. * * Provides access to the GPIO space on the i.MX21 from userspace thru IOCTL * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) #include #endif #define mx21gpio_MAJOR 240 #define mx21gpio_NAME "gpio" #define GPIO_BASE 0x10015000 #define GPIO_MASK 0x00000fff #define GPIO_SIZE 0x600 #define COMMAND_MASK 0x80000000 #undef DEBUG volatile unsigned int *gpio_ptr; unsigned int offset; DECLARE_WAIT_QUEUE_HEAD(mx21gpio_wait); static int mx21gpio_open1 (struct inode *inode, struct file *file) { return 0; } static int mx21gpio_release1 (struct inode *inode, struct file *file) { return 0; } static int mx21gpio_ioctl1(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { int retval = 0; unsigned int *access_addr; unsigned int command_type; unsigned int offset; command_type = 0x80000000ul; // Set the offset for register accesses command_type = COMMAND_MASK & cmd; offset = ~COMMAND_MASK & cmd & GPIO_MASK; if(offset > GPIO_SIZE) retval=-EINVAL; offset = offset >> 2; // adjust to 32bit boundary access_addr = (unsigned int *)(gpio_ptr + offset); #ifdef DEBUG printk("GPIO: Command Type: %d\n Access Address: 0x%.4x (GPIO_PTR: 0x%.4x, Offset: 0x%.4x)\n Arg: 0x%.4x\n Cmd: 0x%.4x \n", command_type, access_addr, gpio_ptr, offset, arg, cmd); #endif switch(command_type) { case 0: //read if(!access_ok(VERIFY_READ, (unsigned int *)arg, sizeof(int))) return -EFAULT; *(unsigned int *)arg = readl((volatile unsigned int *)&gpio_ptr[offset]); break; case COMMAND_MASK: //write if(!access_ok(VERIFY_WRITE, (unsigned int *)arg, sizeof(int))) return -EFAULT; writel(*(unsigned int *)arg,(volatile unsigned int *)&gpio_ptr[offset]); break; default: retval = -EINVAL; } return retval; } // define which file operations are supported struct file_operations mx21gpio_fops = { .owner = THIS_MODULE, .llseek = NULL, .read = NULL, .write = NULL, .readdir = NULL, .poll = NULL, .ioctl = mx21gpio_ioctl1, .mmap = NULL, .open = mx21gpio_open1, .flush = NULL, .release = mx21gpio_release1, .fsync = NULL, .fasync = NULL, .lock = NULL, .readv = NULL, .writev = NULL, }; // initialize module static int __init mx21gpio_init_module (void) { printk("i.MX21 GPIO Interface Module\n"); printk(KERN_INFO "\nGPIO: i.MX21 GPIO Driver Loading.\n"); printk(KERN_INFO "GPIO: Using Major Number %d on %s\n", mx21gpio_MAJOR, mx21gpio_NAME); if (register_chrdev(mx21gpio_MAJOR, mx21gpio_NAME, &mx21gpio_fops)) { printk("GPIO: unable to get major %d. ABORTING!\n", mx21gpio_MAJOR); return -EBUSY; } // Perform Memory REMAP if (check_mem_region(GPIO_BASE, GPIO_SIZE)) { printk(KERN_ERR "GPIO: Unable to acquire GPIO address.\n"); return -EBUSY; } request_mem_region(GPIO_BASE, GPIO_SIZE, mx21gpio_NAME); gpio_ptr = (volatile unsigned int *)__ioremap(GPIO_BASE, GPIO_SIZE, 0); if (!gpio_ptr) { printk(KERN_ERR "GPIO: Unable to map GPIO.\n"); release_mem_region(GPIO_BASE, GPIO_SIZE); return -EBUSY; } printk("GPIO: 0x%.4x size 0x%.4x mapped to 0x%.4x\n", GPIO_BASE, GPIO_SIZE, (unsigned int)gpio_ptr); return 0; } static void __exit mx21gpio_cleanup_module (void) { iounmap((void *)gpio_ptr); release_mem_region(GPIO_BASE, GPIO_SIZE); printk("GPIO: Device released.\n"); unregister_chrdev (mx21gpio_MAJOR, mx21gpio_NAME); } module_init(mx21gpio_init_module); module_exit(mx21gpio_cleanup_module); MODULE_AUTHOR("tarun@virtualcogs.com"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("iMX21 GPIO Device Driver");