/****************************************************************************** * * gpio_driver.c * Copyright Qstreams Networks 2006 * * This file contains a simple Linux device driver for accessing GPIO ports * It is originally targeted for the MPC8349 PowerQUICC II Pro processor, * although with simple modifications it should work with lots of CPUs. Note * that all CPU accesses are big-endian. * * History: * 04/11/2006 BAW Initial Creation * *****************************************************************************/ #include #include #include #include #include #include #include #include "gpio.h" struct gpio_dev { struct gpio_reg *regs; struct cdev cdev; }; MODULE_AUTHOR("Ben Warren"); MODULE_LICENSE("GPL"); static int gpio_major_num; static struct gpio_dev *gpio_devices; static spinlock_t gpioLock[GPIO_NUM_BANKS]; extern phys_addr_t get_immrbase(void); static u32 readGpioReg(u8 bank, u32 * addr) { u32 value; spin_lock(&gpioLock[bank]); value = in_be32(addr); spin_unlock(&gpioLock[bank]); return value; } static void writeGpioReg(u8 bank, u32 * addr, u32 val, u32 mask) { spin_lock(&gpioLock[bank]); out_be32(addr, (in_be32(addr) & ~mask) | val); spin_unlock(&gpioLock[bank]); } /***** * Kernel-mode GPIO access functions. * DO NOT CALL FROM USERSPACE *****/ int kgpio_set_dir(u8 bank, u8 bit, u8 isOutput) { u32 value = GPIO_BIT(bit); if ((bank >= GPIO_NUM_BANKS) || !gpio_devices) { return -ENODEV; } writeGpioReg(bank, &gpio_devices[bank].regs->gpxdir, isOutput ? value : 0, value); return 0; } EXPORT_SYMBOL(kgpio_set_dir); int kgpio_get_dir(u8 bank, u8 bit) { u32 value; if ((bank >= GPIO_NUM_BANKS) || !gpio_devices) { return -ENODEV; } value = readGpioReg(bank, &gpio_devices[bank].regs->gpxdir); return (value & GPIO_BIT(bit) ? 1 : 0); } EXPORT_SYMBOL(kgpio_get_dir); int kgpio_set_data(u8 bank, u8 bit, u8 isHigh) { u32 value = GPIO_BIT(bit); if ((bank >= GPIO_NUM_BANKS) || !gpio_devices) { return -ENODEV; } writeGpioReg(bank, &gpio_devices[bank].regs->gpxdat, isHigh ? value : 0, value); return 0; } EXPORT_SYMBOL(kgpio_set_data); int kgpio_get_data(u8 bank, u8 bit) { u32 value; if ((bank >= GPIO_NUM_BANKS) || !gpio_devices) { return -ENODEV; } value = readGpioReg(bank, &gpio_devices[bank].regs->gpxdat); return (value & GPIO_BIT(bit) ? 1 : 0); } EXPORT_SYMBOL(kgpio_get_data); int gpio_open(struct inode *inode, struct file *filp) { struct gpio_dev *dev; /* Set things up so other methods can access private data */ dev = container_of(inode->i_cdev, struct gpio_dev, cdev); filp->private_data = dev; return 0; } int gpio_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { int rc = 0; gpioDataAccess gpioData = { 0, 0 }; struct gpio_dev *dev; u8 bank; /* Check that the caller really wanted GPIO */ if (_IOC_TYPE(cmd) != GPIO_IOC_MAGIC) return -ENOTTY; if (_IOC_NR(cmd) > GPIO_IOC_MAXNR) return -ENOTTY; /* Check access controls */ if (_IOC_DIR(cmd) & _IOC_READ) rc = !access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd)); else if (_IOC_DIR(cmd) & _IOC_WRITE) rc = !access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd)); if (rc) return -EFAULT; bank = (u8) iminor(inode); dev = filp->private_data; switch (cmd) { /* Direction Register access */ case GPIO_IOC_GET_DIR: return readGpioReg(bank, &dev->regs->gpxdir); case GPIO_IOC_SET_DIR: if (copy_from_user(&gpioData, (const void __user *)arg, sizeof(gpioDataAccess))) { printk(KERN_ERR "copy_from_user failed\n"); return -EFAULT; } writeGpioReg(bank, &dev->regs->gpxdir, gpioData.value, gpioData.mask); break; /* Data Register access */ case GPIO_IOC_GET_DATA: return readGpioReg(bank, &dev->regs->gpxdat); case GPIO_IOC_SET_DATA: if (copy_from_user(&gpioData, (const void __user *)arg, sizeof(gpioDataAccess))) { printk(KERN_ERR "copy_from_user failed\n"); return -EFAULT; } writeGpioReg(bank, &dev->regs->gpxdat, gpioData.value, gpioData.mask); break; default: return -ENOTTY; } return rc; } /* File operations structure. Pretty limited on this device */ struct file_operations gpio_fops = { .owner = THIS_MODULE, .open = gpio_open, .ioctl = gpio_ioctl, }; static void gpio_cleanup(void) { int i; u32 addr; dev_t devno = MKDEV(gpio_major_num, 0); if (gpio_devices) { for (i = 0; i < GPIO_NUM_BANKS; i++) { cdev_del(&gpio_devices[i].cdev); } iounmap(gpio_devices); } addr = get_immrbase() + GPIO1_IO_OFFSET; release_mem_region(addr, sizeof(struct gpio_reg)); addr = get_immrbase() + GPIO2_IO_OFFSET; release_mem_region(addr, sizeof(struct gpio_reg)); unregister_chrdev_region(devno, GPIO_NUM_BANKS); } static void gpio_setup_cdev(struct gpio_dev *dev, int index) { int rc, devno = MKDEV(gpio_major_num, index); cdev_init(&dev->cdev, &gpio_fops); dev->cdev.owner = THIS_MODULE; dev->cdev.ops = &gpio_fops; if ((rc = cdev_add(&dev->cdev, devno, 1))) printk(KERN_CRIT "Error %d adding gpio%d", rc, index); } static int gpio_init(void) { int i, rc; dev_t dev = 0; u32 addr; /* Grab a dynamic major number and GPIO_NUM_BANKS minors */ rc = alloc_chrdev_region(&dev, 0, GPIO_NUM_BANKS, "gpio"); gpio_major_num = MAJOR(dev); if (rc < 0) { printk(KERN_CRIT "GPIO: couldn't get major number\n"); return rc; } if (!(gpio_devices = kmalloc(GPIO_NUM_BANKS * sizeof(struct gpio_dev), GFP_KERNEL))) { gpio_cleanup(); return -ENOMEM; } for (i = 0; i < GPIO_NUM_BANKS; i++) { spin_lock_init(&gpioLock[i]); gpio_setup_cdev(&gpio_devices[i], i); addr = get_immrbase() + (i == 0 ? GPIO1_IO_OFFSET : GPIO2_IO_OFFSET); if (!request_mem_region(addr, sizeof(struct gpio_reg), "gpio")) { printk(KERN_CRIT "gpio: I/O memory request failed\n"); return -ENODEV; } gpio_devices[i].regs = (struct gpio_reg *)ioremap_nocache(addr, sizeof (struct gpio_reg)); if (!gpio_devices[i].regs) { printk(KERN_CRIT "gpio: can't remap I/O memory\n"); return -ENODEV; } } return 0; } module_init(gpio_init); module_exit(gpio_cleanup);