📄 gpio.c
字号:
} p = p->next; } gpio_some_alarms = 0; 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); local_irq_disable(); *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 */ save_flags(flags); cli(); 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 */ } restore_flags(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); local_irq_disable(); *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 */ save_flags(flags); cli(); 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 */ } restore_flags(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; 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 if (USE_PORTS(priv)) { return *priv->port; } else if (priv->minor == GPIO_MINOR_G) { return (*R_PORT_G_DATA) & 0x7FFFFFFF; } break; case IO_SETBITS: local_irq_save(flags); local_irq_disable(); // 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); local_irq_disable(); // 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)) { return *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. */ return (dir_g_shadow | dir_g_out_bits) & 0x7FFFFFFF; } case IO_SETINPUT: /* Use IO_SETGET_INPUT instead! */ /* Set direction 0=unchanged 1=input, * return mask with 1=input */ return 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 */ return setget_output(priv, arg) & 0x7FFFFFFF; case IO_SHUTDOWN: SOFT_SHUTDOWN(); break; case IO_GET_PWR_BT:#if defined (CONFIG_ETRAX_SOFT_SHUTDOWN) return (*R_PORT_G_DATA & ( 1 << CONFIG_ETRAX_POWERBUTTON_BIT));#else return 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; return -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))) return -EFAULT; return 0; 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))) 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; case IO_LED_SETBIT: LED_BIT_SET(arg); break; case IO_LED_CLRBIT: LED_BIT_CLR(arg); break; default: return -EINVAL; } /* switch */ return 0;}struct file_operations gpio_fops = { .owner = THIS_MODULE, .poll = gpio_poll, .ioctl = gpio_ioctl, .write = gpio_write, .open = gpio_open, .release = gpio_release,};static void __init gpio_init_port_g(void){#define GROUPA (0x0000FF3F)#define GROUPB (1<<6 | 1<<7)#define GROUPC (1<<30 | 1<<31)#define GROUPD (0x3FFF0000)#define GROUPD_LOW (0x00FF0000) unsigned long used_in_bits = 0; unsigned long used_out_bits = 0; if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, scsi0, select)){ used_in_bits |= GROUPA | GROUPB | 0 | 0; used_out_bits |= GROUPA | GROUPB | 0 | 0; } if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, ata, select)) { used_in_bits |= GROUPA | GROUPB | GROUPC | (GROUPD & ~(1<<25|1<<26)); used_out_bits |= GROUPA | GROUPB | GROUPC | GROUPD; } if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, par0, select)) { used_in_bits |= (GROUPA & ~(1<<0)) | 0 | 0 | 0; used_out_bits |= (GROUPA & ~(1<<0)) | 0 | 0 | 0; } if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, ser2, select)) { used_in_bits |= 0 | GROUPB | 0 | 0; used_out_bits |= 0 | GROUPB | 0 | 0; } /* mio same as shared RAM ? */ if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, mio, select)) { used_in_bits |= (GROUPA & ~(1<<0)) | 0 |0 |GROUPD_LOW; used_out_bits |= (GROUPA & ~(1<<0|1<<1|1<<2)) | 0 |0 |GROUPD_LOW; } if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, scsi1, select)) { used_in_bits |= 0 | 0 | GROUPC | GROUPD; used_out_bits |= 0 | 0 | GROUPC | GROUPD; } if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, scsi0w, select)) { used_in_bits |= GROUPA | GROUPB | 0 | (GROUPD_LOW | 1<<24); used_out_bits |= GROUPA | GROUPB | 0 | (GROUPD_LOW | 1<<24 | 1<<25|1<<26); } if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, par1, select)) { used_in_bits |= 0 | 0 | 0 | (GROUPD & ~(1<<24)); used_out_bits |= 0 | 0 | 0 | (GROUPD & ~(1<<24)); } if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, ser3, select)) { used_in_bits |= 0 | 0 | GROUPC | 0; used_out_bits |= 0 | 0 | GROUPC | 0; } /* mio same as shared RAM-W? */ if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, mio_w, select)) { used_in_bits |= (GROUPA & ~(1<<0)) | 0 | 0 |GROUPD_LOW; used_out_bits |= (GROUPA & ~(1<<0|1<<1|1<<2)) | 0 | 0 |GROUPD_LOW; } /* TODO: USB p2, parw, sync ser3? */ /* 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); dir_g_in_bits = ~used_in_bits; dir_g_out_bits = ~used_out_bits; changeable_dir_g = 0x01FFFF01; /* all that can change dir */ 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); 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 /* 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 gpio_init_port_g(); printk(KERN_INFO "ETRAX 100LX GPIO driver v2.5, (c) 2001, 2002 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, SA_SHIRQ | SA_INTERRUPT,"gpio poll", NULL)) { printk(KERN_CRIT "err: timer0 irq for gpio\n"); } if (request_irq(PA_IRQ_NBR, gpio_pa_interrupt, SA_SHIRQ | SA_INTERRUPT,"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 + -