📄 gpio.c
字号:
filp->private_data = (void *)priv; return 0;}static intgpio_release(struct inode *inode, struct file *filp){ struct gpio_private *p; struct gpio_private *todel; spin_lock(&gpio_lock); p = alarmlist; todel = (struct gpio_private *)filp->private_data; /* 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; while (p) { if (p->highalarm | p->lowalarm) { gpio_some_alarms = 1; spin_unlock(&gpio_lock); return 0; } p = p->next; } gpio_some_alarms = 0; spin_unlock(&gpio_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; if (USE_PORTS(priv)) { local_irq_save(flags); *priv->dir = *priv->dir_shadow &= ~((unsigned char)arg & priv->changeable_dir); local_irq_restore(flags); return ~(*priv->dir_shadow) & 0xFF; /* Only 8 bits */ } else if (priv->minor == GPIO_MINOR_G) { /* We must fiddle with R_GEN_CONFIG to change dir */ local_irq_save(flags); if (((arg & dir_g_in_bits) != arg) && (arg & changeable_dir_g)) { arg &= changeable_dir_g; /* Clear bits in genconfig to set to input */ if (arg & (1<<0)) { genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG,g0dir); dir_g_in_bits |= (1<<0); dir_g_out_bits &= ~(1<<0); } if ((arg & 0x0000FF00) == 0x0000FF00) { genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG,g8_15dir); dir_g_in_bits |= 0x0000FF00; dir_g_out_bits &= ~0x0000FF00; } if ((arg & 0x00FF0000) == 0x00FF0000) { genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG,g16_23dir); dir_g_in_bits |= 0x00FF0000; dir_g_out_bits &= ~0x00FF0000; } if (arg & (1<<24)) { genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG,g24dir); dir_g_in_bits |= (1<<24); dir_g_out_bits &= ~(1<<24); } D(printk(KERN_INFO "gpio: SETINPUT on port G set " "genconfig to 0x%08lX " "in_bits: 0x%08lX " "out_bits: 0x%08lX\n", (unsigned long)genconfig_shadow, dir_g_in_bits, dir_g_out_bits)); *R_GEN_CONFIG = genconfig_shadow; /* Must be a >120 ns delay before writing this again */ } local_irq_restore(flags); return dir_g_in_bits; } return 0;} /* setget_input */unsigned long inline setget_output(struct gpio_private *priv, unsigned long arg){ unsigned long flags; if (USE_PORTS(priv)) { local_irq_save(flags); *priv->dir = *priv->dir_shadow |= ((unsigned char)arg & priv->changeable_dir); local_irq_restore(flags); return *priv->dir_shadow; } else if (priv->minor == GPIO_MINOR_G) { /* We must fiddle with R_GEN_CONFIG to change dir */ local_irq_save(flags); if (((arg & dir_g_out_bits) != arg) && (arg & changeable_dir_g)) { /* Set bits in genconfig to set to output */ if (arg & (1<<0)) { genconfig_shadow |= IO_MASK(R_GEN_CONFIG,g0dir); dir_g_out_bits |= (1<<0); dir_g_in_bits &= ~(1<<0); } if ((arg & 0x0000FF00) == 0x0000FF00) { genconfig_shadow |= IO_MASK(R_GEN_CONFIG,g8_15dir); dir_g_out_bits |= 0x0000FF00; dir_g_in_bits &= ~0x0000FF00; } if ((arg & 0x00FF0000) == 0x00FF0000) { genconfig_shadow |= IO_MASK(R_GEN_CONFIG,g16_23dir); dir_g_out_bits |= 0x00FF0000; dir_g_in_bits &= ~0x00FF0000; } if (arg & (1<<24)) { genconfig_shadow |= IO_MASK(R_GEN_CONFIG,g24dir); dir_g_out_bits |= (1<<24); dir_g_in_bits &= ~(1<<24); } D(printk(KERN_INFO "gpio: SETOUTPUT on port G set " "genconfig to 0x%08lX " "in_bits: 0x%08lX " "out_bits: 0x%08lX\n", (unsigned long)genconfig_shadow, dir_g_in_bits, dir_g_out_bits)); *R_GEN_CONFIG = genconfig_shadow; /* Must be a >120 ns delay before writing this again */ } local_irq_restore(flags); return dir_g_out_bits & 0x7FFFFFFF; } return 0;} /* 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; int ret = 0; struct gpio_private *priv = (struct gpio_private *)file->private_data; if (_IOC_TYPE(cmd) != ETRAXGPIO_IOCTYPE) { return -EINVAL; } spin_lock(&gpio_lock); switch (_IOC_NR(cmd)) { case IO_READBITS: /* Use IO_READ_INBITS and IO_READ_OUTBITS instead */ // read the port if (USE_PORTS(priv)) { ret = *priv->port; } else if (priv->minor == GPIO_MINOR_G) { ret = (*R_PORT_G_DATA) & 0x7FFFFFFF; } break; case IO_SETBITS: local_irq_save(flags); // set changeable bits with a 1 in arg if (USE_PORTS(priv)) { *priv->port = *priv->shadow |= ((unsigned char)arg & priv->changeable_bits); } else if (priv->minor == GPIO_MINOR_G) { *R_PORT_G_DATA = port_g_data_shadow |= (arg & dir_g_out_bits); } local_irq_restore(flags); break; case IO_CLRBITS: local_irq_save(flags); // clear changeable bits with a 1 in arg if (USE_PORTS(priv)) { *priv->port = *priv->shadow &= ~((unsigned char)arg & priv->changeable_bits); } else if (priv->minor == GPIO_MINOR_G) { *R_PORT_G_DATA = port_g_data_shadow &= ~((unsigned long)arg & dir_g_out_bits); } local_irq_restore(flags); break; case IO_HIGHALARM: // set alarm when bits with 1 in arg go high priv->highalarm |= arg; gpio_some_alarms = 1; break; case IO_LOWALARM: // set alarm when bits with 1 in arg go low priv->lowalarm |= arg; gpio_some_alarms = 1; break; case IO_CLRALARM: // clear alarm for bits with 1 in arg priv->highalarm &= ~arg; priv->lowalarm &= ~arg; { /* Must update gpio_some_alarms */ struct gpio_private *p = alarmlist; int some_alarms; some_alarms = 0; while (p) { if (p->highalarm | p->lowalarm) { some_alarms = 1; break; } p = p->next; } gpio_some_alarms = some_alarms; } break; case IO_READDIR: /* Use IO_SETGET_INPUT/OUTPUT instead! */ /* Read direction 0=input 1=output */ if (USE_PORTS(priv)) { ret = *priv->dir_shadow; } else if (priv->minor == GPIO_MINOR_G) { /* Note: Some bits are both in and out, * Those that are dual is set here as well. */ ret = (dir_g_shadow | dir_g_out_bits) & 0x7FFFFFFF; } break; case IO_SETINPUT: /* Use IO_SETGET_INPUT instead! */ /* Set direction 0=unchanged 1=input, * return mask with 1=input */ ret = setget_input(priv, arg) & 0x7FFFFFFF; break; case IO_SETOUTPUT: /* Use IO_SETGET_OUTPUT instead! */ /* Set direction 0=unchanged 1=output, * return mask with 1=output */ ret = setget_output(priv, arg) & 0x7FFFFFFF; break; case IO_SHUTDOWN: SOFT_SHUTDOWN(); break; case IO_GET_PWR_BT:#if defined (CONFIG_ETRAX_SOFT_SHUTDOWN) ret = (*R_PORT_G_DATA & ( 1 << CONFIG_ETRAX_POWERBUTTON_BIT));#else ret = 0;#endif break; case IO_CFG_WRITE_MODE: 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 & priv->changeable_bits) && (priv->data_mask & priv->changeable_bits) && (priv->clk_mask & *priv->dir_shadow) && (priv->data_mask & *priv->dir_shadow))) { priv->clk_mask = 0; priv->data_mask = 0; ret = -EPERM; } break; case IO_READ_INBITS: /* *arg is result of reading the input pins */ if (USE_PORTS(priv)) { val = *priv->port; } else if (priv->minor == GPIO_MINOR_G) { val = *R_PORT_G_DATA; } if (copy_to_user((unsigned long*)arg, &val, sizeof(val))) ret = -EFAULT; break; case IO_READ_OUTBITS: /* *arg is result of reading the output shadow */ if (USE_PORTS(priv)) { val = *priv->shadow; } else if (priv->minor == GPIO_MINOR_G) { val = port_g_data_shadow; } if (copy_to_user((unsigned long*)arg, &val, sizeof(val))) ret = -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))) { ret = -EFAULT; break; } val = setget_input(priv, val); if (copy_to_user((unsigned long*)arg, &val, sizeof(val))) ret = -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))) { ret = -EFAULT; break; } val = setget_output(priv, val); if (copy_to_user((unsigned long*)arg, &val, sizeof(val))) ret = -EFAULT; break; default: if (priv->minor == GPIO_MINOR_LEDS) ret = gpio_leds_ioctl(cmd, arg); else ret = -EINVAL; } /* switch */ spin_unlock(&gpio_lock); return ret;}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; case IO_LED_SETBIT: LED_BIT_SET(arg); break; case IO_LED_CLRBIT: LED_BIT_CLR(arg); 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,};void ioif_watcher(const unsigned int gpio_in_available, const unsigned int gpio_out_available, const unsigned char pa_available, const unsigned char pb_available){ unsigned long int flags; D(printk("gpio.c: ioif_watcher called\n")); D(printk("gpio.c: G in: 0x%08x G out: 0x%08x PA: 0x%02x PB: 0x%02x\n", gpio_in_available, gpio_out_available, pa_available, pb_available)); spin_lock_irqsave(&gpio_lock, flags); dir_g_in_bits = gpio_in_available; dir_g_out_bits = gpio_out_available; /* Initialise the dir_g_shadow etc. depending on genconfig */ /* 0=input 1=output */ if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, g0dir, out)) dir_g_shadow |= (1 << 0); if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, g8_15dir, out)) dir_g_shadow |= 0x0000FF00; if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, g16_23dir, out)) dir_g_shadow |= 0x00FF0000; if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, g24dir, out)) dir_g_shadow |= (1 << 24); changeable_dir_g = changeable_dir_g_mask; changeable_dir_g &= dir_g_out_bits; changeable_dir_g &= dir_g_in_bits; /* Correct the bits that can change direction */ dir_g_out_bits &= ~changeable_dir_g; dir_g_out_bits |= dir_g_shadow; dir_g_in_bits &= ~changeable_dir_g; dir_g_in_bits |= (~dir_g_shadow & changeable_dir_g); spin_unlock_irqrestore(&gpio_lock, flags); printk(KERN_INFO "GPIO port G: in_bits: 0x%08lX out_bits: 0x%08lX val: %08lX\n", dir_g_in_bits, dir_g_out_bits, (unsigned long)*R_PORT_G_DATA); printk(KERN_INFO "GPIO port G: dir: %08lX changeable: %08lX\n", dir_g_shadow, changeable_dir_g);}/* main driver initialization routine, called from mem.c */static __init intgpio_init(void){ int res;#if defined (CONFIG_ETRAX_CSP0_LEDS) int i;#endif printk("gpio init\n"); /* 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 */#if defined (CONFIG_ETRAX_CSP0_LEDS) || defined (CONFIG_ETRAX_PA_LEDS) || defined (CONFIG_ETRAX_PB_LEDS) LED_NETWORK_SET(0); LED_ACTIVE_SET(0); LED_DISK_READ(0); LED_DISK_WRITE(0);#if defined (CONFIG_ETRAX_CSP0_LEDS) for (i = 0; i < 32; i++) { LED_BIT_SET(i); }#endif#endif /* The I/O interface allocation watcher will be called when * registering it. */ if (cris_io_interface_register_watcher(ioif_watcher)){ printk(KERN_WARNING "gpio_init: Failed to install IO if allocator watcher\n"); } printk(KERN_INFO "ETRAX 100LX GPIO driver v2.5, (c) 2001, 2002, 2003, 2004 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(TIMER0_IRQ_NBR, gpio_poll_timer_interrupt, IRQF_SHARED | IRQF_DISABLED,"gpio poll", NULL)) { printk(KERN_CRIT "err: timer0 irq for gpio\n"); } if (request_irq(PA_IRQ_NBR, gpio_pa_interrupt, IRQF_SHARED | IRQF_DISABLED,"gpio PA", NULL)) { printk(KERN_CRIT "err: PA irq for gpio\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 + -