📄 gpio.c
字号:
/* $Id: gpio.c,v 1.16 2005/06/19 17:06:49 starvik Exp $ * * ETRAX CRISv32 general port I/O device * * Copyright (c) 1999, 2000, 2001, 2002, 2003 Axis Communications AB * * Authors: Bjorn Wesen (initial version) * Ola Knutsson (LED handling) * Johan Adolfsson (read/set directions, write, port G, * port to ETRAX FS. * * $Log: gpio.c,v $ * Revision 1.16 2005/06/19 17:06:49 starvik * Merge of Linux 2.6.12. * * Revision 1.15 2005/05/25 08:22:20 starvik * Changed GPIO port order to fit packages/devices/axis-2.4. * * Revision 1.14 2005/04/24 18:35:08 starvik * Updated with final register headers. * * Revision 1.13 2005/03/15 15:43:00 starvik * dev_id needs to be supplied for shared IRQs. * * Revision 1.12 2005/03/10 17:12:00 starvik * Protect alarm list with spinlock. * * Revision 1.11 2005/01/05 06:08:59 starvik * No need to do local_irq_disable after local_irq_save. * * Revision 1.10 2004/11/19 08:38:31 starvik * Removed old crap. * * Revision 1.9 2004/05/14 07:58:02 starvik * Merge of changes from 2.4 * * Revision 1.8 2003/09/11 07:29:50 starvik * Merge of Linux 2.6.0-test5 * * Revision 1.7 2003/07/10 13:25:46 starvik * Compiles for 2.5.74 * Lindented ethernet.c * * Revision 1.6 2003/07/04 08:27:46 starvik * Merge of Linux 2.5.74 * * Revision 1.5 2003/06/10 08:26:37 johana * Etrax -> ETRAX CRISv32 * * Revision 1.4 2003/06/05 14:22:48 johana * Initialise some_alarms. * * Revision 1.3 2003/06/05 10:15:46 johana * New INTR_VECT macros. * Enable interrupts in global config. * * Revision 1.2 2003/06/03 15:52:50 johana * Initial CRIS v32 version. * * Revision 1.1 2003/06/03 08:53:15 johana * Copy of os/lx25/arch/cris/arch-v10/drivers/gpio.c version 1.7. * */#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 <linux/interrupt.h>#include <linux/spinlock.h>#include <asm/etraxgpio.h>#include <asm/arch/hwregs/reg_map.h>#include <asm/arch/hwregs/reg_rdwr.h>#include <asm/arch/hwregs/gio_defs.h>#include <asm/arch/hwregs/intr_vect_defs.h>#include <asm/io.h>#include <asm/system.h>#include <asm/irq.h>/* The following gio ports on ETRAX FS is available: * pa 8 bits, supports interrupts off, hi, low, set, posedge, negedge anyedge * pb 18 bits * pc 18 bits * pd 18 bits * pe 18 bits * each port has a rw_px_dout, r_px_din and rw_px_oe register. */#define GPIO_MAJOR 120 /* experimental MAJOR number */#define D(x)#if 0static int dp_cnt;#define DP(x) do { dp_cnt++; if (dp_cnt % 1000 == 0) x; }while(0)#else#define DP(x)#endifstatic char gpio_name[] = "etrax gpio";#if 0static wait_queue_head_t *gpio_wq;#endifstatic int gpio_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg);static ssize_t gpio_write(struct file * file, const char * buf, size_t count, loff_t *off);static int gpio_open(struct inode *inode, struct file *filp);static int gpio_release(struct inode *inode, struct file *filp);static unsigned int gpio_poll(struct file *filp, struct poll_table_struct *wait);/* private data per open() of this driver */struct gpio_private { struct gpio_private *next; /* The IO_CFG_WRITE_MODE_VALUE only support 8 bits: */ unsigned char clk_mask; unsigned char data_mask; unsigned char write_msb; unsigned char pad1; /* These fields are generic */ unsigned long highalarm, lowalarm; wait_queue_head_t alarm_wq; int minor;};/* linked list of alarms to check for */static struct gpio_private *alarmlist = 0;static int gpio_some_alarms = 0; /* Set if someone uses alarm */static unsigned long gpio_pa_high_alarms = 0;static unsigned long gpio_pa_low_alarms = 0;static DEFINE_SPINLOCK(alarm_lock);#define NUM_PORTS (GPIO_MINOR_LAST+1)#define GIO_REG_RD_ADDR(reg) (volatile unsigned long*) (regi_gio + REG_RD_ADDR_gio_##reg )#define GIO_REG_WR_ADDR(reg) (volatile unsigned long*) (regi_gio + REG_RD_ADDR_gio_##reg )unsigned long led_dummy;static volatile unsigned long *data_out[NUM_PORTS] = { GIO_REG_WR_ADDR(rw_pa_dout), GIO_REG_WR_ADDR(rw_pb_dout), &led_dummy, GIO_REG_WR_ADDR(rw_pc_dout), GIO_REG_WR_ADDR(rw_pd_dout), GIO_REG_WR_ADDR(rw_pe_dout),};static volatile unsigned long *data_in[NUM_PORTS] = { GIO_REG_RD_ADDR(r_pa_din), GIO_REG_RD_ADDR(r_pb_din), &led_dummy, GIO_REG_RD_ADDR(r_pc_din), GIO_REG_RD_ADDR(r_pd_din), GIO_REG_RD_ADDR(r_pe_din),};static unsigned long changeable_dir[NUM_PORTS] = { CONFIG_ETRAX_PA_CHANGEABLE_DIR, CONFIG_ETRAX_PB_CHANGEABLE_DIR, 0, CONFIG_ETRAX_PC_CHANGEABLE_DIR, CONFIG_ETRAX_PD_CHANGEABLE_DIR, CONFIG_ETRAX_PE_CHANGEABLE_DIR,};static unsigned long changeable_bits[NUM_PORTS] = { CONFIG_ETRAX_PA_CHANGEABLE_BITS, CONFIG_ETRAX_PB_CHANGEABLE_BITS, 0, CONFIG_ETRAX_PC_CHANGEABLE_BITS, CONFIG_ETRAX_PD_CHANGEABLE_BITS, CONFIG_ETRAX_PE_CHANGEABLE_BITS,};static volatile unsigned long *dir_oe[NUM_PORTS] = { GIO_REG_WR_ADDR(rw_pa_oe), GIO_REG_WR_ADDR(rw_pb_oe), &led_dummy, GIO_REG_WR_ADDR(rw_pc_oe), GIO_REG_WR_ADDR(rw_pd_oe), GIO_REG_WR_ADDR(rw_pe_oe),};static unsigned intgpio_poll(struct file *file, poll_table *wait){ unsigned int mask = 0; struct gpio_private *priv = (struct gpio_private *)file->private_data; unsigned long data; poll_wait(file, &priv->alarm_wq, wait); if (priv->minor == GPIO_MINOR_A) { reg_gio_rw_intr_cfg intr_cfg; unsigned long tmp; unsigned long flags; local_irq_save(flags); data = REG_TYPE_CONV(unsigned long, reg_gio_r_pa_din, REG_RD(gio, regi_gio, r_pa_din)); /* PA has support for interrupt * lets activate high for those low and with highalarm set */ intr_cfg = REG_RD(gio, regi_gio, rw_intr_cfg); tmp = ~data & priv->highalarm & 0xFF; if (tmp & (1 << 0)) { intr_cfg.pa0 = regk_gio_hi; } if (tmp & (1 << 1)) { intr_cfg.pa1 = regk_gio_hi; } if (tmp & (1 << 2)) { intr_cfg.pa2 = regk_gio_hi; } if (tmp & (1 << 3)) { intr_cfg.pa3 = regk_gio_hi; } if (tmp & (1 << 4)) { intr_cfg.pa4 = regk_gio_hi; } if (tmp & (1 << 5)) { intr_cfg.pa5 = regk_gio_hi; } if (tmp & (1 << 6)) { intr_cfg.pa6 = regk_gio_hi; } if (tmp & (1 << 7)) { intr_cfg.pa7 = regk_gio_hi; } /* * lets activate low for those high and with lowalarm set */ tmp = data & priv->lowalarm & 0xFF; if (tmp & (1 << 0)) { intr_cfg.pa0 = regk_gio_lo; } if (tmp & (1 << 1)) { intr_cfg.pa1 = regk_gio_lo; } if (tmp & (1 << 2)) { intr_cfg.pa2 = regk_gio_lo; } if (tmp & (1 << 3)) { intr_cfg.pa3 = regk_gio_lo; } if (tmp & (1 << 4)) { intr_cfg.pa4 = regk_gio_lo; } if (tmp & (1 << 5)) { intr_cfg.pa5 = regk_gio_lo; } if (tmp & (1 << 6)) { intr_cfg.pa6 = regk_gio_lo; } if (tmp & (1 << 7)) { intr_cfg.pa7 = regk_gio_lo; } REG_WR(gio, regi_gio, rw_intr_cfg, intr_cfg); local_irq_restore(flags); } else if (priv->minor <= GPIO_MINOR_E) data = *data_in[priv->minor]; else return 0; if ((data & priv->highalarm) || (~data & priv->lowalarm)) { mask = POLLIN|POLLRDNORM; } DP(printk("gpio_poll ready: mask 0x%08X\n", mask)); return mask;}int etrax_gpio_wake_up_check(void){ struct gpio_private *priv = alarmlist; unsigned long data = 0; int ret = 0; while (priv) { data = *data_in[priv->minor]; if ((data & priv->highalarm) || (~data & priv->lowalarm)) { DP(printk("etrax_gpio_wake_up_check %i\n",priv->minor)); wake_up_interruptible(&priv->alarm_wq); ret = 1; } priv = priv->next; } return ret;}static irqreturn_tgpio_poll_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs){ if (gpio_some_alarms) { return IRQ_RETVAL(etrax_gpio_wake_up_check()); } return IRQ_NONE;}static irqreturn_tgpio_pa_interrupt(int irq, void *dev_id, struct pt_regs *regs){ reg_gio_rw_intr_mask intr_mask; reg_gio_r_masked_intr masked_intr; reg_gio_rw_ack_intr ack_intr; unsigned long tmp; unsigned long tmp2; /* Find what PA interrupts are active */ masked_intr = REG_RD(gio, regi_gio, r_masked_intr); tmp = REG_TYPE_CONV(unsigned long, reg_gio_r_masked_intr, masked_intr); /* Find those that we have enabled */ spin_lock(&alarm_lock); tmp &= (gpio_pa_high_alarms | gpio_pa_low_alarms); spin_unlock(&alarm_lock); /* Ack them */ ack_intr = REG_TYPE_CONV(reg_gio_rw_ack_intr, unsigned long, tmp); REG_WR(gio, regi_gio, rw_ack_intr, ack_intr); /* Disable those interrupts.. */ intr_mask = REG_RD(gio, regi_gio, rw_intr_mask); tmp2 = REG_TYPE_CONV(unsigned long, reg_gio_rw_intr_mask, intr_mask); tmp2 &= ~tmp; intr_mask = REG_TYPE_CONV(reg_gio_rw_intr_mask, unsigned long, tmp2); REG_WR(gio, regi_gio, rw_intr_mask, intr_mask); if (gpio_some_alarms) { return IRQ_RETVAL(etrax_gpio_wake_up_check()); } return IRQ_NONE;}static ssize_t gpio_write(struct file * file, const char * buf, size_t count, loff_t *off){ struct gpio_private *priv = (struct gpio_private *)file->private_data; unsigned char data, clk_mask, data_mask, write_msb; unsigned long flags; unsigned long shadow; volatile unsigned long *port; ssize_t retval = count; /* Only bits 0-7 may be used for write operations but allow all devices except leds... */ if (priv->minor == GPIO_MINOR_LEDS) { return -EFAULT; } if (!access_ok(VERIFY_READ, buf, count)) { return -EFAULT; } clk_mask = priv->clk_mask; data_mask = priv->data_mask; /* It must have been configured using the IO_CFG_WRITE_MODE */ /* Perhaps a better error code? */ if (clk_mask == 0 || data_mask == 0) { return -EPERM; } write_msb = priv->write_msb; D(printk("gpio_write: %lu to data 0x%02X clk 0x%02X msb: %i\n",count, data_mask, clk_mask, write_msb)); port = data_out[priv->minor]; while (count--) { int i; data = *buf++;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -