static int __devexit pio2_remove(struct vme_dev *vdev) { int vec; int i; struct pio2_card *card = dev_get_drvdata(&vdev->dev); pio2_gpio_exit(card); for (i = 0; i < 6; i++) { vec = card->irq_vector | PIO2_VECTOR_CNTR[i]; vme_irq_free(vdev, card->irq_level, vec); } for (i = 0; i < 4; i++) { vec = card->irq_vector | PIO2_VECTOR_BANK[i]; vme_irq_free(vdev, card->irq_level, vec); } vec = (card->irq_vector & PIO2_VME_VECTOR_MASK) | PIO2_VME_VECTOR_SPUR; vme_irq_free(vdev, card->irq_level, vec); pio2_reset_card(card); vme_master_set(card->window, 0, 0, 0, VME_A16, 0, VME_D16); vme_master_free(card->window); kfree(card); return 0; }
static int __devinit pio2_probe(struct vme_dev *vdev) { struct pio2_card *card; int retval; int i; u8 reg; int vec; card = kzalloc(sizeof(struct pio2_card), GFP_KERNEL); if (card == NULL) { dev_err(&vdev->dev, "Unable to allocate card structure\n"); retval = -ENOMEM; goto err_struct; } card->id = vdev->num; card->bus = bus[card->id]; card->base = base[card->id]; card->irq_vector = vector[card->id]; card->irq_level = level[card->id] & PIO2_VME_INT_MASK; strncpy(card->variant, variant[card->id], PIO2_VARIANT_LENGTH); card->vdev = vdev; for (i = 0; i < PIO2_VARIANT_LENGTH; i++) { if (isdigit(card->variant[i]) == 0) { dev_err(&card->vdev->dev, "Variant invalid\n"); retval = -EINVAL; goto err_variant; } } /* * Bottom 4 bits of VME interrupt vector used to determine source, * provided vector should only use upper 4 bits. */ if (card->irq_vector & ~PIO2_VME_VECTOR_MASK) { dev_err(&card->vdev->dev, "Invalid VME IRQ Vector, vector must not use lower 4 bits\n"); retval = -EINVAL; goto err_vector; } /* * There is no way to determine the build variant or whether each bank * is input, output or both at run time. The inputs are also inverted * if configured as both. * * We pass in the board variant and use that to determine the * configuration of the banks. */ for (i = 1; i < PIO2_VARIANT_LENGTH; i++) { switch (card->variant[i]) { case '0': card->bank[i-1].config = NOFIT; break; case '1': case '2': case '3': case '4': card->bank[i-1].config = INPUT; break; case '5': card->bank[i-1].config = OUTPUT; break; case '6': case '7': case '8': case '9': card->bank[i-1].config = BOTH; break; } } /* Get a master window and position over regs */ card->window = vme_master_request(vdev, VME_A24, VME_SCT, VME_D16); if (card->window == NULL) { dev_err(&card->vdev->dev, "Unable to assign VME master resource\n"); retval = -EIO; goto err_window; } retval = vme_master_set(card->window, 1, card->base, 0x10000, VME_A24, (VME_SCT | VME_USER | VME_DATA), VME_D16); if (retval) { dev_err(&card->vdev->dev, "Unable to configure VME master resource\n"); goto err_set; } /* * There is also no obvious register which we can probe to determine * whether the provided base is valid. If we can read the "ID Register" * offset and the reset function doesn't error, assume we have a valid * location. */ retval = vme_master_read(card->window, ®, 1, PIO2_REGS_ID); if (retval < 0) { dev_err(&card->vdev->dev, "Unable to read from device\n"); goto err_read; } dev_dbg(&card->vdev->dev, "ID Register:%x\n", reg); /* * Ensure all the I/O is cleared. We can't read back the states, so * this is the only method we have to ensure that the I/O is in a known * state. */ retval = pio2_reset_card(card); if (retval) { dev_err(&card->vdev->dev, "Failed to reset card, is location valid?"); retval = -ENODEV; goto err_reset; } /* Configure VME Interrupts */ reg = card->irq_level; if (pio2_get_led(card)) reg |= PIO2_LED; if (loopback) reg |= PIO2_LOOP; retval = vme_master_write(card->window, ®, 1, PIO2_REGS_CTRL); if (retval < 0) return retval; /* Set VME vector */ retval = vme_master_write(card->window, &card->irq_vector, 1, PIO2_REGS_VME_VECTOR); if (retval < 0) return retval; /* Attach spurious interrupt handler. */ vec = card->irq_vector | PIO2_VME_VECTOR_SPUR; retval = vme_irq_request(vdev, card->irq_level, vec, &pio2_int, (void *)card); if (retval < 0) { dev_err(&card->vdev->dev, "Unable to attach VME interrupt vector0x%x, level 0x%x\n", vec, card->irq_level); goto err_irq; } /* Attach GPIO interrupt handlers. */ for (i = 0; i < 4; i++) { vec = card->irq_vector | PIO2_VECTOR_BANK[i]; retval = vme_irq_request(vdev, card->irq_level, vec, &pio2_int, (void *)card); if (retval < 0) { dev_err(&card->vdev->dev, "Unable to attach VME interrupt vector0x%x, level 0x%x\n", vec, card->irq_level); goto err_gpio_irq; } } /* Attach counter interrupt handlers. */ for (i = 0; i < 6; i++) { vec = card->irq_vector | PIO2_VECTOR_CNTR[i]; retval = vme_irq_request(vdev, card->irq_level, vec, &pio2_int, (void *)card); if (retval < 0) { dev_err(&card->vdev->dev, "Unable to attach VME interrupt vector0x%x, level 0x%x\n", vec, card->irq_level); goto err_cntr_irq; } } /* Register IO */ retval = pio2_gpio_init(card); if (retval < 0) { dev_err(&card->vdev->dev, "Unable to register with GPIO framework\n"); goto err_gpio; } /* Set LED - This also sets interrupt level */ retval = pio2_set_led(card, 0); if (retval < 0) { dev_err(&card->vdev->dev, "Unable to set LED\n"); goto err_led; } dev_set_drvdata(&card->vdev->dev, card); dev_info(&card->vdev->dev, "PIO2 (variant %s) configured at 0x%lx\n", card->variant, card->base); return 0; err_led: pio2_gpio_exit(card); err_gpio: i = 6; err_cntr_irq: while (i > 0) { i--; vec = card->irq_vector | PIO2_VECTOR_CNTR[i]; vme_irq_free(vdev, card->irq_level, vec); } i = 4; err_gpio_irq: while (i > 0) { i--; vec = card->irq_vector | PIO2_VECTOR_BANK[i]; vme_irq_free(vdev, card->irq_level, vec); } vec = (card->irq_vector & PIO2_VME_VECTOR_MASK) | PIO2_VME_VECTOR_SPUR; vme_irq_free(vdev, card->irq_level, vec); err_irq: pio2_reset_card(card); err_reset: err_read: vme_master_set(card->window, 0, 0, 0, VME_A16, 0, VME_D16); err_set: vme_master_free(card->window); err_window: err_vector: err_variant: kfree(card); err_struct: return retval; }
/* * The ioctls provided by the old VME access method (the one at vmelinux.org) * are most certainly wrong as the effectively push the registers layout * through to user space. Given that the VME core can handle multiple bridges, * with different register layouts this is most certainly not the way to go. * * We aren't using the structures defined in the Motorola driver either - these * are also quite low level, however we should use the definitions that have * already been defined. */ static int vme_user_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { struct vme_master master; struct vme_slave slave; unsigned long copied; unsigned int minor = MINOR(inode->i_rdev); int retval; dma_addr_t pci_addr; statistics.ioctls++; switch (type[minor]) { case CONTROL_MINOR: break; case MASTER_MINOR: switch (cmd) { case VME_GET_MASTER: memset(&master, 0, sizeof(struct vme_master)); /* XXX We do not want to push aspace, cycle and width * to userspace as they are */ retval = vme_master_get(image[minor].resource, &(master.enable), &(master.vme_addr), &(master.size), &(master.aspace), &(master.cycle), &(master.dwidth)); copied = copy_to_user((char *)arg, &master, sizeof(struct vme_master)); if (copied != 0) { printk(KERN_WARNING "Partial copy to " "userspace\n"); return -EFAULT; } return retval; break; case VME_SET_MASTER: copied = copy_from_user(&master, (char *)arg, sizeof(master)); if (copied != 0) { printk(KERN_WARNING "Partial copy from " "userspace\n"); return -EFAULT; } /* XXX We do not want to push aspace, cycle and width * to userspace as they are */ return vme_master_set(image[minor].resource, master.enable, master.vme_addr, master.size, master.aspace, master.cycle, master.dwidth); break; } break; case SLAVE_MINOR: switch (cmd) { case VME_GET_SLAVE: memset(&slave, 0, sizeof(struct vme_slave)); /* XXX We do not want to push aspace, cycle and width * to userspace as they are */ retval = vme_slave_get(image[minor].resource, &(slave.enable), &(slave.vme_addr), &(slave.size), &pci_addr, &(slave.aspace), &(slave.cycle)); copied = copy_to_user((char *)arg, &slave, sizeof(struct vme_slave)); if (copied != 0) { printk(KERN_WARNING "Partial copy to " "userspace\n"); return -EFAULT; } return retval; break; case VME_SET_SLAVE: copied = copy_from_user(&slave, (char *)arg, sizeof(slave)); if (copied != 0) { printk(KERN_WARNING "Partial copy from " "userspace\n"); return -EFAULT; } /* XXX We do not want to push aspace, cycle and width * to userspace as they are */ return vme_slave_set(image[minor].resource, slave.enable, slave.vme_addr, slave.size, image[minor].pci_buf, slave.aspace, slave.cycle); break; } break; } return -EINVAL; }