/* * mx21_gpio.c */ #include #include #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 INTERRUPT 107 #define mx21gpio_VERSION "1.0" #define mx21gpio_MAJOR 240 #define mx21gpio_NAME "gpio" #define GPIO_BASE 0x10015000 #define GPIO_MASK 0x00000fff #define GPIO_SIZE 0x600 #define GPIOB_DDIR 64 #define GPIOB_OCR1 65 #define GPIOB_DATA 71 #define GPIOB_GIUS 72 #define GPIOB_IMR 76 #define GPIO_VAL_DDIR 0x00000400 #define GPIO_VAL_OCR1 0x00F00000 #define GPIO_VAL_D1 0x00000400 #define GPIO_VAL_D0 0x00000000 #define GPIO_VAL_GIUS 0x00000C00 #define GPIO_VAL_IMR 0x00000800 #define COMMAND_MASK 0x80000000 #undef DEBUG volatile unsigned int *gpio_ptr; unsigned int offset; int interruptcount = 0; static struct proc_dir_entry *interrupt_arm_file; static struct fasync_struct *fasync_mx21_queue ; DECLARE_WAIT_QUEUE_HEAD(mx21gpio_wait); void interrupt_interrupt_arm(int irq, void *dev_id, struct pt_regs *regs) { interruptcount++; printk(KERN_INFO "\nmx21_gpio: Interrupt detected in kernel \n"); /* Signal the user application that an interupt occured */ kill_fasync(&fasync_mx21_queue, SIGIO, POLL_IN); } static int proc_read_interrupt_arm(char *page, char **start, off_t off, int count, int *eof, void *data) { int len; len = sprintf(page, "Total number of interrupts %19i\n", interruptcount); return len; } 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_fasync1 (int fd, struct file *filp, int on) { printk(KERN_INFO "\nmx21_gpio: Inside mx21_fasync \n"); return fasync_helper(fd, filp, on, &fasync_mx21_queue); } static int mx21gpio_ioctl1(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { int retval = 0; int input; unsigned long value; unsigned int command_type; unsigned int offset; volatile unsigned int *access_addr; 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; 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 access_addr = gpio_ptr + GPIOB_DATA; input = *(unsigned int *)arg; if(!access_ok(VERIFY_WRITE, (unsigned int *)arg, sizeof(int))) return -EFAULT; if (input == 1001) { // Assert PB10 value = GPIO_VAL_D1; printk("\nmx21-1001: Write value %08x to GPIO\n", (unsigned int)value); writel(value, access_addr); // writel(*(unsigned int *)value,(volatile unsigned int *)access_addr); } if (input == 1000) { // Deassert PB10 value = GPIO_VAL_D0; printk("\nmx21-1000: Write value %08x to GPIO\n", (unsigned int)value); writel(value, access_addr); // writel(*(unsigned int *)value,(volatile unsigned int *)access_addr); } 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 = mx21gpio_fasync1, .lock = NULL, .readv = NULL, .writev = NULL, }; // initialize module static int __init mx21gpio_init_module (void) { int rv = 0; unsigned long arg = 0; volatile unsigned int *access_addr; printk("i.MX21 GPIO Interface Module\n"); printk(KERN_INFO "\nmx21_gpio: i.MX21 GPIO Driver Loading.\n"); printk(KERN_INFO "mx21_gpio: Using Major Number %d on %s\n", mx21gpio_MAJOR, mx21gpio_NAME); if (register_chrdev(mx21gpio_MAJOR, mx21gpio_NAME, &mx21gpio_fops)) { printk("mx21_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 "mx21_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 "mx21_gpio: Unable to map GPIO.\n"); release_mem_region(GPIO_BASE, GPIO_SIZE); return -EBUSY; } printk("mx21_gpio: %08x size %08x mapped to %08x\n", GPIO_BASE, GPIO_SIZE, (unsigned int)gpio_ptr); // Set Direction register on PB10 = output, PB11 = input access_addr = gpio_ptr + GPIOB_DDIR; arg = readl(access_addr); arg = arg | GPIO_VAL_DDIR; writel(arg, access_addr); // Set OCR1 Register access_addr = gpio_ptr + GPIOB_OCR1; arg = readl((volatile unsigned int *)access_addr); arg = arg | GPIO_VAL_OCR1; writel(arg, access_addr); // Set GIUS Register access_addr = gpio_ptr + GPIOB_GIUS; arg = readl(access_addr); arg = arg | GPIO_VAL_GIUS; writel(arg, access_addr); // Set IMR Register access_addr = gpio_ptr + GPIOB_IMR; arg = readl(access_addr); arg = arg | GPIO_VAL_IMR; writel(arg, access_addr); interrupt_arm_file = create_proc_entry("interrupt_arm", 0444, NULL); if(interrupt_arm_file == NULL) { printk("mx21_gpio: create /proc entry returned NULL. ABORTING!\n"); return -ENOMEM; } interrupt_arm_file->data = NULL; interrupt_arm_file->read_proc = &proc_read_interrupt_arm; interrupt_arm_file->write_proc = NULL; interrupt_arm_file->owner = THIS_MODULE; /* request interrupt from linux */ rv = request_irq(INTERRUPT, interrupt_interrupt_arm, SA_TRIGGER_RISING, "interrupt_arm", NULL); if ( rv ) { printk("mx21_gpio: Can't get interrupt %d\n", INTERRUPT); goto no_interrupt_arm; } /* everything initialized */ printk(KERN_INFO "mx21_gpio: %s %s Initialized\n", mx21gpio_NAME, mx21gpio_VERSION); return 0; /* remove the proc entry on error */ no_interrupt_arm: remove_proc_entry("interrupt_arm", NULL); return -EBUSY; } static void __exit mx21gpio_cleanup_module (void) { free_irq(INTERRUPT,NULL); iounmap((void *)gpio_ptr); release_mem_region(GPIO_BASE, GPIO_SIZE); printk("mx21_gpio: Device released.\n"); unregister_chrdev (mx21gpio_MAJOR, mx21gpio_NAME); remove_proc_entry("interrupt_arm", NULL); printk(KERN_INFO "mx21_gpio: %s %s removed\n", mx21gpio_NAME, mx21gpio_VERSION); } module_init(mx21gpio_init_module); module_exit(mx21gpio_cleanup_module); MODULE_AUTHOR("tarun@virtualcogs.com"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("iMX21 GPIO Device Driver");