⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 at91_gpio.c

📁 AT91RM9200的嵌入式LINUX下GPIO驱动
💻 C
字号:
/* $Id: at91_gpio.c,v 1.15 2002/05/06 13:19:13 $ * *  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  SOFTWARE  IS PROVIDED   ``AS  IS'' AND   ANY  EXPRESS OR IMPLIED *  WARRANTIES,   INCLUDING, BUT NOT  LIMITED  TO, THE IMPLIED WARRANTIES OF *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN *  NO  EVENT  SHALL   THE AUTHOR  BE	LIABLE FOR ANY   DIRECT, INDIRECT, *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT *  NOT LIMITED   TO, PROCUREMENT OF  SUBSTITUTE GOODS  OR SERVICES; LOSS OF *  USE, DATA,  OR PROFITS; OR  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON *  ANY THEORY OF LIABILITY, WHETHER IN  CONTRACT, STRICT LIABILITY, OR TORT *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF *  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * *  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., *  675 Mass Ave, Cambridge, MA 02139, USA. *//* Emerald GPIO/PCB_ID driver * We allow anyone to open a pin and read the status of a pin and wait on a pin, * but only certain pins can perform output related functions to a pin.  Also  * give ability to read the PCB ID register.This GPIO driver is modeledon a "per pin" basis.  This is so you can open one pin and not haveto worry about another process holding the 32 bit port that holdsyour pin.  I have to credit the Atmel architecture for enabling me todo it this way.  The only place it falls apart is when it comes tointerrupts, as there is only one interrupt routine for each 32 bitport. */#include <linux/config.h>#include <linux/module.h>#include <linux/sched.h>#include <linux/slab.h>#include <linux/ioport.h>#include <linux/errno.h>#include <linux/kernel.h>#include <linux/fs.h>#include <linux/string.h>#include <linux/poll.h>#include <linux/init.h>#include <asm/io.h>#include <asm/system.h>#include <asm/arch/gpio.h>  /* get ioctl stuff for gpio */#include "at91_gpio.h"static int gpio_ioctl(struct inode *inode, struct file *file,		      unsigned int cmd, unsigned long arg);static int gpio_open(struct inode *inode, struct file *filp);static int gpio_release(struct inode *inode, struct file *filp); /* priv lookup table, so that the interrupt routine can get to the priv struct */static struct gpio_private *priv_lut[GPIO_NUM_PORTS * AT91_GPIO_PINS_PER_PORT] = {0};static char *gpio_name_lut[] = {"at91 gpioa", "at91 gpiob", "at91 gpioc", "at91 gpiod", };/* port address lookup table */static AT91PS_PIO gpio_port_addr[GPIO_NUM_PORTS] = {AT91C_BASE_PIOA, AT91C_BASE_PIOB,                           AT91C_BASE_PIOC, AT91C_BASE_PIOD};/* port pin allow table, 0 means it can't do any write oriented operation, bit 0 set means write operations allowed bit 1 set means interrupt allowed*/#define AO 0x01  /* allow output */#define AI 0x02  /* allow interrupt */static char gpio_allow[AT91_GPIO_MINOR_LAST + 1] = {   0,  0,  0,  0,  0,  0,  0,  0,   /* PA0 - PA7 */   0,  0,  0,  0,  0,  0,  0,  0,   /* PA8 - PA15 */   0,  0,  0,  0, AO, AO,  0,  0,   /* PA16 - PA23 */  AI,  0,  0,  0, AO,  0,  0,  0,   /* PA24 - PA31 */   0,  0,  0,  0,  0,  0,  0,  0,   /* PB0 - PB7 */   0,  0,  0, AO,  0,  0,  0,  0,   /* PB8 - PB15 */   0,  0,  0,  0,  0,  0, AO, AO,   /* PB16 - PB23 */  AO, AO, AO,  0,  0,  0,  0,  0,   /* PB24 - PB31 */   0,  0,  0,  0,  0,  0,  0,  0,   /* PC0 - PC7 */   0,  0,  0,  0,  0,  0, AO, AO,   /* PC8 - PC15 */   0,  0,  0,  0,  0,  0,  0,  0,   /* PC16 - PC23 */   0,  0,  0,  0,  0,  0,  0,  0,   /* PC24 - PC31 */  AO, AO,  0, AO,  0,  0, AO,  0,   /* PD0 - PD7 */   0,  0,  0,  0,  0,  0,  0,  0,   /* PD8 - PD15 */   0,  0,  0,  0,  0,  0,  0,  0,   /* PD16 - PD23 */   0,  0,  0,  0,  0,  0,  0,  0,   /* PD24 - PD31 */};static intgpio_open(struct inode *inode, struct file *filp){	struct gpio_private *priv;	int minor = MINOR(inode->i_rdev);	if (minor > AT91_GPIO_MINOR_LAST)		return -EINVAL;	priv = (struct gpio_private *)kmalloc(sizeof(struct gpio_private), 					      GFP_KERNEL);	if (!priv)		return -ENOMEM;        priv_lut[minor] = priv;	priv->minor = minor;	filp->private_data = (void *)priv;	spin_lock_init(&priv->lock1);	return 0;}static intgpio_release(struct inode *inode, struct file *filp){	struct gpio_private *priv = (struct gpio_private *)filp->private_data;        priv_lut[priv->minor] = NULL;  /* mark as not available */	kfree(priv);	return 0;}static intgpio_ioctl(struct inode *inode, struct file *file,	   unsigned int cmd, unsigned long arg){	unsigned long flags;	unsigned int mask, ret, timeout;        AT91PS_PIO p_pio;  /* pointer to the port */	unsigned int minor = MINOR(inode->i_rdev); 	struct gpio_private *priv = (struct gpio_private *)file->private_data;        if (_IOC_NR(cmd) >= AT91_GPIO_OUTPUT && !(gpio_allow[minor] & AO))  /* are we not allowed to output? */		return -EINVAL;        p_pio = gpio_port_addr[minor / AT91_GPIO_PINS_PER_PORT];  /* get port pointer */        mask = 1 << (minor % AT91_GPIO_PINS_PER_PORT);  /* get mask for this pin */	switch (cmd) {                          /* input related ioctl's */	case AT91_GPIO_PIN_DATA:  /* read pin data this pin */          return put_user(p_pio->PIO_PDSR & mask, (u32 *)arg);	case AT91_GPIO_FILTER_ENB:  /* glitch input filter enable this pin */          p_pio->PIO_IFER = mask;  /* enable it */          return 0;	case AT91_GPIO_FILTER_DIS:  /* glitch input filter disable this pin */          p_pio->PIO_IFDR = mask;  /* disble it */          return 0;	case AT91_GPIO_WAIT:  /* set up interrupt and wait on this pin */        if (!(gpio_allow[minor] & AI))  /* are we not allowed to interrupt? */		return -EINVAL;          if (get_user(timeout, (int *)arg) != 0)		return -EINVAL;          init_waitqueue_head(&priv->gpio_wait);          ret = p_pio->PIO_ISR;  /* read to reset int's for this port */	  spin_lock_irqsave(&priv->lock1, flags);  /* stop int's else we wakeup b4 we sleep */          p_pio->PIO_IER = mask;  /* enable it */	  ret = interruptible_sleep_on_timeout(&priv->gpio_wait, timeout);	  spin_unlock_irqrestore(&priv->lock1, flags);          return put_user(ret, (u32 *)arg);  /* return timeout flag */                          /* output related ioctl's */	case AT91_GPIO_OUTPUT_ENB:  /* output enable this bit */          p_pio->PIO_OER = mask;  /* enable it */          return 0;	case AT91_GPIO_OUTPUT_DIS:  /* output disable this bit */          p_pio->PIO_ODR = mask;  /* disable it */          return 0;	case AT91_GPIO_MULTI_ENB:  /* multi driver enable this pin */          p_pio->PIO_MDER = mask;  /* enable it */          return 0;	case AT91_GPIO_MULTI_DIS:  /* multi driver disable this pin */          p_pio->PIO_MDDR = mask;  /* disable it */          return 0;	case AT91_GPIO_OUTPUT_SET:  /* output set this pin */          p_pio->PIO_PER = mask;  /* enable the pio */          p_pio->PIO_SODR = mask;  /* set it */          return 0;	case AT91_GPIO_OUTPUT_CLR:  /* output clear this pin */          p_pio->PIO_PER = mask;  /* enable the pio */          p_pio->PIO_CODR = mask;  /* clear it */          return 0;                          /* other ioctl's */	case AT91_GPIO_PULLUP_ENB:  /* pull up enable this pin */          p_pio->PIO_PPUER = mask;  /* enable it */          return 0;	case AT91_GPIO_PULLUP_DIS:  /* pull up disable this pin */          p_pio->PIO_PPUDR = mask;  /* disable it */          return 0;	case AT91_GPIO_READ_PCB_ID:  /* read the PCD ID */#define PCB_ID_BASE 0x30000000  /* address of PCB ID port */          {          char *pcb_id;                    pcb_id = ioremap((unsigned long)PCB_ID_BASE, SZ_4K);          if (!pcb_id)            {            printk(KERN_ERR "at91_usb-pcb_id: ioremap failed\n");            return -EIO;            }             ret = *pcb_id;  /* get it */          iounmap(pcb_id);          return put_user(ret, (u32 *)arg);          }   	default:          return -EINVAL;	} /* switch */	return 0;}struct file_operations gpio_fops = {	owner:       THIS_MODULE,	ioctl:       gpio_ioctl,	open:        gpio_open,	release:     gpio_release,};static void gpio_irq_handler(int this_irq, void *dev_id, struct pt_regs *regs){        AT91PS_PIO p_pio = dev_id;  /* pointer to the port */        AT91_REG temp;        int cntr, minor;        temp = p_pio->PIO_ISR;  /* get active int's, read to reset bit */        minor = (p_pio - AT91C_BASE_PIOA) * AT91_GPIO_PINS_PER_PORT;  /* start minor # */        /* go thru all the pins on this port, check if dev open (via priv_lut), if ISR bit         is set and if we've allowed interrupts on this pin */        for (cntr = 0; cntr < AT91_GPIO_PINS_PER_PORT; cntr++, minor++)          if (priv_lut[minor] && (temp & (1 << cntr)) && (gpio_allow[minor] & AI))              {              p_pio->PIO_IDR = 1 << cntr;  /* disable it */              wake_up_interruptible(&priv_lut[minor]->gpio_wait);              }        return;}/* main driver initialization routine, called from mem.c */static __init intgpio_init(void){	int res, cntr1, cntr2, cntr3, int_allow;	/* do the formalities */	res = register_chrdev(GPIO_MAJOR, "at91 gpio", &gpio_fops);	if (res < 0) {		printk(KERN_ERR "at91_gpio: couldn't get a major number.\n");		return res;	}        /* find all the port pins that allow interrupts and request irq,           each 32 pin port is mapped to one interrupt */	for (cntr1 = 0, cntr3 = 0; cntr1 < GPIO_NUM_PORTS; cntr1++) {        	int_allow = 0;		for (cntr2 = 0; cntr2 < AT91_GPIO_PINS_PER_PORT; cntr2++, cntr3++)        		int_allow |= gpio_allow[cntr3];        	if (int_allow & AI) {  /* any of this ports pins allow int? */                	if ((res = request_irq(AT91C_ID_PIOA + cntr1,                	          gpio_irq_handler, SA_SHIRQ,                                  gpio_name_lut[cntr1], gpio_port_addr[cntr1])) < 0)			return res;		}        }	printk("AT91 GPIO driver v1.0\n");	return res;}/* this makes sure that gpio_init is called during kernel boot */module_init(gpio_init);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -