📄 gpio.c
字号:
if (priv->write_msb) { for (i = 7; i >= 0;i--) { local_irq_save(flags); shadow = *port; *port = shadow &= ~clk_mask; if (data & 1<<i) *port = shadow |= data_mask; else *port = shadow &= ~data_mask; /* For FPGA: min 5.0ns (DCC) before CCLK high */ *port = shadow |= clk_mask; local_irq_restore(flags); } } else { for (i = 0; i <= 7;i++) { local_irq_save(flags); shadow = *port; *port = shadow &= ~clk_mask; if (data & 1<<i) *port = shadow |= data_mask; else *port = shadow &= ~data_mask; /* For FPGA: min 5.0ns (DCC) before CCLK high */ *port = shadow |= clk_mask; local_irq_restore(flags); } } } return retval;}static intgpio_open(struct inode *inode, struct file *filp){ struct gpio_private *priv; int p = iminor(inode); if (p > GPIO_MINOR_LAST) return -EINVAL; priv = kmalloc(sizeof(struct gpio_private), GFP_KERNEL); if (!priv) return -ENOMEM; priv->minor = p; /* initialize the io/alarm struct and link it into our alarmlist */ priv->next = alarmlist; alarmlist = priv; priv->clk_mask = 0; priv->data_mask = 0; priv->highalarm = 0; priv->lowalarm = 0; init_waitqueue_head(&priv->alarm_wq); filp->private_data = (void *)priv; return 0;}static intgpio_release(struct inode *inode, struct file *filp){ struct gpio_private *p = alarmlist; struct gpio_private *todel = (struct gpio_private *)filp->private_data; /* local copies while updating them: */ unsigned long a_high, a_low; unsigned long some_alarms; /* unlink from alarmlist and free the private structure */ if (p == todel) { alarmlist = todel->next; } else { while (p->next != todel) p = p->next; p->next = todel->next; } kfree(todel); /* Check if there are still any alarms set */ p = alarmlist; some_alarms = 0; a_high = 0; a_low = 0; while (p) { if (p->minor == GPIO_MINOR_A) { a_high |= p->highalarm; a_low |= p->lowalarm; } if (p->highalarm | p->lowalarm) { some_alarms = 1; } p = p->next; } spin_lock(&alarm_lock); gpio_some_alarms = some_alarms; gpio_pa_high_alarms = a_high; gpio_pa_low_alarms = a_low; spin_unlock(&alarm_lock); return 0;}/* Main device API. ioctl's to read/set/clear bits, as well as to * set alarms to wait for using a subsequent select(). */unsigned long inline setget_input(struct gpio_private *priv, unsigned long arg){ /* Set direction 0=unchanged 1=input, * return mask with 1=input */ unsigned long flags; unsigned long dir_shadow; local_irq_save(flags); dir_shadow = *dir_oe[priv->minor]; dir_shadow &= ~(arg & changeable_dir[priv->minor]); *dir_oe[priv->minor] = dir_shadow; local_irq_restore(flags); if (priv->minor == GPIO_MINOR_A) dir_shadow ^= 0xFF; /* Only 8 bits */ else dir_shadow ^= 0x3FFFF; /* Only 18 bits */ return dir_shadow;} /* setget_input */unsigned long inline setget_output(struct gpio_private *priv, unsigned long arg){ unsigned long flags; unsigned long dir_shadow; local_irq_save(flags); dir_shadow = *dir_oe[priv->minor]; dir_shadow |= (arg & changeable_dir[priv->minor]); *dir_oe[priv->minor] = dir_shadow; local_irq_restore(flags); return dir_shadow;} /* setget_output */static intgpio_leds_ioctl(unsigned int cmd, unsigned long arg);static intgpio_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg){ unsigned long flags; unsigned long val; unsigned long shadow; struct gpio_private *priv = (struct gpio_private *)file->private_data; if (_IOC_TYPE(cmd) != ETRAXGPIO_IOCTYPE) { return -EINVAL; } switch (_IOC_NR(cmd)) { case IO_READBITS: /* Use IO_READ_INBITS and IO_READ_OUTBITS instead */ // read the port return *data_in[priv->minor]; break; case IO_SETBITS: local_irq_save(flags); if (arg & 0x04) printk("GPIO SET 2\n"); // set changeable bits with a 1 in arg shadow = *data_out[priv->minor]; shadow |= (arg & changeable_bits[priv->minor]); *data_out[priv->minor] = shadow; local_irq_restore(flags); break; case IO_CLRBITS: local_irq_save(flags); if (arg & 0x04) printk("GPIO CLR 2\n"); // clear changeable bits with a 1 in arg shadow = *data_out[priv->minor]; shadow &= ~(arg & changeable_bits[priv->minor]); *data_out[priv->minor] = shadow; local_irq_restore(flags); break; case IO_HIGHALARM: // set alarm when bits with 1 in arg go high priv->highalarm |= arg; spin_lock(&alarm_lock); gpio_some_alarms = 1; if (priv->minor == GPIO_MINOR_A) { gpio_pa_high_alarms |= arg; } spin_unlock(&alarm_lock); break; case IO_LOWALARM: // set alarm when bits with 1 in arg go low priv->lowalarm |= arg; spin_lock(&alarm_lock); gpio_some_alarms = 1; if (priv->minor == GPIO_MINOR_A) { gpio_pa_low_alarms |= arg; } spin_unlock(&alarm_lock); break; case IO_CLRALARM: // clear alarm for bits with 1 in arg priv->highalarm &= ~arg; priv->lowalarm &= ~arg; spin_lock(&alarm_lock); if (priv->minor == GPIO_MINOR_A) { if (gpio_pa_high_alarms & arg || gpio_pa_low_alarms & arg) { /* Must update the gpio_pa_*alarms masks */ } } spin_unlock(&alarm_lock); break; case IO_READDIR: /* Use IO_SETGET_INPUT/OUTPUT instead! */ /* Read direction 0=input 1=output */ return *dir_oe[priv->minor]; case IO_SETINPUT: /* Use IO_SETGET_INPUT instead! */ /* Set direction 0=unchanged 1=input, * return mask with 1=input */ return setget_input(priv, arg); break; case IO_SETOUTPUT: /* Use IO_SETGET_OUTPUT instead! */ /* Set direction 0=unchanged 1=output, * return mask with 1=output */ return setget_output(priv, arg); case IO_CFG_WRITE_MODE: { unsigned long dir_shadow; dir_shadow = *dir_oe[priv->minor]; priv->clk_mask = arg & 0xFF; priv->data_mask = (arg >> 8) & 0xFF; priv->write_msb = (arg >> 16) & 0x01; /* Check if we're allowed to change the bits and * the direction is correct */ if (!((priv->clk_mask & changeable_bits[priv->minor]) && (priv->data_mask & changeable_bits[priv->minor]) && (priv->clk_mask & dir_shadow) && (priv->data_mask & dir_shadow))) { priv->clk_mask = 0; priv->data_mask = 0; return -EPERM; } break; } case IO_READ_INBITS: /* *arg is result of reading the input pins */ val = *data_in[priv->minor]; if (copy_to_user((unsigned long*)arg, &val, sizeof(val))) return -EFAULT; return 0; break; case IO_READ_OUTBITS: /* *arg is result of reading the output shadow */ val = *data_out[priv->minor]; if (copy_to_user((unsigned long*)arg, &val, sizeof(val))) return -EFAULT; break; case IO_SETGET_INPUT: /* bits set in *arg is set to input, * *arg updated with current input pins. */ if (copy_from_user(&val, (unsigned long*)arg, sizeof(val))) return -EFAULT; val = setget_input(priv, val); if (copy_to_user((unsigned long*)arg, &val, sizeof(val))) return -EFAULT; break; case IO_SETGET_OUTPUT: /* bits set in *arg is set to output, * *arg updated with current output pins. */ if (copy_from_user(&val, (unsigned long*)arg, sizeof(val))) return -EFAULT; val = setget_output(priv, val); if (copy_to_user((unsigned long*)arg, &val, sizeof(val))) return -EFAULT; break; default: if (priv->minor == GPIO_MINOR_LEDS) return gpio_leds_ioctl(cmd, arg); else return -EINVAL; } /* switch */ return 0;}static intgpio_leds_ioctl(unsigned int cmd, unsigned long arg){ unsigned char green; unsigned char red; switch (_IOC_NR(cmd)) { case IO_LEDACTIVE_SET: green = ((unsigned char) arg) & 1; red = (((unsigned char) arg) >> 1) & 1; LED_ACTIVE_SET_G(green); LED_ACTIVE_SET_R(red); break; default: return -EINVAL; } /* switch */ return 0;}const struct file_operations gpio_fops = { .owner = THIS_MODULE, .poll = gpio_poll, .ioctl = gpio_ioctl, .write = gpio_write, .open = gpio_open, .release = gpio_release,};/* main driver initialization routine, called from mem.c */static __init intgpio_init(void){ int res; reg_intr_vect_rw_mask intr_mask; /* do the formalities */ res = register_chrdev(GPIO_MAJOR, gpio_name, &gpio_fops); if (res < 0) { printk(KERN_ERR "gpio: couldn't get a major number.\n"); return res; } /* Clear all leds */ LED_NETWORK_SET(0); LED_ACTIVE_SET(0); LED_DISK_READ(0); LED_DISK_WRITE(0); printk("ETRAX FS GPIO driver v2.5, (c) 2003-2005 Axis Communications AB\n"); /* We call etrax_gpio_wake_up_check() from timer interrupt and * from cpu_idle() in kernel/process.c * The check in cpu_idle() reduces latency from ~15 ms to ~6 ms * in some tests. */ if (request_irq(TIMER_INTR_VECT, gpio_poll_timer_interrupt, IRQF_SHARED | IRQF_DISABLED,"gpio poll", &alarmlist)) { printk("err: timer0 irq for gpio\n"); } if (request_irq(GEN_IO_INTR_VECT, gpio_pa_interrupt, IRQF_SHARED | IRQF_DISABLED,"gpio PA", &alarmlist)) { printk("err: PA irq for gpio\n"); } /* enable the gio and timer irq in global config */ intr_mask = REG_RD(intr_vect, regi_irq, rw_mask); intr_mask.timer = 1; intr_mask.gen_io = 1; REG_WR(intr_vect, regi_irq, rw_mask, intr_mask); 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 + -