/* Resets the state of the passed instance */ static void reset_mailbox(struct labx_mailbox *mailbox) { /* Disable the instance, and wipe its registers */ disable_mailbox(mailbox); }
/* Function containing the "meat" of the probe mechanism - this is used by * the OpenFirmware probe as well as the standard platform device mechanism. * @param name - Name of the instance * @param pdev - Platform device structure * @param addressRange - Resource describing the hardware's I/O range * @param irq - Resource describing the hardware's IRQ */ static int spi_mailbox_probe(const char *name, struct platform_device *pdev, struct resource *addressRange, struct resource *irq) { struct spi_mailbox *mailbox; int returnValue; /* Create and populate a device structure */ mailbox = (struct spi_mailbox*) kmalloc(sizeof(struct spi_mailbox), GFP_KERNEL); if(!mailbox) return(-ENOMEM); /* Request and map the device's I/O memory region into uncacheable space */ mailbox->physicalAddress = addressRange->start; mailbox->addressRangeSize = ((addressRange->end - addressRange->start) + 1); snprintf(mailbox->name, NAME_MAX_SIZE, "%s", name); mailbox->name[NAME_MAX_SIZE - 1] = '\0'; if(request_mem_region(mailbox->physicalAddress, mailbox->addressRangeSize, mailbox->name) == NULL) { returnValue = -ENOMEM; goto free; } mailbox->virtualAddress = (void*) ioremap_nocache(mailbox->physicalAddress, mailbox->addressRangeSize); if(!mailbox->virtualAddress) { returnValue = -ENOMEM; goto release; } /* Ensure that the mailbox and its interrupts are disabled */ disable_mailbox(mailbox); XIo_Out32(REGISTER_ADDRESS(mailbox, IRQ_MASK_REG), NO_IRQS); /* Retain the IRQ and register our handler, if an IRQ resource was supplied. */ if(irq != NULL) { mailbox->irq = irq->start; returnValue = request_irq(mailbox->irq, &biamp_spi_mailbox_interrupt, IRQF_DISABLED, mailbox->name, mailbox); if (returnValue) { printk(KERN_ERR "%s : Could not allocate Biamp SPI Mailbox interrupt (%d).\n", mailbox->name, mailbox->irq); goto unmap; } } else mailbox->irq = NO_IRQ_SUPPLIED; /* Announce the device */ printk(KERN_INFO "%s: Found Biamp mailbox at 0x%08X, ", mailbox->name, (uint32_t)mailbox->physicalAddress); if(mailbox->irq == NO_IRQ_SUPPLIED) { printk("polled interlocks\n"); } else { printk("IRQ %d\n", mailbox->irq); } /* Initialize other resources */ spin_lock_init(&mailbox->mutex); mailbox->opened = false; /* Provide navigation between the device structures */ platform_set_drvdata(pdev, mailbox); mailbox->pdev = pdev; /* Reset the state of the mailbox */ reset_mailbox(mailbox); /* Add as a character device to make the instance available for use */ cdev_init(&mailbox->cdev, &spi_mailbox_fops); mailbox->cdev.owner = THIS_MODULE; kobject_set_name(&mailbox->cdev.kobj, "%s%d", mailbox->name, mailbox->instanceNumber); mailbox->instanceNumber = instanceCount++; cdev_add(&mailbox->cdev, MKDEV(DRIVER_MAJOR, mailbox->instanceNumber), 1); /* Initialize the waitqueue used for synchronized writes */ init_waitqueue_head(&(mailbox->messageReadQueue)); /* Now that the device is configured, enable interrupts if they are to be used */ if(mailbox->irq != NO_IRQ_SUPPLIED) { XIo_Out32(REGISTER_ADDRESS(mailbox, IRQ_FLAGS_REG), ALL_IRQS); XIo_Out32(REGISTER_ADDRESS(mailbox, IRQ_MASK_REG), IRQ_S2H_MSG_RX); } DBG("Mailbox initialized\n"); /* Return success */ return(0); unmap: iounmap(mailbox->virtualAddress); release: release_mem_region(mailbox->physicalAddress, mailbox->addressRangeSize); free: kfree(mailbox); return(returnValue); }
/* Function containing the "meat" of the probe mechanism - this is used by * the OpenFirmware probe as well as the standard platform device mechanism. * @param name - Name of the instance * @param pdev - Platform device structure * @param addressRange - Resource describing the hardware's I/O range * @param irq - Resource describing the hardware's IRQ */ static int mailbox_probe(const char *name, struct platform_device *pdev, struct resource *addressRange, struct resource *irq) { struct labx_mailbox *mailbox; int returnValue; int i; /* Create and populate a device structure */ mailbox = (struct labx_mailbox*) kmalloc(sizeof(struct labx_mailbox), GFP_KERNEL); if(!mailbox) return(-ENOMEM); /* Request and map the device's I/O memory region into uncacheable space */ mailbox->physicalAddress = addressRange->start; mailbox->addressRangeSize = ((addressRange->end - addressRange->start) + 1); snprintf(mailbox->name, NAME_MAX_SIZE, "%s", name); mailbox->name[NAME_MAX_SIZE - 1] = '\0'; if(request_mem_region(mailbox->physicalAddress, mailbox->addressRangeSize, mailbox->name) == NULL) { returnValue = -ENOMEM; goto free; } mailbox->virtualAddress = (void*) ioremap_nocache(mailbox->physicalAddress, mailbox->addressRangeSize); if(!mailbox->virtualAddress) { returnValue = -ENOMEM; goto release; } /* Ensure that the mailbox and its interrupts are disabled */ disable_mailbox(mailbox); XIo_Out32(REGISTER_ADDRESS(mailbox, SUPRV_IRQ_MASK_REG), NO_IRQS); /* Clear the message ready flag for the first time */ mailbox->messageReadyFlag = MESSAGE_NOT_READY; /* Retain the IRQ and register our handler, if an IRQ resource was supplied. */ if(irq != NULL) { mailbox->irq = irq->start; returnValue = request_irq(mailbox->irq, &mailbox_interrupt, IRQF_DISABLED, mailbox->name, mailbox); if (returnValue) { printk(KERN_ERR "%s : Could not allocate Mailbox interrupt (%d).\n", mailbox->name, mailbox->irq); goto unmap; } } else mailbox->irq = NO_IRQ_SUPPLIED; /* Announce the device */ printk(KERN_INFO "%s: Found mailbox at 0x%08X, ", mailbox->name, (uint32_t)mailbox->physicalAddress); if(mailbox->irq == NO_IRQ_SUPPLIED) { printk("polled interlocks\n"); } else { printk("IRQ %d\n", mailbox->irq); } /* Initialize other resources */ spin_lock_init(&mailbox->mutex); mailbox->opened = true; /* Provide navigation between the device structures */ platform_set_drvdata(pdev, mailbox); mailbox->pdev = pdev; /* Reset the state of the mailbox */ reset_mailbox(mailbox); /* Initialize the waitqueue used for synchronized writes */ init_waitqueue_head(&(mailbox->messageReadQueue)); /* Initialize the netlink state and start the thread */ mailbox->netlinkSequence = 0; mailbox->netlinkTask = kthread_run(netlink_thread, (void*)mailbox, "%s:netlink", mailbox->name); if (IS_ERR(mailbox->netlinkTask)) { printk(KERN_ERR "Mailbox netlink task creation failed.\n"); returnValue = -EIO; goto free; } /* Now that the device is configured, enable interrupts if they are to be used */ if(mailbox->irq != NO_IRQ_SUPPLIED) { XIo_Out32(REGISTER_ADDRESS(mailbox, SUPRV_IRQ_MASK_REG), ALL_IRQS); XIo_Out32(REGISTER_ADDRESS(mailbox, SUPRV_IRQ_FLAGS_REG), ALL_IRQS); } // Add the mailbox instance to the list of current devices for(i=0;i<MAX_MAILBOX_DEVICES;i++) { if(NULL == labx_mailboxes[i]) { labx_mailboxes[i] = mailbox; printk("Adding mailbox: %s\n", labx_mailboxes[i]->name); break; } } DBG("Mailbox initialized\n"); /* Return success */ return(0); unmap: iounmap(mailbox->virtualAddress); release: release_mem_region(mailbox->physicalAddress, mailbox->addressRangeSize); free: kfree(mailbox); return(returnValue); }
/* I/O control operations for the driver */ static int spi_mailbox_ioctl(struct inode *inode, struct file *flip, unsigned int command, unsigned long arg) { // Switch on the request int returnValue = 0; struct spi_mailbox *mailbox = (struct spi_mailbox*)flip->private_data; switch(command) { case IOC_START_MBOX: enable_mailbox(mailbox); break; case IOC_STOP_MBOX: disable_mailbox(mailbox); break; case IOC_READ_MBOX: { MessageData userMessage; MessageData localMessage; /* Copy into our local descriptor, then obtain buffer pointer from userland */ if(copy_from_user(&userMessage, (void __user*)arg, sizeof(userMessage)) != 0) { return(-EFAULT); } returnValue = await_message_ready(mailbox); if(returnValue < 0) { return(returnValue); } localMessage.length = read_mailbox_message(mailbox, messageBuffer); if(copy_to_user((void __user*)userMessage.messageContent, messageBuffer, (min(userMessage.length, localMessage.length))) != 0) { return(-EFAULT); } if(copy_to_user((void __user*)arg, &localMessage.length, sizeof(localMessage.length)) != 0 ) { return(-EFAULT); } } break; case IOC_WRITE_RESP: { MessageData userMessage; /* Copy into our local descriptor, then obtain buffer pointer from userland */ if(copy_from_user(&userMessage, (void __user*)arg, sizeof(userMessage)) != 0) { return(-EFAULT); } if(userMessage.length > MAX_MESSAGE_DATA) { return(-EINVAL); } if(copy_from_user(messageBuffer, (void __user*)userMessage.messageContent, userMessage.length) != 0) { return(-EFAULT); } userMessage.messageContent = messageBuffer; send_message_response(mailbox, &userMessage); } break; case IOC_SET_SPI_IRQ_FLAGS: { set_spi_irq_flags(mailbox, ((uint8_t)arg)); } break; case IOC_CLEAR_SPI_IRQ_FLAGS: { clear_spi_irq_flags(mailbox, ((uint8_t)arg)); } case IOC_READ_SPI_IRQ_FLAGS: { uint32_t returnValue = read_spi_irq_flags(mailbox); if(copy_to_user((void __user*)arg, &returnValue, sizeof(returnValue)) != 0 ) { return(-EFAULT); } } case IOC_SET_SPI_IRQ_MASK: { set_spi_irq_mask(mailbox, ((uint8_t)arg)); } case IOC_READ_SPI_IRQ_MASK: { uint32_t returnValue = read_spi_irq_mask(mailbox); if(copy_to_user((void __user*)arg, &returnValue, sizeof(returnValue)) != 0 ) { return(-EFAULT); } } default: /* We don't recognize this command. */ returnValue = -EINVAL; } /* Return an error code appropriate to the command */ return(returnValue); }